| // Copyright (c) 2013 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 "gn/target.h" | |
| #include <memory> | |
| #include <utility> | |
| #include "gn/build_settings.h" | |
| #include "gn/config.h" | |
| #include "gn/resolved_target_data.h" | |
| #include "gn/scheduler.h" | |
| #include "gn/settings.h" | |
| #include "gn/test_with_scheduler.h" | |
| #include "gn/test_with_scope.h" | |
| #include "gn/toolchain.h" | |
| #include "util/test/test.h" | |
| namespace { | |
| // Asserts that the current global scheduler has a single unknown generated | |
| // file with the given name from the given target. | |
| void AssertSchedulerHasOneUnknownFileMatching(const Target* target, | |
| const SourceFile& file) { | |
| auto unknown = g_scheduler->GetUnknownGeneratedInputs(); | |
| ASSERT_EQ(1u, unknown.size()); // Should be one unknown file. | |
| auto found = unknown.find(file); | |
| ASSERT_TRUE(found != unknown.end()) << file.value(); | |
| EXPECT_TRUE(target == found->second) | |
| << "Target doesn't match. Expected\n " | |
| << target->label().GetUserVisibleName(false) << "\nBut got\n " | |
| << found->second->label().GetUserVisibleName(false); | |
| } | |
| } // namespace | |
| using TargetTest = TestWithScheduler; | |
| // Test all_dependent_configs and public_config inheritance. | |
| TEST_F(TargetTest, DependentConfigs) { | |
| TestWithScope setup; | |
| Err err; | |
| // Set up a dependency chain of a -> b -> c | |
| TestTarget a(setup, "//foo:a", Target::EXECUTABLE); | |
| TestTarget b(setup, "//foo:b", Target::STATIC_LIBRARY); | |
| TestTarget c(setup, "//foo:c", Target::STATIC_LIBRARY); | |
| a.private_deps().push_back(LabelTargetPair(&b)); | |
| b.private_deps().push_back(LabelTargetPair(&c)); | |
| // Normal non-inherited config. | |
| Config config(setup.settings(), Label(SourceDir("//foo/"), "config")); | |
| config.visibility().SetPublic(); | |
| ASSERT_TRUE(config.OnResolved(&err)); | |
| c.configs().push_back(LabelConfigPair(&config)); | |
| // All dependent config. | |
| Config all(setup.settings(), Label(SourceDir("//foo/"), "all")); | |
| all.visibility().SetPublic(); | |
| ASSERT_TRUE(all.OnResolved(&err)); | |
| c.all_dependent_configs().push_back(LabelConfigPair(&all)); | |
| // Direct dependent config. | |
| Config direct(setup.settings(), Label(SourceDir("//foo/"), "direct")); | |
| direct.visibility().SetPublic(); | |
| ASSERT_TRUE(direct.OnResolved(&err)); | |
| c.public_configs().push_back(LabelConfigPair(&direct)); | |
| ASSERT_TRUE(c.OnResolved(&err)); | |
| ASSERT_TRUE(b.OnResolved(&err)); | |
| ASSERT_TRUE(a.OnResolved(&err)); | |
| // B should have gotten both dependent configs from C. | |
| ASSERT_EQ(2u, b.configs().size()); | |
| EXPECT_EQ(&all, b.configs()[0].ptr); | |
| EXPECT_EQ(&direct, b.configs()[1].ptr); | |
| ASSERT_EQ(1u, b.all_dependent_configs().size()); | |
| EXPECT_EQ(&all, b.all_dependent_configs()[0].ptr); | |
| // A should have just gotten the "all" dependent config from C. | |
| ASSERT_EQ(1u, a.configs().size()); | |
| EXPECT_EQ(&all, a.configs()[0].ptr); | |
| EXPECT_EQ(&all, a.all_dependent_configs()[0].ptr); | |
| // Making an an alternate A and B with B forwarding the direct dependents. | |
| TestTarget a_fwd(setup, "//foo:a_fwd", Target::EXECUTABLE); | |
| TestTarget b_fwd(setup, "//foo:b_fwd", Target::STATIC_LIBRARY); | |
| a_fwd.private_deps().push_back(LabelTargetPair(&b_fwd)); | |
| b_fwd.private_deps().push_back(LabelTargetPair(&c)); | |
| ASSERT_TRUE(b_fwd.OnResolved(&err)); | |
| ASSERT_TRUE(a_fwd.OnResolved(&err)); | |
| // A_fwd should now have both configs. | |
| ASSERT_EQ(1u, a_fwd.configs().size()); | |
| EXPECT_EQ(&all, a_fwd.configs()[0].ptr); | |
| ASSERT_EQ(1u, a_fwd.all_dependent_configs().size()); | |
| EXPECT_EQ(&all, a_fwd.all_dependent_configs()[0].ptr); | |
| } | |
| // Tests that dependent configs don't propagate between toolchains. | |
| TEST_F(TargetTest, NoDependentConfigsBetweenToolchains) { | |
| TestWithScope setup; | |
| Err err; | |
| // Create another toolchain. | |
| Toolchain other_toolchain(setup.settings(), | |
| Label(SourceDir("//other/"), "toolchain")); | |
| TestWithScope::SetupToolchain(&other_toolchain); | |
| // Set up a dependency chain of |a| -> |b| -> |c| where |a| has a different | |
| // toolchain. | |
| Target a(setup.settings(), | |
| Label(SourceDir("//foo/"), "a", other_toolchain.label().dir(), | |
| other_toolchain.label().name())); | |
| a.set_output_type(Target::EXECUTABLE); | |
| EXPECT_TRUE(a.SetToolchain(&other_toolchain, &err)); | |
| TestTarget b(setup, "//foo:b", Target::EXECUTABLE); | |
| TestTarget c(setup, "//foo:c", Target::SOURCE_SET); | |
| a.private_deps().push_back(LabelTargetPair(&b)); | |
| b.private_deps().push_back(LabelTargetPair(&c)); | |
| // All dependent config. | |
| Config all_dependent(setup.settings(), Label(SourceDir("//foo/"), "all")); | |
| all_dependent.visibility().SetPublic(); | |
| ASSERT_TRUE(all_dependent.OnResolved(&err)); | |
| c.all_dependent_configs().push_back(LabelConfigPair(&all_dependent)); | |
| // Public config. | |
| Config public_config(setup.settings(), Label(SourceDir("//foo/"), "public")); | |
| public_config.visibility().SetPublic(); | |
| ASSERT_TRUE(public_config.OnResolved(&err)); | |
| c.public_configs().push_back(LabelConfigPair(&public_config)); | |
| // Another public config. | |
| Config public_config2(setup.settings(), | |
| Label(SourceDir("//foo/"), "public2")); | |
| public_config2.visibility().SetPublic(); | |
| ASSERT_TRUE(public_config2.OnResolved(&err)); | |
| b.public_configs().push_back(LabelConfigPair(&public_config2)); | |
| ASSERT_TRUE(c.OnResolved(&err)); | |
| ASSERT_TRUE(b.OnResolved(&err)); | |
| ASSERT_TRUE(a.OnResolved(&err)); | |
| // B should have gotten the configs from C. | |
| ASSERT_EQ(3u, b.configs().size()); | |
| EXPECT_EQ(&public_config2, b.configs()[0].ptr); | |
| EXPECT_EQ(&all_dependent, b.configs()[1].ptr); | |
| EXPECT_EQ(&public_config, b.configs()[2].ptr); | |
| ASSERT_EQ(1u, b.all_dependent_configs().size()); | |
| EXPECT_EQ(&all_dependent, b.all_dependent_configs()[0].ptr); | |
| // A should not have gotten any configs from B or C. | |
| ASSERT_EQ(0u, a.configs().size()); | |
| ASSERT_EQ(0u, a.all_dependent_configs().size()); | |
| } | |
| // Tests that dependent configs propagate between toolchains if | |
| // propagates_configs is set. | |
| TEST_F(TargetTest, DependentConfigsBetweenToolchainsWhenSet) { | |
| TestWithScope setup; | |
| Err err; | |
| // Create another toolchain. | |
| Toolchain other_toolchain(setup.settings(), | |
| Label(SourceDir("//other/"), "toolchain")); | |
| TestWithScope::SetupToolchain(&other_toolchain); | |
| other_toolchain.set_propagates_configs(true); | |
| // Set up a dependency chain of |a| -> |b| where |b| has a different | |
| // toolchain (with propagate_configs set). | |
| TestTarget a(setup, "//foo:a", Target::EXECUTABLE); | |
| Target b(setup.settings(), | |
| Label(SourceDir("//foo/"), "b", other_toolchain.label().dir(), | |
| other_toolchain.label().name())); | |
| b.visibility().SetPublic(); | |
| b.set_output_type(Target::SHARED_LIBRARY); | |
| EXPECT_TRUE(b.SetToolchain(&other_toolchain, &err)); | |
| a.private_deps().push_back(LabelTargetPair(&b)); | |
| // All dependent config. | |
| Config all_dependent(setup.settings(), Label(SourceDir("//foo/"), "all")); | |
| all_dependent.visibility().SetPublic(); | |
| ASSERT_TRUE(all_dependent.OnResolved(&err)); | |
| b.all_dependent_configs().push_back(LabelConfigPair(&all_dependent)); | |
| // Public config. | |
| Config public_config(setup.settings(), Label(SourceDir("//foo/"), "public")); | |
| public_config.visibility().SetPublic(); | |
| ASSERT_TRUE(public_config.OnResolved(&err)); | |
| b.public_configs().push_back(LabelConfigPair(&public_config)); | |
| ASSERT_TRUE(b.OnResolved(&err)); | |
| ASSERT_TRUE(a.OnResolved(&err)); | |
| // A should have gotten the configs from B. | |
| ASSERT_EQ(2u, a.configs().size()); | |
| EXPECT_EQ(&all_dependent, a.configs()[0].ptr); | |
| EXPECT_EQ(&public_config, a.configs()[1].ptr); | |
| ASSERT_EQ(1u, a.all_dependent_configs().size()); | |
| EXPECT_EQ(&all_dependent, a.all_dependent_configs()[0].ptr); | |
| } | |
| TEST_F(TargetTest, GetComputedOutputName) { | |
| TestWithScope setup; | |
| Err err; | |
| // Basic target with no prefix (executable type tool in the TestWithScope has | |
| // no prefix) or output name. | |
| TestTarget basic(setup, "//foo:bar", Target::EXECUTABLE); | |
| ASSERT_TRUE(basic.OnResolved(&err)); | |
| EXPECT_EQ("bar", basic.GetComputedOutputName()); | |
| // Target with no prefix but an output name. | |
| TestTarget with_name(setup, "//foo:bar", Target::EXECUTABLE); | |
| with_name.set_output_name("myoutput"); | |
| ASSERT_TRUE(with_name.OnResolved(&err)); | |
| EXPECT_EQ("myoutput", with_name.GetComputedOutputName()); | |
| // Target with a "lib" prefix (the static library tool in the TestWithScope | |
| // should specify a "lib" output prefix). | |
| TestTarget with_prefix(setup, "//foo:bar", Target::STATIC_LIBRARY); | |
| ASSERT_TRUE(with_prefix.OnResolved(&err)); | |
| EXPECT_EQ("libbar", with_prefix.GetComputedOutputName()); | |
| // Target with a "lib" prefix that already has it applied. The prefix should | |
| // not duplicate something already in the target name. | |
| TestTarget dup_prefix(setup, "//foo:bar", Target::STATIC_LIBRARY); | |
| dup_prefix.set_output_name("libbar"); | |
| ASSERT_TRUE(dup_prefix.OnResolved(&err)); | |
| EXPECT_EQ("libbar", dup_prefix.GetComputedOutputName()); | |
| // Target with an output prefix override should not have a prefix. | |
| TestTarget override_prefix(setup, "//foo:bar", Target::SHARED_LIBRARY); | |
| override_prefix.set_output_prefix_override(true); | |
| ASSERT_TRUE(dup_prefix.OnResolved(&err)); | |
| EXPECT_EQ("bar", override_prefix.GetComputedOutputName()); | |
| } | |
| // Test visibility failure case. | |
| TEST_F(TargetTest, VisibilityFails) { | |
| TestWithScope setup; | |
| Err err; | |
| TestTarget b(setup, "//private:b", Target::STATIC_LIBRARY); | |
| b.visibility().SetPrivate(b.label().dir()); | |
| ASSERT_TRUE(b.OnResolved(&err)); | |
| // Make a target depending on "b". The dependency must have an origin to mark | |
| // it as user-set so we check visibility. This check should fail. | |
| TestTarget a(setup, "//app:a", Target::EXECUTABLE); | |
| a.private_deps().push_back(LabelTargetPair(&b)); | |
| IdentifierNode origin; // Dummy origin. | |
| a.private_deps()[0].origin = &origin; | |
| ASSERT_FALSE(a.OnResolved(&err)); | |
| } | |
| // Test config visibility failure cases. | |
| TEST_F(TargetTest, VisibilityConfigFails) { | |
| TestWithScope setup; | |
| Err err; | |
| Label config_label(SourceDir("//a/"), "config"); | |
| Config config(setup.settings(), config_label); | |
| config.visibility().SetPrivate(config.label().dir()); | |
| ASSERT_TRUE(config.OnResolved(&err)); | |
| // Make a target using configs. This should fail. | |
| TestTarget a(setup, "//app:a", Target::EXECUTABLE); | |
| a.configs().push_back(LabelConfigPair(&config)); | |
| ASSERT_FALSE(a.OnResolved(&err)); | |
| // A target using public_configs should also fail. | |
| TestTarget b(setup, "//app:b", Target::EXECUTABLE); | |
| b.public_configs().push_back(LabelConfigPair(&config)); | |
| ASSERT_FALSE(b.OnResolved(&err)); | |
| // A target using all_dependent_configs should fail as well. | |
| TestTarget c(setup, "//app:c", Target::EXECUTABLE); | |
| c.all_dependent_configs().push_back(LabelConfigPair(&config)); | |
| ASSERT_FALSE(c.OnResolved(&err)); | |
| } | |
| // Test Config -> Group -> A where the config is group is visible from A but | |
| // the config isn't, and the config is visible from the group. | |
| TEST_F(TargetTest, VisibilityConfigGroup) { | |
| TestWithScope setup; | |
| Err err; | |
| Label config_label(SourceDir("//a/"), "config"); | |
| Config config(setup.settings(), config_label); | |
| config.visibility().SetPrivate(config.label().dir()); | |
| ASSERT_TRUE(config.OnResolved(&err)); | |
| // Make a target using the config in the same directory. | |
| TestTarget a(setup, "//a:a", Target::GROUP); | |
| a.public_configs().push_back(LabelConfigPair(&config)); | |
| ASSERT_TRUE(a.OnResolved(&err)); | |
| // A target depending on a should be okay. | |
| TestTarget b(setup, "//app:b", Target::EXECUTABLE); | |
| b.private_deps().push_back(LabelTargetPair(&a)); | |
| ASSERT_TRUE(b.OnResolved(&err)); | |
| } | |
| // Test visibility with a single data_dep. | |
| TEST_F(TargetTest, VisibilityDatadeps) { | |
| TestWithScope setup; | |
| Err err; | |
| TestTarget b(setup, "//public:b", Target::STATIC_LIBRARY); | |
| ASSERT_TRUE(b.OnResolved(&err)); | |
| // Make a target depending on "b". The dependency must have an origin to mark | |
| // it as user-set so we check visibility. This check should fail. | |
| TestTarget a(setup, "//app:a", Target::EXECUTABLE); | |
| a.data_deps().push_back(LabelTargetPair(&b)); | |
| IdentifierNode origin; // Dummy origin. | |
| a.data_deps()[0].origin = &origin; | |
| ASSERT_TRUE(a.OnResolved(&err)) << err.help_text(); | |
| } | |
| // Tests that A -> Group -> B where the group is visible from A but B isn't, | |
| // passes visibility even though the group's deps get expanded into A. | |
| TEST_F(TargetTest, VisibilityGroup) { | |
| TestWithScope setup; | |
| Err err; | |
| IdentifierNode origin; // Dummy origin. | |
| // B has private visibility. This lets the group see it since the group is in | |
| // the same directory. | |
| TestTarget b(setup, "//private:b", Target::STATIC_LIBRARY); | |
| b.visibility().SetPrivate(b.label().dir()); | |
| ASSERT_TRUE(b.OnResolved(&err)); | |
| // The group has public visibility and depends on b. | |
| TestTarget g(setup, "//public:g", Target::GROUP); | |
| g.private_deps().push_back(LabelTargetPair(&b)); | |
| g.private_deps()[0].origin = &origin; | |
| ASSERT_TRUE(b.OnResolved(&err)); | |
| // Make a target depending on "g". This should succeed. | |
| TestTarget a(setup, "//app:a", Target::EXECUTABLE); | |
| a.private_deps().push_back(LabelTargetPair(&g)); | |
| a.private_deps()[0].origin = &origin; | |
| ASSERT_TRUE(a.OnResolved(&err)); | |
| } | |
| // Verifies that only testonly targets can depend on other testonly targets. | |
| // Many of the above dependency checking cases covered the non-testonly | |
| // case. | |
| TEST_F(TargetTest, Testonly) { | |
| TestWithScope setup; | |
| Err err; | |
| // "testlib" is a test-only library. | |
| TestTarget testlib(setup, "//test:testlib", Target::STATIC_LIBRARY); | |
| testlib.set_testonly(true); | |
| ASSERT_TRUE(testlib.OnResolved(&err)); | |
| // "test" is a test-only executable depending on testlib, this is OK. | |
| TestTarget test(setup, "//test:test", Target::EXECUTABLE); | |
| test.set_testonly(true); | |
| test.private_deps().push_back(LabelTargetPair(&testlib)); | |
| ASSERT_TRUE(test.OnResolved(&err)); | |
| // "product" is a non-test depending on testlib. This should fail. | |
| TestTarget product(setup, "//app:product", Target::EXECUTABLE); | |
| product.set_testonly(false); | |
| product.private_deps().push_back(LabelTargetPair(&testlib)); | |
| ASSERT_FALSE(product.OnResolved(&err)); | |
| // "package" is a non-test with a testonly validation, this is OK. | |
| TestTarget package(setup, "//app:package", Target::EXECUTABLE); | |
| package.set_testonly(false); | |
| package.validations().push_back(LabelTargetPair(&test)); | |
| ASSERT_TRUE(package.OnResolved(&err)); | |
| } | |
| // Configs can be testonly too. | |
| // Repeat the testonly test with a config. | |
| TEST_F(TargetTest, TestonlyConfig) { | |
| TestWithScope setup; | |
| Err err; | |
| // "testconfig" is a test-only config. | |
| Config testconfig(setup.settings(), Label(SourceDir("//test/"), "config")); | |
| testconfig.set_testonly(true); | |
| testconfig.visibility().SetPublic(); | |
| ASSERT_TRUE(testconfig.OnResolved(&err)); | |
| // "test" is a test-only executable that uses testconfig, this is OK. | |
| TestTarget test(setup, "//test:test", Target::EXECUTABLE); | |
| test.set_testonly(true); | |
| test.configs().push_back(LabelConfigPair(&testconfig)); | |
| ASSERT_TRUE(test.OnResolved(&err)); | |
| // "product" is a non-test that uses testconfig. This should fail. | |
| TestTarget product(setup, "//app:product", Target::EXECUTABLE); | |
| product.set_testonly(false); | |
| product.configs().push_back(LabelConfigPair(&testconfig)); | |
| ASSERT_FALSE(product.OnResolved(&err)); | |
| } | |
| TEST_F(TargetTest, PublicConfigs) { | |
| TestWithScope setup; | |
| Err err; | |
| Label pub_config_label(SourceDir("//a/"), "pubconfig"); | |
| Config pub_config(setup.settings(), pub_config_label); | |
| pub_config.visibility().SetPublic(); | |
| LibFile lib_name("testlib"); | |
| pub_config.own_values().libs().push_back(lib_name); | |
| ASSERT_TRUE(pub_config.OnResolved(&err)); | |
| // This is the destination target that has a public config. | |
| TestTarget dest(setup, "//a:a", Target::SOURCE_SET); | |
| dest.public_configs().push_back(LabelConfigPair(&pub_config)); | |
| ASSERT_TRUE(dest.OnResolved(&err)); | |
| // This target has a public dependency on dest. | |
| TestTarget pub(setup, "//a:pub", Target::SOURCE_SET); | |
| pub.public_deps().push_back(LabelTargetPair(&dest)); | |
| ASSERT_TRUE(pub.OnResolved(&err)); | |
| // Depending on the target with the public dependency should forward dest's | |
| // to the current target. | |
| TestTarget dep_on_pub(setup, "//a:dop", Target::SOURCE_SET); | |
| dep_on_pub.private_deps().push_back(LabelTargetPair(&pub)); | |
| ASSERT_TRUE(dep_on_pub.OnResolved(&err)); | |
| ASSERT_EQ(1u, dep_on_pub.configs().size()); | |
| EXPECT_EQ(&pub_config, dep_on_pub.configs()[0].ptr); | |
| // Libs have special handling, check that they were forwarded from the | |
| // public config to all_libs. | |
| ResolvedTargetData resolved; | |
| const auto& dep_on_pub_all_libs = resolved.GetLinkedLibraries(&dep_on_pub); | |
| ASSERT_EQ(1u, dep_on_pub_all_libs.size()); | |
| ASSERT_EQ(lib_name, dep_on_pub_all_libs[0]); | |
| // This target has a private dependency on dest for forwards configs. | |
| TestTarget forward(setup, "//a:f", Target::SOURCE_SET); | |
| forward.private_deps().push_back(LabelTargetPair(&dest)); | |
| ASSERT_TRUE(forward.OnResolved(&err)); | |
| } | |
| // Tests that configs are ordered properly between local and pulled ones. | |
| TEST_F(TargetTest, ConfigOrdering) { | |
| TestWithScope setup; | |
| Err err; | |
| // Make Dep1. It has all_dependent_configs and public_configs. | |
| TestTarget dep1(setup, "//:dep1", Target::SOURCE_SET); | |
| Label dep1_all_config_label(SourceDir("//"), "dep1_all_config"); | |
| Config dep1_all_config(setup.settings(), dep1_all_config_label); | |
| dep1_all_config.visibility().SetPublic(); | |
| ASSERT_TRUE(dep1_all_config.OnResolved(&err)); | |
| dep1.all_dependent_configs().push_back(LabelConfigPair(&dep1_all_config)); | |
| Label dep1_public_config_label(SourceDir("//"), "dep1_public_config"); | |
| Config dep1_public_config(setup.settings(), dep1_public_config_label); | |
| dep1_public_config.visibility().SetPublic(); | |
| ASSERT_TRUE(dep1_public_config.OnResolved(&err)); | |
| dep1.public_configs().push_back(LabelConfigPair(&dep1_public_config)); | |
| ASSERT_TRUE(dep1.OnResolved(&err)); | |
| // Make Dep2 with the same structure. | |
| TestTarget dep2(setup, "//:dep2", Target::SOURCE_SET); | |
| Label dep2_all_config_label(SourceDir("//"), "dep2_all_config"); | |
| Config dep2_all_config(setup.settings(), dep2_all_config_label); | |
| dep2_all_config.visibility().SetPublic(); | |
| ASSERT_TRUE(dep2_all_config.OnResolved(&err)); | |
| dep2.all_dependent_configs().push_back(LabelConfigPair(&dep2_all_config)); | |
| Label dep2_public_config_label(SourceDir("//"), "dep2_public_config"); | |
| Config dep2_public_config(setup.settings(), dep2_public_config_label); | |
| dep2_public_config.visibility().SetPublic(); | |
| ASSERT_TRUE(dep2_public_config.OnResolved(&err)); | |
| dep2.public_configs().push_back(LabelConfigPair(&dep2_public_config)); | |
| ASSERT_TRUE(dep2.OnResolved(&err)); | |
| // This target depends on both previous targets. | |
| TestTarget target(setup, "//:foo", Target::SOURCE_SET); | |
| target.private_deps().push_back(LabelTargetPair(&dep1)); | |
| target.private_deps().push_back(LabelTargetPair(&dep2)); | |
| // It also has a private and public config. | |
| Label public_config_label(SourceDir("//"), "public"); | |
| Config public_config(setup.settings(), public_config_label); | |
| public_config.visibility().SetPublic(); | |
| ASSERT_TRUE(public_config.OnResolved(&err)); | |
| target.public_configs().push_back(LabelConfigPair(&public_config)); | |
| Label private_config_label(SourceDir("//"), "private"); | |
| Config private_config(setup.settings(), private_config_label); | |
| private_config.visibility().SetPublic(); | |
| ASSERT_TRUE(private_config.OnResolved(&err)); | |
| target.configs().push_back(LabelConfigPair(&private_config)); | |
| // Resolve to get the computed list of configs applying. | |
| ASSERT_TRUE(target.OnResolved(&err)); | |
| const auto& computed = target.configs(); | |
| // Order should be: | |
| // 1. local private | |
| // 2. local public | |
| // 3. inherited all dependent | |
| // 4. inherited public | |
| ASSERT_EQ(6u, computed.size()); | |
| EXPECT_EQ(private_config_label, computed[0].label); | |
| EXPECT_EQ(public_config_label, computed[1].label); | |
| EXPECT_EQ(dep1_all_config_label, computed[2].label); | |
| EXPECT_EQ(dep2_all_config_label, computed[3].label); | |
| EXPECT_EQ(dep1_public_config_label, computed[4].label); | |
| EXPECT_EQ(dep2_public_config_label, computed[5].label); | |
| } | |
| // Tests that different link/depend outputs work for solink tools. | |
| TEST_F(TargetTest, LinkAndDepOutputs) { | |
| TestWithScope setup; | |
| Err err; | |
| Toolchain toolchain(setup.settings(), Label(SourceDir("//tc/"), "tc")); | |
| std::unique_ptr<Tool> solink = Tool::CreateTool(CTool::kCToolSolink); | |
| CTool* solink_tool = solink->AsC(); | |
| solink_tool->set_output_prefix("lib"); | |
| solink_tool->set_default_output_extension(".so"); | |
| const char kLinkPattern[] = | |
| "{{root_out_dir}}/{{target_output_name}}{{output_extension}}"; | |
| SubstitutionPattern link_output = | |
| SubstitutionPattern::MakeForTest(kLinkPattern); | |
| solink_tool->set_link_output(link_output); | |
| const char kDependPattern[] = | |
| "{{root_out_dir}}/{{target_output_name}}{{output_extension}}.TOC"; | |
| SubstitutionPattern depend_output = | |
| SubstitutionPattern::MakeForTest(kDependPattern); | |
| solink_tool->set_depend_output(depend_output); | |
| solink_tool->set_outputs( | |
| SubstitutionList::MakeForTest(kLinkPattern, kDependPattern)); | |
| toolchain.SetTool(std::move(solink)); | |
| Target target(setup.settings(), Label(SourceDir("//a/"), "a")); | |
| target.set_output_type(Target::SHARED_LIBRARY); | |
| target.SetToolchain(&toolchain); | |
| ASSERT_TRUE(target.OnResolved(&err)); | |
| EXPECT_EQ("./liba.so", target.link_output_file().value()); | |
| ASSERT_TRUE(target.has_dependency_output_file()); | |
| EXPECT_EQ("./liba.so.TOC", target.dependency_output_file().value()); | |
| ASSERT_EQ(1u, target.runtime_outputs().size()); | |
| EXPECT_EQ("./liba.so", target.runtime_outputs()[0].value()); | |
| } | |
| TEST_F(TargetTest, RustLinkAndDepOutputs) { | |
| TestWithScope setup; | |
| Err err; | |
| Toolchain toolchain(setup.settings(), Label(SourceDir("//tc/"), "tc")); | |
| std::unique_ptr<Tool> tool = Tool::CreateTool(RustTool::kRsToolDylib); | |
| RustTool* rust_tool = tool->AsRust(); | |
| rust_tool->set_output_prefix("lib"); | |
| rust_tool->set_default_output_extension(".so"); | |
| const char kLinkPattern[] = | |
| "{{root_out_dir}}/{{target_output_name}}{{output_extension}}"; | |
| SubstitutionPattern link_output = | |
| SubstitutionPattern::MakeForTest(kLinkPattern); | |
| rust_tool->set_link_output(link_output); | |
| const char kDependPattern[] = | |
| "{{root_out_dir}}/{{target_output_name}}{{output_extension}}.TOC"; | |
| SubstitutionPattern depend_output = | |
| SubstitutionPattern::MakeForTest(kDependPattern); | |
| rust_tool->set_depend_output(depend_output); | |
| rust_tool->set_outputs( | |
| SubstitutionList::MakeForTest(kLinkPattern, kDependPattern)); | |
| toolchain.SetTool(std::move(tool)); | |
| Target target(setup.settings(), Label(SourceDir("//a/"), "a")); | |
| target.source_types_used().Set(SourceFile::SOURCE_RS); | |
| target.rust_values().set_crate_type(RustValues::CRATE_DYLIB); | |
| target.set_output_type(Target::SHARED_LIBRARY); | |
| target.SetToolchain(&toolchain); | |
| ASSERT_TRUE(target.OnResolved(&err)); | |
| EXPECT_EQ("./liba.so", target.link_output_file().value()); | |
| EXPECT_EQ("./liba.so.TOC", target.dependency_output_file().value()); | |
| ASSERT_EQ(1u, target.runtime_outputs().size()); | |
| EXPECT_EQ("./liba.so", target.runtime_outputs()[0].value()); | |
| } | |
| // Tests that runtime_outputs works without an explicit link_output for | |
| // solink tools. | |
| // | |
| // Also tests GetOutputsAsSourceFiles() for binaries (the setup is the same). | |
| TEST_F(TargetTest, RuntimeOuputs) { | |
| TestWithScope setup; | |
| Err err; | |
| Toolchain toolchain(setup.settings(), Label(SourceDir("//tc/"), "tc")); | |
| std::unique_ptr<Tool> solink = Tool::CreateTool(CTool::kCToolSolink); | |
| CTool* solink_tool = solink->AsC(); | |
| solink_tool->set_output_prefix(""); | |
| solink_tool->set_default_output_extension(".dll"); | |
| // Say the linker makes a DLL< an import library, and a symbol file we want | |
| // to treat as a runtime output. | |
| const char kLibPattern[] = | |
| "{{root_out_dir}}/{{target_output_name}}{{output_extension}}.lib"; | |
| const char kDllPattern[] = | |
| "{{root_out_dir}}/{{target_output_name}}{{output_extension}}"; | |
| const char kPdbPattern[] = "{{root_out_dir}}/{{target_output_name}}.pdb"; | |
| SubstitutionPattern pdb_pattern = | |
| SubstitutionPattern::MakeForTest(kPdbPattern); | |
| solink_tool->set_outputs( | |
| SubstitutionList::MakeForTest(kLibPattern, kDllPattern, kPdbPattern)); | |
| // Say we only want the DLL and symbol file treaded as runtime outputs. | |
| solink_tool->set_runtime_outputs( | |
| SubstitutionList::MakeForTest(kDllPattern, kPdbPattern)); | |
| toolchain.SetTool(std::move(solink)); | |
| Target target(setup.settings(), Label(SourceDir("//a/"), "a")); | |
| target.set_output_type(Target::SHARED_LIBRARY); | |
| target.SetToolchain(&toolchain); | |
| ASSERT_TRUE(target.OnResolved(&err)); | |
| EXPECT_EQ("./a.dll.lib", target.link_output_file().value()); | |
| ASSERT_TRUE(target.has_dependency_output_file()); | |
| EXPECT_EQ("./a.dll.lib", target.dependency_output_file().value()); | |
| ASSERT_EQ(2u, target.runtime_outputs().size()); | |
| EXPECT_EQ("./a.dll", target.runtime_outputs()[0].value()); | |
| EXPECT_EQ("./a.pdb", target.runtime_outputs()[1].value()); | |
| // Test GetOutputsAsSourceFiles(). | |
| std::vector<SourceFile> computed_outputs; | |
| EXPECT_TRUE(target.GetOutputsAsSourceFiles(LocationRange(), true, | |
| &computed_outputs, &err)); | |
| ASSERT_EQ(3u, computed_outputs.size()); | |
| EXPECT_EQ("//out/Debug/a.dll.lib", computed_outputs[0].value()); | |
| EXPECT_EQ("//out/Debug/a.dll", computed_outputs[1].value()); | |
| EXPECT_EQ("//out/Debug/a.pdb", computed_outputs[2].value()); | |
| } | |
| TEST_F(TargetTest, RustRuntimeOuputs) { | |
| TestWithScope setup; | |
| Err err; | |
| Toolchain toolchain(setup.settings(), Label(SourceDir("//tc/"), "tc")); | |
| std::unique_ptr<Tool> tool = Tool::CreateTool(RustTool::kRsToolCDylib); | |
| RustTool* rust_tool = tool->AsRust(); | |
| rust_tool->set_output_prefix(""); | |
| rust_tool->set_default_output_extension(".dll"); | |
| // Say the linker makes a DLL< an import library, and a symbol file we want | |
| // to treat as a runtime output. | |
| const char kLibPattern[] = | |
| "{{root_out_dir}}/{{target_output_name}}{{output_extension}}.lib"; | |
| const char kDllPattern[] = | |
| "{{root_out_dir}}/{{target_output_name}}{{output_extension}}"; | |
| const char kPdbPattern[] = "{{root_out_dir}}/{{target_output_name}}.pdb"; | |
| SubstitutionPattern pdb_pattern = | |
| SubstitutionPattern::MakeForTest(kPdbPattern); | |
| rust_tool->set_outputs( | |
| SubstitutionList::MakeForTest(kLibPattern, kDllPattern, kPdbPattern)); | |
| // Say we only want the DLL and symbol file treaded as runtime outputs. | |
| rust_tool->set_runtime_outputs( | |
| SubstitutionList::MakeForTest(kDllPattern, kPdbPattern)); | |
| toolchain.SetTool(std::move(tool)); | |
| Target target(setup.settings(), Label(SourceDir("//a/"), "a")); | |
| target.source_types_used().Set(SourceFile::SOURCE_RS); | |
| target.rust_values().set_crate_type(RustValues::CRATE_CDYLIB); | |
| target.set_output_type(Target::SHARED_LIBRARY); | |
| target.SetToolchain(&toolchain); | |
| ASSERT_TRUE(target.OnResolved(&err)); | |
| EXPECT_EQ("./a.dll.lib", target.link_output_file().value()); | |
| EXPECT_EQ("./a.dll.lib", target.dependency_output_file().value()); | |
| ASSERT_EQ(2u, target.runtime_outputs().size()); | |
| EXPECT_EQ("./a.dll", target.runtime_outputs()[0].value()); | |
| EXPECT_EQ("./a.pdb", target.runtime_outputs()[1].value()); | |
| // Test GetOutputsAsSourceFiles(). | |
| std::vector<SourceFile> computed_outputs; | |
| EXPECT_TRUE(target.GetOutputsAsSourceFiles(LocationRange(), true, | |
| &computed_outputs, &err)); | |
| ASSERT_EQ(3u, computed_outputs.size()); | |
| EXPECT_EQ("//out/Debug/a.dll.lib", computed_outputs[0].value()); | |
| EXPECT_EQ("//out/Debug/a.dll", computed_outputs[1].value()); | |
| EXPECT_EQ("//out/Debug/a.pdb", computed_outputs[2].value()); | |
| } | |
| // Tests Target::GetOutputFilesForSource for binary targets (these require a | |
| // tool definition). Also tests GetOutputsAsSourceFiles() for source sets. | |
| TEST_F(TargetTest, GetOutputFilesForSource_Binary) { | |
| TestWithScope setup; | |
| Toolchain toolchain(setup.settings(), Label(SourceDir("//tc/"), "tc")); | |
| std::unique_ptr<Tool> tool = Tool::CreateTool(CTool::kCToolCxx); | |
| CTool* cxx = tool->AsC(); | |
| cxx->set_outputs(SubstitutionList::MakeForTest("{{source_file_part}}.o")); | |
| toolchain.SetTool(std::move(tool)); | |
| Target target(setup.settings(), Label(SourceDir("//a/"), "a")); | |
| target.set_output_type(Target::SOURCE_SET); | |
| target.sources().push_back(SourceFile("//a/source_file1.cc")); | |
| target.SetToolchain(&toolchain); | |
| Err err; | |
| ASSERT_TRUE(target.OnResolved(&err)); | |
| const char* computed_tool_type = nullptr; | |
| std::vector<OutputFile> output; | |
| bool result = target.GetOutputFilesForSource(SourceFile("//source/input.cc"), | |
| &computed_tool_type, &output); | |
| ASSERT_TRUE(result); | |
| EXPECT_EQ(std::string("cxx"), std::string(computed_tool_type)); | |
| // Outputs are relative to the build directory "//out/Debug/". | |
| ASSERT_EQ(1u, output.size()); | |
| EXPECT_EQ("input.cc.o", output[0].value()) << output[0].value(); | |
| // Test GetOutputsAsSourceFiles(). Since this is a source set it should give a | |
| // stamp file. | |
| std::vector<SourceFile> computed_outputs; | |
| EXPECT_TRUE(target.GetOutputsAsSourceFiles(LocationRange(), true, | |
| &computed_outputs, &err)); | |
| ASSERT_EQ(1u, computed_outputs.size()); | |
| EXPECT_EQ("//out/Debug/phony/a/a", computed_outputs[0].value()); | |
| } | |
| TEST_F(TargetTest, CheckStampFileName) { | |
| TestWithScope setup; | |
| Toolchain toolchain(setup.settings(), Label(SourceDir("//tc/"), "tc")); | |
| std::unique_ptr<Tool> tool = Tool::CreateTool(CTool::kCToolCxx); | |
| CTool* cxx = tool->AsC(); | |
| cxx->set_outputs(SubstitutionList::MakeForTest("{{source_file_part}}.o")); | |
| toolchain.SetTool(std::move(tool)); | |
| Target target(setup.settings(), Label(SourceDir("//a/"), "a")); | |
| target.set_output_type(Target::SOURCE_SET); | |
| target.SetToolchain(&toolchain); | |
| // Change the output artifact name on purpose. | |
| target.set_output_name("b"); | |
| Err err; | |
| ASSERT_TRUE(target.OnResolved(&err)); | |
| // Test GetOutputsAsSourceFiles(). Since this is a source set it should give a | |
| // stamp file. | |
| std::vector<SourceFile> computed_outputs; | |
| EXPECT_TRUE(target.GetOutputsAsSourceFiles(LocationRange(), true, | |
| &computed_outputs, &err)); | |
| ASSERT_EQ(0u, computed_outputs.size()) << computed_outputs.size(); | |
| } | |
| // Tests Target::GetOutputFilesForSource for action_foreach targets (these, like | |
| // copy targets, apply a pattern to the source file). Also tests | |
| // GetOutputsAsSourceFiles() for action_foreach(). | |
| TEST_F(TargetTest, GetOutputFilesForSource_ActionForEach) { | |
| TestWithScope setup; | |
| TestTarget target(setup, "//a:a", Target::ACTION_FOREACH); | |
| target.sources().push_back(SourceFile("//a/source_file1.txt")); | |
| target.sources().push_back(SourceFile("//a/source_file2.txt")); | |
| target.action_values().outputs() = | |
| SubstitutionList::MakeForTest("//out/Debug/{{source_file_part}}.one", | |
| "//out/Debug/{{source_file_part}}.two"); | |
| Err err; | |
| ASSERT_TRUE(target.OnResolved(&err)); | |
| const char* computed_tool_type = nullptr; | |
| std::vector<OutputFile> output; | |
| bool result = target.GetOutputFilesForSource(SourceFile("//source/input.txt"), | |
| &computed_tool_type, &output); | |
| ASSERT_TRUE(result); | |
| // Outputs are relative to the build directory "//out/Debug/". | |
| ASSERT_EQ(2u, output.size()); | |
| EXPECT_EQ("input.txt.one", output[0].value()); | |
| EXPECT_EQ("input.txt.two", output[1].value()); | |
| // Test GetOutputsAsSourceFiles(). It should give both outputs for each of the | |
| // two inputs. | |
| std::vector<SourceFile> computed_outputs; | |
| EXPECT_TRUE(target.GetOutputsAsSourceFiles(LocationRange(), true, | |
| &computed_outputs, &err)); | |
| ASSERT_EQ(4u, computed_outputs.size()); | |
| EXPECT_EQ("//out/Debug/source_file1.txt.one", computed_outputs[0].value()); | |
| EXPECT_EQ("//out/Debug/source_file1.txt.two", computed_outputs[1].value()); | |
| EXPECT_EQ("//out/Debug/source_file2.txt.one", computed_outputs[2].value()); | |
| EXPECT_EQ("//out/Debug/source_file2.txt.two", computed_outputs[3].value()); | |
| } | |
| // Tests Target::GetOutputFilesForSource for action targets (these just list the | |
| // output of the action as the result of all possible inputs). This should also | |
| // cover everything other than binary, action_foreach, and copy targets. | |
| TEST_F(TargetTest, GetOutputFilesForSource_Action) { | |
| TestWithScope setup; | |
| TestTarget target(setup, "//a:a", Target::ACTION); | |
| target.sources().push_back(SourceFile("//a/source_file1.txt")); | |
| target.action_values().outputs() = | |
| SubstitutionList::MakeForTest("//out/Debug/one", "//out/Debug/two"); | |
| Err err; | |
| ASSERT_TRUE(target.OnResolved(&err)); | |
| const char* computed_tool_type = nullptr; | |
| std::vector<OutputFile> output; | |
| bool result = target.GetOutputFilesForSource(SourceFile("//source/input.txt"), | |
| &computed_tool_type, &output); | |
| ASSERT_TRUE(result); | |
| // Outputs are relative to the build directory "//out/Debug/". | |
| ASSERT_EQ(2u, output.size()); | |
| EXPECT_EQ("one", output[0].value()); | |
| EXPECT_EQ("two", output[1].value()); | |
| // Test GetOutputsAsSourceFiles(). It should give the listed outputs. | |
| std::vector<SourceFile> computed_outputs; | |
| EXPECT_TRUE(target.GetOutputsAsSourceFiles(LocationRange(), true, | |
| &computed_outputs, &err)); | |
| ASSERT_EQ(2u, computed_outputs.size()); | |
| EXPECT_EQ("//out/Debug/one", computed_outputs[0].value()); | |
| EXPECT_EQ("//out/Debug/two", computed_outputs[1].value()); | |
| // Test that the copy target type behaves the same. This target requires only | |
| // one output. | |
| target.action_values().outputs() = | |
| SubstitutionList::MakeForTest("//out/Debug/one"); | |
| target.set_output_type(Target::COPY_FILES); | |
| // Outputs are relative to the build directory "//out/Debug/". | |
| result = target.GetOutputFilesForSource(SourceFile("//source/input.txt"), | |
| &computed_tool_type, &output); | |
| ASSERT_TRUE(result); | |
| ASSERT_EQ(1u, output.size()); | |
| EXPECT_EQ("one", output[0].value()); | |
| // Test GetOutputsAsSourceFiles() for the copy case. | |
| EXPECT_TRUE(target.GetOutputsAsSourceFiles(LocationRange(), true, | |
| &computed_outputs, &err)); | |
| ASSERT_EQ(1u, computed_outputs.size()) << computed_outputs.size(); | |
| EXPECT_EQ("//out/Debug/one", computed_outputs[0].value()); | |
| } | |
| TEST_F(TargetTest, HasRealInputs) { | |
| TestWithScope setup; | |
| Err err; | |
| // Action always have real inputs. | |
| TestTarget target_a(setup, "//a:a", Target::ACTION); | |
| ASSERT_TRUE(target_a.FillOutputFiles(&err)); | |
| EXPECT_TRUE(target_a.HasRealInputs()); | |
| // A target with no inputs and no deps has no real inputs. | |
| TestTarget target_b(setup, "//a:b", Target::GROUP); | |
| ASSERT_TRUE(target_b.FillOutputFiles(&err)); | |
| EXPECT_FALSE(target_b.HasRealInputs()); | |
| // A target with no inputs and one dep with real inputs has real inputs. | |
| target_b.private_deps().push_back(LabelTargetPair(&target_a)); | |
| ASSERT_TRUE(target_b.FillOutputFiles(&err)); | |
| EXPECT_TRUE(target_b.HasRealInputs()); | |
| // A target with one input with no tool, and no deps, has no real inputs. | |
| TestTarget target_c(setup, "//a:c", Target::SOURCE_SET); | |
| target_c.config_values().inputs().push_back(SourceFile("//a/no_tool.txt")); | |
| ASSERT_TRUE(target_c.FillOutputFiles(&err)); | |
| EXPECT_FALSE(target_c.HasRealInputs()); // One input with no tool, no deps. | |
| // The same, but with one dep without a dependency output. | |
| TestTarget target_d(setup, "//a:c2", Target::GROUP); | |
| target_c.private_deps().push_back(LabelTargetPair(&target_d)); | |
| ASSERT_TRUE(target_c.FillOutputFiles(&err)); | |
| EXPECT_FALSE(target_c.HasRealInputs()); | |
| // The same, but with one dep with a dependency output. | |
| TestTarget target_e(setup, "//a:d", Target::EXECUTABLE); | |
| target_e.sources().push_back(SourceFile("//a/source.cc")); | |
| ASSERT_TRUE(target_e.FillOutputFiles(&err)); | |
| EXPECT_TRUE(target_e.HasRealInputs()); | |
| target_c.private_deps().push_back(LabelTargetPair(&target_e)); | |
| ASSERT_TRUE(target_c.FillOutputFiles(&err)); | |
| EXPECT_TRUE(target_c.HasRealInputs()); | |
| } | |
| TEST_F(TargetTest, GeneratedInputs) { | |
| TestWithScope setup; | |
| Err err; | |
| SourceFile generated_file("//out/Debug/generated.cc"); | |
| // This target has a generated input and no dependency makes it. | |
| TestTarget non_existent_generator(setup, "//foo:non_existent_generator", | |
| Target::EXECUTABLE); | |
| non_existent_generator.sources().push_back(generated_file); | |
| EXPECT_TRUE(non_existent_generator.OnResolved(&err)) << err.message(); | |
| AssertSchedulerHasOneUnknownFileMatching(&non_existent_generator, | |
| generated_file); | |
| scheduler().ClearUnknownGeneratedInputsAndWrittenFiles(); | |
| // Make a target that generates the file. | |
| TestTarget generator(setup, "//foo:generator", Target::ACTION); | |
| generator.action_values().outputs() = | |
| SubstitutionList::MakeForTest(generated_file.value().c_str()); | |
| err = Err(); | |
| EXPECT_TRUE(generator.OnResolved(&err)) << err.message(); | |
| // A target that depends on the generator that uses the file as a source | |
| // should be OK. This uses a private dep (will be used later). | |
| TestTarget existent_generator(setup, "//foo:existent_generator", | |
| Target::SHARED_LIBRARY); | |
| existent_generator.sources().push_back(generated_file); | |
| existent_generator.private_deps().push_back(LabelTargetPair(&generator)); | |
| EXPECT_TRUE(existent_generator.OnResolved(&err)) << err.message(); | |
| EXPECT_TRUE(scheduler().GetUnknownGeneratedInputs().empty()); | |
| // A target that depends on the previous one should *not* be allowed to | |
| // use the generated file, because existent_generator used private deps. | |
| // This is: | |
| // indirect_private --> existent_generator --[private]--> generator | |
| TestTarget indirect_private(setup, "//foo:indirect_private", | |
| Target::EXECUTABLE); | |
| indirect_private.sources().push_back(generated_file); | |
| indirect_private.public_deps().push_back( | |
| LabelTargetPair(&existent_generator)); | |
| EXPECT_TRUE(indirect_private.OnResolved(&err)); | |
| AssertSchedulerHasOneUnknownFileMatching(&indirect_private, generated_file); | |
| scheduler().ClearUnknownGeneratedInputsAndWrittenFiles(); | |
| // Now make a chain like the above but with all public deps, it should be OK. | |
| TestTarget existent_public(setup, "//foo:existent_public", | |
| Target::SHARED_LIBRARY); | |
| existent_public.public_deps().push_back(LabelTargetPair(&generator)); | |
| EXPECT_TRUE(existent_public.OnResolved(&err)) << err.message(); | |
| TestTarget indirect_public(setup, "//foo:indirect_public", | |
| Target::EXECUTABLE); | |
| indirect_public.sources().push_back(generated_file); | |
| indirect_public.public_deps().push_back(LabelTargetPair(&existent_public)); | |
| EXPECT_TRUE(indirect_public.OnResolved(&err)) << err.message(); | |
| EXPECT_TRUE(scheduler().GetUnknownGeneratedInputs().empty()); | |
| } | |
| // This is sort of a Scheduler test, but is related to the above test more. | |
| TEST_F(TargetTest, WriteFileGeneratedInputs) { | |
| TestWithScope setup; | |
| Err err; | |
| SourceFile generated_file("//out/Debug/generated.data"); | |
| // This target has a generated input and no dependency makes it. | |
| TestTarget non_existent_generator(setup, "//foo:non_existent_generator", | |
| Target::EXECUTABLE); | |
| non_existent_generator.sources().push_back(generated_file); | |
| EXPECT_TRUE(non_existent_generator.OnResolved(&err)); | |
| AssertSchedulerHasOneUnknownFileMatching(&non_existent_generator, | |
| generated_file); | |
| scheduler().ClearUnknownGeneratedInputsAndWrittenFiles(); | |
| // This target has a generated file and we've decared we write it. | |
| TestTarget existent_generator(setup, "//foo:existent_generator", | |
| Target::EXECUTABLE); | |
| existent_generator.sources().push_back(generated_file); | |
| EXPECT_TRUE(existent_generator.OnResolved(&err)); | |
| scheduler().AddWrittenFile(generated_file); | |
| // Should be OK. | |
| EXPECT_TRUE(scheduler().GetUnknownGeneratedInputs().empty()); | |
| } | |
| TEST_F(TargetTest, WriteRuntimeDepsGeneratedInputs) { | |
| TestWithScope setup; | |
| Err err; | |
| SourceFile source_file("//out/Debug/generated.runtime_deps"); | |
| OutputFile output_file(setup.build_settings(), source_file); | |
| TestTarget generator(setup, "//foo:generator", Target::EXECUTABLE); | |
| generator.set_write_runtime_deps_output(output_file); | |
| g_scheduler->AddWriteRuntimeDepsTarget(&generator); | |
| TestTarget middle_data_dep(setup, "//foo:middle", Target::EXECUTABLE); | |
| middle_data_dep.data_deps().push_back(LabelTargetPair(&generator)); | |
| // This target has a generated input and no dependency makes it. | |
| TestTarget dep_missing(setup, "//foo:no_dep", Target::EXECUTABLE); | |
| dep_missing.sources().push_back(source_file); | |
| EXPECT_TRUE(dep_missing.OnResolved(&err)); | |
| AssertSchedulerHasOneUnknownFileMatching(&dep_missing, source_file); | |
| scheduler().ClearUnknownGeneratedInputsAndWrittenFiles(); | |
| // This target has a generated file and we've directly dependended on it. | |
| TestTarget dep_present(setup, "//foo:with_dep", Target::EXECUTABLE); | |
| dep_present.sources().push_back(source_file); | |
| dep_present.private_deps().push_back(LabelTargetPair(&generator)); | |
| EXPECT_TRUE(dep_present.OnResolved(&err)); | |
| EXPECT_TRUE(scheduler().GetUnknownGeneratedInputs().empty()); | |
| // This target has a generated file and we've indirectly dependended on it | |
| // via data_deps. | |
| TestTarget dep_indirect(setup, "//foo:with_dep", Target::EXECUTABLE); | |
| dep_indirect.sources().push_back(source_file); | |
| dep_indirect.data_deps().push_back(LabelTargetPair(&middle_data_dep)); | |
| EXPECT_TRUE(dep_indirect.OnResolved(&err)); | |
| AssertSchedulerHasOneUnknownFileMatching(&dep_indirect, source_file); | |
| scheduler().ClearUnknownGeneratedInputsAndWrittenFiles(); | |
| // This target has a generated file and we've directly dependended on it | |
| // via data_deps. | |
| TestTarget data_dep_present(setup, "//foo:with_dep", Target::EXECUTABLE); | |
| data_dep_present.sources().push_back(source_file); | |
| data_dep_present.data_deps().push_back(LabelTargetPair(&generator)); | |
| EXPECT_TRUE(data_dep_present.OnResolved(&err)); | |
| EXPECT_TRUE(scheduler().GetUnknownGeneratedInputs().empty()); | |
| } | |
| // Tests that intermediate object files generated by binary targets are also | |
| // considered generated for the purposes of input checking. Above, we tested | |
| // the failure cases for generated inputs, so here only test .o files that are | |
| // present. | |
| TEST_F(TargetTest, ObjectGeneratedInputs) { | |
| TestWithScope setup; | |
| Err err; | |
| // This target compiles the source. | |
| SourceFile source_file("//source.cc"); | |
| TestTarget source_generator(setup, "//:source_target", Target::SOURCE_SET); | |
| source_generator.sources().push_back(source_file); | |
| EXPECT_TRUE(source_generator.OnResolved(&err)); | |
| // This is the object file that the test toolchain generates for the source. | |
| SourceFile object_file("//out/Debug/obj/source_target.source.o"); | |
| TestTarget final_target(setup, "//:final", Target::ACTION); | |
| final_target.config_values().inputs().push_back(object_file); | |
| EXPECT_TRUE(final_target.OnResolved(&err)); | |
| AssertSchedulerHasOneUnknownFileMatching(&final_target, object_file); | |
| } | |
| TEST_F(TargetTest, ResolvePrecompiledHeaders) { | |
| TestWithScope setup; | |
| Err err; | |
| Target target(setup.settings(), Label(SourceDir("//foo/"), "bar", | |
| SourceDir("//toolchain/"), "default")); | |
| // Target with no settings, no configs, should be a no-op. | |
| EXPECT_TRUE(target.ResolvePrecompiledHeaders(&err)); | |
| // Config with PCH values. | |
| Config config_1( | |
| setup.settings(), | |
| Label(SourceDir("//foo/"), "c1", SourceDir("//toolchain/"), "default")); | |
| std::string pch_1("pch.h"); | |
| SourceFile pcs_1("//pcs.cc"); | |
| config_1.own_values().set_precompiled_header(pch_1); | |
| config_1.own_values().set_precompiled_source(pcs_1); | |
| ASSERT_TRUE(config_1.OnResolved(&err)); | |
| target.configs().push_back(LabelConfigPair(&config_1)); | |
| // No PCH info specified on TargetTest, but the config specifies one, the | |
| // values should get copied to the target. | |
| EXPECT_TRUE(target.ResolvePrecompiledHeaders(&err)); | |
| EXPECT_EQ(pch_1, target.config_values().precompiled_header()); | |
| EXPECT_TRUE(target.config_values().precompiled_source() == pcs_1); | |
| // Now both target and config have matching PCH values. Resolving again | |
| // should be a no-op since they all match. | |
| EXPECT_TRUE(target.ResolvePrecompiledHeaders(&err)); | |
| EXPECT_TRUE(target.config_values().precompiled_header() == pch_1); | |
| EXPECT_TRUE(target.config_values().precompiled_source() == pcs_1); | |
| // Second config with different PCH values. | |
| Config config_2( | |
| setup.settings(), | |
| Label(SourceDir("//foo/"), "c2", SourceDir("//toolchain/"), "default")); | |
| std::string pch_2("pch2.h"); | |
| SourceFile pcs_2("//pcs2.cc"); | |
| config_2.own_values().set_precompiled_header(pch_2); | |
| config_2.own_values().set_precompiled_source(pcs_2); | |
| ASSERT_TRUE(config_2.OnResolved(&err)); | |
| target.configs().push_back(LabelConfigPair(&config_2)); | |
| // This should be an error since they don't match. | |
| EXPECT_FALSE(target.ResolvePrecompiledHeaders(&err)); | |
| // Make sure the proper labels are blamed. | |
| EXPECT_EQ( | |
| "The target //foo:bar\n" | |
| "has conflicting precompiled header settings.\n" | |
| "\n" | |
| "From //foo:bar\n" | |
| " header: pch.h\n" | |
| " source: //pcs.cc\n" | |
| "\n" | |
| "From //foo:c2\n" | |
| " header: pch2.h\n" | |
| " source: //pcs2.cc", | |
| err.help_text()); | |
| } | |
| TEST_F(TargetTest, AssertNoDeps) { | |
| TestWithScope setup; | |
| Err err; | |
| // A target. | |
| TestTarget a(setup, "//a", Target::SHARED_LIBRARY); | |
| ASSERT_TRUE(a.OnResolved(&err)); | |
| // B depends on A and has an assert_no_deps for a random dir. | |
| TestTarget b(setup, "//b", Target::SHARED_LIBRARY); | |
| b.private_deps().push_back(LabelTargetPair(&a)); | |
| b.assert_no_deps().push_back(LabelPattern(LabelPattern::RECURSIVE_DIRECTORY, | |
| SourceDir("//disallowed/"), | |
| std::string(), Label())); | |
| ASSERT_TRUE(b.OnResolved(&err)); | |
| LabelPattern disallow_a(LabelPattern::RECURSIVE_DIRECTORY, SourceDir("//a/"), | |
| std::string(), Label()); | |
| // C depends on B and disallows depending on A. This should fail. | |
| TestTarget c(setup, "//c", Target::EXECUTABLE); | |
| c.private_deps().push_back(LabelTargetPair(&b)); | |
| c.assert_no_deps().push_back(disallow_a); | |
| ASSERT_FALSE(c.OnResolved(&err)); | |
| // Validate the error message has the proper path. | |
| EXPECT_EQ( | |
| "//c:c has an assert_no_deps entry:\n" | |
| " //a/*\n" | |
| "which fails for the dependency path:\n" | |
| " //c:c ->\n" | |
| " //b:b ->\n" | |
| " //a:a", | |
| err.help_text()); | |
| err = Err(); | |
| // Add an intermediate executable with: exe -> b -> a | |
| TestTarget exe(setup, "//exe", Target::EXECUTABLE); | |
| exe.private_deps().push_back(LabelTargetPair(&b)); | |
| ASSERT_TRUE(exe.OnResolved(&err)); | |
| // D depends on the executable and disallows depending on A. Since there is | |
| // an intermediate executable, this should be OK. | |
| TestTarget d(setup, "//d", Target::EXECUTABLE); | |
| d.private_deps().push_back(LabelTargetPair(&exe)); | |
| d.assert_no_deps().push_back(disallow_a); | |
| ASSERT_TRUE(d.OnResolved(&err)); | |
| // A2 disallows depending on anything in its own directory, but the | |
| // assertions should not match the target itself so this should be OK. | |
| TestTarget a2(setup, "//a:a2", Target::EXECUTABLE); | |
| a2.assert_no_deps().push_back(disallow_a); | |
| ASSERT_TRUE(a2.OnResolved(&err)); | |
| } | |
| TEST_F(TargetTest, PullRecursiveBundleData) { | |
| TestWithScope setup; | |
| Err err; | |
| // We have the following dependency graph: | |
| // A (create_bundle) -> B (bundle_data) | |
| // \-> C (create_bundle) -> D (bundle_data) | |
| // \-> E (group) -> F (bundle_data) | |
| // \-> B (bundle_data) | |
| TestTarget a(setup, "//foo:a", Target::CREATE_BUNDLE); | |
| TestTarget b(setup, "//foo:b", Target::BUNDLE_DATA); | |
| TestTarget c(setup, "//foo:c", Target::CREATE_BUNDLE); | |
| TestTarget d(setup, "//foo:d", Target::BUNDLE_DATA); | |
| TestTarget e(setup, "//foo:e", Target::GROUP); | |
| TestTarget f(setup, "//foo:f", Target::BUNDLE_DATA); | |
| a.public_deps().push_back(LabelTargetPair(&b)); | |
| a.public_deps().push_back(LabelTargetPair(&c)); | |
| a.public_deps().push_back(LabelTargetPair(&e)); | |
| c.public_deps().push_back(LabelTargetPair(&d)); | |
| e.public_deps().push_back(LabelTargetPair(&f)); | |
| e.public_deps().push_back(LabelTargetPair(&b)); | |
| a.bundle_data().root_dir() = SourceDir("//out/foo_a.bundle"); | |
| a.bundle_data().resources_dir() = SourceDir("//out/foo_a.bundle/Resources"); | |
| b.sources().push_back(SourceFile("//foo/b1.txt")); | |
| b.sources().push_back(SourceFile("//foo/b2.txt")); | |
| b.action_values().outputs() = SubstitutionList::MakeForTest( | |
| "{{bundle_resources_dir}}/{{source_file_part}}"); | |
| ASSERT_TRUE(b.OnResolved(&err)); | |
| c.bundle_data().root_dir() = SourceDir("//out/foo_c.bundle"); | |
| c.bundle_data().resources_dir() = SourceDir("//out/foo_c.bundle/Resources"); | |
| d.sources().push_back(SourceFile("//foo/d.txt")); | |
| d.action_values().outputs() = SubstitutionList::MakeForTest( | |
| "{{bundle_resources_dir}}/{{source_file_part}}"); | |
| ASSERT_TRUE(d.OnResolved(&err)); | |
| f.sources().push_back(SourceFile("//foo/f1.txt")); | |
| f.sources().push_back(SourceFile("//foo/f2.txt")); | |
| f.sources().push_back(SourceFile("//foo/f3.txt")); | |
| f.sources().push_back( | |
| SourceFile("//foo/Foo.xcassets/foo.imageset/Contents.json")); | |
| f.sources().push_back( | |
| SourceFile("//foo/Foo.xcassets/foo.imageset/FooEmpty-29.png")); | |
| f.sources().push_back( | |
| SourceFile("//foo/Foo.xcassets/foo.imageset/FooEmpty-29@2x.png")); | |
| f.sources().push_back( | |
| SourceFile("//foo/Foo.xcassets/foo.imageset/FooEmpty-29@3x.png")); | |
| f.sources().push_back( | |
| SourceFile("//foo/Foo.xcassets/file/with/no/known/pattern")); | |
| f.sources().push_back( | |
| SourceFile("//foo/Foo.xcassets/nested/bar.xcassets/my/file")); | |
| f.action_values().outputs() = SubstitutionList::MakeForTest( | |
| "{{bundle_resources_dir}}/{{source_file_part}}"); | |
| ASSERT_TRUE(f.OnResolved(&err)); | |
| ASSERT_TRUE(e.OnResolved(&err)); | |
| ASSERT_TRUE(c.OnResolved(&err)); | |
| ASSERT_TRUE(a.OnResolved(&err)); | |
| // A gets its data from B and F. | |
| ASSERT_EQ(a.bundle_data().file_rules().size(), 2u); | |
| ASSERT_EQ(a.bundle_data().file_rules()[0].sources().size(), 2u); | |
| ASSERT_EQ(a.bundle_data().file_rules()[1].sources().size(), 3u); | |
| ASSERT_EQ(a.bundle_data().assets_catalog_sources().size(), 1u); | |
| ASSERT_EQ(a.bundle_data().forwarded_bundle_deps().size(), 2u); | |
| // C gets its data from D. | |
| ASSERT_EQ(c.bundle_data().file_rules().size(), 1u); | |
| ASSERT_EQ(c.bundle_data().file_rules()[0].sources().size(), 1u); | |
| ASSERT_EQ(c.bundle_data().forwarded_bundle_deps().size(), 1u); | |
| // E does not have any bundle_data information but gets a list of | |
| // forwarded_bundle_deps to propagate them during target resolution. | |
| ASSERT_TRUE(e.bundle_data().file_rules().empty()); | |
| ASSERT_TRUE(e.bundle_data().assets_catalog_sources().empty()); | |
| ASSERT_EQ(e.bundle_data().forwarded_bundle_deps().size(), 2u); | |
| } | |
| TEST(TargetTest, CollectMetadataNoRecurse) { | |
| TestWithScope setup; | |
| TestTarget one(setup, "//foo:one", Target::SOURCE_SET); | |
| Value a_expected(nullptr, Value::LIST); | |
| a_expected.list_value().push_back(Value(nullptr, "foo")); | |
| one.metadata().contents().insert( | |
| std::pair<std::string_view, Value>("a", a_expected)); | |
| Value b_expected(nullptr, Value::LIST); | |
| b_expected.list_value().push_back(Value(nullptr, true)); | |
| one.metadata().contents().insert( | |
| std::pair<std::string_view, Value>("b", b_expected)); | |
| one.metadata().set_source_dir(SourceDir("/usr/home/files/")); | |
| std::vector<std::string> data_keys; | |
| data_keys.push_back("a"); | |
| data_keys.push_back("b"); | |
| std::vector<std::string> walk_keys; | |
| Err err; | |
| std::vector<Value> result; | |
| TargetSet targets; | |
| one.GetMetadata(data_keys, walk_keys, SourceDir(), false, &result, &targets, | |
| &err); | |
| EXPECT_SUCCESS(err); | |
| std::vector<Value> expected; | |
| expected.push_back(Value(nullptr, "foo")); | |
| expected.push_back(Value(nullptr, true)); | |
| EXPECT_EQ(result, expected); | |
| } | |
| TEST(TargetTest, CollectMetadataWithRecurse) { | |
| TestWithScope setup; | |
| TestTarget one(setup, "//foo:one", Target::SOURCE_SET); | |
| Value a_expected(nullptr, Value::LIST); | |
| a_expected.list_value().push_back(Value(nullptr, "foo")); | |
| one.metadata().contents().insert( | |
| std::pair<std::string_view, Value>("a", a_expected)); | |
| Value b_expected(nullptr, Value::LIST); | |
| b_expected.list_value().push_back(Value(nullptr, true)); | |
| one.metadata().contents().insert( | |
| std::pair<std::string_view, Value>("b", b_expected)); | |
| TestTarget two(setup, "//foo:two", Target::SOURCE_SET); | |
| Value a_2_expected(nullptr, Value::LIST); | |
| a_2_expected.list_value().push_back(Value(nullptr, "bar")); | |
| two.metadata().contents().insert( | |
| std::pair<std::string_view, Value>("a", a_2_expected)); | |
| one.public_deps().push_back(LabelTargetPair(&two)); | |
| std::vector<std::string> data_keys; | |
| data_keys.push_back("a"); | |
| data_keys.push_back("b"); | |
| std::vector<std::string> walk_keys; | |
| Err err; | |
| std::vector<Value> result; | |
| TargetSet targets; | |
| one.GetMetadata(data_keys, walk_keys, SourceDir(), false, &result, &targets, | |
| &err); | |
| EXPECT_SUCCESS(err); | |
| std::vector<Value> expected; | |
| expected.push_back(Value(nullptr, "bar")); | |
| expected.push_back(Value(nullptr, "foo")); | |
| expected.push_back(Value(nullptr, true)); | |
| EXPECT_EQ(result, expected); | |
| } | |
| TEST(TargetTest, CollectMetadataWithRecurseHole) { | |
| TestWithScope setup; | |
| TestTarget one(setup, "//foo:one", Target::SOURCE_SET); | |
| Value a_expected(nullptr, Value::LIST); | |
| a_expected.list_value().push_back(Value(nullptr, "foo")); | |
| one.metadata().contents().insert( | |
| std::pair<std::string_view, Value>("a", a_expected)); | |
| Value b_expected(nullptr, Value::LIST); | |
| b_expected.list_value().push_back(Value(nullptr, true)); | |
| one.metadata().contents().insert( | |
| std::pair<std::string_view, Value>("b", b_expected)); | |
| // Target two does not have metadata but depends on three | |
| // which does. | |
| TestTarget two(setup, "//foo:two", Target::SOURCE_SET); | |
| TestTarget three(setup, "//foo:three", Target::SOURCE_SET); | |
| Value a_3_expected(nullptr, Value::LIST); | |
| a_3_expected.list_value().push_back(Value(nullptr, "bar")); | |
| three.metadata().contents().insert( | |
| std::pair<std::string_view, Value>("a", a_3_expected)); | |
| one.public_deps().push_back(LabelTargetPair(&two)); | |
| two.public_deps().push_back(LabelTargetPair(&three)); | |
| std::vector<std::string> data_keys; | |
| data_keys.push_back("a"); | |
| data_keys.push_back("b"); | |
| std::vector<std::string> walk_keys; | |
| Err err; | |
| std::vector<Value> result; | |
| TargetSet targets; | |
| one.GetMetadata(data_keys, walk_keys, SourceDir(), false, &result, &targets, | |
| &err); | |
| EXPECT_SUCCESS(err); | |
| std::vector<Value> expected; | |
| expected.push_back(Value(nullptr, "bar")); | |
| expected.push_back(Value(nullptr, "foo")); | |
| expected.push_back(Value(nullptr, true)); | |
| EXPECT_EQ(result, expected); | |
| } | |
| TEST(TargetTest, CollectMetadataWithBarrier) { | |
| TestWithScope setup; | |
| TestTarget one(setup, "//foo:one", Target::SOURCE_SET); | |
| Value a_expected(nullptr, Value::LIST); | |
| a_expected.list_value().push_back(Value(nullptr, "foo")); | |
| one.metadata().contents().insert( | |
| std::pair<std::string_view, Value>("a", a_expected)); | |
| Value walk_expected(nullptr, Value::LIST); | |
| walk_expected.list_value().push_back(Value(nullptr, "two")); | |
| one.metadata().contents().insert( | |
| std::pair<std::string_view, Value>("walk", walk_expected)); | |
| TestTarget two(setup, "//foo/two:two", Target::SOURCE_SET); | |
| Value a_2_expected(nullptr, Value::LIST); | |
| a_2_expected.list_value().push_back(Value(nullptr, "bar")); | |
| two.metadata().contents().insert( | |
| std::pair<std::string_view, Value>("a", a_2_expected)); | |
| TestTarget three(setup, "//foo:three", Target::SOURCE_SET); | |
| Value a_3_expected(nullptr, Value::LIST); | |
| a_3_expected.list_value().push_back(Value(nullptr, "baz")); | |
| three.metadata().contents().insert( | |
| std::pair<std::string_view, Value>("a", a_3_expected)); | |
| one.private_deps().push_back(LabelTargetPair(&two)); | |
| one.public_deps().push_back(LabelTargetPair(&three)); | |
| std::vector<std::string> data_keys; | |
| data_keys.push_back("a"); | |
| std::vector<std::string> walk_keys; | |
| walk_keys.push_back("walk"); | |
| Err err; | |
| std::vector<Value> result; | |
| TargetSet targets; | |
| one.GetMetadata(data_keys, walk_keys, SourceDir(), false, &result, &targets, | |
| &err); | |
| EXPECT_SUCCESS(err); | |
| std::vector<Value> expected; | |
| expected.push_back(Value(nullptr, "bar")); | |
| expected.push_back(Value(nullptr, "foo")); | |
| EXPECT_EQ(result, expected) << result.size(); | |
| } | |
| TEST(TargetTest, CollectMetadataWithError) { | |
| TestWithScope setup; | |
| TestTarget one(setup, "//foo:one", Target::SOURCE_SET); | |
| Value a_expected(nullptr, Value::LIST); | |
| a_expected.list_value().push_back(Value(nullptr, "foo")); | |
| one.metadata().contents().insert( | |
| std::pair<std::string_view, Value>("a", a_expected)); | |
| Value walk_expected(nullptr, Value::LIST); | |
| walk_expected.list_value().push_back(Value(nullptr, "//foo:missing")); | |
| one.metadata().contents().insert( | |
| std::pair<std::string_view, Value>("walk", walk_expected)); | |
| std::vector<std::string> data_keys; | |
| data_keys.push_back("a"); | |
| std::vector<std::string> walk_keys; | |
| walk_keys.push_back("walk"); | |
| Err err; | |
| std::vector<Value> result; | |
| TargetSet targets; | |
| one.GetMetadata(data_keys, walk_keys, SourceDir(), false, &result, &targets, | |
| &err); | |
| EXPECT_TRUE(err.has_error()); | |
| EXPECT_EQ(err.message(), | |
| "I was expecting //foo:missing(//toolchain:default) to be a " | |
| "dependency of //foo:one(//toolchain:default). " | |
| "Make sure it's included in the deps or data_deps, and that you've " | |
| "specified the appropriate toolchain.") | |
| << err.message(); | |
| } | |
| TEST_F(TargetTest, WriteMetadataCollection) { | |
| TestWithScope setup; | |
| Err err; | |
| SourceFile source_file("//out/Debug/metadata.json"); | |
| OutputFile output_file(setup.build_settings(), source_file); | |
| TestTarget generator(setup, "//foo:write", Target::GENERATED_FILE); | |
| generator.action_values().outputs() = | |
| SubstitutionList::MakeForTest("//out/Debug/metadata.json"); | |
| EXPECT_TRUE(generator.OnResolved(&err)); | |
| TestTarget middle_data_dep(setup, "//foo:middle", Target::EXECUTABLE); | |
| middle_data_dep.data_deps().push_back(LabelTargetPair(&generator)); | |
| EXPECT_TRUE(middle_data_dep.OnResolved(&err)); | |
| // This target has a generated metadata input and no dependency makes it. | |
| TestTarget dep_missing(setup, "//foo:no_dep", Target::EXECUTABLE); | |
| dep_missing.sources().push_back(source_file); | |
| EXPECT_TRUE(dep_missing.OnResolved(&err)); | |
| AssertSchedulerHasOneUnknownFileMatching(&dep_missing, source_file); | |
| scheduler().ClearUnknownGeneratedInputsAndWrittenFiles(); | |
| // This target has a generated file and we've directly dependended on it. | |
| TestTarget dep_present(setup, "//foo:with_dep", Target::EXECUTABLE); | |
| dep_present.sources().push_back(source_file); | |
| dep_present.private_deps().push_back(LabelTargetPair(&generator)); | |
| EXPECT_TRUE(dep_present.OnResolved(&err)); | |
| EXPECT_TRUE(scheduler().GetUnknownGeneratedInputs().empty()); | |
| // This target has a generated file and we've indirectly dependended on it | |
| // via data_deps. | |
| TestTarget dep_indirect(setup, "//foo:indirect_dep", Target::EXECUTABLE); | |
| dep_indirect.sources().push_back(source_file); | |
| dep_indirect.data_deps().push_back(LabelTargetPair(&middle_data_dep)); | |
| EXPECT_TRUE(dep_indirect.OnResolved(&err)); | |
| AssertSchedulerHasOneUnknownFileMatching(&dep_indirect, source_file); | |
| scheduler().ClearUnknownGeneratedInputsAndWrittenFiles(); | |
| // This target has a generated file and we've directly dependended on it | |
| // via data_deps. | |
| TestTarget data_dep_present(setup, "//foo:with_data_dep", Target::EXECUTABLE); | |
| data_dep_present.sources().push_back(source_file); | |
| data_dep_present.data_deps().push_back(LabelTargetPair(&generator)); | |
| EXPECT_TRUE(data_dep_present.OnResolved(&err)); | |
| EXPECT_TRUE(scheduler().GetUnknownGeneratedInputs().empty()); | |
| } | |
| // Tests that modulemap files use the cxx_module tool. | |
| TEST_F(TargetTest, ModuleMap) { | |
| TestWithScope setup; | |
| Toolchain toolchain(setup.settings(), Label(SourceDir("//tc/"), "tc")); | |
| std::unique_ptr<Tool> tool = Tool::CreateTool(CTool::kCToolCxxModule); | |
| CTool* cxx_module = tool->AsC(); | |
| cxx_module->set_outputs( | |
| SubstitutionList::MakeForTest("{{source_file_part}}.pcm")); | |
| toolchain.SetTool(std::move(tool)); | |
| Target target(setup.settings(), Label(SourceDir("//a/"), "a")); | |
| target.set_output_type(Target::SOURCE_SET); | |
| target.SetToolchain(&toolchain); | |
| Err err; | |
| ASSERT_TRUE(target.OnResolved(&err)); | |
| const char* computed_tool_type = nullptr; | |
| std::vector<OutputFile> output; | |
| bool result = target.GetOutputFilesForSource( | |
| SourceFile("//source/input.modulemap"), &computed_tool_type, &output); | |
| ASSERT_TRUE(result); | |
| EXPECT_EQ(std::string("cxx_module"), std::string(computed_tool_type)); | |
| // Outputs are relative to the build directory "//out/Debug/". | |
| ASSERT_EQ(1u, output.size()); | |
| EXPECT_EQ("input.modulemap.pcm", output[0].value()) << output[0].value(); | |
| } | |
| TEST(TargetTest, CollectMetadataWithValidation) { | |
| TestWithScope setup; | |
| TestTarget a(setup, "//foo:a", Target::SOURCE_SET); | |
| Value a_expected(nullptr, Value::LIST); | |
| a_expected.list_value().push_back(Value(nullptr, "foo")); | |
| a.metadata().contents().emplace("walk", a_expected); | |
| TestTarget b(setup, "//foo:b", Target::SOURCE_SET); | |
| Value b_expected(nullptr, Value::LIST); | |
| b_expected.list_value().push_back(Value(nullptr, "bar")); | |
| b.metadata().contents().emplace("walk", b_expected); | |
| a.validations().push_back(LabelTargetPair(&b)); | |
| std::vector<std::string> data_keys = {"walk"}; | |
| std::vector<std::string> walk_keys; | |
| std::vector<Value> result; | |
| TargetSet targets; | |
| Err err; | |
| a.GetMetadata(data_keys, walk_keys, SourceDir(), false, &result, &targets, | |
| &err); | |
| EXPECT_SUCCESS(err); | |
| std::vector<Value> expected = {Value(nullptr, "bar"), Value(nullptr, "foo")}; | |
| EXPECT_EQ(result, expected); | |
| } |