|  | // 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 "build_config.h" | 
|  | #include "test/test.h" | 
|  | #include "tools/gn/err.h" | 
|  | #include "tools/gn/escape.h" | 
|  | #include "tools/gn/substitution_list.h" | 
|  | #include "tools/gn/substitution_pattern.h" | 
|  | #include "tools/gn/substitution_writer.h" | 
|  | #include "tools/gn/target.h" | 
|  | #include "tools/gn/test_with_scope.h" | 
|  |  | 
|  | TEST(SubstitutionWriter, GetListAs) { | 
|  | TestWithScope setup; | 
|  |  | 
|  | SubstitutionList list = SubstitutionList::MakeForTest( | 
|  | "//foo/bar/a.cc", | 
|  | "//foo/bar/b.cc"); | 
|  |  | 
|  | std::vector<SourceFile> sources; | 
|  | SubstitutionWriter::GetListAsSourceFiles(list, &sources); | 
|  | ASSERT_EQ(2u, sources.size()); | 
|  | EXPECT_EQ("//foo/bar/a.cc", sources[0].value()); | 
|  | EXPECT_EQ("//foo/bar/b.cc", sources[1].value()); | 
|  |  | 
|  | std::vector<OutputFile> outputs; | 
|  | SubstitutionWriter::GetListAsOutputFiles(setup.settings(), list, &outputs); | 
|  | ASSERT_EQ(2u, outputs.size()); | 
|  | EXPECT_EQ("../../foo/bar/a.cc", outputs[0].value()); | 
|  | EXPECT_EQ("../../foo/bar/b.cc", outputs[1].value()); | 
|  | } | 
|  |  | 
|  | TEST(SubstitutionWriter, ApplyPatternToSource) { | 
|  | TestWithScope setup; | 
|  |  | 
|  | SubstitutionPattern pattern; | 
|  | Err err; | 
|  | ASSERT_TRUE(pattern.Parse("{{source_gen_dir}}/{{source_name_part}}.tmp", | 
|  | nullptr, &err)); | 
|  |  | 
|  | SourceFile result = SubstitutionWriter::ApplyPatternToSource( | 
|  | nullptr, setup.settings(), pattern, SourceFile("//foo/bar/myfile.txt")); | 
|  | ASSERT_EQ("//out/Debug/gen/foo/bar/myfile.tmp", result.value()); | 
|  | } | 
|  |  | 
|  | TEST(SubstitutionWriter, ApplyPatternToSourceAsOutputFile) { | 
|  | TestWithScope setup; | 
|  |  | 
|  | SubstitutionPattern pattern; | 
|  | Err err; | 
|  | ASSERT_TRUE(pattern.Parse("{{source_gen_dir}}/{{source_name_part}}.tmp", | 
|  | nullptr, &err)); | 
|  |  | 
|  | OutputFile result = SubstitutionWriter::ApplyPatternToSourceAsOutputFile( | 
|  | nullptr, setup.settings(), pattern, SourceFile("//foo/bar/myfile.txt")); | 
|  | ASSERT_EQ("gen/foo/bar/myfile.tmp", result.value()); | 
|  | } | 
|  |  | 
|  | TEST(SubstitutionWriter, WriteNinjaVariablesForSource) { | 
|  | TestWithScope setup; | 
|  |  | 
|  | std::vector<SubstitutionType> types; | 
|  | types.push_back(SUBSTITUTION_SOURCE); | 
|  | types.push_back(SUBSTITUTION_SOURCE_NAME_PART); | 
|  | types.push_back(SUBSTITUTION_SOURCE_DIR); | 
|  |  | 
|  | EscapeOptions options; | 
|  | options.mode = ESCAPE_NONE; | 
|  |  | 
|  | std::ostringstream out; | 
|  | SubstitutionWriter::WriteNinjaVariablesForSource( | 
|  | nullptr, setup.settings(), SourceFile("//foo/bar/baz.txt"), types, | 
|  | options, out); | 
|  |  | 
|  | // The "source" should be skipped since that will expand to $in which is | 
|  | // implicit. | 
|  | EXPECT_EQ( | 
|  | "  source_name_part = baz\n" | 
|  | "  source_dir = ../../foo/bar\n", | 
|  | out.str()); | 
|  | } | 
|  |  | 
|  | TEST(SubstitutionWriter, WriteWithNinjaVariables) { | 
|  | Err err; | 
|  | SubstitutionPattern pattern; | 
|  | ASSERT_TRUE(pattern.Parse("-i {{source}} --out=bar\"{{source_name_part}}\".o", | 
|  | nullptr, &err)); | 
|  | EXPECT_FALSE(err.has_error()); | 
|  |  | 
|  | EscapeOptions options; | 
|  | options.mode = ESCAPE_NONE; | 
|  |  | 
|  | std::ostringstream out; | 
|  | SubstitutionWriter::WriteWithNinjaVariables(pattern, options, out); | 
|  |  | 
|  | EXPECT_EQ( | 
|  | "-i ${in} --out=bar\"${source_name_part}\".o", | 
|  | out.str()); | 
|  | } | 
|  |  | 
|  | TEST(SubstitutionWriter, SourceSubstitutions) { | 
|  | TestWithScope setup; | 
|  | Err err; | 
|  |  | 
|  | Target target(setup.settings(), Label(SourceDir("//foo/bar/"), "baz")); | 
|  | target.set_output_type(Target::STATIC_LIBRARY); | 
|  | target.SetToolchain(setup.toolchain()); | 
|  | ASSERT_TRUE(target.OnResolved(&err)); | 
|  |  | 
|  | // Call to get substitutions relative to the build dir. | 
|  | #define GetRelSubst(str, what) \ | 
|  | SubstitutionWriter::GetSourceSubstitution( \ | 
|  | &target, \ | 
|  | setup.settings(), \ | 
|  | SourceFile(str), \ | 
|  | what, \ | 
|  | SubstitutionWriter::OUTPUT_RELATIVE, \ | 
|  | setup.settings()->build_settings()->build_dir()) | 
|  |  | 
|  | // Call to get absolute directory substitutions. | 
|  | #define GetAbsSubst(str, what) \ | 
|  | SubstitutionWriter::GetSourceSubstitution( \ | 
|  | &target, \ | 
|  | setup.settings(), \ | 
|  | SourceFile(str), \ | 
|  | what, \ | 
|  | SubstitutionWriter::OUTPUT_ABSOLUTE, \ | 
|  | SourceDir()) | 
|  |  | 
|  | // Try all possible templates with a normal looking string. | 
|  | EXPECT_EQ("../../foo/bar/baz.txt", | 
|  | GetRelSubst("//foo/bar/baz.txt", SUBSTITUTION_SOURCE)); | 
|  | EXPECT_EQ("//foo/bar/baz.txt", | 
|  | GetAbsSubst("//foo/bar/baz.txt", SUBSTITUTION_SOURCE)); | 
|  |  | 
|  | EXPECT_EQ("baz", | 
|  | GetRelSubst("//foo/bar/baz.txt", SUBSTITUTION_SOURCE_NAME_PART)); | 
|  | EXPECT_EQ("baz", | 
|  | GetAbsSubst("//foo/bar/baz.txt", SUBSTITUTION_SOURCE_NAME_PART)); | 
|  |  | 
|  | EXPECT_EQ("baz.txt", | 
|  | GetRelSubst("//foo/bar/baz.txt", SUBSTITUTION_SOURCE_FILE_PART)); | 
|  | EXPECT_EQ("baz.txt", | 
|  | GetAbsSubst("//foo/bar/baz.txt", SUBSTITUTION_SOURCE_FILE_PART)); | 
|  |  | 
|  | EXPECT_EQ("../../foo/bar", | 
|  | GetRelSubst("//foo/bar/baz.txt", SUBSTITUTION_SOURCE_DIR)); | 
|  | EXPECT_EQ("//foo/bar", | 
|  | GetAbsSubst("//foo/bar/baz.txt", SUBSTITUTION_SOURCE_DIR)); | 
|  |  | 
|  | EXPECT_EQ("foo/bar", GetRelSubst("//foo/bar/baz.txt", | 
|  | SUBSTITUTION_SOURCE_ROOT_RELATIVE_DIR)); | 
|  | EXPECT_EQ("foo/bar", GetAbsSubst("//foo/bar/baz.txt", | 
|  | SUBSTITUTION_SOURCE_ROOT_RELATIVE_DIR)); | 
|  |  | 
|  | EXPECT_EQ("gen/foo/bar", | 
|  | GetRelSubst("//foo/bar/baz.txt", SUBSTITUTION_SOURCE_GEN_DIR)); | 
|  | EXPECT_EQ("//out/Debug/gen/foo/bar", | 
|  | GetAbsSubst("//foo/bar/baz.txt", SUBSTITUTION_SOURCE_GEN_DIR)); | 
|  |  | 
|  | EXPECT_EQ("obj/foo/bar", | 
|  | GetRelSubst("//foo/bar/baz.txt", SUBSTITUTION_SOURCE_OUT_DIR)); | 
|  | EXPECT_EQ("//out/Debug/obj/foo/bar", | 
|  | GetAbsSubst("//foo/bar/baz.txt", SUBSTITUTION_SOURCE_OUT_DIR)); | 
|  |  | 
|  | // Operations on an absolute path. | 
|  | EXPECT_EQ("/baz.txt", GetRelSubst("/baz.txt", SUBSTITUTION_SOURCE)); | 
|  | EXPECT_EQ("/.", GetRelSubst("/baz.txt", SUBSTITUTION_SOURCE_DIR)); | 
|  | EXPECT_EQ("gen/ABS_PATH", | 
|  | GetRelSubst("/baz.txt", SUBSTITUTION_SOURCE_GEN_DIR)); | 
|  | EXPECT_EQ("obj/ABS_PATH", | 
|  | GetRelSubst("/baz.txt", SUBSTITUTION_SOURCE_OUT_DIR)); | 
|  | #if defined(OS_WIN) | 
|  | EXPECT_EQ("gen/ABS_PATH/C", | 
|  | GetRelSubst("/C:/baz.txt", SUBSTITUTION_SOURCE_GEN_DIR)); | 
|  | EXPECT_EQ("obj/ABS_PATH/C", | 
|  | GetRelSubst("/C:/baz.txt", SUBSTITUTION_SOURCE_OUT_DIR)); | 
|  | #endif | 
|  |  | 
|  | EXPECT_EQ(".", | 
|  | GetRelSubst("//baz.txt", SUBSTITUTION_SOURCE_ROOT_RELATIVE_DIR)); | 
|  |  | 
|  | EXPECT_EQ("baz.txt", | 
|  | GetRelSubst("//foo/bar/baz.txt", SUBSTITUTION_SOURCE_TARGET_RELATIVE)); | 
|  | EXPECT_EQ("baz.txt", | 
|  | GetAbsSubst("//foo/bar/baz.txt", SUBSTITUTION_SOURCE_TARGET_RELATIVE)); | 
|  |  | 
|  | #undef GetAbsSubst | 
|  | #undef GetRelSubst | 
|  | } | 
|  |  | 
|  | TEST(SubstitutionWriter, TargetSubstitutions) { | 
|  | TestWithScope setup; | 
|  | Err err; | 
|  |  | 
|  | Target target(setup.settings(), Label(SourceDir("//foo/bar/"), "baz")); | 
|  | target.set_output_type(Target::STATIC_LIBRARY); | 
|  | target.SetToolchain(setup.toolchain()); | 
|  | ASSERT_TRUE(target.OnResolved(&err)); | 
|  |  | 
|  | std::string result; | 
|  | EXPECT_TRUE(SubstitutionWriter::GetTargetSubstitution( | 
|  | &target, SUBSTITUTION_LABEL, &result)); | 
|  | EXPECT_EQ("//foo/bar:baz", result); | 
|  |  | 
|  | EXPECT_TRUE(SubstitutionWriter::GetTargetSubstitution( | 
|  | &target, SUBSTITUTION_LABEL_NAME, &result)); | 
|  | EXPECT_EQ("baz", result); | 
|  |  | 
|  | EXPECT_TRUE(SubstitutionWriter::GetTargetSubstitution( | 
|  | &target, SUBSTITUTION_ROOT_GEN_DIR, &result)); | 
|  | EXPECT_EQ("gen", result); | 
|  |  | 
|  | EXPECT_TRUE(SubstitutionWriter::GetTargetSubstitution( | 
|  | &target, SUBSTITUTION_ROOT_OUT_DIR, &result)); | 
|  | EXPECT_EQ(".", result); | 
|  |  | 
|  | EXPECT_TRUE(SubstitutionWriter::GetTargetSubstitution( | 
|  | &target, SUBSTITUTION_TARGET_GEN_DIR, &result)); | 
|  | EXPECT_EQ("gen/foo/bar", result); | 
|  |  | 
|  | EXPECT_TRUE(SubstitutionWriter::GetTargetSubstitution( | 
|  | &target, SUBSTITUTION_TARGET_OUT_DIR, &result)); | 
|  | EXPECT_EQ("obj/foo/bar", result); | 
|  |  | 
|  | EXPECT_TRUE(SubstitutionWriter::GetTargetSubstitution( | 
|  | &target, SUBSTITUTION_TARGET_OUTPUT_NAME, &result)); | 
|  | EXPECT_EQ("libbaz", result); | 
|  | } | 
|  |  | 
|  | TEST(SubstitutionWriter, CompilerSubstitutions) { | 
|  | TestWithScope setup; | 
|  | Err err; | 
|  |  | 
|  | Target target(setup.settings(), Label(SourceDir("//foo/bar/"), "baz")); | 
|  | target.set_output_type(Target::STATIC_LIBRARY); | 
|  | target.SetToolchain(setup.toolchain()); | 
|  | ASSERT_TRUE(target.OnResolved(&err)); | 
|  |  | 
|  | // The compiler substitution is just source + target combined. So test one | 
|  | // of each of those classes of things to make sure this is hooked up. | 
|  | EXPECT_EQ("file", | 
|  | SubstitutionWriter::GetCompilerSubstitution( | 
|  | &target, SourceFile("//foo/bar/file.txt"), | 
|  | SUBSTITUTION_SOURCE_NAME_PART)); | 
|  | EXPECT_EQ("gen/foo/bar", | 
|  | SubstitutionWriter::GetCompilerSubstitution( | 
|  | &target, SourceFile("//foo/bar/file.txt"), | 
|  | SUBSTITUTION_TARGET_GEN_DIR)); | 
|  | } | 
|  |  | 
|  | TEST(SubstitutionWriter, LinkerSubstitutions) { | 
|  | TestWithScope setup; | 
|  | Err err; | 
|  |  | 
|  | Target target(setup.settings(), Label(SourceDir("//foo/bar/"), "baz")); | 
|  | target.set_output_type(Target::SHARED_LIBRARY); | 
|  | target.SetToolchain(setup.toolchain()); | 
|  | ASSERT_TRUE(target.OnResolved(&err)); | 
|  |  | 
|  | const Tool* tool = setup.toolchain()->GetToolForTargetFinalOutput(&target); | 
|  |  | 
|  | // The compiler substitution is just target + OUTPUT_EXTENSION combined. So | 
|  | // test one target one plus the output extension. | 
|  | EXPECT_EQ(".so", | 
|  | SubstitutionWriter::GetLinkerSubstitution( | 
|  | &target, tool, SUBSTITUTION_OUTPUT_EXTENSION)); | 
|  | EXPECT_EQ("gen/foo/bar", | 
|  | SubstitutionWriter::GetLinkerSubstitution( | 
|  | &target, tool, SUBSTITUTION_TARGET_GEN_DIR)); | 
|  |  | 
|  | // Test that we handle paths that end up in the root build dir properly | 
|  | // (no leading "./" or "/"). | 
|  | SubstitutionPattern pattern; | 
|  | ASSERT_TRUE(pattern.Parse("{{root_out_dir}}/{{target_output_name}}.so", | 
|  | nullptr, &err)); | 
|  |  | 
|  | OutputFile output = SubstitutionWriter::ApplyPatternToLinkerAsOutputFile( | 
|  | &target, tool, pattern); | 
|  | EXPECT_EQ("./libbaz.so", output.value()); | 
|  |  | 
|  | // Output extensions can be overridden. | 
|  | target.set_output_extension("extension"); | 
|  | EXPECT_EQ(".extension", | 
|  | SubstitutionWriter::GetLinkerSubstitution( | 
|  | &target, tool, SUBSTITUTION_OUTPUT_EXTENSION)); | 
|  | target.set_output_extension(""); | 
|  | EXPECT_EQ("", | 
|  | SubstitutionWriter::GetLinkerSubstitution( | 
|  | &target, tool, SUBSTITUTION_OUTPUT_EXTENSION)); | 
|  |  | 
|  | // Output directory is tested in a separate test below. | 
|  | } | 
|  |  | 
|  | TEST(SubstitutionWriter, OutputDir) { | 
|  | TestWithScope setup; | 
|  | Err err; | 
|  |  | 
|  | // This tool has an output directory pattern and uses that for the | 
|  | // output name. | 
|  | Tool tool; | 
|  | SubstitutionPattern out_dir_pattern; | 
|  | ASSERT_TRUE(out_dir_pattern.Parse("{{root_out_dir}}/{{target_output_name}}", | 
|  | nullptr, &err)); | 
|  | tool.set_default_output_dir(out_dir_pattern); | 
|  | tool.SetComplete(); | 
|  |  | 
|  | // Default target with no output dir overrides. | 
|  | Target target(setup.settings(), Label(SourceDir("//foo/"), "baz")); | 
|  | target.set_output_type(Target::EXECUTABLE); | 
|  | target.SetToolchain(setup.toolchain()); | 
|  | ASSERT_TRUE(target.OnResolved(&err)); | 
|  |  | 
|  | // The output should expand the default from the patterns in the tool. | 
|  | SubstitutionPattern output_name; | 
|  | ASSERT_TRUE(output_name.Parse("{{output_dir}}/{{target_output_name}}.exe", | 
|  | nullptr, &err)); | 
|  | EXPECT_EQ("./baz/baz.exe", | 
|  | SubstitutionWriter::ApplyPatternToLinkerAsOutputFile( | 
|  | &target, &tool, output_name).value()); | 
|  |  | 
|  | // Override the output name to the root build dir. | 
|  | target.set_output_dir(SourceDir("//out/Debug/")); | 
|  | EXPECT_EQ("./baz.exe", | 
|  | SubstitutionWriter::ApplyPatternToLinkerAsOutputFile( | 
|  | &target, &tool, output_name).value()); | 
|  |  | 
|  | // Override the output name to a new subdirectory. | 
|  | target.set_output_dir(SourceDir("//out/Debug/foo/bar")); | 
|  | EXPECT_EQ("foo/bar/baz.exe", | 
|  | SubstitutionWriter::ApplyPatternToLinkerAsOutputFile( | 
|  | &target, &tool, output_name).value()); | 
|  | } |