| // 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/config.h" | |
| #include "gn/config_values_extractors.h" | |
| #include "gn/config_values_generator.h" | |
| #include "gn/target.h" | |
| #include "gn/test_with_scope.h" | |
| #include "util/test/test.h" | |
| namespace { | |
| struct FlagWriter { | |
| void operator()(const std::string& dir, std::ostream& out) const { | |
| out << dir << " "; | |
| } | |
| }; | |
| struct IncludeWriter { | |
| void operator()(const SourceDir& dir, std::ostream& out) const { | |
| out << dir.value() << " "; | |
| } | |
| }; | |
| } // namespace | |
| TEST(ConfigValuesExtractors, IncludeOrdering) { | |
| TestWithScope setup; | |
| Err err; | |
| // Construct a chain of dependencies: target -> dep1 -> dep2 | |
| // Add representative values: cflags (opaque, always copied) and include_dirs | |
| // (uniquified) to each one so we can check what comes out the other end. | |
| // Set up dep2, direct and all dependent configs. | |
| Config dep2_all(setup.settings(), Label(SourceDir("//dep2/"), "all")); | |
| dep2_all.visibility().SetPublic(); | |
| dep2_all.own_values().cflags().push_back("--dep2-all"); | |
| dep2_all.own_values().cflags().push_back("--dep2-all"); | |
| dep2_all.own_values().include_dirs().push_back(SourceDir("//dep2/all/")); | |
| dep2_all.own_values().include_dirs().push_back(SourceDir("//dep2/all/")); | |
| ASSERT_TRUE(dep2_all.OnResolved(&err)); | |
| Config dep2_direct(setup.settings(), Label(SourceDir("//dep2/"), "direct")); | |
| dep2_direct.visibility().SetPublic(); | |
| dep2_direct.own_values().cflags().push_back("--dep2-direct"); | |
| dep2_direct.own_values().include_dirs().push_back( | |
| SourceDir("//dep2/direct/")); | |
| ASSERT_TRUE(dep2_direct.OnResolved(&err)); | |
| Target dep2(setup.settings(), Label(SourceDir("//dep2/"), "dep2")); | |
| dep2.set_output_type(Target::SOURCE_SET); | |
| dep2.visibility().SetPublic(); | |
| dep2.SetToolchain(setup.toolchain()); | |
| dep2.all_dependent_configs().push_back(LabelConfigPair(&dep2_all)); | |
| dep2.public_configs().push_back(LabelConfigPair(&dep2_direct)); | |
| // Set up dep1, direct and all dependent configs. Also set up a subconfig | |
| // on "dep1_all" to test sub configs. | |
| Config dep1_all_sub(setup.settings(), Label(SourceDir("//dep1"), "allch")); | |
| dep1_all_sub.visibility().SetPublic(); | |
| dep1_all_sub.own_values().cflags().push_back("--dep1-all-sub"); | |
| ASSERT_TRUE(dep1_all_sub.OnResolved(&err)); | |
| Config dep1_all(setup.settings(), Label(SourceDir("//dep1/"), "all")); | |
| dep1_all.visibility().SetPublic(); | |
| dep1_all.own_values().cflags().push_back("--dep1-all"); | |
| dep1_all.own_values().include_dirs().push_back(SourceDir("//dep1/all/")); | |
| dep1_all.configs().push_back(LabelConfigPair(&dep1_all_sub)); | |
| ASSERT_TRUE(dep1_all.OnResolved(&err)); | |
| Config dep1_direct(setup.settings(), Label(SourceDir("//dep1/"), "direct")); | |
| dep1_direct.visibility().SetPublic(); | |
| dep1_direct.own_values().cflags().push_back("--dep1-direct"); | |
| dep1_direct.own_values().include_dirs().push_back( | |
| SourceDir("//dep1/direct/")); | |
| ASSERT_TRUE(dep1_direct.OnResolved(&err)); | |
| Target dep1(setup.settings(), Label(SourceDir("//dep1/"), "dep1")); | |
| dep1.set_output_type(Target::SOURCE_SET); | |
| dep1.visibility().SetPublic(); | |
| dep1.SetToolchain(setup.toolchain()); | |
| dep1.all_dependent_configs().push_back(LabelConfigPair(&dep1_all)); | |
| dep1.public_configs().push_back(LabelConfigPair(&dep1_direct)); | |
| dep1.private_deps().push_back(LabelTargetPair(&dep2)); | |
| // Set up target, direct and all dependent configs. | |
| Config target_all(setup.settings(), Label(SourceDir("//target/"), "all")); | |
| target_all.visibility().SetPublic(); | |
| target_all.own_values().cflags().push_back("--target-all"); | |
| target_all.own_values().include_dirs().push_back(SourceDir("//target/all/")); | |
| ASSERT_TRUE(target_all.OnResolved(&err)); | |
| Config target_direct(setup.settings(), | |
| Label(SourceDir("//target/"), "direct")); | |
| target_direct.visibility().SetPublic(); | |
| target_direct.own_values().cflags().push_back("--target-direct"); | |
| target_direct.own_values().include_dirs().push_back( | |
| SourceDir("//target/direct/")); | |
| ASSERT_TRUE(target_direct.OnResolved(&err)); | |
| // This config is applied directly to target. | |
| Config target_config(setup.settings(), | |
| Label(SourceDir("//target/"), "config")); | |
| target_config.visibility().SetPublic(); | |
| target_config.own_values().cflags().push_back("--target-config"); | |
| target_config.own_values().include_dirs().push_back( | |
| SourceDir("//target/config/")); | |
| ASSERT_TRUE(target_config.OnResolved(&err)); | |
| Target target(setup.settings(), Label(SourceDir("//target/"), "target")); | |
| target.set_output_type(Target::SOURCE_SET); | |
| target.SetToolchain(setup.toolchain()); | |
| target.all_dependent_configs().push_back(LabelConfigPair(&target_all)); | |
| target.public_configs().push_back(LabelConfigPair(&target_direct)); | |
| target.configs().push_back(LabelConfigPair(&target_config)); | |
| target.private_deps().push_back(LabelTargetPair(&dep1)); | |
| // Additionally add some values directly on "target". | |
| target.config_values().cflags().push_back("--target"); | |
| target.config_values().include_dirs().push_back(SourceDir("//target/")); | |
| // Mark targets resolved. This should push dependent configs. | |
| ASSERT_TRUE(dep2.OnResolved(&err)); | |
| ASSERT_TRUE(dep1.OnResolved(&err)); | |
| ASSERT_TRUE(target.OnResolved(&err)); | |
| // Verify cflags by serializing. | |
| std::ostringstream flag_out; | |
| FlagWriter flag_writer; | |
| RecursiveTargetConfigToStream<std::string, FlagWriter>( | |
| kRecursiveWriterKeepDuplicates, &target, &ConfigValues::cflags, | |
| flag_writer, flag_out); | |
| EXPECT_EQ(flag_out.str(), | |
| "--target --target-config --target-all --target-direct " | |
| "--dep1-all --dep1-all-sub --dep2-all --dep2-all --dep1-direct "); | |
| // Verify include dirs by serializing. | |
| std::ostringstream include_out; | |
| IncludeWriter include_writer; | |
| RecursiveTargetConfigToStream<SourceDir, IncludeWriter>( | |
| kRecursiveWriterSkipDuplicates, &target, &ConfigValues::include_dirs, | |
| include_writer, include_out); | |
| EXPECT_EQ(include_out.str(), | |
| "//target/ //target/config/ //target/all/ //target/direct/ " | |
| "//dep1/all/ //dep2/all/ //dep1/direct/ "); | |
| } | |
| TEST(ConfigValuesGenerator, DefinesWithNewlineError) { | |
| TestWithScope setup; | |
| Err err; | |
| // Set up a scope with a defines list containing a newline. | |
| Value defines_value(nullptr, Value::LIST); | |
| defines_value.list_value().push_back(Value(nullptr, "GOOD")); | |
| defines_value.list_value().push_back(Value(nullptr, "BAD=a\nb")); | |
| setup.scope()->SetValue("defines", defines_value, nullptr); | |
| ConfigValues config_values; | |
| ConfigValuesGenerator gen(&config_values, setup.scope(), SourceDir("//foo/"), | |
| &err); | |
| gen.Run(); | |
| EXPECT_TRUE(err.has_error()); | |
| EXPECT_EQ(err.message(), "Newlines in defines values are not supported."); | |
| EXPECT_EQ(err.help_text(), "The value `BAD=a\nb` contains a newline."); | |
| } | |
| TEST(ConfigValuesGenerator, CflagsWithNewlineError) { | |
| TestWithScope setup; | |
| Err err; | |
| Value cflags_value(nullptr, Value::LIST); | |
| cflags_value.list_value().push_back(Value(nullptr, "-Dfoo\nbar")); | |
| setup.scope()->SetValue("cflags", cflags_value, nullptr); | |
| ConfigValues config_values; | |
| ConfigValuesGenerator gen(&config_values, setup.scope(), SourceDir("//foo/"), | |
| &err); | |
| gen.Run(); | |
| EXPECT_TRUE(err.has_error()); | |
| EXPECT_EQ(err.message(), "Newlines in cflags values are not supported."); | |
| } | |
| TEST(ConfigValuesGenerator, AdditionalOutputs) { | |
| TestWithScope setup; | |
| Err err; | |
| Value outputs_value(nullptr, Value::LIST); | |
| outputs_value.list_value().push_back( | |
| Value(nullptr, "{{target_out_dir}}/{{source_name_part}}.dwo")); | |
| setup.scope()->SetValue("c_additional_outputs", outputs_value, nullptr); | |
| ConfigValues config_values; | |
| ConfigValuesGenerator gen(&config_values, setup.scope(), SourceDir("//foo/"), | |
| &err); | |
| gen.Run(); | |
| EXPECT_SUCCESS(err); | |
| ASSERT_EQ(config_values.c_additional_outputs().size(), 1u); | |
| EXPECT_EQ(config_values.c_additional_outputs()[0].AsString(), | |
| "{{target_out_dir}}/{{source_name_part}}.dwo"); | |
| } |