|  | // 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 "tools/gn/test_with_scope.h" | 
|  |  | 
|  | #include <memory> | 
|  | #include <utility> | 
|  |  | 
|  | #include "base/bind.h" | 
|  | #include "tools/gn/parser.h" | 
|  | #include "tools/gn/tokenizer.h" | 
|  |  | 
|  | namespace { | 
|  |  | 
|  | BuildSettings CreateBuildSettingsForTest() { | 
|  | BuildSettings build_settings; | 
|  | build_settings.SetBuildDir(SourceDir("//out/Debug/")); | 
|  | return build_settings; | 
|  | } | 
|  |  | 
|  | }  // namespace | 
|  |  | 
|  | TestWithScope::TestWithScope() | 
|  | : build_settings_(CreateBuildSettingsForTest()), | 
|  | settings_(&build_settings_, std::string()), | 
|  | toolchain_(&settings_, Label(SourceDir("//toolchain/"), "default")), | 
|  | scope_(&settings_), | 
|  | scope_progammatic_provider_(&scope_, true) { | 
|  | build_settings_.set_print_callback( | 
|  | base::Bind(&TestWithScope::AppendPrintOutput, base::Unretained(this))); | 
|  |  | 
|  | settings_.set_toolchain_label(toolchain_.label()); | 
|  | settings_.set_default_toolchain_label(toolchain_.label()); | 
|  |  | 
|  | SetupToolchain(&toolchain_); | 
|  | scope_.set_item_collector(&items_); | 
|  | } | 
|  |  | 
|  | TestWithScope::~TestWithScope() = default; | 
|  |  | 
|  | Label TestWithScope::ParseLabel(const std::string& str) const { | 
|  | Err err; | 
|  | Label result = Label::Resolve(SourceDir("//"), toolchain_.label(), | 
|  | Value(nullptr, str), &err); | 
|  | CHECK(!err.has_error()); | 
|  | return result; | 
|  | } | 
|  |  | 
|  | bool TestWithScope::ExecuteSnippet(const std::string& str, Err* err) { | 
|  | TestParseInput input(str); | 
|  | if (input.has_error()) { | 
|  | *err = input.parse_err(); | 
|  | return false; | 
|  | } | 
|  |  | 
|  | size_t first_item = items_.size(); | 
|  | input.parsed()->Execute(&scope_, err); | 
|  | if (err->has_error()) | 
|  | return false; | 
|  |  | 
|  | for (size_t i = first_item; i < items_.size(); ++i) { | 
|  | CHECK(items_[i]->AsTarget() != nullptr) | 
|  | << "Only targets are supported in ExecuteSnippet()"; | 
|  | items_[i]->AsTarget()->SetToolchain(&toolchain_); | 
|  | if (!items_[i]->OnResolved(err)) | 
|  | return false; | 
|  | } | 
|  | return true; | 
|  | } | 
|  |  | 
|  | // static | 
|  | void TestWithScope::SetupToolchain(Toolchain* toolchain) { | 
|  | Err err; | 
|  |  | 
|  | // CC | 
|  | std::unique_ptr<Tool> cc_tool = std::make_unique<Tool>(); | 
|  | SetCommandForTool( | 
|  | "cc {{source}} {{cflags}} {{cflags_c}} {{defines}} {{include_dirs}} " | 
|  | "-o {{output}}", | 
|  | cc_tool.get()); | 
|  | cc_tool->set_outputs(SubstitutionList::MakeForTest( | 
|  | "{{source_out_dir}}/{{target_output_name}}.{{source_name_part}}.o")); | 
|  | toolchain->SetTool(Toolchain::TYPE_CC, std::move(cc_tool)); | 
|  |  | 
|  | // CXX | 
|  | std::unique_ptr<Tool> cxx_tool = std::make_unique<Tool>(); | 
|  | SetCommandForTool( | 
|  | "c++ {{source}} {{cflags}} {{cflags_cc}} {{defines}} {{include_dirs}} " | 
|  | "-o {{output}}", | 
|  | cxx_tool.get()); | 
|  | cxx_tool->set_outputs(SubstitutionList::MakeForTest( | 
|  | "{{source_out_dir}}/{{target_output_name}}.{{source_name_part}}.o")); | 
|  | toolchain->SetTool(Toolchain::TYPE_CXX, std::move(cxx_tool)); | 
|  |  | 
|  | // OBJC | 
|  | std::unique_ptr<Tool> objc_tool = std::make_unique<Tool>(); | 
|  | SetCommandForTool( | 
|  | "objcc {{source}} {{cflags}} {{cflags_objc}} {{defines}} " | 
|  | "{{include_dirs}} -o {{output}}", | 
|  | objc_tool.get()); | 
|  | objc_tool->set_outputs(SubstitutionList::MakeForTest( | 
|  | "{{source_out_dir}}/{{target_output_name}}.{{source_name_part}}.o")); | 
|  | toolchain->SetTool(Toolchain::TYPE_OBJC, std::move(objc_tool)); | 
|  |  | 
|  | // OBJC | 
|  | std::unique_ptr<Tool> objcxx_tool = std::make_unique<Tool>(); | 
|  | SetCommandForTool( | 
|  | "objcxx {{source}} {{cflags}} {{cflags_objcc}} {{defines}} " | 
|  | "{{include_dirs}} -o {{output}}", | 
|  | objcxx_tool.get()); | 
|  | objcxx_tool->set_outputs(SubstitutionList::MakeForTest( | 
|  | "{{source_out_dir}}/{{target_output_name}}.{{source_name_part}}.o")); | 
|  | toolchain->SetTool(Toolchain::TYPE_OBJCXX, std::move(objcxx_tool)); | 
|  |  | 
|  | // Don't use RC and ASM tools in unit tests yet. Add here if needed. | 
|  |  | 
|  | // ALINK | 
|  | std::unique_ptr<Tool> alink_tool = std::make_unique<Tool>(); | 
|  | SetCommandForTool("ar {{output}} {{source}}", alink_tool.get()); | 
|  | alink_tool->set_lib_switch("-l"); | 
|  | alink_tool->set_lib_dir_switch("-L"); | 
|  | alink_tool->set_output_prefix("lib"); | 
|  | alink_tool->set_outputs(SubstitutionList::MakeForTest( | 
|  | "{{target_out_dir}}/{{target_output_name}}.a")); | 
|  | toolchain->SetTool(Toolchain::TYPE_ALINK, std::move(alink_tool)); | 
|  |  | 
|  | // SOLINK | 
|  | std::unique_ptr<Tool> solink_tool = std::make_unique<Tool>(); | 
|  | SetCommandForTool("ld -shared -o {{target_output_name}}.so {{inputs}} " | 
|  | "{{ldflags}} {{libs}}", solink_tool.get()); | 
|  | solink_tool->set_lib_switch("-l"); | 
|  | solink_tool->set_lib_dir_switch("-L"); | 
|  | solink_tool->set_output_prefix("lib"); | 
|  | solink_tool->set_default_output_extension(".so"); | 
|  | solink_tool->set_outputs(SubstitutionList::MakeForTest( | 
|  | "{{root_out_dir}}/{{target_output_name}}{{output_extension}}")); | 
|  | toolchain->SetTool(Toolchain::TYPE_SOLINK, std::move(solink_tool)); | 
|  |  | 
|  | // SOLINK_MODULE | 
|  | std::unique_ptr<Tool> solink_module_tool = std::make_unique<Tool>(); | 
|  | SetCommandForTool("ld -bundle -o {{target_output_name}}.so {{inputs}} " | 
|  | "{{ldflags}} {{libs}}", solink_module_tool.get()); | 
|  | solink_module_tool->set_lib_switch("-l"); | 
|  | solink_module_tool->set_lib_dir_switch("-L"); | 
|  | solink_module_tool->set_output_prefix("lib"); | 
|  | solink_module_tool->set_default_output_extension(".so"); | 
|  | solink_module_tool->set_outputs(SubstitutionList::MakeForTest( | 
|  | "{{root_out_dir}}/{{target_output_name}}{{output_extension}}")); | 
|  | toolchain->SetTool(Toolchain::TYPE_SOLINK_MODULE, | 
|  | std::move(solink_module_tool)); | 
|  |  | 
|  | // LINK | 
|  | std::unique_ptr<Tool> link_tool = std::make_unique<Tool>(); | 
|  | SetCommandForTool("ld -o {{target_output_name}} {{source}} " | 
|  | "{{ldflags}} {{libs}}", link_tool.get()); | 
|  | link_tool->set_lib_switch("-l"); | 
|  | link_tool->set_lib_dir_switch("-L"); | 
|  | link_tool->set_outputs(SubstitutionList::MakeForTest( | 
|  | "{{root_out_dir}}/{{target_output_name}}")); | 
|  | toolchain->SetTool(Toolchain::TYPE_LINK, std::move(link_tool)); | 
|  |  | 
|  | // STAMP | 
|  | std::unique_ptr<Tool> stamp_tool = std::make_unique<Tool>(); | 
|  | SetCommandForTool("touch {{output}}", stamp_tool.get()); | 
|  | toolchain->SetTool(Toolchain::TYPE_STAMP, std::move(stamp_tool)); | 
|  |  | 
|  | // COPY | 
|  | std::unique_ptr<Tool> copy_tool = std::make_unique<Tool>(); | 
|  | SetCommandForTool("cp {{source}} {{output}}", copy_tool.get()); | 
|  | toolchain->SetTool(Toolchain::TYPE_COPY, std::move(copy_tool)); | 
|  |  | 
|  | // COPY_BUNDLE_DATA | 
|  | std::unique_ptr<Tool> copy_bundle_data_tool = std::make_unique<Tool>(); | 
|  | SetCommandForTool("cp {{source}} {{output}}", copy_bundle_data_tool.get()); | 
|  | toolchain->SetTool(Toolchain::TYPE_COPY_BUNDLE_DATA, | 
|  | std::move(copy_bundle_data_tool)); | 
|  |  | 
|  | // COMPILE_XCASSETS | 
|  | std::unique_ptr<Tool> compile_xcassets_tool = std::make_unique<Tool>(); | 
|  | SetCommandForTool("touch {{output}}", compile_xcassets_tool.get()); | 
|  | toolchain->SetTool(Toolchain::TYPE_COMPILE_XCASSETS, | 
|  | std::move(compile_xcassets_tool)); | 
|  |  | 
|  | toolchain->ToolchainSetupComplete(); | 
|  | } | 
|  |  | 
|  | // static | 
|  | void TestWithScope::SetCommandForTool(const std::string& cmd, Tool* tool) { | 
|  | Err err; | 
|  | SubstitutionPattern command; | 
|  | command.Parse(cmd, nullptr, &err); | 
|  | CHECK(!err.has_error()) | 
|  | << "Couldn't parse \"" << cmd << "\", " << "got " << err.message(); | 
|  | tool->set_command(command); | 
|  | } | 
|  |  | 
|  | void TestWithScope::AppendPrintOutput(const std::string& str) { | 
|  | print_output_.append(str); | 
|  | } | 
|  |  | 
|  | TestParseInput::TestParseInput(const std::string& input) | 
|  | : input_file_(SourceFile("//test")) { | 
|  | input_file_.SetContents(input); | 
|  |  | 
|  | tokens_ = Tokenizer::Tokenize(&input_file_, &parse_err_); | 
|  | if (!parse_err_.has_error()) | 
|  | parsed_ = Parser::Parse(tokens_, &parse_err_); | 
|  | } | 
|  |  | 
|  | TestParseInput::~TestParseInput() = default; | 
|  |  | 
|  | TestTarget::TestTarget(const TestWithScope& setup, | 
|  | const std::string& label_string, | 
|  | Target::OutputType type) | 
|  | : Target(setup.settings(), setup.ParseLabel(label_string)) { | 
|  | visibility().SetPublic(); | 
|  | set_output_type(type); | 
|  | SetToolchain(setup.toolchain()); | 
|  | } | 
|  |  | 
|  | TestTarget::~TestTarget() = default; |