Reland "Fix toolchain dependencies not triggering rebuilds in Ninja" This reverts commit 576a0ebd4cc674fb52681bd1becfd0facec8449e. Reason for revert: Siso fix has rolled out: b/512275057 Original change's description: > Revert "Fix toolchain dependencies not triggering rebuilds in Ninja" > > This reverts commit 9ece3f5254c273cb46606a6571963f931c3b012d. > > Reason for revert: This doesn't pass chromium CQ. > https://chromium-review.git.corp.google.com/c/chromium/src/+/7810380 > > Original change's description: > > Fix toolchain dependencies not triggering rebuilds in Ninja > > > > GN previously emitted order-only dependencies on toolchain dependencies > > (via the .inputdeps.stamp file). This meant that if the toolchain changed, > > targets compiled by it were not rebuilt. > > > > This change modifies WriteInputDepsStampOrPhonyAndGetDep (WIDSOPAGD) > > to return implicit dependencies separately from order-only > > dependencies. Toolchain dependencies are now returned as implicit > > dependencies, which ensures that Ninja triggers a rebuild when the > > toolchain changes. > > > > All target writers have been updated to write the result of WIDSOPAGD as > > either implicit or order-only dependencies. The action target writer is > > a special case, because before this change it would write the result of > > WIDSOPAGD as an implicit dependency, unlike all other target writers, > > which would write it as an order-only dependency. This means that before > > this change, all dependencies returned by WIDSOPAGD were implicit > > dependencies for action targets. Because scripts may depend on this > > behavior, any phony targets created by WIDSOPAGD for action targets have > > only implicit dependencies. > > > > Bug: 40643484 > > Change-Id: Iae5dbf87cd67ecd5c9d0f6138f17085233c844b3 > > Reviewed-on: https://gn-review.googlesource.com/c/gn/+/22000 > > Reviewed-by: Takuto Ikuta <tikuta@google.com> > > Commit-Queue: Peter Collingbourne <pcc@google.com> > > # Not skipping CQ checks because original CL landed > 1 day ago. > > Bug: 40643484 > Change-Id: I8f38fc48dba7a214a6ef354d2550f91a842ca8c6 > Reviewed-on: https://gn-review.googlesource.com/c/gn/+/22520 > Commit-Queue: Takuto Ikuta <tikuta@google.com> > Reviewed-by: Junji Watanabe <jwata@google.com> # Not skipping CQ checks because original CL landed > 1 day ago. Bug: 40643484 Change-Id: I4e5c263c92a1e24abfaaefa7f5f0c62565b233d6 Reviewed-on: https://gn-review.googlesource.com/c/gn/+/22860 Reviewed-by: Junji Watanabe <jwata@google.com> Reviewed-by: Takuto Ikuta <tikuta@google.com> Commit-Queue: Peter Collingbourne <pcc@chromium.org>
diff --git a/src/gn/ninja_action_target_writer.cc b/src/gn/ninja_action_target_writer.cc index 3aa5686..0d01d07 100644 --- a/src/gn/ninja_action_target_writer.cc +++ b/src/gn/ninja_action_target_writer.cc
@@ -73,8 +73,11 @@ // and the action will just depend on all the input deps directly. size_t num_output_uses = target_->output_type() == Target::ACTION ? 1u : target_->sources().size(); - std::vector<OutputFile> input_deps = WriteInputDepsStampOrPhonyAndGetDep( + NinjaTargetWriter::InputDeps stamp_deps = WriteInputDepsStampOrPhonyAndGetDep( additional_hard_deps, num_output_uses); + std::vector<OutputFile> input_deps = stamp_deps.implicit; + input_deps.insert(input_deps.end(), stamp_deps.order_only.begin(), + stamp_deps.order_only.end()); out_ << std::endl; // Collects all output files for writing below.
diff --git a/src/gn/ninja_bundle_data_target_writer.cc b/src/gn/ninja_bundle_data_target_writer.cc index 4ba1733..d6c2e56 100644 --- a/src/gn/ninja_bundle_data_target_writer.cc +++ b/src/gn/ninja_bundle_data_target_writer.cc
@@ -21,11 +21,14 @@ OutputFile(settings_->build_settings(), source_file)); } - std::vector<OutputFile> input_deps = WriteInputDepsStampOrPhonyAndGetDep( - std::vector<const Target*>(), /*num_output_uses=*/1); - output_files.insert(output_files.end(), input_deps.begin(), input_deps.end()); + NinjaTargetWriter::InputDeps stamp_deps = + WriteInputDepsStampOrPhonyAndGetDep(std::vector<const Target*>(), + /*num_output_uses=*/1); + std::vector<OutputFile> implicit_deps = stamp_deps.implicit; + std::vector<OutputFile> order_only_deps = stamp_deps.order_only; + output_files.insert(output_files.end(), implicit_deps.begin(), + implicit_deps.end()); - std::vector<OutputFile> order_only_deps; for (const Target* data_dep : resolved().GetDataDeps(target_)) { if (data_dep->has_dependency_output()) order_only_deps.push_back(data_dep->dependency_output());
diff --git a/src/gn/ninja_c_binary_target_writer.cc b/src/gn/ninja_c_binary_target_writer.cc index 4f19645..812803e 100644 --- a/src/gn/ninja_c_binary_target_writer.cc +++ b/src/gn/ninja_c_binary_target_writer.cc
@@ -104,8 +104,11 @@ // that also use PCH files won't have a phony target even though having // one would make output ninja file size a bit lower. That's ok, binary // targets with a single source are rare. - std::vector<OutputFile> order_only_deps = WriteInputDepsStampOrPhonyAndGetDep( + NinjaTargetWriter::InputDeps stamp_deps = WriteInputDepsStampOrPhonyAndGetDep( std::vector<const Target*>(), num_output_uses); + input_deps.insert(input_deps.end(), stamp_deps.implicit.begin(), + stamp_deps.implicit.end()); + std::vector<OutputFile> order_only_deps = stamp_deps.order_only; // For GCC builds, the .gch files are not object files, but still need to be // added as explicit dependencies below. The .gch output files are placed in
diff --git a/src/gn/ninja_copy_target_writer.cc b/src/gn/ninja_copy_target_writer.cc index 0952c6f..16fca06 100644 --- a/src/gn/ninja_copy_target_writer.cc +++ b/src/gn/ninja_copy_target_writer.cc
@@ -72,13 +72,14 @@ GetNinjaRulePrefixForToolchain(settings_) + GeneralTool::kGeneralToolCopy; size_t num_output_uses = target_->sources().size(); - std::vector<OutputFile> input_deps = WriteInputDepsStampOrPhonyAndGetDep( + NinjaTargetWriter::InputDeps stamp_deps = WriteInputDepsStampOrPhonyAndGetDep( std::vector<const Target*>(), num_output_uses); + std::vector<OutputFile> implicit_deps = stamp_deps.implicit; + std::vector<OutputFile> order_only_deps = stamp_deps.order_only; - std::vector<OutputFile> data_outs; for (const Target* data_dep : resolved().GetDataDeps(target_)) { if (data_dep->has_dependency_output()) - data_outs.push_back(data_dep->dependency_output()); + order_only_deps.push_back(data_dep->dependency_output()); } // Note that we don't write implicit deps for copy steps. "copy" only @@ -119,10 +120,13 @@ out_ << ": " << tool_name << " "; path_output_.WriteFile(out_, input_file); - if (!input_deps.empty() || !data_outs.empty()) { + if (!implicit_deps.empty()) { + out_ << " |"; + path_output_.WriteFiles(out_, implicit_deps); + } + if (!order_only_deps.empty()) { out_ << " ||"; - path_output_.WriteFiles(out_, input_deps); - path_output_.WriteFiles(out_, data_outs); + path_output_.WriteFiles(out_, order_only_deps); } WriteValidations(); out_ << std::endl;
diff --git a/src/gn/ninja_copy_target_writer_unittest.cc b/src/gn/ninja_copy_target_writer_unittest.cc index da7da59..6fc5e43 100644 --- a/src/gn/ninja_copy_target_writer_unittest.cc +++ b/src/gn/ninja_copy_target_writer_unittest.cc
@@ -208,10 +208,10 @@ writer.Run(); const char expected_linux[] = - "build phony/foo/bar.inputdeps: phony phony/foo/action1 " + "build phony/foo/bar.inputdeps: phony || phony/foo/action1 " "phony/foo/action2\n" - "build action1.copy: copy action1.out || phony/foo/bar.inputdeps\n" - "build action2.copy: copy action2.out || phony/foo/bar.inputdeps\n" + "build action1.copy: copy action1.out | phony/foo/bar.inputdeps\n" + "build action2.copy: copy action2.out | phony/foo/bar.inputdeps\n" "\n" "build phony/foo/bar: phony action1.copy action2.copy\n"; std::string out_str = out.str();
diff --git a/src/gn/ninja_create_bundle_target_writer.cc b/src/gn/ninja_create_bundle_target_writer.cc index b3a15ba..ca305e6 100644 --- a/src/gn/ninja_create_bundle_target_writer.cc +++ b/src/gn/ninja_create_bundle_target_writer.cc
@@ -81,16 +81,18 @@ // Stamp users are CopyBundleData, CompileAssetsCatalog, PostProcessing and // StampForTarget. size_t num_stamp_uses = 4; - std::vector<OutputFile> order_only_deps = WriteInputDepsStampOrPhonyAndGetDep( + NinjaTargetWriter::InputDeps stamp_deps = WriteInputDepsStampOrPhonyAndGetDep( std::vector<const Target*>(), num_stamp_uses); + std::vector<OutputFile> implicit_deps = stamp_deps.implicit; + std::vector<OutputFile> order_only_deps = stamp_deps.order_only; std::string post_processing_rule_name = WritePostProcessingRuleDefinition(); std::vector<OutputFile> output_files; - WriteCopyBundleDataSteps(order_only_deps, &output_files); - WriteCompileAssetsCatalogStep(order_only_deps, &output_files); - WritePostProcessingStep(post_processing_rule_name, order_only_deps, - &output_files); + WriteCopyBundleDataSteps(implicit_deps, order_only_deps, &output_files); + WriteCompileAssetsCatalogStep(implicit_deps, order_only_deps, &output_files); + WritePostProcessingStep(post_processing_rule_name, implicit_deps, + order_only_deps, &output_files); for (const Target* data_dep : resolved().GetDataDeps(target_)) { if (data_dep->has_dependency_output()) @@ -179,14 +181,17 @@ } void NinjaCreateBundleTargetWriter::WriteCopyBundleDataSteps( + const std::vector<OutputFile>& implicit_deps, const std::vector<OutputFile>& order_only_deps, std::vector<OutputFile>* output_files) { for (const BundleFileRule& file_rule : target_->bundle_data().file_rules()) - WriteCopyBundleFileRuleSteps(file_rule, order_only_deps, output_files); + WriteCopyBundleFileRuleSteps(file_rule, implicit_deps, order_only_deps, + output_files); } void NinjaCreateBundleTargetWriter::WriteCopyBundleFileRuleSteps( const BundleFileRule& file_rule, + const std::vector<OutputFile>& implicit_deps, const std::vector<OutputFile>& order_only_deps, std::vector<OutputFile>* output_files) { // Note that we don't write implicit deps for copy steps. "copy_bundle_data" @@ -209,6 +214,10 @@ << GeneralTool::kGeneralToolCopyBundleData << " "; path_output_.WriteFile(out_, source_file); + if (!implicit_deps.empty()) { + out_ << " |"; + path_output_.WriteFiles(out_, implicit_deps); + } if (!order_only_deps.empty()) { out_ << " ||"; path_output_.WriteFiles(out_, order_only_deps); @@ -219,6 +228,7 @@ } void NinjaCreateBundleTargetWriter::WriteCompileAssetsCatalogStep( + const std::vector<OutputFile>& implicit_deps, const std::vector<OutputFile>& order_only_deps, std::vector<OutputFile>* output_files) { if (!TargetRequireAssetCatalogCompilation(target_)) @@ -251,6 +261,10 @@ WriteOutput(partial_info_plist); out_ << ": " << GetNinjaRulePrefixForToolchain(settings_) << GeneralTool::kGeneralToolStamp; + if (!implicit_deps.empty()) { + out_ << " |"; + path_output_.WriteFiles(out_, implicit_deps); + } if (!order_only_deps.empty()) { out_ << " ||"; path_output_.WriteFiles(out_, order_only_deps); @@ -286,6 +300,7 @@ out_ << " | "; path_output_.WriteFile(out_, input_dep); + path_output_.WriteFiles(out_, implicit_deps); if (!order_only_deps.empty()) { out_ << " ||"; @@ -362,13 +377,15 @@ void NinjaCreateBundleTargetWriter::WritePostProcessingStep( const std::string& post_processing_rule_name, + const std::vector<OutputFile>& implicit_deps, const std::vector<OutputFile>& order_only_deps, std::vector<OutputFile>* output_files) { if (post_processing_rule_name.empty()) return; OutputFile post_processing_input_stamp_file = - WritePostProcessingInputDepsStampOrPhony(order_only_deps, output_files); + WritePostProcessingInputDepsStampOrPhony(implicit_deps, order_only_deps, + output_files); DCHECK(!post_processing_input_stamp_file.value().empty()); out_ << "build"; @@ -391,6 +408,7 @@ OutputFile NinjaCreateBundleTargetWriter::WritePostProcessingInputDepsStampOrPhony( + const std::vector<OutputFile>& implicit_deps, const std::vector<OutputFile>& order_only_deps, std::vector<OutputFile>* output_files) { std::vector<SourceFile> post_processing_input_files; @@ -406,7 +424,8 @@ } DCHECK(!post_processing_input_files.empty()); - if (post_processing_input_files.size() == 1 && order_only_deps.empty()) + if (post_processing_input_files.size() == 1 && implicit_deps.empty() && + order_only_deps.empty()) return OutputFile(settings_->build_settings(), post_processing_input_files[0]); @@ -438,6 +457,10 @@ out_ << " "; path_output_.WriteFile(out_, source); } + if (!implicit_deps.empty()) { + out_ << " |"; + path_output_.WriteFiles(out_, implicit_deps); + } if (!order_only_deps.empty()) { out_ << " ||"; path_output_.WriteFiles(out_, order_only_deps);
diff --git a/src/gn/ninja_create_bundle_target_writer.h b/src/gn/ninja_create_bundle_target_writer.h index 6b5d6cc..7d603a0 100644 --- a/src/gn/ninja_create_bundle_target_writer.h +++ b/src/gn/ninja_create_bundle_target_writer.h
@@ -30,7 +30,8 @@ // Writes the steps to copy files into the bundle. // // The list of newly created files will be added to |output_files|. - void WriteCopyBundleDataSteps(const std::vector<OutputFile>& order_only_deps, + void WriteCopyBundleDataSteps(const std::vector<OutputFile>& implicit_deps, + const std::vector<OutputFile>& order_only_deps, std::vector<OutputFile>* output_files); // Writes the step to copy files BundleFileRule into the bundle. @@ -38,6 +39,7 @@ // The list of newly created files will be added to |output_files|. void WriteCopyBundleFileRuleSteps( const BundleFileRule& file_rule, + const std::vector<OutputFile>& implicit_deps, const std::vector<OutputFile>& order_only_deps, std::vector<OutputFile>* output_files); @@ -45,6 +47,7 @@ // // The list of newly created files will be added to |output_files|. void WriteCompileAssetsCatalogStep( + const std::vector<OutputFile>& implicit_deps, const std::vector<OutputFile>& order_only_deps, std::vector<OutputFile>* output_files); @@ -59,12 +62,14 @@ // post-processing may depends on the full bundle structure, this step will // depends on all files generated via other rules. void WritePostProcessingStep(const std::string& post_processing_rule_name, + const std::vector<OutputFile>& implicit_deps, const std::vector<OutputFile>& order_only_deps, std::vector<OutputFile>* output_files); // Writes the stamp file or phony target for the post-processing input // dependencies. OutputFile WritePostProcessingInputDepsStampOrPhony( + const std::vector<OutputFile>& implicit_deps, const std::vector<OutputFile>& order_only_deps, std::vector<OutputFile>* output_files);
diff --git a/src/gn/ninja_create_bundle_target_writer_unittest.cc b/src/gn/ninja_create_bundle_target_writer_unittest.cc index 3dfd6a3..7aa1313 100644 --- a/src/gn/ninja_create_bundle_target_writer_unittest.cc +++ b/src/gn/ninja_create_bundle_target_writer_unittest.cc
@@ -75,16 +75,15 @@ writer.Run(); const char expected[] = - "build phony/baz/bar.inputdeps: phony phony/foo/bar " + "build phony/baz/bar.inputdeps: phony || phony/foo/bar " "phony/foo/data\n" "build bar.bundle/Contents/Resources/input1.txt: copy_bundle_data " - "../../foo/input1.txt || phony/baz/bar.inputdeps\n" + "../../foo/input1.txt | phony/baz/bar.inputdeps\n" "build bar.bundle/Contents/Resources/input2.txt: copy_bundle_data " - "../../foo/input2.txt || phony/baz/bar.inputdeps\n" + "../../foo/input2.txt | phony/baz/bar.inputdeps\n" "build phony/baz/bar: phony " "bar.bundle/Contents/Resources/input1.txt " - "bar.bundle/Contents/Resources/input2.txt" - " || phony/baz/bar.inputdeps\n" + "bar.bundle/Contents/Resources/input2.txt\n" "build bar.bundle: phony phony/baz/bar\n"; std::string out_str = out.str(); EXPECT_EQ(expected, out_str); @@ -124,16 +123,15 @@ writer.Run(); const char expected[] = - "build phony/baz/bar.inputdeps: phony phony/foo/bar " + "build phony/baz/bar.inputdeps: phony || phony/foo/bar " "phony/foo/data\n" "build gen/bar.bundle/Contents/Resources/input1.txt: copy_bundle_data " - "../../foo/input1.txt || phony/baz/bar.inputdeps\n" + "../../foo/input1.txt | phony/baz/bar.inputdeps\n" "build gen/bar.bundle/Contents/Resources/input2.txt: copy_bundle_data " - "../../foo/input2.txt || phony/baz/bar.inputdeps\n" + "../../foo/input2.txt | phony/baz/bar.inputdeps\n" "build phony/baz/bar: phony " "gen/bar.bundle/Contents/Resources/input1.txt " - "gen/bar.bundle/Contents/Resources/input2.txt || " - "phony/baz/bar.inputdeps\n" + "gen/bar.bundle/Contents/Resources/input2.txt\n" "build gen/bar.bundle: phony phony/baz/bar\n"; std::string out_str = out.str(); EXPECT_EQ(expected, out_str); @@ -229,16 +227,14 @@ writer.Run(); const char expected[] = - "build phony/baz/bar.inputdeps: phony phony/foo/bar " + "build phony/baz/bar.inputdeps: phony || phony/foo/bar " "phony/foo/data\n" "build bar.bundle/Contents/Resources/Assets.car: compile_xcassets " - "../../foo/Foo.xcassets | phony/foo/data || " - "phony/baz/bar.inputdeps\n" + "../../foo/Foo.xcassets | phony/foo/data phony/baz/bar.inputdeps\n" " product_type = com.apple.product-type\n" " xcasset_compiler_flags = --app-icon foo\n" "build phony/baz/bar: phony " - "bar.bundle/Contents/Resources/Assets.car || " - "phony/baz/bar.inputdeps\n" + "bar.bundle/Contents/Resources/Assets.car\n" "build bar.bundle: phony phony/baz/bar\n"; std::string out_str = out.str(); EXPECT_EQ(expected, out_str); @@ -393,22 +389,22 @@ writer.Run(); const char expected[] = - "build phony/baz/bar.inputdeps: phony phony/biz/assets " + "build phony/baz/bar.inputdeps: phony || phony/biz/assets " "phony/foo/assets phony/foo/bar phony/foo/data " "phony/qux/info_plist phony/quz/assets\n" "build bar.bundle/Contents/Info.plist: copy_bundle_data " - "../../qux/qux-Info.plist || phony/baz/bar.inputdeps\n" + "../../qux/qux-Info.plist | phony/baz/bar.inputdeps\n" "build bar.bundle/Contents/Resources/input1.txt: copy_bundle_data " - "../../foo/input1.txt || phony/baz/bar.inputdeps\n" + "../../foo/input1.txt | phony/baz/bar.inputdeps\n" "build bar.bundle/Contents/Resources/input2.txt: copy_bundle_data " - "../../foo/input2.txt || phony/baz/bar.inputdeps\n" + "../../foo/input2.txt | phony/baz/bar.inputdeps\n" "build phony/baz/bar.xcassets.inputdeps: phony " "phony/foo/assets " "phony/quz/assets phony/biz/assets\n" "build bar.bundle/Contents/Resources/Assets.car | " "baz/bar/bar_partial_info.plist: compile_xcassets " "../../foo/Foo.xcassets ../../quz/Quz.xcassets " - "../../biz/Biz.xcassets | phony/baz/bar.xcassets.inputdeps || " + "../../biz/Biz.xcassets | phony/baz/bar.xcassets.inputdeps " "phony/baz/bar.inputdeps\n" " product_type = com.apple.product-type\n" " partial_info_plist = baz/bar/bar_partial_info.plist\n" @@ -417,7 +413,7 @@ "bar.bundle/Contents/Resources/input1.txt " "bar.bundle/Contents/Resources/input2.txt " "bar.bundle/Contents/Resources/Assets.car " - "baz/bar/bar_partial_info.plist || phony/baz/bar.inputdeps\n" + "baz/bar/bar_partial_info.plist\n" "build bar.bundle: phony phony/baz/bar\n"; std::string out_str = out.str(); EXPECT_EQ(expected, out_str); @@ -475,7 +471,7 @@ writer.Run(); const char expected[] = - "build phony/baz/bar.inputdeps: phony ./quz phony/foo/bar " + "build phony/baz/bar.inputdeps: phony || ./quz phony/foo/bar " "phony/foo/data\n" "rule __baz_bar___toolchain_default__post_processing_rule\n" " command = ../../build/codesign.py -b=quz bar.bundle\n" @@ -483,21 +479,21 @@ " restat = 1\n" "\n" "build bar.bundle/Contents/Resources/input1.txt: copy_bundle_data " - "../../foo/input1.txt || phony/baz/bar.inputdeps\n" + "../../foo/input1.txt | phony/baz/bar.inputdeps\n" "build bar.bundle/Contents/Resources/input2.txt: copy_bundle_data " - "../../foo/input2.txt || phony/baz/bar.inputdeps\n" + "../../foo/input2.txt | phony/baz/bar.inputdeps\n" "build phony/baz/bar.postprocessing.inputdeps: phony " "../../build/codesign.py " "quz " "bar.bundle/Contents/Resources/input1.txt " - "bar.bundle/Contents/Resources/input2.txt || " + "bar.bundle/Contents/Resources/input2.txt | " "phony/baz/bar.inputdeps\n" "build bar.bundle/Contents/quz bar.bundle/_CodeSignature/CodeResources: " "__baz_bar___toolchain_default__post_processing_rule " "| phony/baz/bar.postprocessing.inputdeps\n" "build phony/baz/bar: phony " "bar.bundle/Contents/quz " - "bar.bundle/_CodeSignature/CodeResources || phony/baz/bar.inputdeps\n" + "bar.bundle/_CodeSignature/CodeResources\n" "build bar.bundle: phony phony/baz/bar\n"; std::string out_str = out.str(); EXPECT_EQ(expected, out_str); @@ -558,7 +554,7 @@ writer.Run(); const char expected[] = - "build toolchain/phony/baz/bar.inputdeps: phony ./quz " + "build toolchain/phony/baz/bar.inputdeps: phony || ./quz " "toolchain/phony/foo/bar " "toolchain/phony/foo/data\n" "rule __baz_bar___toolchain_default__post_processing_rule\n" @@ -568,23 +564,22 @@ "\n" "build bar.bundle/Contents/Resources/input1.txt: " "toolchain_copy_bundle_data " - "../../foo/input1.txt || toolchain/phony/baz/bar.inputdeps\n" + "../../foo/input1.txt | toolchain/phony/baz/bar.inputdeps\n" "build bar.bundle/Contents/Resources/input2.txt: " "toolchain_copy_bundle_data " - "../../foo/input2.txt || toolchain/phony/baz/bar.inputdeps\n" + "../../foo/input2.txt | toolchain/phony/baz/bar.inputdeps\n" "build toolchain/phony/baz/bar.postprocessing.inputdeps: phony " "../../build/codesign.py " "quz " "bar.bundle/Contents/Resources/input1.txt " - "bar.bundle/Contents/Resources/input2.txt || " + "bar.bundle/Contents/Resources/input2.txt | " "toolchain/phony/baz/bar.inputdeps\n" "build bar.bundle/Contents/quz bar.bundle/_CodeSignature/CodeResources: " "__baz_bar___toolchain_default__post_processing_rule " "| toolchain/phony/baz/bar.postprocessing.inputdeps\n" "build toolchain/phony/baz/bar: phony " "bar.bundle/Contents/quz " - "bar.bundle/_CodeSignature/CodeResources || " - "toolchain/phony/baz/bar.inputdeps\n" + "bar.bundle/_CodeSignature/CodeResources\n" "build bar.bundle: phony toolchain/phony/baz/bar\n"; std::string out_str = out.str(); EXPECT_EQ(expected, out_str);
diff --git a/src/gn/ninja_rust_binary_target_writer.cc b/src/gn/ninja_rust_binary_target_writer.cc index e9b44e8..b77157a 100644 --- a/src/gn/ninja_rust_binary_target_writer.cc +++ b/src/gn/ninja_rust_binary_target_writer.cc
@@ -125,8 +125,9 @@ // Ninja to make sure the inputs are up to date before compiling this source, // but changes in the inputs deps won't cause the file to be recompiled. See // the comment on NinjaCBinaryTargetWriter::Run for more detailed explanation. - std::vector<OutputFile> order_only_deps = WriteInputDepsStampOrPhonyAndGetDep( + NinjaTargetWriter::InputDeps stamp_deps = WriteInputDepsStampOrPhonyAndGetDep( std::vector<const Target*>(), num_output_uses); + std::vector<OutputFile> order_only_deps = stamp_deps.order_only; std::copy(input_deps.begin(), input_deps.end(), std::back_inserter(order_only_deps)); @@ -135,6 +136,7 @@ // -Ldependency. Also assemble a list of extra (i.e. implicit) deps // for ninja dependency tracking. UniqueVector<OutputFile> implicit_deps; + implicit_deps.Append(stamp_deps.implicit.begin(), stamp_deps.implicit.end()); AppendSourcesAndInputsToImplicitDeps(&implicit_deps); implicit_deps.Append(classified_deps.extra_object_files.begin(), classified_deps.extra_object_files.end());
diff --git a/src/gn/ninja_target_writer.cc b/src/gn/ninja_target_writer.cc index 9b45628..affc7d6 100644 --- a/src/gn/ninja_target_writer.cc +++ b/src/gn/ninja_target_writer.cc
@@ -453,7 +453,8 @@ } } -std::vector<OutputFile> NinjaTargetWriter::WriteInputDepsStampOrPhonyAndGetDep( +NinjaTargetWriter::InputDeps +NinjaTargetWriter::WriteInputDepsStampOrPhonyAndGetDep( const std::vector<const Target*>& additional_hard_deps, size_t num_output_uses) const { CHECK(target_->toolchain()) << "Toolchain not set on target " @@ -519,55 +520,62 @@ // toolchains often have more than one dependency, we could consider writing // a toolchain-specific phony target and only include the phony here. // Note that these are usually empty/small. + std::vector<const Target*> toolchain_deps_targets; const LabelTargetVector& toolchain_deps = target_->toolchain()->deps(); for (const auto& toolchain_dep : toolchain_deps) { // This could theoretically duplicate dependencies already in the list, // but it shouldn't happen in practice, is inconvenient to check for, // and only results in harmless redundant dependencies listed. - input_deps_targets.push_back(toolchain_dep.ptr); + toolchain_deps_targets.push_back(toolchain_dep.ptr); } // --------- // Write the outputs. - if (input_deps_sources.size() + input_deps_targets.size() == 0) - return std::vector<OutputFile>(); // No input dependencies. + if (input_deps_sources.empty() && input_deps_targets.empty() && + toolchain_deps_targets.empty()) + return InputDeps{}; // No input dependencies. - // If we're only generating one input dependency, return it directly instead - // of writing a phony target for it. - if (input_deps_sources.size() == 1 && input_deps_targets.size() == 0) - return std::vector<OutputFile>{ - OutputFile(settings_->build_settings(), *input_deps_sources[0])}; - if (input_deps_sources.size() == 0 && input_deps_targets.size() == 1) { - const auto& dep = *input_deps_targets[0]; - if (!dep.has_dependency_output()) - return std::vector<OutputFile>(); - return std::vector<OutputFile>{dep.dependency_output()}; - } - - std::vector<OutputFile> outs; + InputDeps deps; // File input deps. for (const SourceFile* source : input_deps_sources) - outs.push_back(OutputFile(settings_->build_settings(), *source)); + deps.order_only.push_back(OutputFile(settings_->build_settings(), *source)); // Target input deps. Sort by label so the output is deterministic (otherwise // some of the targets will have gone through std::sets which will have // sorted them by pointer). - std::sort( - input_deps_targets.begin(), input_deps_targets.end(), - [](const Target* a, const Target* b) { return a->label() < b->label(); }); - for (auto* dep : input_deps_targets) { - if (dep->has_dependency_output()) - outs.push_back(dep->dependency_output()); - } + auto add_target_deps = [](std::vector<OutputFile>& deps, + std::vector<const Target*>& targets) { + std::sort(targets.begin(), targets.end(), + [](const Target* a, const Target* b) { + return a->label() < b->label(); + }); + for (auto* dep : targets) { + if (dep->has_dependency_output()) + deps.push_back(dep->dependency_output()); + } + }; + add_target_deps(deps.order_only, input_deps_targets); + add_target_deps(deps.implicit, toolchain_deps_targets); - // If there are multiple inputs, but the phony target would be referenced only - // once, don't write it but depend on the inputs directly. - if (num_output_uses == 1u) - return outs; + // If we're only generating one input dependency, or if there are no + // dependencies, return it directly instead of writing a phony target for it. + // Also, if there are multiple inputs, but the phony target would be + // referenced only once, don't write it but depend on the inputs directly. + if (deps.implicit.size() + deps.order_only.size() <= 1 || + num_output_uses == 1u) + return deps; - // If outs is empty, we don't need stamp or phony target for this. - if (outs.empty()) { - return outs; + // Action targets are special because all of their dependencies are implicit + // dependencies. This is because prior to + // https://gn-review.googlesource.com/c/gn/+/22000 action targets had an + // implicit dependency on inputdeps whereas other target types had an + // order-only dependency on inputdeps (which at the time only had implicit + // dependencies), and scripts may be depending on that. + if (target_->output_type() == Target::ACTION || + target_->output_type() == Target::ACTION_FOREACH) { + deps.implicit.insert(deps.implicit.end(), deps.order_only.begin(), + deps.order_only.end()); + deps.order_only.clear(); } OutputFile input_stamp_or_phony; @@ -596,9 +604,16 @@ out_ << "build "; path_output_.WriteFile(out_, input_stamp_or_phony); out_ << ": " << tool; - path_output_.WriteFiles(out_, outs); + path_output_.WriteFiles(out_, deps.implicit); + if (!deps.order_only.empty()) { + out_ << " ||"; + path_output_.WriteFiles(out_, deps.order_only); + } out_ << "\n"; - return std::vector<OutputFile>{input_stamp_or_phony}; + + InputDeps result; + result.implicit.push_back(input_stamp_or_phony); + return result; } void NinjaTargetWriter::WriteStampOrPhonyForTarget(
diff --git a/src/gn/ninja_target_writer.h b/src/gn/ninja_target_writer.h index 0936033..2da7e76 100644 --- a/src/gn/ninja_target_writer.h +++ b/src/gn/ninja_target_writer.h
@@ -82,14 +82,19 @@ bool indent, bool always_write); + struct InputDeps { + std::vector<OutputFile> implicit; + std::vector<OutputFile> order_only; + }; + // Writes to the output stream a phony rule for input dependencies, and // returns the file to be appended to source rules that encodes the - // order-only dependencies for the current target. + // dependencies for the current target. // If num_output_uses is small, this might return all input dependencies // directly, without writing a phony rule. // If there are no implicit dependencies and no additional target dependencies - // are passed in, this returns an empty vector. - std::vector<OutputFile> WriteInputDepsStampOrPhonyAndGetDep( + // are passed in, this returns an empty struct. + InputDeps WriteInputDepsStampOrPhonyAndGetDep( const std::vector<const Target*>& additional_hard_deps, size_t num_output_uses) const;
diff --git a/src/gn/ninja_target_writer_unittest.cc b/src/gn/ninja_target_writer_unittest.cc index 317ce2a..cd4a5e4 100644 --- a/src/gn/ninja_target_writer_unittest.cc +++ b/src/gn/ninja_target_writer_unittest.cc
@@ -22,7 +22,7 @@ void Run() override {} // Make this public so the test can call it. - std::vector<OutputFile> WriteInputDepsStampOrPhonyAndGetDep( + NinjaTargetWriter::InputDeps WriteInputDepsStampOrPhonyAndGetDep( const std::vector<const Target*>& additional_hard_deps, size_t num_stamp_uses) { return NinjaTargetWriter::WriteInputDepsStampOrPhonyAndGetDep( @@ -119,13 +119,14 @@ { std::ostringstream stream; TestingNinjaTargetWriter writer(&base_target, setup.toolchain(), stream); - std::vector<OutputFile> dep = writer.WriteInputDepsStampOrPhonyAndGetDep( + auto dep = writer.WriteInputDepsStampOrPhonyAndGetDep( std::vector<const Target*>(), 10u); // Since there is only one dependency, it should just be returned and // nothing written to the stream. - ASSERT_EQ(1u, dep.size()); - EXPECT_EQ("../../foo/script.py", dep[0].value()); + ASSERT_EQ(0u, dep.implicit.size()); + ASSERT_EQ(1u, dep.order_only.size()); + EXPECT_EQ("../../foo/script.py", dep.order_only[0].value()); EXPECT_EQ("", stream.str()); } @@ -133,13 +134,14 @@ { std::ostringstream stream; TestingNinjaTargetWriter writer(&target, setup.toolchain(), stream); - std::vector<OutputFile> dep = writer.WriteInputDepsStampOrPhonyAndGetDep( + auto dep = writer.WriteInputDepsStampOrPhonyAndGetDep( std::vector<const Target*>(), 10u); // Since there is only one dependency, a stamp file will be returned // directly without writing any additional rules. - ASSERT_EQ(1u, dep.size()); - EXPECT_EQ("phony/foo/base", dep[0].value()); + ASSERT_EQ(0u, dep.implicit.size()); + ASSERT_EQ(1u, dep.order_only.size()); + EXPECT_EQ("phony/foo/base", dep.order_only[0].value()); } { @@ -164,11 +166,12 @@ { std::ostringstream stream; TestingNinjaTargetWriter writer(&action, setup.toolchain(), stream); - std::vector<OutputFile> dep = writer.WriteInputDepsStampOrPhonyAndGetDep( + auto dep = writer.WriteInputDepsStampOrPhonyAndGetDep( std::vector<const Target*>(), 10u); - ASSERT_EQ(1u, dep.size()); - EXPECT_EQ("phony/foo/action.inputdeps", dep[0].value()); + ASSERT_EQ(1u, dep.implicit.size()); + ASSERT_EQ(0u, dep.order_only.size()); + EXPECT_EQ("phony/foo/action.inputdeps", dep.implicit[0].value()); EXPECT_EQ( "build phony/foo/action.inputdeps: phony ../../foo/script.py " "../../foo/action_source.txt ./target\n", @@ -216,13 +219,14 @@ { std::ostringstream stream; TestingNinjaTargetWriter writer(&base_target, setup.toolchain(), stream); - std::vector<OutputFile> dep = writer.WriteInputDepsStampOrPhonyAndGetDep( + auto dep = writer.WriteInputDepsStampOrPhonyAndGetDep( std::vector<const Target*>(), 10u); // Since there is only one dependency, it should just be returned and // nothing written to the stream. - ASSERT_EQ(1u, dep.size()); - EXPECT_EQ("../../foo/script.py", dep[0].value()); + ASSERT_EQ(0u, dep.implicit.size()); + ASSERT_EQ(1u, dep.order_only.size()); + EXPECT_EQ("../../foo/script.py", dep.order_only[0].value()); EXPECT_EQ("", stream.str()); } @@ -230,13 +234,14 @@ { std::ostringstream stream; TestingNinjaTargetWriter writer(&target, setup.toolchain(), stream); - std::vector<OutputFile> dep = writer.WriteInputDepsStampOrPhonyAndGetDep( + auto dep = writer.WriteInputDepsStampOrPhonyAndGetDep( std::vector<const Target*>(), 10u); // Since there is only one dependency, a stamp file will be returned // directly without writing any additional rules. - ASSERT_EQ(1u, dep.size()); - EXPECT_EQ("obj/foo/base.stamp", dep[0].value()); + ASSERT_EQ(0u, dep.implicit.size()); + ASSERT_EQ(1u, dep.order_only.size()); + EXPECT_EQ("obj/foo/base.stamp", dep.order_only[0].value()); } { @@ -261,11 +266,12 @@ { std::ostringstream stream; TestingNinjaTargetWriter writer(&action, setup.toolchain(), stream); - std::vector<OutputFile> dep = writer.WriteInputDepsStampOrPhonyAndGetDep( + auto dep = writer.WriteInputDepsStampOrPhonyAndGetDep( std::vector<const Target*>(), 10u); - ASSERT_EQ(1u, dep.size()); - EXPECT_EQ("obj/foo/action.inputdeps.stamp", dep[0].value()); + ASSERT_EQ(1u, dep.implicit.size()); + ASSERT_EQ(0u, dep.order_only.size()); + EXPECT_EQ("obj/foo/action.inputdeps.stamp", dep.implicit[0].value()); EXPECT_EQ( "build obj/foo/action.inputdeps.stamp: stamp ../../foo/script.py " "../../foo/action_source.txt ./target\n", @@ -297,16 +303,60 @@ std::ostringstream stream; TestingNinjaTargetWriter writer(&target, setup.toolchain(), stream); - std::vector<OutputFile> dep = writer.WriteInputDepsStampOrPhonyAndGetDep( + auto dep = writer.WriteInputDepsStampOrPhonyAndGetDep( std::vector<const Target*>(), 10u); // Since there is more than one dependency, a stamp file will be returned // and the rule for the stamp file will be written to the stream. - ASSERT_EQ(1u, dep.size()); - EXPECT_EQ("phony/foo/setup", dep[0].value()); + ASSERT_EQ(1u, dep.implicit.size()); + ASSERT_EQ(0u, dep.order_only.size()); + EXPECT_EQ("phony/foo/setup", dep.implicit[0].value()); EXPECT_EQ("", stream.str()); } +// Tests WriteInputDepsStampOrPhonyAndGetDep when a toolchain dependency and +// another dependency are present. +TEST(NinjaTargetWriter, + WriteInputDepsStampOrPhonyAndGetDepWithToolchainAndAnotherDep) { + TestWithScope setup; + Err err; + + Target toolchain_dep_target(setup.settings(), + Label(SourceDir("//foo/"), "setup")); + toolchain_dep_target.set_output_type(Target::ACTION); + toolchain_dep_target.SetToolchain(setup.toolchain()); + ASSERT_TRUE(toolchain_dep_target.OnResolved(&err)); + setup.toolchain()->deps().push_back(LabelTargetPair(&toolchain_dep_target)); + + Target base_target(setup.settings(), Label(SourceDir("//foo/"), "base")); + base_target.set_output_type(Target::ACTION); + base_target.visibility().SetPublic(); + base_target.SetToolchain(setup.toolchain()); + base_target.action_values().set_script(SourceFile("//foo/script.py")); + ASSERT_TRUE(base_target.OnResolved(&err)); + + Target target(setup.settings(), Label(SourceDir("//foo/"), "target")); + target.set_output_type(Target::EXECUTABLE); + target.SetToolchain(setup.toolchain()); + target.public_deps().push_back(LabelTargetPair(&base_target)); + ASSERT_TRUE(target.OnResolved(&err)); + + std::ostringstream stream; + TestingNinjaTargetWriter writer(&target, setup.toolchain(), stream); + auto dep = writer.WriteInputDepsStampOrPhonyAndGetDep( + std::vector<const Target*>(), 10u); + + // Since there is a toolchain dependency, it should be returned as an implicit + // dependency. + ASSERT_EQ(1u, dep.implicit.size()); + EXPECT_EQ("phony/foo/target.inputdeps", dep.implicit[0].value()); + ASSERT_EQ(0u, dep.order_only.size()); + EXPECT_EQ( + "build phony/foo/target.inputdeps: phony phony/foo/setup || " + "phony/foo/base\n", + stream.str()); +} + // Tests that validation dependencies are written to the generated Ninja file // with the "|@" syntax for a generic target. TEST(NinjaTargetWriter, WriteValidations) {