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