| // Copyright 2014 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include <sstream> |
| |
| #include "gn/ninja_action_target_writer.h" |
| #include "gn/ninja_group_target_writer.h" |
| #include "gn/ninja_target_writer.h" |
| #include "gn/target.h" |
| #include "gn/test_with_scope.h" |
| #include "util/test/test.h" |
| |
| namespace { |
| |
| class TestingNinjaTargetWriter : public NinjaTargetWriter { |
| public: |
| TestingNinjaTargetWriter(const Target* target, |
| const Toolchain* toolchain, |
| std::ostream& out) |
| : NinjaTargetWriter(target, out) {} |
| |
| void Run() override {} |
| |
| // Make this public so the test can call it. |
| NinjaTargetWriter::InputDeps WriteInputDepsStampOrPhonyAndGetDep( |
| const std::vector<const Target*>& additional_hard_deps, |
| size_t num_stamp_uses) { |
| return NinjaTargetWriter::WriteInputDepsStampOrPhonyAndGetDep( |
| additional_hard_deps, num_stamp_uses); |
| } |
| |
| void WriteStampOrPhonyForTarget( |
| const std::vector<OutputFile>& deps, |
| const std::vector<OutputFile>& order_only_deps) { |
| NinjaTargetWriter::WriteStampOrPhonyForTarget(deps, order_only_deps); |
| } |
| }; |
| |
| } // namespace |
| |
| TEST(NinjaTargetWriter, ResolvedCreatedOnDemand) { |
| TestWithScope setup; |
| Err err; |
| |
| // Make a base target that's a hard dep (action). |
| 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)); |
| |
| std::ostringstream stream; |
| TestingNinjaTargetWriter writer(&base_target, setup.toolchain(), stream); |
| |
| const auto* resolved_ptr = &writer.resolved(); |
| ASSERT_TRUE(resolved_ptr); |
| |
| // Same address should be returned on second call. |
| EXPECT_EQ(resolved_ptr, &writer.resolved()); |
| } |
| |
| TEST(NinjaTargetWriter, ResolvedSetExplicitly) { |
| TestWithScope setup; |
| Err err; |
| |
| // Make a base target that's a hard dep (action). |
| 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)); |
| |
| ResolvedTargetData resolved; |
| std::ostringstream stream; |
| TestingNinjaTargetWriter writer(&base_target, setup.toolchain(), stream); |
| writer.SetResolvedTargetData(&resolved); |
| |
| EXPECT_EQ(&resolved, &writer.resolved()); |
| } |
| |
| TEST(NinjaTargetWriter, WriteInputDepsStampOrPhonyAndGetDep) { |
| TestWithScope setup; |
| Err err; |
| |
| // Make a base target that's a hard dep (action). |
| 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")); |
| |
| // Dependent target that also includes a source prerequisite (should get |
| // included) and a source (should not be included). |
| Target target(setup.settings(), Label(SourceDir("//foo/"), "target")); |
| target.set_output_type(Target::EXECUTABLE); |
| target.visibility().SetPublic(); |
| target.SetToolchain(setup.toolchain()); |
| target.config_values().inputs().push_back(SourceFile("//foo/input.txt")); |
| target.sources().push_back(SourceFile("//foo/source.txt")); |
| target.public_deps().push_back(LabelTargetPair(&base_target)); |
| |
| // Dependent action to test that action sources will be treated the same as |
| // inputs. |
| Target action(setup.settings(), Label(SourceDir("//foo/"), "action")); |
| action.set_output_type(Target::ACTION); |
| action.visibility().SetPublic(); |
| action.SetToolchain(setup.toolchain()); |
| action.action_values().set_script(SourceFile("//foo/script.py")); |
| action.sources().push_back(SourceFile("//foo/action_source.txt")); |
| action.public_deps().push_back(LabelTargetPair(&target)); |
| |
| ASSERT_TRUE(base_target.OnResolved(&err)); |
| ASSERT_TRUE(target.OnResolved(&err)); |
| ASSERT_TRUE(action.OnResolved(&err)); |
| |
| // Input deps for the base (should be only the script itself). |
| { |
| std::ostringstream stream; |
| TestingNinjaTargetWriter writer(&base_target, setup.toolchain(), stream); |
| 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(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()); |
| } |
| |
| // Input deps for the target (should depend on the base). |
| { |
| std::ostringstream stream; |
| TestingNinjaTargetWriter writer(&target, setup.toolchain(), stream); |
| 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(0u, dep.implicit.size()); |
| ASSERT_EQ(1u, dep.order_only.size()); |
| EXPECT_EQ("phony/foo/base", dep.order_only[0].value()); |
| } |
| |
| { |
| std::ostringstream stream; |
| NinjaActionTargetWriter writer(&action, stream); |
| writer.Run(); |
| EXPECT_EQ( |
| "rule __foo_action___rule\n" |
| " command = ../../foo/script.py\n" |
| " description = ACTION //foo:action()\n" |
| " restat = 1\n" |
| "\n" |
| "build: __foo_action___rule | ../../foo/script.py" |
| " ../../foo/action_source.txt ./target\n" |
| "\n" |
| "build phony/foo/action: phony ./target\n", |
| stream.str()); |
| } |
| |
| // Input deps for action which should depend on the base since its a hard dep |
| // that is a (indirect) dependency, as well as the the action source. |
| { |
| std::ostringstream stream; |
| TestingNinjaTargetWriter writer(&action, setup.toolchain(), stream); |
| auto dep = writer.WriteInputDepsStampOrPhonyAndGetDep( |
| std::vector<const Target*>(), 10u); |
| |
| 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", |
| stream.str()); |
| } |
| } |
| |
| TEST(NinjaTargetWriter, WriteInputDepsStampOrPhonyAndGetDepUseStampFiles) { |
| TestWithScope setup; |
| Err err; |
| setup.build_settings()->set_no_stamp_files(false); |
| |
| // Make a base target that's a hard dep (action). |
| 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")); |
| |
| // Dependent target that also includes a source prerequisite (should get |
| // included) and a source (should not be included). |
| Target target(setup.settings(), Label(SourceDir("//foo/"), "target")); |
| target.set_output_type(Target::EXECUTABLE); |
| target.visibility().SetPublic(); |
| target.SetToolchain(setup.toolchain()); |
| target.config_values().inputs().push_back(SourceFile("//foo/input.txt")); |
| target.sources().push_back(SourceFile("//foo/source.txt")); |
| target.public_deps().push_back(LabelTargetPair(&base_target)); |
| |
| // Dependent action to test that action sources will be treated the same as |
| // inputs. |
| Target action(setup.settings(), Label(SourceDir("//foo/"), "action")); |
| action.set_output_type(Target::ACTION); |
| action.visibility().SetPublic(); |
| action.SetToolchain(setup.toolchain()); |
| action.action_values().set_script(SourceFile("//foo/script.py")); |
| action.sources().push_back(SourceFile("//foo/action_source.txt")); |
| action.public_deps().push_back(LabelTargetPair(&target)); |
| |
| ASSERT_TRUE(base_target.OnResolved(&err)); |
| ASSERT_TRUE(target.OnResolved(&err)); |
| ASSERT_TRUE(action.OnResolved(&err)); |
| |
| // Input deps for the base (should be only the script itself). |
| { |
| std::ostringstream stream; |
| TestingNinjaTargetWriter writer(&base_target, setup.toolchain(), stream); |
| 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(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()); |
| } |
| |
| // Input deps for the target (should depend on the base). |
| { |
| std::ostringstream stream; |
| TestingNinjaTargetWriter writer(&target, setup.toolchain(), stream); |
| 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(0u, dep.implicit.size()); |
| ASSERT_EQ(1u, dep.order_only.size()); |
| EXPECT_EQ("obj/foo/base.stamp", dep.order_only[0].value()); |
| } |
| |
| { |
| std::ostringstream stream; |
| NinjaActionTargetWriter writer(&action, stream); |
| writer.Run(); |
| EXPECT_EQ( |
| "rule __foo_action___rule\n" |
| " command = ../../foo/script.py\n" |
| " description = ACTION //foo:action()\n" |
| " restat = 1\n" |
| "\n" |
| "build: __foo_action___rule | ../../foo/script.py" |
| " ../../foo/action_source.txt ./target\n" |
| "\n" |
| "build obj/foo/action.stamp: stamp ./target\n", |
| stream.str()); |
| } |
| |
| // Input deps for action which should depend on the base since its a hard dep |
| // that is a (indirect) dependency, as well as the the action source. |
| { |
| std::ostringstream stream; |
| TestingNinjaTargetWriter writer(&action, setup.toolchain(), stream); |
| auto dep = writer.WriteInputDepsStampOrPhonyAndGetDep( |
| std::vector<const Target*>(), 10u); |
| |
| 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", |
| stream.str()); |
| } |
| } |
| |
| // Tests WriteInputDepsStampOrPhonyAndGetDep when toolchain deps are present. |
| TEST(NinjaTargetWriter, WriteInputDepsStampOrPhonyAndGetDepWithToolchainDeps) { |
| TestWithScope setup; |
| Err err; |
| |
| // Toolchain dependency. Here we make a target in the same toolchain for |
| // simplicity, but in real life (using the Builder) this would be rejected |
| // because it would be a circular dependency (the target depends on its |
| // toolchain, and the toolchain depends on this target). |
| 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)); |
| |
| // Make a binary target |
| Target target(setup.settings(), Label(SourceDir("//foo/"), "target")); |
| target.set_output_type(Target::EXECUTABLE); |
| target.SetToolchain(setup.toolchain()); |
| 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 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.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) { |
| TestWithScope setup; |
| setup.build_settings()->set_no_stamp_files(false); |
| Err err; |
| |
| Target validation_target(setup.settings(), Label(SourceDir("//foo/"), "val")); |
| validation_target.set_output_type(Target::ACTION); |
| validation_target.visibility().SetPublic(); |
| validation_target.SetToolchain(setup.toolchain()); |
| validation_target.action_values().set_script(SourceFile("//foo/script.py")); |
| ASSERT_TRUE(validation_target.OnResolved(&err)); |
| |
| Target target(setup.settings(), Label(SourceDir("//foo/"), "target")); |
| target.set_output_type(Target::GROUP); |
| target.visibility().SetPublic(); |
| target.SetToolchain(setup.toolchain()); |
| target.validations().push_back(LabelTargetPair(&validation_target)); |
| ASSERT_TRUE(target.OnResolved(&err)); |
| |
| std::ostringstream stream; |
| TestingNinjaTargetWriter writer(&target, setup.toolchain(), stream); |
| |
| std::vector<OutputFile> deps; |
| std::vector<OutputFile> order_only; |
| deps.push_back(OutputFile("obj/foo/target.stamp")); |
| |
| writer.WriteStampOrPhonyForTarget(deps, order_only); |
| |
| std::string out = stream.str(); |
| EXPECT_EQ( |
| "build obj/foo/target.stamp: stamp obj/foo/target.stamp |@ " |
| "obj/foo/val.stamp\n", |
| out); |
| } |
| |
| // Tests that if a validation target has no output (e.g., an empty group), |
| // no validation dependency is written to the Ninja file. |
| TEST(NinjaTargetWriter, ValidationsWithNoOutput) { |
| TestWithScope setup; |
| setup.build_settings()->set_no_stamp_files(true); |
| Err err; |
| |
| // Validation target with no output (empty group, no stamp files). |
| Target validation_target(setup.settings(), Label(SourceDir("//foo/"), "val")); |
| validation_target.set_output_type(Target::GROUP); |
| validation_target.visibility().SetPublic(); |
| validation_target.SetToolchain(setup.toolchain()); |
| ASSERT_TRUE(validation_target.OnResolved(&err)); |
| ASSERT_FALSE(validation_target.has_dependency_output()); |
| |
| // A dependency that HAS an output, so 'target' will have an output alias. |
| Target real_dep(setup.settings(), Label(SourceDir("//foo/"), "dep")); |
| real_dep.set_output_type(Target::ACTION); |
| real_dep.visibility().SetPublic(); |
| real_dep.SetToolchain(setup.toolchain()); |
| real_dep.action_values().set_script(SourceFile("//foo/script.py")); |
| ASSERT_TRUE(real_dep.OnResolved(&err)); |
| |
| Target target(setup.settings(), Label(SourceDir("//foo/"), "target")); |
| target.set_output_type(Target::GROUP); |
| target.visibility().SetPublic(); |
| target.SetToolchain(setup.toolchain()); |
| target.public_deps().push_back(LabelTargetPair(&real_dep)); |
| target.validations().push_back(LabelTargetPair(&validation_target)); |
| ASSERT_TRUE(target.OnResolved(&err)); |
| ASSERT_TRUE(target.has_dependency_output()); |
| |
| std::ostringstream stream; |
| TestingNinjaTargetWriter writer(&target, setup.toolchain(), stream); |
| |
| std::vector<OutputFile> deps; |
| std::vector<OutputFile> order_only; |
| deps.push_back(OutputFile("phony/foo/dep")); |
| |
| writer.WriteStampOrPhonyForTarget(deps, order_only); |
| |
| std::string out = stream.str(); |
| // Should not contain validation separator since the validation target has no |
| // output. |
| EXPECT_EQ("build phony/foo/target: phony phony/foo/dep\n", out); |
| } |
| |
| TEST(NinjaTargetWriter, PhonyPropagation) { |
| TestWithScope setup; |
| Err err; |
| |
| // Set up common target A (action) |
| Target a(setup.settings(), Label(SourceDir("//foo/"), "a")); |
| a.set_output_type(Target::ACTION); |
| a.visibility().SetPublic(); |
| a.SetToolchain(setup.toolchain()); |
| a.action_values().set_script(SourceFile("//foo/script.py")); |
| a.action_values().outputs() = |
| SubstitutionList::MakeForTest("//out/Debug/a.out"); |
| |
| // Set up B_pub (action, public_dep A) |
| Target b_pub(setup.settings(), Label(SourceDir("//foo/"), "b_pub")); |
| b_pub.set_output_type(Target::ACTION); |
| b_pub.visibility().SetPublic(); |
| b_pub.SetToolchain(setup.toolchain()); |
| b_pub.action_values().set_script(SourceFile("//foo/script.py")); |
| b_pub.action_values().outputs() = |
| SubstitutionList::MakeForTest("//out/Debug/b_pub.out"); |
| b_pub.public_deps().push_back(LabelTargetPair(&a)); |
| |
| // Set up B_priv (action, private_dep A) |
| Target b_priv(setup.settings(), Label(SourceDir("//foo/"), "b_priv")); |
| b_priv.set_output_type(Target::ACTION); |
| b_priv.visibility().SetPublic(); |
| b_priv.SetToolchain(setup.toolchain()); |
| b_priv.action_values().set_script(SourceFile("//foo/script.py")); |
| b_priv.action_values().outputs() = |
| SubstitutionList::MakeForTest("//out/Debug/b_priv.out"); |
| b_priv.private_deps().push_back(LabelTargetPair(&a)); |
| |
| // Set up C_pub (action, private_dep B_pub) |
| Target c_pub(setup.settings(), Label(SourceDir("//foo/"), "c_pub")); |
| c_pub.set_output_type(Target::ACTION); |
| c_pub.visibility().SetPublic(); |
| c_pub.SetToolchain(setup.toolchain()); |
| c_pub.action_values().set_script(SourceFile("//foo/script.py")); |
| c_pub.private_deps().push_back(LabelTargetPair(&b_pub)); |
| |
| // Set up C_priv (action, private_dep B_priv) |
| Target c_priv(setup.settings(), Label(SourceDir("//foo/"), "c_priv")); |
| c_priv.set_output_type(Target::ACTION); |
| c_priv.visibility().SetPublic(); |
| c_priv.SetToolchain(setup.toolchain()); |
| c_priv.action_values().set_script(SourceFile("//foo/script.py")); |
| c_priv.private_deps().push_back(LabelTargetPair(&b_priv)); |
| |
| // Set up B_data (action, data_dep A) |
| Target b_data(setup.settings(), Label(SourceDir("//foo/"), "b_data")); |
| b_data.set_output_type(Target::ACTION); |
| b_data.visibility().SetPublic(); |
| b_data.SetToolchain(setup.toolchain()); |
| b_data.action_values().set_script(SourceFile("//foo/script.py")); |
| b_data.action_values().outputs() = |
| SubstitutionList::MakeForTest("//out/Debug/b_data.out"); |
| b_data.data_deps().push_back(LabelTargetPair(&a)); |
| |
| // Set up C_data (action, public_dep B_data) |
| Target c_data(setup.settings(), Label(SourceDir("//foo/"), "c_data")); |
| c_data.set_output_type(Target::ACTION); |
| c_data.visibility().SetPublic(); |
| c_data.SetToolchain(setup.toolchain()); |
| c_data.action_values().set_script(SourceFile("//foo/script.py")); |
| c_data.action_values().outputs() = |
| SubstitutionList::MakeForTest("//out/Debug/c_data.out"); |
| c_data.public_deps().push_back(LabelTargetPair(&b_data)); |
| |
| // Set up D_data (action, private_dep C_data) |
| Target d_data(setup.settings(), Label(SourceDir("//foo/"), "d_data")); |
| d_data.set_output_type(Target::ACTION); |
| d_data.visibility().SetPublic(); |
| d_data.SetToolchain(setup.toolchain()); |
| d_data.action_values().set_script(SourceFile("//foo/script.py")); |
| d_data.private_deps().push_back(LabelTargetPair(&c_data)); |
| |
| ASSERT_TRUE(a.OnResolved(&err)); |
| ASSERT_TRUE(b_pub.OnResolved(&err)); |
| ASSERT_TRUE(b_priv.OnResolved(&err)); |
| ASSERT_TRUE(c_pub.OnResolved(&err)); |
| ASSERT_TRUE(c_priv.OnResolved(&err)); |
| ASSERT_TRUE(b_data.OnResolved(&err)); |
| ASSERT_TRUE(c_data.OnResolved(&err)); |
| ASSERT_TRUE(d_data.OnResolved(&err)); |
| |
| // 1. Verify B_pub's stamp/phony target. It should contain A's output |
| // (phony/foo/a). |
| { |
| std::ostringstream stream; |
| NinjaActionTargetWriter writer(&b_pub, stream); |
| writer.Run(); |
| EXPECT_EQ( |
| "rule __foo_b_pub___rule\n" |
| " command = ../../foo/script.py\n" |
| " description = ACTION //foo:b_pub()\n" |
| " restat = 1\n" |
| "\n" |
| "build b_pub.out: __foo_b_pub___rule | ../../foo/script.py " |
| "phony/foo/a\n" |
| "\n" |
| "build phony/foo/b_pub: phony b_pub.out phony/foo/a\n", |
| stream.str()); |
| } |
| |
| // 2. Verify B_priv's stamp/phony target. It should NOT contain A's output. |
| { |
| std::ostringstream stream; |
| NinjaActionTargetWriter writer(&b_priv, stream); |
| writer.Run(); |
| EXPECT_EQ( |
| "rule __foo_b_priv___rule\n" |
| " command = ../../foo/script.py\n" |
| " description = ACTION //foo:b_priv()\n" |
| " restat = 1\n" |
| "\n" |
| "build b_priv.out: __foo_b_priv___rule | ../../foo/script.py " |
| "phony/foo/a\n" |
| "\n" |
| "build phony/foo/b_priv: phony b_priv.out\n", |
| stream.str()); |
| } |
| |
| // 3. Verify C_pub's inputdeps contains B_pub's stamp/phony. |
| { |
| std::ostringstream stream; |
| TestingNinjaTargetWriter writer(&c_pub, setup.toolchain(), stream); |
| auto dep = writer.WriteInputDepsStampOrPhonyAndGetDep( |
| std::vector<const Target*>(), 10u); |
| |
| ASSERT_EQ(1u, dep.implicit.size()); |
| EXPECT_EQ("phony/foo/c_pub.inputdeps", dep.implicit[0].value()); |
| EXPECT_EQ( |
| "build phony/foo/c_pub.inputdeps: phony ../../foo/script.py " |
| "phony/foo/b_pub\n", |
| stream.str()); |
| } |
| |
| // 4. Verify C_priv's inputdeps contains B_priv's stamp/phony. |
| { |
| std::ostringstream stream; |
| TestingNinjaTargetWriter writer(&c_priv, setup.toolchain(), stream); |
| auto dep = writer.WriteInputDepsStampOrPhonyAndGetDep( |
| std::vector<const Target*>(), 10u); |
| |
| ASSERT_EQ(1u, dep.implicit.size()); |
| EXPECT_EQ("phony/foo/c_priv.inputdeps", dep.implicit[0].value()); |
| EXPECT_EQ( |
| "build phony/foo/c_priv.inputdeps: phony ../../foo/script.py " |
| "phony/foo/b_priv\n", |
| stream.str()); |
| } |
| |
| // 5. Verify B_data's stamp/phony target. It should NOT contain A's output |
| // in implicit deps (since A is a data_dep), but in order_only_deps. |
| { |
| std::ostringstream stream; |
| NinjaActionTargetWriter writer(&b_data, stream); |
| writer.Run(); |
| EXPECT_EQ( |
| "rule __foo_b_data___rule\n" |
| " command = ../../foo/script.py\n" |
| " description = ACTION //foo:b_data()\n" |
| " restat = 1\n" |
| "\n" |
| "build b_data.out: __foo_b_data___rule | ../../foo/script.py || " |
| "phony/foo/a\n" |
| "\n" |
| "build phony/foo/b_data: phony b_data.out\n", |
| stream.str()); |
| } |
| |
| // 6. Verify C_data's stamp/phony target. It should contain B_data, but NOT A. |
| { |
| std::ostringstream stream; |
| NinjaActionTargetWriter writer(&c_data, stream); |
| writer.Run(); |
| EXPECT_EQ( |
| "rule __foo_c_data___rule\n" |
| " command = ../../foo/script.py\n" |
| " description = ACTION //foo:c_data()\n" |
| " restat = 1\n" |
| "\n" |
| "build c_data.out: __foo_c_data___rule | ../../foo/script.py " |
| "phony/foo/b_data\n" |
| "\n" |
| "build phony/foo/c_data: phony c_data.out phony/foo/b_data\n", |
| stream.str()); |
| } |
| |
| // 7. Verify D_data's inputdeps contains C_data, but NOT A. |
| { |
| std::ostringstream stream; |
| TestingNinjaTargetWriter writer(&d_data, setup.toolchain(), stream); |
| auto dep = writer.WriteInputDepsStampOrPhonyAndGetDep( |
| std::vector<const Target*>(), 10u); |
| |
| ASSERT_EQ(1u, dep.implicit.size()); |
| EXPECT_EQ("phony/foo/d_data.inputdeps", dep.implicit[0].value()); |
| EXPECT_EQ( |
| "build phony/foo/d_data.inputdeps: phony ../../foo/script.py " |
| "phony/foo/c_data\n", |
| stream.str()); |
| } |
| } |
| |
| TEST(NinjaTargetWriter, PublicInputs) { |
| TestWithScope setup; |
| Err err; |
| |
| // Action A has public_inputs. |
| Target a(setup.settings(), Label(SourceDir("//foo/"), "a")); |
| a.set_output_type(Target::ACTION); |
| a.visibility().SetPublic(); |
| a.SetToolchain(setup.toolchain()); |
| a.action_values().set_script(SourceFile("//foo/script.py")); |
| a.public_inputs().push_back(SourceFile("//foo/a.in")); |
| |
| // Action B depends on A. |
| Target b(setup.settings(), Label(SourceDir("//foo/"), "b")); |
| b.set_output_type(Target::ACTION); |
| b.visibility().SetPublic(); |
| b.SetToolchain(setup.toolchain()); |
| b.action_values().set_script(SourceFile("//foo/script.py")); |
| b.private_deps().push_back(LabelTargetPair(&a)); |
| |
| ASSERT_TRUE(a.OnResolved(&err)); |
| ASSERT_TRUE(b.OnResolved(&err)); |
| |
| // 1. Verify A's public_inputs phony/stamp target is written. |
| { |
| std::ostringstream stream; |
| ResolvedTargetData resolved; |
| TestingNinjaTargetWriter::WritePublicInputsStampOrPhony(&a, &resolved, |
| stream); |
| EXPECT_EQ("build phony/foo/a.public_inputs: phony ../../foo/a.in\n\n", |
| stream.str()); |
| } |
| |
| // 2. Verify B's input deps. It should depend on A's public_inputs. |
| { |
| std::ostringstream stream; |
| TestingNinjaTargetWriter writer(&b, setup.toolchain(), stream); |
| auto dep = writer.WriteInputDepsStampOrPhonyAndGetDep( |
| std::vector<const Target*>(), 10u); |
| |
| ASSERT_EQ(1u, dep.implicit.size()); |
| EXPECT_EQ("phony/foo/b.inputdeps", dep.implicit[0].value()); |
| |
| std::string out = stream.str(); |
| EXPECT_TRUE(out.find("phony/foo/a.public_inputs") != std::string::npos) |
| << out; |
| EXPECT_TRUE(out.find("../../foo/a.in") == std::string::npos) << out; |
| } |
| } |