1 // Copyright (c) 2013 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "gn/test_with_scope.h"
6
7 #include <memory>
8 #include <utility>
9
10 #include "gn/parser.h"
11 #include "gn/tokenizer.h"
12
13 namespace {
14
CreateBuildSettingsForTest()15 BuildSettings CreateBuildSettingsForTest() {
16 BuildSettings build_settings;
17 build_settings.SetBuildDir(SourceDir("//out/Debug/"));
18 return build_settings;
19 }
20
21 } // namespace
22
TestWithScope()23 TestWithScope::TestWithScope()
24 : build_settings_(CreateBuildSettingsForTest()),
25 settings_(&build_settings_, std::string()),
26 toolchain_(&settings_, Label(SourceDir("//toolchain/"), "default")),
27 scope_(&settings_),
28 scope_progammatic_provider_(&scope_, true) {
29 build_settings_.set_print_callback(
30 [this](const std::string& str) { AppendPrintOutput(str); });
31
32 settings_.set_toolchain_label(toolchain_.label());
33 settings_.set_default_toolchain_label(toolchain_.label());
34
35 SetupToolchain(&toolchain_);
36 scope_.set_item_collector(&items_);
37 }
38
39 TestWithScope::~TestWithScope() = default;
40
ParseLabel(const std::string & str) const41 Label TestWithScope::ParseLabel(const std::string& str) const {
42 Err err;
43 Label result = Label::Resolve(SourceDir("//"), std::string_view(),
44 toolchain_.label(), Value(nullptr, str), &err);
45 CHECK(!err.has_error());
46 return result;
47 }
48
ExecuteSnippet(const std::string & str,Err * err)49 bool TestWithScope::ExecuteSnippet(const std::string& str, Err* err) {
50 TestParseInput input(str);
51 if (input.has_error()) {
52 *err = input.parse_err();
53 return false;
54 }
55
56 size_t first_item = items_.size();
57 input.parsed()->Execute(&scope_, err);
58 if (err->has_error())
59 return false;
60
61 for (size_t i = first_item; i < items_.size(); ++i) {
62 CHECK(items_[i]->AsTarget() != nullptr)
63 << "Only targets are supported in ExecuteSnippet()";
64 items_[i]->AsTarget()->SetToolchain(&toolchain_);
65 if (!items_[i]->OnResolved(err))
66 return false;
67 }
68 return true;
69 }
70
ExecuteExpression(const std::string & expr,Err * err)71 Value TestWithScope::ExecuteExpression(const std::string& expr, Err* err) {
72 InputFile input_file(SourceFile("//test"));
73 input_file.SetContents(expr);
74
75 std::vector<Token> tokens = Tokenizer::Tokenize(&input_file, err);
76 if (err->has_error()) {
77 return Value();
78 }
79 std::unique_ptr<ParseNode> node = Parser::ParseExpression(tokens, err);
80 if (err->has_error()) {
81 return Value();
82 }
83
84 return node->Execute(&scope_, err);
85 }
86
87 // static
SetupToolchain(Toolchain * toolchain,bool use_toc)88 void TestWithScope::SetupToolchain(Toolchain* toolchain, bool use_toc) {
89 Err err;
90
91 // CC
92 std::unique_ptr<Tool> cc_tool = Tool::CreateTool(CTool::kCToolCc);
93 SetCommandForTool(
94 "cc {{source}} {{cflags}} {{cflags_c}} {{defines}} {{include_dirs}} "
95 "-o {{output}}",
96 cc_tool.get());
97 cc_tool->set_outputs(SubstitutionList::MakeForTest(
98 "{{source_out_dir}}/{{target_output_name}}.{{source_name_part}}.o"));
99 toolchain->SetTool(std::move(cc_tool));
100
101 // CXX
102 std::unique_ptr<Tool> cxx_tool = Tool::CreateTool(CTool::kCToolCxx);
103 SetCommandForTool(
104 "c++ {{source}} {{cflags}} {{cflags_cc}} {{defines}} {{include_dirs}} "
105 "-o {{output}}",
106 cxx_tool.get());
107 cxx_tool->set_outputs(SubstitutionList::MakeForTest(
108 "{{source_out_dir}}/{{target_output_name}}.{{source_name_part}}.o"));
109 cxx_tool->set_command_launcher("launcher");
110 toolchain->SetTool(std::move(cxx_tool));
111
112 // OBJC
113 std::unique_ptr<Tool> objc_tool = Tool::CreateTool(CTool::kCToolObjC);
114 SetCommandForTool(
115 "objcc {{source}} {{cflags}} {{cflags_objc}} {{defines}} "
116 "{{include_dirs}} -o {{output}}",
117 objc_tool.get());
118 objc_tool->set_outputs(SubstitutionList::MakeForTest(
119 "{{source_out_dir}}/{{target_output_name}}.{{source_name_part}}.o"));
120 toolchain->SetTool(std::move(objc_tool));
121
122 // OBJC
123 std::unique_ptr<Tool> objcxx_tool = Tool::CreateTool(CTool::kCToolObjCxx);
124 SetCommandForTool(
125 "objcxx {{source}} {{cflags}} {{cflags_objcc}} {{defines}} "
126 "{{include_dirs}} -o {{output}}",
127 objcxx_tool.get());
128 objcxx_tool->set_outputs(SubstitutionList::MakeForTest(
129 "{{source_out_dir}}/{{target_output_name}}.{{source_name_part}}.o"));
130 toolchain->SetTool(std::move(objcxx_tool));
131
132 // Don't use RC and ASM tools in unit tests yet. Add here if needed.
133
134 // ALINK
135 std::unique_ptr<Tool> alink = Tool::CreateTool(CTool::kCToolAlink);
136 CTool* alink_tool = alink->AsC();
137 SetCommandForTool("ar {{output}} {{source}}", alink_tool);
138 alink_tool->set_lib_switch("-l");
139 alink_tool->set_lib_dir_switch("-L");
140 alink_tool->set_output_prefix("lib");
141 alink_tool->set_outputs(SubstitutionList::MakeForTest(
142 "{{target_out_dir}}/{{target_output_name}}.a"));
143 toolchain->SetTool(std::move(alink));
144
145 // SOLINK
146 std::unique_ptr<Tool> solink = Tool::CreateTool(CTool::kCToolSolink);
147 CTool* solink_tool = solink->AsC();
148 SetCommandForTool(
149 "ld -shared -o {{target_output_name}}.so {{inputs}} "
150 "{{ldflags}} {{libs}}",
151 solink_tool);
152 solink_tool->set_lib_switch("-l");
153 solink_tool->set_lib_dir_switch("-L");
154 solink_tool->set_output_prefix("lib");
155 solink_tool->set_default_output_extension(".so");
156 if (use_toc) {
157 solink_tool->set_outputs(SubstitutionList::MakeForTest(
158 "{{root_out_dir}}/{{target_output_name}}{{output_extension}}.TOC",
159 "{{root_out_dir}}/{{target_output_name}}{{output_extension}}"));
160 solink_tool->set_link_output(SubstitutionPattern::MakeForTest(
161 "{{root_out_dir}}/{{target_output_name}}{{output_extension}}"));
162 solink_tool->set_depend_output(SubstitutionPattern::MakeForTest(
163 "{{root_out_dir}}/{{target_output_name}}{{output_extension}}.TOC"));
164 } else {
165 solink_tool->set_outputs(SubstitutionList::MakeForTest(
166 "{{root_out_dir}}/{{target_output_name}}{{output_extension}}"));
167 }
168 toolchain->SetTool(std::move(solink));
169
170 // SOLINK_MODULE
171 std::unique_ptr<Tool> solink_module =
172 Tool::CreateTool(CTool::kCToolSolinkModule);
173 CTool* solink_module_tool = solink_module->AsC();
174 SetCommandForTool(
175 "ld -bundle -o {{target_output_name}}.so {{inputs}} "
176 "{{ldflags}} {{libs}}",
177 solink_module_tool);
178 solink_module_tool->set_lib_switch("-l");
179 solink_module_tool->set_lib_dir_switch("-L");
180 solink_module_tool->set_output_prefix("lib");
181 solink_module_tool->set_default_output_extension(".so");
182 solink_module_tool->set_outputs(SubstitutionList::MakeForTest(
183 "{{root_out_dir}}/{{target_output_name}}{{output_extension}}"));
184 toolchain->SetTool(std::move(solink_module));
185
186 // LINK
187 std::unique_ptr<Tool> link = Tool::CreateTool(CTool::kCToolLink);
188 CTool* link_tool = link->AsC();
189 SetCommandForTool(
190 "ld -o {{target_output_name}} {{source}} "
191 "{{ldflags}} {{libs}}",
192 link_tool);
193 link_tool->set_lib_switch("-l");
194 link_tool->set_lib_dir_switch("-L");
195 link_tool->set_outputs(
196 SubstitutionList::MakeForTest("{{root_out_dir}}/{{target_output_name}}"));
197 toolchain->SetTool(std::move(link));
198
199 // STAMP
200 std::unique_ptr<Tool> stamp_tool =
201 Tool::CreateTool(GeneralTool::kGeneralToolStamp);
202 SetCommandForTool("touch {{output}}", stamp_tool.get());
203 toolchain->SetTool(std::move(stamp_tool));
204
205 // COPY
206 std::unique_ptr<Tool> copy_tool =
207 Tool::CreateTool(GeneralTool::kGeneralToolCopy);
208 SetCommandForTool("cp {{source}} {{output}}", copy_tool.get());
209 toolchain->SetTool(std::move(copy_tool));
210
211 // COPY_BUNDLE_DATA
212 std::unique_ptr<Tool> copy_bundle_data_tool =
213 Tool::CreateTool(GeneralTool::kGeneralToolCopyBundleData);
214 SetCommandForTool("cp {{source}} {{output}}", copy_bundle_data_tool.get());
215 toolchain->SetTool(std::move(copy_bundle_data_tool));
216
217 // COMPILE_XCASSETS
218 std::unique_ptr<Tool> compile_xcassets_tool =
219 Tool::CreateTool(GeneralTool::kGeneralToolCompileXCAssets);
220 SetCommandForTool("touch {{output}}", compile_xcassets_tool.get());
221 toolchain->SetTool(std::move(compile_xcassets_tool));
222
223 // RUST
224 std::unique_ptr<Tool> rustc_tool = Tool::CreateTool(RustTool::kRsToolBin);
225 SetCommandForTool(
226 "{{rustenv}} rustc --crate-name {{crate_name}} {{source}} "
227 "--crate-type {{crate_type}} {{rustflags}} -o {{output}} "
228 "{{rustdeps}} {{externs}}",
229 rustc_tool.get());
230 rustc_tool->set_outputs(SubstitutionList::MakeForTest(
231 "{{root_out_dir}}/{{crate_name}}{{output_extension}}"));
232 toolchain->SetTool(std::move(rustc_tool));
233
234 // SWIFT
235 std::unique_ptr<Tool> swift_tool = Tool::CreateTool(CTool::kCToolSwift);
236 SetCommandForTool(
237 "swiftc --module-name {{module_name}} {{module_dirs}} {{inputs}}",
238 swift_tool.get());
239 swift_tool->set_outputs(SubstitutionList::MakeForTest(
240 "{{target_out_dir}}/{{module_name}}.swiftmodule"));
241 swift_tool->set_partial_outputs(SubstitutionList::MakeForTest(
242 "{{target_out_dir}}/{{source_name_part}}.o"));
243 toolchain->SetTool(std::move(swift_tool));
244
245 // CDYLIB
246 std::unique_ptr<Tool> cdylib_tool = Tool::CreateTool(RustTool::kRsToolCDylib);
247 SetCommandForTool(
248 "{{rustenv}} rustc --crate-name {{crate_name}} {{source}} "
249 "--crate-type {{crate_type}} {{rustflags}} -o {{output}} "
250 "{{rustdeps}} {{externs}}",
251 cdylib_tool.get());
252 cdylib_tool->set_output_prefix("lib");
253 cdylib_tool->set_default_output_extension(".so");
254 cdylib_tool->set_outputs(SubstitutionList::MakeForTest(
255 "{{target_out_dir}}/{{target_output_name}}{{output_extension}}"));
256 toolchain->SetTool(std::move(cdylib_tool));
257
258 // DYLIB
259 std::unique_ptr<Tool> dylib_tool = Tool::CreateTool(RustTool::kRsToolDylib);
260 SetCommandForTool(
261 "{{rustenv}} rustc --crate-name {{crate_name}} {{source}} "
262 "--crate-type {{crate_type}} {{rustflags}} -o {{output}} "
263 "{{rustdeps}} {{externs}}",
264 dylib_tool.get());
265 dylib_tool->set_output_prefix("lib");
266 dylib_tool->set_default_output_extension(".so");
267 dylib_tool->set_outputs(SubstitutionList::MakeForTest(
268 "{{target_out_dir}}/{{target_output_name}}{{output_extension}}"));
269 toolchain->SetTool(std::move(dylib_tool));
270
271 // RUST_PROC_MACRO
272 std::unique_ptr<Tool> rust_proc_macro_tool =
273 Tool::CreateTool(RustTool::kRsToolMacro);
274 SetCommandForTool(
275 "{{rustenv}} rustc --crate-name {{crate_name}} {{source}} "
276 "--crate-type {{crate_type}} {{rustflags}} -o {{output}} "
277 "{{rustdeps}} {{externs}}",
278 rust_proc_macro_tool.get());
279 rust_proc_macro_tool->set_output_prefix("lib");
280 rust_proc_macro_tool->set_default_output_extension(".so");
281 rust_proc_macro_tool->set_outputs(SubstitutionList::MakeForTest(
282 "{{target_out_dir}}/{{target_output_name}}{{output_extension}}"));
283 toolchain->SetTool(std::move(rust_proc_macro_tool));
284
285 // RLIB
286 std::unique_ptr<Tool> rlib_tool = Tool::CreateTool(RustTool::kRsToolRlib);
287 SetCommandForTool(
288 "{{rustenv}} rustc --crate-name {{crate_name}} {{source}} "
289 "--crate-type {{crate_type}} {{rustflags}} -o {{output}} "
290 "{{rustdeps}} {{externs}}",
291 rlib_tool.get());
292 rlib_tool->set_output_prefix("lib");
293 rlib_tool->set_default_output_extension(".rlib");
294 rlib_tool->set_outputs(SubstitutionList::MakeForTest(
295 "{{target_out_dir}}/{{target_output_name}}{{output_extension}}"));
296 toolchain->SetTool(std::move(rlib_tool));
297
298 // STATICLIB
299 std::unique_ptr<Tool> staticlib_tool =
300 Tool::CreateTool(RustTool::kRsToolStaticlib);
301 SetCommandForTool(
302 "{{rustenv}} rustc --crate-name {{crate_name}} {{source}} "
303 "--crate-type {{crate_type}} {{rustflags}} -o {{output}} "
304 "{{rustdeps}} {{externs}}",
305 staticlib_tool.get());
306 staticlib_tool->set_output_prefix("lib");
307 staticlib_tool->set_default_output_extension(".a");
308 staticlib_tool->set_outputs(SubstitutionList::MakeForTest(
309 "{{target_out_dir}}/{{target_output_name}}{{output_extension}}"));
310 toolchain->SetTool(std::move(staticlib_tool));
311
312 toolchain->ToolchainSetupComplete();
313 }
314
315 // static
SetCommandForTool(const std::string & cmd,Tool * tool)316 void TestWithScope::SetCommandForTool(const std::string& cmd, Tool* tool) {
317 Err err;
318 SubstitutionPattern command;
319 command.Parse(cmd, nullptr, &err);
320 CHECK(!err.has_error()) << "Couldn't parse \"" << cmd << "\", "
321 << "got " << err.message();
322 tool->set_command(command);
323 }
324
AppendPrintOutput(const std::string & str)325 void TestWithScope::AppendPrintOutput(const std::string& str) {
326 print_output_.append(str);
327 }
328
TestParseInput(const std::string & input)329 TestParseInput::TestParseInput(const std::string& input)
330 : input_file_(SourceFile("//test")) {
331 input_file_.SetContents(input);
332
333 tokens_ = Tokenizer::Tokenize(&input_file_, &parse_err_);
334 if (!parse_err_.has_error())
335 parsed_ = Parser::Parse(tokens_, &parse_err_);
336 }
337
338 TestParseInput::~TestParseInput() = default;
339
TestTarget(const TestWithScope & setup,const std::string & label_string,Target::OutputType type)340 TestTarget::TestTarget(const TestWithScope& setup,
341 const std::string& label_string,
342 Target::OutputType type)
343 : Target(setup.settings(), setup.ParseLabel(label_string)) {
344 visibility().SetPublic();
345 set_output_type(type);
346 SetToolchain(setup.toolchain());
347 }
348
349 TestTarget::~TestTarget() = default;
350