On Sat, Jun 1, 2024 at 4:21 PM Jeenu Viswambharan <jee...@gmail.com> wrote:
>
> There's little point in my repeating the question here. As suggested
> by an answer to the post, the issue I reported is solved if I add a
> semicolon to a rule.
>
> But I further wanted to understand _why_ the solution works. Somehow,
> make behaves differently when a semicolon is added vs. when it's
> absent.
>

When the semicolon is present, the rule for your js/*.js targets
includes a recipe. This changes the target from what Make might call a
no-recipe target into an empty-recipe target.

The high-level summary is that Make does not update the mtime for
no-recipe targets after rebuilding them. Adding the empty recipe
ensures a new mtime is recorded after the target is rebuilt, even
though no additional work is performed.

This behavior happens because a target with no recipe is a file
external to the build that Make cannot create or update. Make can
assume it existed prior to the build starting and that it will not be
changed by any action during the build. If a no-recipe target doesn't
exist when it's needed as a prerequisite, the build will fail. When
Make is asked to remake a no-recipe target and the file exists, it
completes successfully without running a recipe or updating the
target's mtime.

An empty-recipe target is something that Make knows how to create or
update, and is expected to be changed by actions during the build.
When Make is asked to remake an empty-recipe target, it still runs the
empty recipe even though there are no commands to execute.

The internal value of a file's mtime is updated in at least two cases:
1. When the target is first evaluated to determine whether something
needs to be rebuilt
2. When a recipe runs successfully and would result in file modifications

I believe what's happening here is that the mtime for js/a.js is being
evaluated and recorded prior to remaking ".compiled". It's the first
prerequisite in the depth-first traversal of the dependency graph with
"bundle" at the root.

Once ".compiled" is rebuilt, Make recognizes the need to rebuild
js/a.js, but the lack of a recipe ensures Make will not update its
internal mtime for this file. When pkg/a.js and js/a.js are compared,
the mtime value observed before ".compiled" was rebuilt is used.

Now that pkg/a.js has been rebuilt, pkg/b.js will be evaluated for the
first time (depth-first). It is also a no-recipe target, but its mtime
has not previously been evaluated, and so the initial evaluation
receives the newer value. As a result it is packaged and bundled.

Could this be considered a Make bug? Maybe.

The three work-arounds are: an empty recipe implicit rule, the new
grouped targets implicit rule feature, or pattern rules, which work
differently. Pattern rules may be what you want, unfortunately they're
often not flexible enough to match all target/prerequisite file paths.

I hope that this helps (and is correct).
Mike

Reply via email to