Re: I wish to register a complaint! (about BSD make)

Ingo Schwarze <[email protected]>
Newsgroups gmane.comp.printing.groff.general,gmane.org.unix-heritage.general
Message-ID <[email protected]>
Hello Branden,

i have a hard time understanding what your actual problem is,
but i'll try anyway (even though im not an expert in make(1)).

In case i'm missing your point - which seems possible, because you did
not adhere to normal bug reporting methods, which would be saying:
 * what exactly you are doing (which exact commands on which exact system)
 * what exactly you expect (regarding behaviour and output)
 * what exactly you observe instead (exact behaviour and output)
please clarify your question.

G. Branden Robinson wrote on Wed, Jan 21, 2026 at 02:30:04AM -0600:

> [CCing Warren due to my usual GMail->TUHS interference problems]
> 
> ...that's how my Hungarian phrasebook tells me to word it.
> 
> Since this issue burns me every time I do a groff release (formerly as
> deputy maintainer, now as a full bird), I wish to risk offending BSD
> partisans by griping about their make.
> 
> Apparently BSD make tracks file identity in its dependency graph by
> using...strings.
> 
> That's it.  Just strings.

Not sure that is entirely accurate (again, i'm not a make(1) expert),
but yes, according to my understanding, string comparison is the
main *basis* for resolving targets.  It seems there are refining
aspects in some cases; i don't know the exact details and did not
investigate at this time.

> So "foo" != "./foo" != "./././foo" and so on.  To BSD make, these are
> different things, even though by design they must be file specifications
> ("pathnames") _on the file system_, and on every Unix, they _are the
> same_.
> 
> So you get spurious build failures when, say, macro contents naming a
> target disagree with target names in rules in ways that don't make sense
> when you know what's going on (outside of make implementation details).
> 
> Let me offer some simplified examples.
> 
> FOO = ./foo
> 
> $(FOO): bar
> 	frob < bar > $(FOO)

Trying that on OpenBSD-current:

   $ make
  make: don't know how to make bar (prerequisite of: ./foo)
  Stop in /home/schwarze/Test/make/branden_dot

No surprise so far, so let's provide the input file:

  $ echo Hello world! > bar
  $ make
 frob < bar > ./foo
 /bin/sh: frob: not found
 *** Error 127 in /home/schwarze/Test/make/branden_dot (Makefile:4 './foo')

  $ make foo
 frob < bar > ./foo
 /bin/sh: frob: not found
 *** Error 127 in /home/schwarze/Test/make/branden_dot (Makefile:4 './foo')

  $ make ./././foo
 `./././foo' is up to date.

  $ rm -f foo
  $ make ./././foo
 frob < bar > ./foo
 /bin/sh: frob: not found
 *** Error 127 in /home/schwarze/Test/make/branden_dot (Makefile:4 './foo')

Except that frob(1) is not a real command, your example works as
expected for me.  In particular, the ./foo: rule is found even when i ask
for "foo" or "./././foo".

What exactly am i supposed to type to reproduce your problem, and how
exactly does your problem present?

> If the "./" prefix seems like a silly thing to do, consider that it may
> itself be the result of macro expansion.
> 
> FOO = $(BAZ)/foo
> 
> all: $(FOO)
> 
> $(FOO): bar
> 	frob < bar > $(FOO)
> 
> $ make BAZ=/look/over/there

  $ rm -f bar foo
  $ make BAZ=/look/over/there
 make: don't know how to make bar (prerequisite of: /look/over/there/foo)
 Stop in /home/schwarze/Test/make/branden_dot

  $ echo Hello world! > bar
  $ make BAZ=/look/over/there
 frob < bar > /look/over/there/foo
 /bin/sh: cannot create /look/over/there/foo: No such file or directory
 *** Error 1 in /home/schwarze/Test/make/branden_dot
                (Makefile:6 '/look/over/there/foo')

Again, except that there is nothing "over/there", the command you propose
works as expected for me.

What exactly do you think is wrong make running the command

  frob < bar > /look/over/there/foo

and what do you want make(1) to do instead?

> (Yes, a real Makefile for BSD make would probably not be written this
> way.[1])

If you somehow make sure that BAZ is not empty, it even might be OK,
but that discussion seems unrelated.

> This design is a landmine with a tripwire in front of it because in the
> shell command "recipes" of a makefile rule, you once again get the
> semantics you expect as a Unix file system user, because the _shell_
> resolves whatever $(FOO) expands to.  So it is not fooled by "./" or
> "./././." or any other variation on that theme.
> 
> I find this design choice even more confusing because Unix has had hard
> links since day one, or just about.  The _name_ of a file as a string or
> even a directory entry is not its uniquely identifying characteristic
> (ignoring content).
> 
> Why does the BSD make dependency graph not track the device and inode
> pair instead?

I'm sorry, but i'm not following.  The point of a dependency rule
is to provide a recipe for how to create a target that does not yet
exist.  What exactly do you suggest make(1) should do?  Identity a
target by its inode number?  A target that does not yet exist does
not have an inode number because it does not exist.  A target that
already exists no longer needs a rule.  So what exactly do you
propose to use which inode number for?  I feel confused.

> And won't this string-based approach fail to detect file identity in the
> case of symbolic links as well?

Not sure what exactly you are asking here.
I'm trying to come up with a complete set of cases you might possibly mean:

The target exists and is a valid symbolic link:

  $ echo Hello world! > bar
  $ echo old news > old
  $ ln -s old foo
  $ make BAZ=/look/over/there
 frob < bar > /look/over/there/foo
 /bin/sh: cannot create /look/over/there/foo: No such file or directory
 *** Error 1 in /home/schwarze/Test/make/branden_dot
                (Makefile:6 '/look/over/there/foo')

The target exists but points into the woods:

  $ rm -f foo
  $ ln -s /invalid foo
  $ make BAZ=/look/over/there
 frob < bar > /look/over/there/foo
 /bin/sh: cannot create /look/over/there/foo: No such file or directory
 *** Error 1 in /home/schwarze/Test/make/branden_dot
             (Makefile:6 '/look/over/there/foo')

The target does not exist, but is the target of a symbolic link:

  $ rm -f foo old
  $ make BAZ=/look/over/there
 frob < bar > /look/over/there/foo
 /bin/sh: cannot create /look/over/there/foo: No such file or directory
 *** Error 1 in /home/schwarze/Test/make/branden_dot
                (Makefile:6 '/look/over/there/foo')

The behaviour makes sense to me in all three cases.

Can you explain more clearly which exact scenario you worry about?

> Didn't BSD _invent_ symbolic links?

At least that's what our manual page claims, yes:

   $ man symlink | grep -A1 ^H
  HISTORY
     The symlink() system call first appeared in 4.1cBSD.  The symlinkat()

> Can someone make this make sense?

So far, unfortunately, no, i failed to make sense of your question...

Yours,
  Ingo
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.