-
Notifications
You must be signed in to change notification settings - Fork 1.6k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Improve incremental build: make ninja handle dynamic outputs (continuation) #2366
base: master
Are you sure you want to change the base?
Improve incremental build: make ninja handle dynamic outputs (continuation) #2366
Conversation
I think your use case is already supported by dyndeps (which can add dynamic outputs, unlike depfiles), so I am unsure there is a need for such a change now. Maybe @jhasse has a different opinion though? |
Hi :) Sorry, I don't have enough free time to take a look at this atm. |
@digit-google The difference in use case between Similarly, this In my case |
Thanks for the clarification, I agree that dyndeps are a pain to generate (and the Ninja-specific format doesn´t help). I would favor being able to support specifying additional outputs in the depfiles directly, to keep everything simpler. I don't have the time to look at that until next year, but I remember that currently the depfile loader just verifies that all outputs listed in the file are also listed as edge outputs in the manifest, and will complain otherwise. I hope changing this will not break other use cases though, or will not require Ninja-specific changes to the depfile format. The depslog should be relatively easy to adapt though. I probably won't have the time to look at your change until next year. I recommend you refactor / rebase your commits into a smaller stack, getting rid of the random fixes, to make it easier to review. |
While I see the value in having ninja know about dynamic outputs, how are they intended to be used during a build? If the use case is just to allow There are then followups for things like installing generated headers and such that have no concrete representation in I think a section on limitations might be warranted? Other things that come to mind that may warrant a test case (I only skimmed; maybe these are covered):
|
6a51ecf
to
f4e6b51
Compare
@mathstuf I'm not sure that systems such as CMake will utilize this atm, but for other users it may be a good feature to improve the clean command. To try to answer some of your questions here:
Does this sound reasonable? |
Ok, I just wanted to be clear on the intended scope. It seems to be mostly about |
src/dynout_parser.cc
Outdated
} | ||
|
||
} | ||
bool DynoutParser::Parse(State* state, DiskInterface* disk_interface, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: Call this function "Load()" since it doesn't only parse, but also modifies the graph non-trivially.
@@ -147,7 +147,7 @@ bool DependencyScan::RecomputeNodeDirty(Node* node, std::vector<Node*>* stack, | |||
if (!edge->deps_loaded_) { | |||
// This is our first encounter with this edge. Load discovered deps. | |||
edge->deps_loaded_ = true; | |||
if (!dep_loader_.LoadDeps(edge, err)) { | |||
if (!dep_loader_.LoadDeps(edge, err) || !dep_loader_.LoadImplicitOutputs(edge, err)) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You are modifying the logic of graph.cc in non-trivial way, please provide unit-tests that verify the new behavior in the same commit to make it easier to verify that everything works correctly.
Consider splitting this commit into two: one that introduces the Dynout parser/loader + its own unit-tests (without modifying the graph), then another one that uses the new information to modify the graph + appropriate unit-tests.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've added tests in graph_test.cc and a few more build and clean tests to verify the new behaviour. Also separated the dynout parser into its own commit as you suggested.
@@ -35,7 +35,7 @@ using namespace std; | |||
// The version is stored as 4 bytes after the signature and also serves as a | |||
// byte order mark. Signature and version combined are 16 bytes long. | |||
const char kFileSignature[] = "# ninjadeps\n"; | |||
const int kCurrentVersion = 4; | |||
const int kCurrentVersion = 5; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I suggest moving the code that modifies the deps log format to its own commit for clarity. Adjust tests appropriately in the same one if needed.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done!
f4e6b51
to
6ac6f90
Compare
@digit-google I think I've addressed all your comments, please let me know if there's anything else that needs work. The one major change I've made is that the build graph is no longer modified when parsing dynout files during the build. Dynamic outputs are now added to the build graph only at the start of the build. During the build, only the deps log is modified. This is in line with how dynamic dependencies work. As @johanneslerch pointed out, updating the graph during the build can produce a temporarily invalid graph. Dynamic outputs (like dynamic dependencies) are not intended to dynamically change the build plan, so we need only require that they form a valid graph at the start (and end) of a build. @mathstuf To address your comments:
Correct, the output set is only updated when the edge is built, meaning that only the files in X are cleaned. I've added a test case for this.
The build updates the output set, so the clean will remove the files in Y. This is covered by testing that:
The cleaner makes no difference between dynamic and explicit outputs, so all these special files work exactly the same as when declared as explicit outputs in a build statement. I don't think there is a need for tests or documentation of such special behaviour in relation to this feature.
The deps log preserves the order of dynamic outputs, so they are cleaned in the order they are declared. It is up to the command generating the dynout file to ensure that files are listed before any parent directories. This is consistent with how build statements work, where outputs are cleaned from first to last. |
e391fe7
to
f6ff662
Compare
This is to support dynamically computed outputs for the 'dynout' attribute
Co-authored-by: Hampus Adolfsson <[email protected]>
f6ff662
to
4ace578
Compare
My change of no longer modifying the graph during the build had the side effect of dynamic outputs no longer being stored in the build log, and causing some edges to be considered dirty on the second build. I've changed the build log so that it takes dynamic outputs as an extra parameter when recording a command, and updated the tests to cover this case. |
Hi. Regarding the change to add the dynamic outputs to the build graph only at the start of the build: What would be the behavior when the dynamic outputs are read from dynout file in case the file is missing? For example, the rule command didn't generate the list of outputs so the dynout file doesn't exist at that time. |
Please see #1953 (comment) |
4ace578
to
f3926f5
Compare
If an edge has no entry in the deps log and has a missing dynout file, it is marked as dirty.
Your examples exposed a bug in the error handling when loading dynouts, hence the rebuilds when using the file containing the conflicting outputs. I've fixed this, and it now properly aborts the build for that file. As you've noticed, dynout files are not well suited to use cases where an output is sometimes dynamically detected and sometimes declared in the ninja file. If an output is sometimes dynamically detected, I'd suggest sticking to using dynout files even when the output is known beforehand. If that doesn't work, dyndep files may support your use case better, since they can modify the build graph during a build. |
Co-authored-by: Hampus Adolfsson <[email protected]>
'outputs' lists all the outputs generated by the graph, including dynamic outputs. 'dynouts' lists all dynamic outputs stored in the deps log. Co-authored-by: Hampus Adolfsson <[email protected]>
f3926f5
to
fb6170d
Compare
Thanks for the update. We will have a closer look at it. //edit: PR is here #2481 |
This is a continuation of #1953, since it's gone stale. I'll copy the original description for convenience:
Besides small fixes and documentation changes (see my commits for details), I've made the following changes:
Handling multi-target edges
Regarding @mathstuf's comment that the depslog supports multi-target edges:
An edge now loads dynamic outputs from the depslog for all its targets/outputs. I'll note that this is not how depslog dependencies are handled for multi-target edges (see LoadDepsFromLog). Depslog dependencies are only loaded for the first output, which seems incorrect to me.
Maintaining
deps
tool semanticsChanged the
deps
tool to only output dependencies from the depslog, not dynamic outputs. This means it keeps the same behaviour/semantics as before. I've added adynouts
tool which behaves likedeps
but instead outputs dynamic outputs from the depslog.There was also a comment about newlines in file names. The dynout parser does not have any escaping mechanism to allow newlines in file names. I can add this, but I'll leave it to reviewers to decide whether that's necessary (does ninja supports paths with newlines elsewhere?).
cc: @Dragnalith, @mathstuf