// 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 "testing/gtest/include/gtest/gtest.h" #include "tools/gn/build_settings.h" #include "tools/gn/config.h" #include "tools/gn/settings.h" #include "tools/gn/target.h" #include "tools/gn/test_with_scope.h" #include "tools/gn/toolchain.h" // Tests that lib[_dir]s are inherited across deps boundaries for static // libraries but not executables. TEST(Target, LibInheritance) { TestWithScope setup; Err err; const std::string lib("foo"); const SourceDir libdir("/foo_dir/"); // Leaf target with ldflags set. Target z(setup.settings(), Label(SourceDir("//foo/"), "z")); z.set_output_type(Target::STATIC_LIBRARY); z.config_values().libs().push_back(lib); z.config_values().lib_dirs().push_back(libdir); z.visibility().SetPublic(); z.SetToolchain(setup.toolchain()); ASSERT_TRUE(z.OnResolved(&err)); // All lib[_dir]s should be set when target is resolved. ASSERT_EQ(1u, z.all_libs().size()); EXPECT_EQ(lib, z.all_libs()[0]); ASSERT_EQ(1u, z.all_lib_dirs().size()); EXPECT_EQ(libdir, z.all_lib_dirs()[0]); // Shared library target should inherit the libs from the static library // and its own. Its own flag should be before the inherited one. const std::string second_lib("bar"); const SourceDir second_libdir("/bar_dir/"); Target shared(setup.settings(), Label(SourceDir("//foo/"), "shared")); shared.set_output_type(Target::SHARED_LIBRARY); shared.config_values().libs().push_back(second_lib); shared.config_values().lib_dirs().push_back(second_libdir); shared.private_deps().push_back(LabelTargetPair(&z)); shared.visibility().SetPublic(); shared.SetToolchain(setup.toolchain()); ASSERT_TRUE(shared.OnResolved(&err)); ASSERT_EQ(2u, shared.all_libs().size()); EXPECT_EQ(second_lib, shared.all_libs()[0]); EXPECT_EQ(lib, shared.all_libs()[1]); ASSERT_EQ(2u, shared.all_lib_dirs().size()); EXPECT_EQ(second_libdir, shared.all_lib_dirs()[0]); EXPECT_EQ(libdir, shared.all_lib_dirs()[1]); // Executable target shouldn't get either by depending on shared. Target exec(setup.settings(), Label(SourceDir("//foo/"), "exec")); exec.set_output_type(Target::EXECUTABLE); exec.private_deps().push_back(LabelTargetPair(&shared)); exec.SetToolchain(setup.toolchain()); ASSERT_TRUE(exec.OnResolved(&err)); EXPECT_EQ(0u, exec.all_libs().size()); EXPECT_EQ(0u, exec.all_lib_dirs().size()); } // Test all_dependent_configs, public_config inheritance, and // forward_dependent_configs_from TEST(Target, DependentConfigs) { TestWithScope setup; Err err; // Set up a dependency chain of a -> b -> c Target a(setup.settings(), Label(SourceDir("//foo/"), "a")); a.set_output_type(Target::EXECUTABLE); a.visibility().SetPublic(); a.SetToolchain(setup.toolchain()); Target b(setup.settings(), Label(SourceDir("//foo/"), "b")); b.set_output_type(Target::STATIC_LIBRARY); b.visibility().SetPublic(); b.SetToolchain(setup.toolchain()); Target c(setup.settings(), Label(SourceDir("//foo/"), "c")); c.set_output_type(Target::STATIC_LIBRARY); c.visibility().SetPublic(); c.SetToolchain(setup.toolchain()); 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")); c.configs().push_back(LabelConfigPair(&config)); // All dependent config. Config all(setup.settings(), Label(SourceDir("//foo/"), "all")); c.all_dependent_configs().push_back(LabelConfigPair(&all)); // Direct dependent config. Config direct(setup.settings(), Label(SourceDir("//foo/"), "direct")); 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. Target a_fwd(setup.settings(), Label(SourceDir("//foo/"), "a_fwd")); a_fwd.set_output_type(Target::EXECUTABLE); a_fwd.visibility().SetPublic(); a_fwd.SetToolchain(setup.toolchain()); Target b_fwd(setup.settings(), Label(SourceDir("//foo/"), "b_fwd")); b_fwd.set_output_type(Target::STATIC_LIBRARY); b_fwd.SetToolchain(setup.toolchain()); b_fwd.visibility().SetPublic(); a_fwd.private_deps().push_back(LabelTargetPair(&b_fwd)); b_fwd.private_deps().push_back(LabelTargetPair(&c)); b_fwd.forward_dependent_configs().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(2u, a_fwd.configs().size()); EXPECT_EQ(&all, a_fwd.configs()[0].ptr); EXPECT_EQ(&direct, a_fwd.configs()[1].ptr); ASSERT_EQ(1u, a_fwd.all_dependent_configs().size()); EXPECT_EQ(&all, a_fwd.all_dependent_configs()[0].ptr); } TEST(Target, InheritLibs) { TestWithScope setup; Err err; // Create a dependency chain: // A (executable) -> B (shared lib) -> C (static lib) -> D (source set) Target a(setup.settings(), Label(SourceDir("//foo/"), "a")); a.set_output_type(Target::EXECUTABLE); a.visibility().SetPublic(); a.SetToolchain(setup.toolchain()); Target b(setup.settings(), Label(SourceDir("//foo/"), "b")); b.set_output_type(Target::SHARED_LIBRARY); b.visibility().SetPublic(); b.SetToolchain(setup.toolchain()); Target c(setup.settings(), Label(SourceDir("//foo/"), "c")); c.set_output_type(Target::STATIC_LIBRARY); c.visibility().SetPublic(); c.SetToolchain(setup.toolchain()); Target d(setup.settings(), Label(SourceDir("//foo/"), "d")); d.set_output_type(Target::SOURCE_SET); d.visibility().SetPublic(); d.SetToolchain(setup.toolchain()); a.private_deps().push_back(LabelTargetPair(&b)); b.private_deps().push_back(LabelTargetPair(&c)); c.private_deps().push_back(LabelTargetPair(&d)); ASSERT_TRUE(d.OnResolved(&err)); ASSERT_TRUE(c.OnResolved(&err)); ASSERT_TRUE(b.OnResolved(&err)); ASSERT_TRUE(a.OnResolved(&err)); // C should have D in its inherited libs. std::vector c_inherited = c.inherited_libraries().GetOrdered(); ASSERT_EQ(1u, c_inherited.size()); EXPECT_EQ(&d, c_inherited[0]); // B should have C and D in its inherited libs. std::vector b_inherited = b.inherited_libraries().GetOrdered(); ASSERT_EQ(2u, b_inherited.size()); EXPECT_EQ(&c, b_inherited[0]); EXPECT_EQ(&d, b_inherited[1]); // A should have B in its inherited libs, but not any others (the shared // library will include the static library and source set). std::vector a_inherited = a.inherited_libraries().GetOrdered(); ASSERT_EQ(1u, a_inherited.size()); EXPECT_EQ(&b, a_inherited[0]); } TEST(Target, InheritCompleteStaticLib) { TestWithScope setup; Err err; // Create a dependency chain: // A (executable) -> B (complete static lib) -> C (source set) Target a(setup.settings(), Label(SourceDir("//foo/"), "a")); a.set_output_type(Target::EXECUTABLE); a.visibility().SetPublic(); a.SetToolchain(setup.toolchain()); Target b(setup.settings(), Label(SourceDir("//foo/"), "b")); b.set_output_type(Target::STATIC_LIBRARY); b.visibility().SetPublic(); b.set_complete_static_lib(true); b.SetToolchain(setup.toolchain()); Target c(setup.settings(), Label(SourceDir("//foo/"), "c")); c.set_output_type(Target::SOURCE_SET); c.visibility().SetPublic(); c.SetToolchain(setup.toolchain()); a.public_deps().push_back(LabelTargetPair(&b)); b.public_deps().push_back(LabelTargetPair(&c)); ASSERT_TRUE(c.OnResolved(&err)); ASSERT_TRUE(b.OnResolved(&err)); ASSERT_TRUE(a.OnResolved(&err)); // B should have C in its inherited libs. std::vector b_inherited = b.inherited_libraries().GetOrdered(); ASSERT_EQ(1u, b_inherited.size()); EXPECT_EQ(&c, b_inherited[0]); // A should have B in its inherited libs, but not any others (the complete // static library will include the source set). std::vector a_inherited = a.inherited_libraries().GetOrdered(); EXPECT_EQ(1u, a_inherited.size()); EXPECT_EQ(&b, a_inherited[0]); } TEST(Target, InheritCompleteStaticLibNoDirectStaticLibDeps) { TestWithScope setup; Err err; // Create a dependency chain: // A (complete static lib) -> B (static lib) Target a(setup.settings(), Label(SourceDir("//foo/"), "a")); a.set_output_type(Target::STATIC_LIBRARY); a.visibility().SetPublic(); a.set_complete_static_lib(true); a.SetToolchain(setup.toolchain()); Target b(setup.settings(), Label(SourceDir("//foo/"), "b")); b.set_output_type(Target::STATIC_LIBRARY); b.visibility().SetPublic(); b.SetToolchain(setup.toolchain()); a.public_deps().push_back(LabelTargetPair(&b)); ASSERT_TRUE(b.OnResolved(&err)); ASSERT_FALSE(a.OnResolved(&err)); } TEST(Target, InheritCompleteStaticLibNoIheritedStaticLibDeps) { TestWithScope setup; Err err; // Create a dependency chain: // A (complete static lib) -> B (source set) -> C (static lib) Target a(setup.settings(), Label(SourceDir("//foo/"), "a")); a.set_output_type(Target::STATIC_LIBRARY); a.visibility().SetPublic(); a.set_complete_static_lib(true); a.SetToolchain(setup.toolchain()); Target b(setup.settings(), Label(SourceDir("//foo/"), "b")); b.set_output_type(Target::SOURCE_SET); b.visibility().SetPublic(); b.SetToolchain(setup.toolchain()); Target c(setup.settings(), Label(SourceDir("//foo/"), "c")); c.set_output_type(Target::STATIC_LIBRARY); c.visibility().SetPublic(); c.SetToolchain(setup.toolchain()); a.public_deps().push_back(LabelTargetPair(&b)); b.public_deps().push_back(LabelTargetPair(&c)); ASSERT_TRUE(c.OnResolved(&err)); ASSERT_TRUE(b.OnResolved(&err)); ASSERT_FALSE(a.OnResolved(&err)); } TEST(Target, GetComputedOutputName) { TestWithScope setup; Err err; // Basic target with no prefix (executable type tool in the TestWithScope has // no prefix) or output name. Target basic(setup.settings(), Label(SourceDir("//foo/"), "bar")); basic.set_output_type(Target::EXECUTABLE); basic.SetToolchain(setup.toolchain()); ASSERT_TRUE(basic.OnResolved(&err)); EXPECT_EQ("bar", basic.GetComputedOutputName(false)); EXPECT_EQ("bar", basic.GetComputedOutputName(true)); // Target with no prefix but an output name. Target with_name(setup.settings(), Label(SourceDir("//foo/"), "bar")); with_name.set_output_type(Target::EXECUTABLE); with_name.set_output_name("myoutput"); with_name.SetToolchain(setup.toolchain()); ASSERT_TRUE(with_name.OnResolved(&err)); EXPECT_EQ("myoutput", with_name.GetComputedOutputName(false)); EXPECT_EQ("myoutput", with_name.GetComputedOutputName(true)); // Target with a "lib" prefix (the static library tool in the TestWithScope // should specify a "lib" output prefix). Target with_prefix(setup.settings(), Label(SourceDir("//foo/"), "bar")); with_prefix.set_output_type(Target::STATIC_LIBRARY); with_prefix.SetToolchain(setup.toolchain()); ASSERT_TRUE(with_prefix.OnResolved(&err)); EXPECT_EQ("bar", with_prefix.GetComputedOutputName(false)); EXPECT_EQ("libbar", with_prefix.GetComputedOutputName(true)); // Target with a "lib" prefix that already has it applied. The prefix should // not duplicate something already in the target name. Target dup_prefix(setup.settings(), Label(SourceDir("//foo/"), "bar")); dup_prefix.set_output_type(Target::STATIC_LIBRARY); dup_prefix.set_output_name("libbar"); dup_prefix.SetToolchain(setup.toolchain()); ASSERT_TRUE(dup_prefix.OnResolved(&err)); EXPECT_EQ("libbar", dup_prefix.GetComputedOutputName(false)); EXPECT_EQ("libbar", dup_prefix.GetComputedOutputName(true)); } // Test visibility failure case. TEST(Target, VisibilityFails) { TestWithScope setup; Err err; Target b(setup.settings(), Label(SourceDir("//private/"), "b")); b.set_output_type(Target::STATIC_LIBRARY); b.SetToolchain(setup.toolchain()); 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. Target a(setup.settings(), Label(SourceDir("//app/"), "a")); a.set_output_type(Target::EXECUTABLE); a.private_deps().push_back(LabelTargetPair(&b)); IdentifierNode origin; // Dummy origin. a.private_deps()[0].origin = &origin; a.SetToolchain(setup.toolchain()); ASSERT_FALSE(a.OnResolved(&err)); } // Test visibility with a single data_dep. TEST(Target, VisibilityDatadeps) { TestWithScope setup; Err err; Target b(setup.settings(), Label(SourceDir("//public/"), "b")); b.set_output_type(Target::STATIC_LIBRARY); b.SetToolchain(setup.toolchain()); b.visibility().SetPublic(); 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. Target a(setup.settings(), Label(SourceDir("//app/"), "a")); a.set_output_type(Target::EXECUTABLE); a.data_deps().push_back(LabelTargetPair(&b)); IdentifierNode origin; // Dummy origin. a.data_deps()[0].origin = &origin; a.SetToolchain(setup.toolchain()); 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(Target, 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. Target b(setup.settings(), Label(SourceDir("//private/"), "b")); b.set_output_type(Target::STATIC_LIBRARY); b.SetToolchain(setup.toolchain()); b.visibility().SetPrivate(b.label().dir()); ASSERT_TRUE(b.OnResolved(&err)); // The group has public visibility and depends on b. Target g(setup.settings(), Label(SourceDir("//private/"), "g")); g.set_output_type(Target::GROUP); g.SetToolchain(setup.toolchain()); g.private_deps().push_back(LabelTargetPair(&b)); g.private_deps()[0].origin = &origin; g.visibility().SetPublic(); ASSERT_TRUE(b.OnResolved(&err)); // Make a target depending on "g". This should succeed. Target a(setup.settings(), Label(SourceDir("//app/"), "a")); a.set_output_type(Target::EXECUTABLE); a.private_deps().push_back(LabelTargetPair(&g)); a.private_deps()[0].origin = &origin; a.SetToolchain(setup.toolchain()); 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(Target, Testonly) { TestWithScope setup; Err err; // "testlib" is a test-only library. Target testlib(setup.settings(), Label(SourceDir("//test/"), "testlib")); testlib.set_testonly(true); testlib.set_output_type(Target::STATIC_LIBRARY); testlib.visibility().SetPublic(); testlib.SetToolchain(setup.toolchain()); ASSERT_TRUE(testlib.OnResolved(&err)); // "test" is a test-only executable depending on testlib, this is OK. Target test(setup.settings(), Label(SourceDir("//test/"), "test")); test.set_testonly(true); test.set_output_type(Target::EXECUTABLE); test.private_deps().push_back(LabelTargetPair(&testlib)); test.SetToolchain(setup.toolchain()); ASSERT_TRUE(test.OnResolved(&err)); // "product" is a non-test depending on testlib. This should fail. Target product(setup.settings(), Label(SourceDir("//app/"), "product")); product.set_testonly(false); product.set_output_type(Target::EXECUTABLE); product.private_deps().push_back(LabelTargetPair(&testlib)); product.SetToolchain(setup.toolchain()); ASSERT_FALSE(product.OnResolved(&err)); } TEST(Target, PublicConfigs) { TestWithScope setup; Err err; Label pub_config_label(SourceDir("//a/"), "pubconfig"); Config pub_config(setup.settings(), pub_config_label); // This is the destination target that has a public config. Target dest(setup.settings(), Label(SourceDir("//a/"), "a")); dest.set_output_type(Target::SOURCE_SET); dest.visibility().SetPublic(); dest.SetToolchain(setup.toolchain()); dest.public_configs().push_back(LabelConfigPair(&pub_config)); ASSERT_TRUE(dest.OnResolved(&err)); // This target has a public dependency on dest. Target pub(setup.settings(), Label(SourceDir("//a/"), "pub")); pub.set_output_type(Target::SOURCE_SET); pub.visibility().SetPublic(); pub.SetToolchain(setup.toolchain()); 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. Target dep_on_pub(setup.settings(), Label(SourceDir("//a/"), "dop")); dep_on_pub.set_output_type(Target::SOURCE_SET); dep_on_pub.visibility().SetPublic(); dep_on_pub.SetToolchain(setup.toolchain()); 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); // This target has a private dependency on dest for forwards configs. Target forward(setup.settings(), Label(SourceDir("//a/"), "f")); forward.set_output_type(Target::SOURCE_SET); forward.visibility().SetPublic(); forward.SetToolchain(setup.toolchain()); forward.private_deps().push_back(LabelTargetPair(&dest)); forward.forward_dependent_configs().push_back(LabelTargetPair(&dest)); ASSERT_TRUE(forward.OnResolved(&err)); // Depending on the forward target should apply the config. Target dep_on_forward(setup.settings(), Label(SourceDir("//a/"), "dof")); dep_on_forward.set_output_type(Target::SOURCE_SET); dep_on_forward.visibility().SetPublic(); dep_on_forward.SetToolchain(setup.toolchain()); dep_on_forward.private_deps().push_back(LabelTargetPair(&forward)); ASSERT_TRUE(dep_on_forward.OnResolved(&err)); ASSERT_EQ(1u, dep_on_forward.configs().size()); EXPECT_EQ(&pub_config, dep_on_forward.configs()[0].ptr); } // Tests that different link/depend outputs work for solink tools. TEST(Target, LinkAndDepOutputs) { TestWithScope setup; Err err; Toolchain toolchain(setup.settings(), Label(SourceDir("//tc/"), "tc")); scoped_ptr solink_tool(new Tool()); 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(Toolchain::TYPE_SOLINK, solink_tool.Pass()); 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()); EXPECT_EQ("./liba.so.TOC", target.dependency_output_file().value()); } // Shared libraries should be inherited across public shared liobrary // boundaries. TEST(Target, SharedInheritance) { TestWithScope setup; Err err; // Create two leaf shared libraries. Target pub(setup.settings(), Label(SourceDir("//foo/"), "pub")); pub.set_output_type(Target::SHARED_LIBRARY); pub.visibility().SetPublic(); pub.SetToolchain(setup.toolchain()); ASSERT_TRUE(pub.OnResolved(&err)); Target priv(setup.settings(), Label(SourceDir("//foo/"), "priv")); priv.set_output_type(Target::SHARED_LIBRARY); priv.visibility().SetPublic(); priv.SetToolchain(setup.toolchain()); ASSERT_TRUE(priv.OnResolved(&err)); // Intermediate shared library with the leaf shared libraries as // dependencies, one public, one private. Target inter(setup.settings(), Label(SourceDir("//foo/"), "inter")); inter.set_output_type(Target::SHARED_LIBRARY); inter.visibility().SetPublic(); inter.public_deps().push_back(LabelTargetPair(&pub)); inter.private_deps().push_back(LabelTargetPair(&priv)); inter.SetToolchain(setup.toolchain()); ASSERT_TRUE(inter.OnResolved(&err)); // The intermediate shared library should have both "pub" and "priv" in its // inherited libraries. std::vector inter_inherited = inter.inherited_libraries().GetOrdered(); ASSERT_EQ(2u, inter_inherited.size()); EXPECT_EQ(&pub, inter_inherited[0]); EXPECT_EQ(&priv, inter_inherited[1]); // Make a toplevel executable target depending on the intermediate one. Target exe(setup.settings(), Label(SourceDir("//foo/"), "exe")); exe.set_output_type(Target::SHARED_LIBRARY); exe.visibility().SetPublic(); exe.private_deps().push_back(LabelTargetPair(&inter)); exe.SetToolchain(setup.toolchain()); ASSERT_TRUE(exe.OnResolved(&err)); // The exe's inherited libraries should be "inter" (because it depended // directly on it) and "pub" (because inter depended publicly on it). std::vector exe_inherited = exe.inherited_libraries().GetOrdered(); ASSERT_EQ(2u, exe_inherited.size()); EXPECT_EQ(&inter, exe_inherited[0]); EXPECT_EQ(&pub, exe_inherited[1]); }