1 // Copyright 2018 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/setup.h"
6
7 #include "base/command_line.h"
8 #include "base/files/file_path.h"
9 #include "base/files/file_util.h"
10 #include "base/files/scoped_temp_dir.h"
11 #include "gn/builder_record.h"
12 #include "gn/filesystem_utils.h"
13 #include "gn/switches.h"
14 #include "gn/test_with_scheduler.h"
15 #include "util/build_config.h"
16
17 using SetupTest = TestWithScheduler;
18
WriteFile(const base::FilePath & file,const std::string & data)19 static void WriteFile(const base::FilePath& file, const std::string& data) {
20 CHECK_EQ(static_cast<int>(data.size()), // Way smaller than INT_MAX.
21 base::WriteFile(file, data.data(), data.size()));
22 }
23
TEST_F(SetupTest,DotGNFileIsGenDep)24 TEST_F(SetupTest, DotGNFileIsGenDep) {
25 base::CommandLine cmdline(base::CommandLine::NO_PROGRAM);
26
27 // Create a temp directory containing a .gn file and a BUILDCONFIG.gn file,
28 // pass it as --root.
29 base::ScopedTempDir in_temp_dir;
30 ASSERT_TRUE(in_temp_dir.CreateUniqueTempDir());
31 base::FilePath in_path = in_temp_dir.GetPath();
32 base::FilePath dot_gn_name = in_path.Append(FILE_PATH_LITERAL(".gn"));
33 WriteFile(dot_gn_name, "buildconfig = \"//BUILDCONFIG.gn\"\n");
34 WriteFile(in_path.Append(FILE_PATH_LITERAL("BUILDCONFIG.gn")), "");
35 cmdline.AppendSwitchPath(switches::kRoot, in_path);
36
37 // Create another temp dir for writing the generated files to.
38 base::ScopedTempDir build_temp_dir;
39 ASSERT_TRUE(build_temp_dir.CreateUniqueTempDir());
40
41 // Run setup and check that the .gn file is in the scheduler's gen deps.
42 Setup setup;
43 EXPECT_TRUE(
44 setup.DoSetup(FilePathToUTF8(build_temp_dir.GetPath()), true, cmdline));
45 std::vector<base::FilePath> gen_deps = g_scheduler->GetGenDependencies();
46 ASSERT_EQ(1u, gen_deps.size());
47 EXPECT_EQ(gen_deps[0], base::MakeAbsoluteFilePath(dot_gn_name));
48 }
49
TEST_F(SetupTest,EmptyScriptExecutableDoesNotGenerateError)50 TEST_F(SetupTest, EmptyScriptExecutableDoesNotGenerateError) {
51 base::CommandLine cmdline(base::CommandLine::NO_PROGRAM);
52
53 const char kDotfileContents[] = R"(
54 buildconfig = "//BUILDCONFIG.gn"
55 script_executable = ""
56 )";
57
58 // Create a temp directory containing a .gn file and a BUILDCONFIG.gn file,
59 // pass it as --root.
60 base::ScopedTempDir in_temp_dir;
61 ASSERT_TRUE(in_temp_dir.CreateUniqueTempDir());
62 base::FilePath in_path = in_temp_dir.GetPath();
63 base::FilePath dot_gn_name = in_path.Append(FILE_PATH_LITERAL(".gn"));
64 WriteFile(dot_gn_name, kDotfileContents);
65
66 WriteFile(in_path.Append(FILE_PATH_LITERAL("BUILDCONFIG.gn")), "");
67 cmdline.AppendSwitchPath(switches::kRoot, in_path);
68
69 // Create another temp dir for writing the generated files to.
70 base::ScopedTempDir build_temp_dir;
71 ASSERT_TRUE(build_temp_dir.CreateUniqueTempDir());
72
73 // Run setup and check that the .gn file is in the scheduler's gen deps.
74 Setup setup;
75 Err err;
76 EXPECT_TRUE(setup.DoSetupWithErr(FilePathToUTF8(build_temp_dir.GetPath()),
77 true, cmdline, &err));
78 }
79
80 #if defined(OS_WIN)
81 TEST_F(SetupTest, MissingScriptExeGeneratesSetupErrorOnWindows) {
82 base::CommandLine cmdline(base::CommandLine::NO_PROGRAM);
83
84 const char kDotfileContents[] = R"(
85 buildconfig = "//BUILDCONFIG.gn"
86 script_executable = "this_does_not_exist"
87 )";
88
89 // Create a temp directory containing a .gn file and a BUILDCONFIG.gn file,
90 // pass it as --root.
91 base::ScopedTempDir in_temp_dir;
92 ASSERT_TRUE(in_temp_dir.CreateUniqueTempDir());
93 base::FilePath in_path = in_temp_dir.GetPath();
94 base::FilePath dot_gn_name = in_path.Append(FILE_PATH_LITERAL(".gn"));
95 WriteFile(dot_gn_name, kDotfileContents);
96
97 WriteFile(in_path.Append(FILE_PATH_LITERAL("BUILDCONFIG.gn")), "");
98 cmdline.AppendSwitchPath(switches::kRoot, in_path);
99
100 // Create another temp dir for writing the generated files to.
101 base::ScopedTempDir build_temp_dir;
102 ASSERT_TRUE(build_temp_dir.CreateUniqueTempDir());
103
104 // Run setup and check that the .gn file is in the scheduler's gen deps.
105 Setup setup;
106 Err err;
107 EXPECT_FALSE(setup.DoSetupWithErr(FilePathToUTF8(build_temp_dir.GetPath()),
108 true, cmdline, &err));
109 EXPECT_TRUE(err.has_error());
110 }
111 #endif // defined(OS_WIN)
112
113 static void RunExtensionCheckTest(std::string extension,
114 bool success,
115 const std::string& expected_error_message) {
116 base::CommandLine cmdline(base::CommandLine::NO_PROGRAM);
117
118 // Create a temp directory containing a .gn file and a BUILDCONFIG.gn file,
119 // pass it as --root.
120 base::ScopedTempDir in_temp_dir;
121 ASSERT_TRUE(in_temp_dir.CreateUniqueTempDir());
122 base::FilePath in_path = in_temp_dir.GetPath();
123 base::FilePath dot_gn_name = in_path.Append(FILE_PATH_LITERAL(".gn"));
124 WriteFile(dot_gn_name,
125 "buildconfig = \"//BUILDCONFIG.gn\"\n\
126 build_file_extension = \"" +
127 extension + "\"");
128 WriteFile(in_path.Append(FILE_PATH_LITERAL("BUILDCONFIG.gn")), "");
129 cmdline.AppendSwitchPath(switches::kRoot, in_path);
130
131 // Create another temp dir for writing the generated files to.
132 base::ScopedTempDir build_temp_dir;
133 ASSERT_TRUE(build_temp_dir.CreateUniqueTempDir());
134
135 // Run setup and check that its status.
136 Setup setup;
137 Err err;
138 EXPECT_EQ(success,
139 setup.DoSetupWithErr(FilePathToUTF8(build_temp_dir.GetPath()), true,
140 cmdline, &err));
141 EXPECT_EQ(success, !err.has_error());
142 }
143
TEST_F(SetupTest,NoSeparatorInExtension)144 TEST_F(SetupTest, NoSeparatorInExtension) {
145 RunExtensionCheckTest(
146 "hello" + std::string(1, base::FilePath::kSeparators[0]) + "world", false,
147 #if defined(OS_WIN)
148 "Build file extension 'hello\\world' cannot contain a path separator"
149 #else
150 "Build file extension 'hello/world' cannot contain a path separator"
151 #endif
152 );
153 }
154
TEST_F(SetupTest,Extension)155 TEST_F(SetupTest, Extension) {
156 RunExtensionCheckTest("yay", true, "");
157 }
158
TEST_F(SetupTest,AddExportCompileCommands)159 TEST_F(SetupTest, AddExportCompileCommands) {
160 base::CommandLine cmdline(base::CommandLine::NO_PROGRAM);
161
162 // Provide a project default export compile command list.
163 const char kDotfileContents[] = R"(
164 buildconfig = "//BUILDCONFIG.gn"
165 export_compile_commands = [ "//base/*" ]
166 )";
167
168 // Create a temp directory containing the build.
169 base::ScopedTempDir in_temp_dir;
170 ASSERT_TRUE(in_temp_dir.CreateUniqueTempDir());
171 base::FilePath in_path = in_temp_dir.GetPath();
172 base::FilePath dot_gn_name = in_path.Append(FILE_PATH_LITERAL(".gn"));
173 WriteFile(dot_gn_name, kDotfileContents);
174
175 WriteFile(in_path.Append(FILE_PATH_LITERAL("BUILDCONFIG.gn")), "");
176 cmdline.AppendSwitch(switches::kRoot, FilePathToUTF8(in_path));
177
178 // Two additions to the compile commands list.
179 cmdline.AppendSwitch(switches::kAddExportCompileCommands,
180 "//tools:doom_melon");
181 cmdline.AppendSwitch(switches::kAddExportCompileCommands, "//src/gn:*");
182
183 // Create another temp dir for writing the generated files to.
184 base::ScopedTempDir build_temp_dir;
185 ASSERT_TRUE(build_temp_dir.CreateUniqueTempDir());
186
187 // Run setup and check that the .gn file is in the scheduler's gen deps.
188 Setup setup;
189 Err err;
190 EXPECT_TRUE(setup.DoSetupWithErr(FilePathToUTF8(build_temp_dir.GetPath()),
191 true, cmdline, &err));
192
193 // The export compile commands should have three items.
194 const std::vector<LabelPattern>& export_cc = setup.export_compile_commands();
195 ASSERT_EQ(3u, export_cc.size());
196 EXPECT_EQ("//base/*", export_cc[0].Describe());
197 EXPECT_EQ("//tools:doom_melon", export_cc[1].Describe());
198 EXPECT_EQ("//src/gn:*", export_cc[2].Describe());
199 }
200
TEST_F(SetupTest,RootPatternsInGnConfig)201 TEST_F(SetupTest, RootPatternsInGnConfig) {
202 base::CommandLine cmdline(base::CommandLine::NO_PROGRAM);
203
204 // Provide a default root pattern for all top-level targets from //BUILD.gn
205 const char kDotfileContents[] = R"(
206 buildconfig = "//BUILDCONFIG.gn"
207 root_patterns = [ "//:*" ]
208 )";
209
210 // Create a temp directory containing the build.
211 base::ScopedTempDir in_temp_dir;
212 ASSERT_TRUE(in_temp_dir.CreateUniqueTempDir());
213 base::FilePath in_path = in_temp_dir.GetPath();
214
215 WriteFile(in_path.Append(FILE_PATH_LITERAL("BUILDCONFIG.gn")), "");
216 WriteFile(in_path.Append(FILE_PATH_LITERAL(".gn")), kDotfileContents);
217
218 cmdline.AppendSwitch(switches::kRoot, FilePathToUTF8(in_path));
219
220 // Create another temp dir for writing the generated files to.
221 base::ScopedTempDir build_temp_dir;
222 ASSERT_TRUE(build_temp_dir.CreateUniqueTempDir());
223
224 // Run setup and check that the .gn file is in the scheduler's gen deps.
225 Setup setup;
226 Err err;
227 EXPECT_TRUE(setup.DoSetupWithErr(FilePathToUTF8(build_temp_dir.GetPath()),
228 true, cmdline, &err));
229
230 const std::vector<LabelPattern>& root_patterns =
231 setup.build_settings().root_patterns();
232 ASSERT_EQ(1u, root_patterns.size());
233 EXPECT_EQ("//.:*", root_patterns[0].Describe());
234 }
235
TEST_F(SetupTest,RootPatternsOnCommandLineOverrideGnConfig)236 TEST_F(SetupTest, RootPatternsOnCommandLineOverrideGnConfig) {
237 base::CommandLine cmdline(base::CommandLine::NO_PROGRAM);
238
239 // Provide a default root pattern for only //:foo
240 const char kDotfileContents[] = R"(
241 buildconfig = "//BUILDCONFIG.gn"
242 root_patterns = [ "//:foo" ]
243 )";
244
245 // Create a temp directory containing the build.
246 base::ScopedTempDir in_temp_dir;
247 ASSERT_TRUE(in_temp_dir.CreateUniqueTempDir());
248 base::FilePath in_path = in_temp_dir.GetPath();
249
250 WriteFile(in_path.Append(FILE_PATH_LITERAL("BUILDCONFIG.gn")), "");
251 WriteFile(in_path.Append(FILE_PATH_LITERAL(".gn")), kDotfileContents);
252
253 cmdline.AppendSwitch(switches::kRoot, FilePathToUTF8(in_path));
254
255 // Override the default root pattern list.
256 cmdline.AppendSwitch(switches::kRootPattern, "//:bar");
257 cmdline.AppendSwitch(switches::kRootPattern, "//:qux");
258
259 // Create another temp dir for writing the generated files to.
260 base::ScopedTempDir build_temp_dir;
261 ASSERT_TRUE(build_temp_dir.CreateUniqueTempDir());
262
263 // Run setup and check that the .gn file is in the scheduler's gen deps.
264 Setup setup;
265 Err err;
266 EXPECT_TRUE(setup.DoSetupWithErr(FilePathToUTF8(build_temp_dir.GetPath()),
267 true, cmdline, &err));
268
269 const std::vector<LabelPattern>& root_patterns =
270 setup.build_settings().root_patterns();
271 ASSERT_EQ(2u, root_patterns.size());
272 EXPECT_EQ("//.:bar", root_patterns[0].Describe());
273 EXPECT_EQ("//.:qux", root_patterns[1].Describe());
274 }
275
TEST_F(SetupTest,RootPatternsFiltersPatterns)276 TEST_F(SetupTest, RootPatternsFiltersPatterns) {
277 base::CommandLine cmdline(base::CommandLine::NO_PROGRAM);
278
279 const char kDotfileContents[] = R"(
280 buildconfig = "//BUILDCONFIG.gn"
281 root_patterns = [ "//:foo" ]
282 )";
283
284 const char kBuildConfigContents[] = R"(
285 set_default_toolchain("//:toolchain")
286 )";
287
288 const char kBuildGnContents[] = R"(
289 group("foo") {
290 deps = [ ":bar" ]
291 }
292
293 group("bar") {
294 }
295
296 group("zoo") {
297 }
298
299 group("qux") {
300 }
301
302 # Minimal default toolchain definition for this test. Non-functional.
303 toolchain("toolchain") {
304 tool("stamp") {
305 command = "stamp"
306 }
307 }
308 )";
309
310 // Create a temp directory containing the build.
311 base::ScopedTempDir in_temp_dir;
312 ASSERT_TRUE(in_temp_dir.CreateUniqueTempDir());
313 base::FilePath in_path = in_temp_dir.GetPath();
314
315 WriteFile(in_path.Append(FILE_PATH_LITERAL("BUILD.gn")), kBuildGnContents);
316 WriteFile(in_path.Append(FILE_PATH_LITERAL("BUILDCONFIG.gn")),
317 kBuildConfigContents);
318 WriteFile(in_path.Append(FILE_PATH_LITERAL(".gn")), kDotfileContents);
319
320 cmdline.AppendSwitch(switches::kRoot, FilePathToUTF8(in_path));
321
322 // Create another temp dir for writing the generated files to.
323 base::ScopedTempDir build_temp_dir;
324 ASSERT_TRUE(build_temp_dir.CreateUniqueTempDir());
325
326 // Run setup and check that the .gn file is in the scheduler's gen deps.
327 Setup setup;
328 Err err;
329 EXPECT_TRUE(setup.DoSetupWithErr(FilePathToUTF8(build_temp_dir.GetPath()),
330 true, cmdline, &err));
331
332 const std::vector<LabelPattern>& root_patterns =
333 setup.build_settings().root_patterns();
334 ASSERT_EQ(1u, root_patterns.size());
335 EXPECT_EQ("//.:foo", root_patterns[0].Describe());
336
337 // Now build the graph, then verify it only includes //:foo and //:bar
338 ASSERT_TRUE(setup.Run(cmdline));
339
340 SourceDir top_dir("//");
341
342 const BuilderRecord* foo_record =
343 setup.builder().GetRecord(Label(top_dir, "foo", top_dir, "toolchain"));
344 const BuilderRecord* bar_record =
345 setup.builder().GetRecord(Label(top_dir, "bar", top_dir, "toolchain"));
346 const BuilderRecord* qux_record =
347 setup.builder().GetRecord(Label(top_dir, "qux", top_dir, "toolchain"));
348 const BuilderRecord* zoo_record =
349 setup.builder().GetRecord(Label(top_dir, "zoo", top_dir, "toolchain"));
350
351 // All four targets were added as build graph records.
352 ASSERT_TRUE(foo_record);
353 ASSERT_TRUE(bar_record);
354 ASSERT_TRUE(zoo_record);
355 ASSERT_TRUE(qux_record);
356
357 // But only foo and bar should be generated in the Ninja plan.
358 EXPECT_TRUE(foo_record->should_generate());
359 EXPECT_TRUE(bar_record->should_generate());
360 EXPECT_FALSE(qux_record->should_generate());
361 EXPECT_FALSE(zoo_record->should_generate());
362 }
363