Subject: [bug#60535] [PATCH] depend2: switch echo|sed to automatic vars

From: Nick Bowler <[email-masked]>
Date: Wed, 4 Jan 2023 21:10:55 -0500

On 2023-01-03, Mike Frysinger <[email-masked]> wrote:
> The echo|sed is used to split the dirname & filename so it can insert
> $(DEPDIR) in the middle, and then chop the trailing object suffix.  In
> the generic case, %OBJ% is $@, so we can leverage the POSIX vars $(@D)
> and $(@F) to do the pathname splitting and insert $(DEPDIR) in between.
> For chopping the object suffix, we can use the $(xxx:...=...) syntax.
> This avoids invoking sed completely.

I think this is a generally good idea.

> These dirname/filename vars have been in POSIX since at least 2004.

Except for one minor detail, $(@F) and $(@D) are highly portable.  I
expect they were in the very first POSIX.2 specs as they predate the
earliest standards; I believe they first appeared in UNIX System V (ca.
1983) and were later added to BSD in 4.3BSD-Reno (ca. 1990).

The only potential problem I am aware of with these features is one
implementation (dmake) which does not follow the POSIX rule of excluding
the trailing slash from the directory part, and (a bit more troublesome)
will expand $(@D) to the empty string if the directory part is empty,
instead of . as required by POSIX.  Usually this is not a major issue,
as writing something like ./$(@D)/$(@F) is usually sufficient to avoid
any problems with empty $(@D).

It's worth noting that $(?F), $(?D), $(%F) and $(%D) were never added
to BSD and as a result are missing from many BSD derivatives.  However,
these variables are of limited utility and are unlikely to be missed.

The $(x:a=b) suffix substitutions are of identical vintage, and as
far as I know every make supporting file/directory variables also,
in principle, supports suffix substitution too.  However, there are
many bugs in real-world implementations of this feature.

Of particular relevance to this patch: older versions of NetBSD make,
while they do support suffix substitution, fail to correctly parse
$(@F:.o=):

  % cat >Makefile <<'EOF'
foo/bar.o:
	echo $(@F:.o=)
EOF
  % make
  echo bar.o.o=)
  Syntax error: ")" unexpected
  *** Error code 2

NetBSD make was widely adopted by other systems, so this particular
issue may actually be found elsewhere.  This bug was present as recently
as NetBSD 7.2 (ca. 2018).  Furthermore, old versions of FreeBSD (before
they ditched their own make in favour of NetBSD's version) also fail to
correctly handle $(@F:.o=), but in a different way.

The good news is that these problems can be worked around pretty easily
simply by using another variable:

  % cat >Makefile <<'EOF'
at_f = $(@F)
foo/bar.o:
	echo $(at_f:.o=)
EOF
  % make
  echo bar
  bar

But this workaround trips over a different suffix substitution bug on
ULTRIX, which would otherwise work just fine with plain $(@F:.o=).
Maybe ULTRIX is not worth worrying about these days, but nevertheless,
it is not too hard to detect and work around both problems in pure
shell, which should still work to avoid the additional forks/sed,
maybe something like:

  % cat >Makefile <<'EOF'
at_f = $(@F)
foo/bar.o:
	a='$(@F:.o=)' b='$(at_f:.o=)'; test x"$$a.o" = x"$(@F)" || a=$$b;\
	  echo $$a
EOF

Cheers,
  Nick