RFR: 8348190: Framework for tracing makefile inclusion and parsing
Magnus Ihse Bursie
ihse at openjdk.org
Fri Jan 31 18:16:45 UTC 2025
A major problem when debugging the build system is figuring out what makefiles are evaluated, and in which order.
Our makefiles comes in three flavors, which I have termed "files", "includes" and "snippets", for the purpose of being able to reason about them. The difference is this:
* "files" (that is, "top-level files") are run directly by calling `make -f FooFile.gmk`
* "includes" are included by other makesfiles using `include FooInclude.gmk`
* "snippets" are automatically included by the framework, based on special naming rules
This patch adds a systematic structure to files, includes and snippets, which allows for a logging framework to be attached.
The log will look something like this:
:Enter Main.gmk
: Enter common/MakeBase.gmk [include]
: Enter common/Utils.gmk [include]
: Leave common/Utils.gmk
: Enter common/FileUtils.gmk [include]
: Leave common/FileUtils.gmk
: Leave common/MakeBase.gmk
: Enter MainSupport.gmk [include]
: Leave MainSupport.gmk
: Enter common/FindTests.gmk [include]
: Leave common/FindTests.gmk
: Enter common/Modules.gmk [include]
: Leave common/Modules.gmk
:Leave Main.gmk [now executing rules]
Furthermore, this patch achieves a secondary benefit, which is arguably just as important, and that is simplifying the boilerplate in makefiles. I am not saying that it offers strictly *less* boilerplate -- in some cases, it is actually more than what we have right now. But it does offer *systematic* boilerplate. You only have to include the proper start/end makefile and then you're good to go. These start/end files implements the best practices that have evolved over the year (like include guards for includes), and standards we employ in the JDK build system (like always starting with a `default: all` rule). It became painfully obvious when implementing this PR that our adherence to these best practices are sketchy, at best. From now on, it will not be possible to miss declaring best practice boilerplate, since it is all concentrated in a single set of files.
While updating all makefiles to use these new systematic start/end includes, I took the opportunity to clean up the style used and make it more consistent. So now all makefiles follows this pattern (as far as applicable):
include Make[File/Include]Start.gmk
################################################################################
# High-level description of this file (if present).
################################################################################
include Foo.gmk # includes ordered alphabetically
include Zoo.gmk
... actual code
$(TARGETS) += my-targets ...
################################################################################
include Make[File/Include]End.gmk
This new framework also allows for systematic inclusion of custom makefile snippets, which were previously not possible. A side effect of this is that some custom include file names have changed, and the actual place where it is included has changed to the very top or bottom, which was not always the case. This change in placement made it necessary to adapt some code so that the hooks could still work. I have updated the corresponding Oracle closed source, but if there are some other company or organization that uses custom makefile snippets, they might have to adapt your custom sources. I will announce this in a separate mail to `build-dev` before integration, since it is a potentially breaking change.
I have been wrestling with this patch from time to time, over a long period of time. I have kept my half-baked patch mostly up to date with main, and used this branch to debug tricky makefile include issues. There were several tricky makefile quirks I needed to come up with to get this to work. I tried several prototypes along the way, but they all failed by being too complicated, or not just "feeling" good to use. This is, I believe, as good as it can possibly get, given the limitations inherent in Make. I'm happy to answer questions or provide rationale for any specific parts of the implementation.
To facilitate reviewing, I have split this PR into two separate commits, which is basically "the tricky parts" and "the trivial parts". The first commit introduces the new framework, and makes necessary changes to be able to employ it. The second commit goes through each and every makefile, and updates them to use the new framework (and to adhere to the style standard).
-------------
Commit messages:
- Apply framework changes to makefiles
- Create new framework for logging flow
Changes: https://git.openjdk.org/jdk/pull/23399/files
Webrev: https://webrevs.openjdk.org/?repo=jdk&pr=23399&range=00
Issue: https://bugs.openjdk.org/browse/JDK-8348190
Stats: 2702 lines in 273 files changed: 1647 ins; 485 del; 570 mod
Patch: https://git.openjdk.org/jdk/pull/23399.diff
Fetch: git fetch https://git.openjdk.org/jdk.git pull/23399/head:pull/23399
PR: https://git.openjdk.org/jdk/pull/23399
More information about the build-dev
mailing list