Some illustrations to the different scenarios:
# Initial run, graph complete, node statuses according to the file system
$ make -f -
.POSIX:
t1: t2; > $@
t2: t3; set -C; > $@
t3:; > $@
> t3
set -C; > t2
> t1
$ ls -l
total 0
-rw-r--r-- 1 ... 0 Jun 25 13:48 t1
-rw-r--r-- 1 ... 0 Jun 25 13:48 t2
-rw-r--r-- 1 ... 0 Jun 25 13:48 t3
$ make -d -n -f - | make2graph -d color=red
.POSIX:
t1: t2; > $@
t2: t3; set -C; > $@
t3:; > $@
digraph G {
n2 [label="t1"];
n3 [label="t2"];
n4 [label="t3"];
n3 -> n2 ;
n4 -> n3 ;
}
# Independent prerequisite 't3' updated, graph complete, node statuses
according to the file system
$ touch t3
$ make -f -
.POSIX:
t1: t2; > $@
t2: t3; set -C; > $@
t3:; > $@
set -C; > t2
/bin/sh: line 1: t2: cannot overwrite existing file
$ make -d -n -f - | make2graph -d color=red
.POSIX:
t1: t2; > $@
t2: t3; set -C; > $@
t3:; > $@
digraph G {
n2 [label="t1", color=red];
n3 [label="t2", color=red];
n4 [label="t3"];
n3 -> n2 ;
n4 -> n3 ;
}
# Forcing status of 't2' with option '-o', node statuses as aimed but graph
incomplete
$ make -f - -o t2
.POSIX:
t1: t2; > $@
t2: t3; set -C; > $@
t3:; > $@
make: 't1' is up to date.
$ make -d -n -f - -o t2 | make2graph -d color=red
.POSIX:
t1: t2; > $@
t2: t3; set -C; > $@
t3:; > $@
digraph G {
n2 [label="t1"];
n3 [label="t2"];
n3 -> n2 ;
}
# Forcing status of 't2' with option '-W', re-make of 't3' correct, node
statuses not as aimed but graph complete
$ make -f - -W t2
.POSIX:
t1: t2; > $@
t2: t3; set -C; > $@
t3:; > $@
> t1
$ ls -l
total 0
-rw-r--r-- 1 ... 0 Jun 25 13:57 t1
-rw-r--r-- 1 ... 0 Jun 25 13:48 t2
-rw-r--r-- 1 ... 0 Jun 25 13:53 t3
$ make -d -n -f - -W t2 | make2graph -d color=red
.POSIX:
t1: t2; > $@
t2: t3; set -C; > $@
t3:; > $@
digraph G {
n2 [label="t1", color=red];
n3 [label="t2"];
n4 [label="t3"];
n3 -> n2 ;
n4 -> n3 ;
}
# Forcing status of 't2' with option '-W', re-make of 't3' unnecessary, node
statuses not as aimed but graph complete
$ make -f - -W t2
.POSIX:
t1: t2; > $@
t2: t3; set -C; > $@
t3:; > $@
> t1
$ ls -l
total 0
-rw-r--r-- 1 ... 0 Jun 25 13:58 t1
-rw-r--r-- 1 ... 0 Jun 25 13:48 t2
-rw-r--r-- 1 ... 0 Jun 25 13:53 t3
$ make -d -n -f - -W t2 | make2graph -d color=red
.POSIX:
t1: t2; > $@
t2: t3; set -C; > $@
t3:; > $@
digraph G {
n2 [label="t1", color=red];
n3 [label="t2"];
n4 [label="t3"];
n3 -> n2 ;
n4 -> n3 ;
}
# Forcing status of 't2' with the theoretical option
'--just-enough-up-to-date', node statuses as aimed, graph complete
$ cp -p t2 t2.bak
$ ls -l
total 0
-rw-r--r-- 1 ... 0 Jun 25 13:58 t1
-rw-r--r-- 1 ... 0 Jun 25 13:48 t2
-rw-r--r-- 1 ... 0 Jun 25 13:48 t2.bak
-rw-r--r-- 1 ... 0 Jun 25 13:53 t3
$ touch -r t3 t2 # = make ... --just-enough-up-to-date t2
$ ls -l
total 0
-rw-r--r-- 1 ... 0 Jun 25 13:58 t1
-rw-r--r-- 1 ... 0 Jun 25 13:53 t2
-rw-r--r-- 1 ... 0 Jun 25 13:48 t2.bak
-rw-r--r-- 1 ... 0 Jun 25 13:53 t3
$ make -f - # = make ... --just-enough-up-to-date t2
.POSIX:
t1: t2; > $@
t2: t3; set -C; > $@
t3:; > $@
make: 't1' is up to date.
$ make -d -n -f - | make2graph -d color=red # = make ...
--just-enough-up-to-date t2
.POSIX:
t1: t2; > $@
t2: t3; set -C; > $@
t3:; > $@
digraph G {
n2 [label="t1"];
n3 [label="t2"];
n4 [label="t3"];
n3 -> n2 ;
n4 -> n3 ;
}
On Tuesday, June 23rd, 2026 at 7:36 PM, WaitronCharm <[email protected]>
wrote:
> Hello everyone
>
> I've spent some time digging into 'src/remake.c' to better understand how
> this behaves and wanted to share my findings, along with a potential logic
> change I am considering.
>
> To clarify the exact case driving this:
>
> 1. I have a specific target that can no longer be remade because it relies on
> a web resource that is not available anymore.
> 2. However, it has other local prerequisites that still get updated
> regularly. These updates would normally trigger a remake of this target and
> would not succeed in any meaningful way.
> 3. I would need to keep these prerequisites in a dependency graph (which I
> capture via 'makefile2graph').
> 4. Other higher targets rely on this outdated but locally still fine target.
> They can be updated at any time, but I don't want them constantly and
> endlessly rebuilding on every single make run.
>
> As noted before, using '-o' keeps the status clean but prunes the
> prerequisites from the graph entirely. Conversely, '-W' (which I haven't
> tried but assume) would preserve the graph but constantly force higher
> rebuilds. I also cannot simply touch the target with a custom timestamp
> because its real modification date must remain strictly bound to its last
> effective, successful remake.
>
> This led me to the conclusion -- which might be wrong -- that make could
> internally fake this target's timestamp to be 'just good enough' (i.e.,
> exactly equal to or one tick newer than its latest prerequisite).
>
> Doing so would:
>
> - Keep its prerequisites alive in the traversal graph.
> - Report a correctly faked 'up-to-date' status in the visual log.
> - Avoid triggering cascading rebuilds for higher targets.
>
> I am considering hacking together a local patch to introduce a new
> command-line option -- something along the lines of
> '--not-too-old-not-too-young-but-up-to-date=TARGET' -- to enable this
> behavior for specific targets. The logic would essentially intercept the
> target, declare it up-to-date so no recipe executes and dynamically set the
> file's internal modification date to match the highest timestamp of its
> prerequisites so subsequent processing runs smoothly.
>
> Could anyone familiar with 'src/remake.c' point me toward the correct place
> where these specific date comparisons and status assignments happen? Thanks.
>
> On Tuesday, May 26th, 2026 at 3:50 PM, WaitronCharm <[email protected]>
> wrote:
>
> > To clarify further, simply removing -o and using -n (--just-print) instead
> > is also not a viable workaround. While -n would traverse the prerequisites
> > and make the graph structurally complete, the reported target build
> > statuses would be wrong for this use case. The debug output would show
> > targets as 'needing to be remade' rather than treating them as up-to-date
> > via the -o override.
> >
> > There seems to be no combination of current options that yields both a
> > fully traversed dependency graph and the correct target statuses in the
> > debug logs.
> >
> > On Tuesday, May 26th, 2026 at 3:30 PM, WaitronCharm
> > <[email protected]> wrote:
> >
> > > Hello,
> > >
> > > I am working on reconstructing the visual dependency graph of a build
> > > using GNU Make's --debug output. However, I have run into a limitation
> > > when combining debugging with the -o (--old-file) option.
> > >
> > > When a target is marked as old via -o, GNU Make seems to immediately
> > > optimize away the traversal of that target's prerequisites. Because these
> > > prerequisites are not traversed, they are completely omitted from the
> > > debug log. This leaves the reconstructed visual graph incomplete.
> > >
> > > From an execution standpoint, optimizing out these checks makes perfect
> > > sense. However, for tooling and visualization, it creates a blind spot.
> > > Could the final decision to consider a target up-to-date (based on -o) be
> > > deferred until after its prerequisites have been traversed in the graph?
> > >
> > > I tried using the -n (--just-print / dry-run) option as an alternative to
> > > see the full graph without executing commands, but it has a different
> > > limitation: rules that update included makefiles are still actively
> > > executed under -n.
> > >
> > > This leaves a gap in the current options. There appears to be no
> > > combination of flags that allows a user to safely skip a target's
> > > execution while still fully discovering and dumping its underlying
> > > dependency tree in the debug output.
> > >
> > > Here is a minimal example demonstrating the behavior:
> > >
> > > $ make --debug -f - -o t1
> > > GNU Make 4.4.1
> > > Built for x86_64-pc-cygwin
> > > Copyright (C) 1988-2023 Free Software Foundation, Inc.
> > > License GPLv3+: GNU GPL version 3 or later
> > > <https://gnu.org/licenses/gpl.html>
> > > This is free software: you are free to change and redistribute it.
> > > There is NO WARRANTY, to the extent permitted by law.
> > > t1: t2; echo $@
> > > t2:; echo $@
> > > Reading makefiles...
> > > Updating makefiles....
> > > Updating goal targets....
> > > make: 't1' is up to date.
> > >
> > > Notice that t2 is never mentioned or evaluated in the debug output above.
> > > Compare this to a normal run where the full relationship is explicitly
> > > printed:
> > >
> > > $ make --debug -f -
> > > GNU Make 4.4.1
> > > Built for x86_64-pc-cygwin
> > > Copyright (C) 1988-2023 Free Software Foundation, Inc.
> > > License GPLv3+: GNU GPL version 3 or later
> > > <https://gnu.org/licenses/gpl.html>
> > > This is free software: you are free to change and redistribute it.
> > > There is NO WARRANTY, to the extent permitted by law.
> > > t1: t2; echo $@
> > > t2:; echo $@
> > > Reading makefiles...
> > > Updating makefiles....
> > > Updating goal targets....
> > > File 't1' does not exist.
> > > File 't2' does not exist.
> > > Must remake target 't2'.
> > > echo t2
> > > t2
> > > Successfully remade target file 't2'.
> > > Must remake target 't1'.
> > > echo t1
> > > t1
> > > Successfully remade target file 't1'.
> > >
> > > Is this immediate pruning of the graph an intentional design constraint
> > > for performance, or would the maintainers consider a patch/option that
> > > forces prerequisite traversal even when a target is short-circuited by -o?
> > >
> > > I would love to hear your thoughts on whether this use case is currently
> > > supported through other means, or if a feature request is appropriate
> > > here.
> > >
> > >