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

Reply via email to