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/compile_commands_writer.h"
6
7 #include <memory>
8 #include <sstream>
9 #include <utility>
10
11 #include "gn/config.h"
12 #include "gn/ninja_target_command_util.h"
13 #include "gn/scheduler.h"
14 #include "gn/target.h"
15 #include "gn/test_with_scheduler.h"
16 #include "gn/test_with_scope.h"
17 #include "util/build_config.h"
18 #include "util/test/test.h"
19
20 namespace {
21
CompareLabel(const Target* a, const Target* b)22 bool CompareLabel(const Target* a, const Target* b) {
23 return a->label() < b->label();
24 }
25
26 // InputConversion needs a global scheduler object.
27 class CompileCommandsTest : public TestWithScheduler {
28 public:
29 CompileCommandsTest() = default;
30
build_settings()31 const BuildSettings* build_settings() { return setup_.build_settings(); }
settings()32 const Settings* settings() { return setup_.settings(); }
setup()33 const TestWithScope& setup() { return setup_; }
toolchain()34 const Toolchain* toolchain() { return setup_.toolchain(); }
35
36 // Returns true if the two target vectors contain the same targets, order
37 // independent.
VectorsEqual(std::vector<const Target*> a, std::vector<const Target*> b) const38 bool VectorsEqual(std::vector<const Target*> a,
39 std::vector<const Target*> b) const {
40 std::sort(a.begin(), a.end(), &CompareLabel);
41 std::sort(b.begin(), b.end(), &CompareLabel);
42 return a == b;
43 }
44
45 private:
46 TestWithScope setup_;
47 };
48
49 } // namespace
50
TEST_F(CompileCommandsTest, SourceSet)51 TEST_F(CompileCommandsTest, SourceSet) {
52 Err err;
53
54 std::vector<const Target*> targets;
55 Target target(settings(), Label(SourceDir("//foo/"), "bar"));
56 target.set_output_type(Target::SOURCE_SET);
57 target.visibility().SetPublic();
58 target.sources().push_back(SourceFile("//foo/input1.cc"));
59 target.sources().push_back(SourceFile("//foo/input2.cc"));
60 // Also test object files, which should be just passed through to the
61 // dependents to link.
62 target.sources().push_back(SourceFile("//foo/input3.o"));
63 target.sources().push_back(SourceFile("//foo/input4.obj"));
64 target.SetToolchain(toolchain());
65 ASSERT_TRUE(target.OnResolved(&err));
66 targets.push_back(&target);
67
68 // Source set itself.
69 {
70 CompileCommandsWriter writer;
71 std::string out = writer.RenderJSON(build_settings(), targets);
72
73 #if defined(OS_WIN)
74 const char expected[] =
75 "[\r\n"
76 " {\r\n"
77 " \"file\": \"../../foo/input1.cc\",\r\n"
78 " \"directory\": \"out/Debug\",\r\n"
79 " \"command\": \"c++ ../../foo/input1.cc -o "
80 "obj/foo/bar.input1.o\"\r\n"
81 " },\r\n"
82 " {\r\n"
83 " \"file\": \"../../foo/input2.cc\",\r\n"
84 " \"directory\": \"out/Debug\",\r\n"
85 " \"command\": \"c++ ../../foo/input2.cc -o "
86 "obj/foo/bar.input2.o\"\r\n"
87 " }\r\n"
88 "]\r\n";
89 #else
90 const char expected[] =
91 "[\n"
92 " {\n"
93 " \"file\": \"../../foo/input1.cc\",\n"
94 " \"directory\": \"out/Debug\",\n"
95 " \"command\": \"c++ ../../foo/input1.cc -o "
96 "obj/foo/bar.input1.o\"\n"
97 " },\n"
98 " {\n"
99 " \"file\": \"../../foo/input2.cc\",\n"
100 " \"directory\": \"out/Debug\",\n"
101 " \"command\": \"c++ ../../foo/input2.cc -o "
102 "obj/foo/bar.input2.o\"\n"
103 " }\n"
104 "]\n";
105 #endif
106 EXPECT_EQ(expected, out);
107 }
108
109 // A shared library that depends on the source set.
110 Target shlib_target(settings(), Label(SourceDir("//foo/"), "shlib"));
111 shlib_target.sources().push_back(SourceFile("//foo/input3.cc"));
112 shlib_target.set_output_type(Target::SHARED_LIBRARY);
113 shlib_target.public_deps().push_back(LabelTargetPair(&target));
114 shlib_target.SetToolchain(toolchain());
115 ASSERT_TRUE(shlib_target.OnResolved(&err));
116 targets.push_back(&shlib_target);
117
118 {
119 CompileCommandsWriter writer;
120 std::string out = writer.RenderJSON(build_settings(), targets);
121
122 #if defined(OS_WIN)
123 const char expected[] =
124 "[\r\n"
125 " {\r\n"
126 " \"file\": \"../../foo/input1.cc\",\r\n"
127 " \"directory\": \"out/Debug\",\r\n"
128 " \"command\": \"c++ ../../foo/input1.cc -o "
129 "obj/foo/bar.input1.o\"\r\n"
130 " },\r\n"
131 " {\r\n"
132 " \"file\": \"../../foo/input2.cc\",\r\n"
133 " \"directory\": \"out/Debug\",\r\n"
134 " \"command\": \"c++ ../../foo/input2.cc -o "
135 "obj/foo/bar.input2.o\"\r\n"
136 " },\r\n"
137 " {\r\n"
138 " \"file\": \"../../foo/input3.cc\",\r\n"
139 " \"directory\": \"out/Debug\",\r\n"
140 " \"command\": \"c++ ../../foo/input3.cc -o "
141 "obj/foo/libshlib.input3.o\"\r\n"
142 " }\r\n"
143 "]\r\n";
144 #else
145 const char expected[] =
146 "[\n"
147 " {\n"
148 " \"file\": \"../../foo/input1.cc\",\n"
149 " \"directory\": \"out/Debug\",\n"
150 " \"command\": \"c++ ../../foo/input1.cc -o "
151 "obj/foo/bar.input1.o\"\n"
152 " },\n"
153 " {\n"
154 " \"file\": \"../../foo/input2.cc\",\n"
155 " \"directory\": \"out/Debug\",\n"
156 " \"command\": \"c++ ../../foo/input2.cc -o "
157 "obj/foo/bar.input2.o\"\n"
158 " },\n"
159 " {\n"
160 " \"file\": \"../../foo/input3.cc\",\n"
161 " \"directory\": \"out/Debug\",\n"
162 " \"command\": \"c++ ../../foo/input3.cc -o "
163 "obj/foo/libshlib.input3.o\"\n"
164 " }\n"
165 "]\n";
166 #endif
167 EXPECT_EQ(expected, out);
168 }
169
170 // A static library that depends on the source set (should not link it).
171 Target stlib_target(settings(), Label(SourceDir("//foo/"), "stlib"));
172 stlib_target.sources().push_back(SourceFile("//foo/input4.cc"));
173 stlib_target.set_output_type(Target::STATIC_LIBRARY);
174 stlib_target.public_deps().push_back(LabelTargetPair(&target));
175 stlib_target.SetToolchain(toolchain());
176 ASSERT_TRUE(stlib_target.OnResolved(&err));
177 targets.push_back(&stlib_target);
178
179 {
180 CompileCommandsWriter writer;
181 std::string out = writer.RenderJSON(build_settings(), targets);
182
183 #if defined(OS_WIN)
184 const char expected[] =
185 "[\r\n"
186 " {\r\n"
187 " \"file\": \"../../foo/input1.cc\",\r\n"
188 " \"directory\": \"out/Debug\",\r\n"
189 " \"command\": \"c++ ../../foo/input1.cc -o "
190 "obj/foo/bar.input1.o\"\r\n"
191 " },\r\n"
192 " {\r\n"
193 " \"file\": \"../../foo/input2.cc\",\r\n"
194 " \"directory\": \"out/Debug\",\r\n"
195 " \"command\": \"c++ ../../foo/input2.cc -o "
196 "obj/foo/bar.input2.o\"\r\n"
197 " },\r\n"
198 " {\r\n"
199 " \"file\": \"../../foo/input3.cc\",\r\n"
200 " \"directory\": \"out/Debug\",\r\n"
201 " \"command\": \"c++ ../../foo/input3.cc -o "
202 "obj/foo/libshlib.input3.o\"\r\n"
203 " },\r\n"
204 " {\r\n"
205 " \"file\": \"../../foo/input4.cc\",\r\n"
206 " \"directory\": \"out/Debug\",\r\n"
207 " \"command\": \"c++ ../../foo/input4.cc -o "
208 "obj/foo/libstlib.input4.o\"\r\n"
209 " }\r\n"
210 "]\r\n";
211 #else
212 const char expected[] =
213 "[\n"
214 " {\n"
215 " \"file\": \"../../foo/input1.cc\",\n"
216 " \"directory\": \"out/Debug\",\n"
217 " \"command\": \"c++ ../../foo/input1.cc -o "
218 "obj/foo/bar.input1.o\"\n"
219 " },\n"
220 " {\n"
221 " \"file\": \"../../foo/input2.cc\",\n"
222 " \"directory\": \"out/Debug\",\n"
223 " \"command\": \"c++ ../../foo/input2.cc -o "
224 "obj/foo/bar.input2.o\"\n"
225 " },\n"
226 " {\n"
227 " \"file\": \"../../foo/input3.cc\",\n"
228 " \"directory\": \"out/Debug\",\n"
229 " \"command\": \"c++ ../../foo/input3.cc -o "
230 "obj/foo/libshlib.input3.o\"\n"
231 " },\n"
232 " {\n"
233 " \"file\": \"../../foo/input4.cc\",\n"
234 " \"directory\": \"out/Debug\",\n"
235 " \"command\": \"c++ ../../foo/input4.cc -o "
236 "obj/foo/libstlib.input4.o\"\n"
237 " }\n"
238 "]\n";
239 #endif
240 EXPECT_EQ(expected, out);
241 }
242 }
243
TEST_F(CompileCommandsTest, EscapeDefines)244 TEST_F(CompileCommandsTest, EscapeDefines) {
245 Err err;
246
247 std::vector<const Target*> targets;
248 TestTarget target(setup(), "//foo:bar", Target::STATIC_LIBRARY);
249 target.sources().push_back(SourceFile("//foo/input.cc"));
250 target.config_values().defines().push_back("BOOL_DEF");
251 target.config_values().defines().push_back("INT_DEF=123");
252 target.config_values().defines().push_back("STR_DEF=\"ABCD 1\"");
253 target.config_values().defines().push_back("INCLUDE=<header.h>");
254 ASSERT_TRUE(target.OnResolved(&err));
255 targets.push_back(&target);
256
257 CompileCommandsWriter writer;
258 std::string out = writer.RenderJSON(build_settings(), targets);
259
260 const char expected[] =
261 "-DBOOL_DEF -DINT_DEF=123 \\\"-DSTR_DEF=\\\\\\\"ABCD 1\\\\\\\"\\\" "
262 "\\\"-DINCLUDE=\\u003Cheader.h>\\\"";
263 EXPECT_TRUE(out.find(expected) != std::string::npos);
264 }
265
TEST_F(CompileCommandsTest, WinPrecompiledHeaders)266 TEST_F(CompileCommandsTest, WinPrecompiledHeaders) {
267 Err err;
268
269 // This setup's toolchain does not have precompiled headers defined.
270 // A precompiled header toolchain.
271 Settings pch_settings(build_settings(), "withpch/");
272 Toolchain pch_toolchain(&pch_settings,
273 Label(SourceDir("//toolchain/"), "withpch"));
274 pch_settings.set_toolchain_label(pch_toolchain.label());
275 pch_settings.set_default_toolchain_label(toolchain()->label());
276
277 // Declare a C++ compiler that supports PCH.
278 std::unique_ptr<Tool> cxx = Tool::CreateTool(CTool::kCToolCxx);
279 CTool* cxx_tool = cxx->AsC();
280 TestWithScope::SetCommandForTool(
281 "c++ {{source}} {{cflags}} {{cflags_cc}} {{defines}} {{include_dirs}} "
282 "-o {{output}}",
283 cxx_tool);
284 cxx_tool->set_outputs(SubstitutionList::MakeForTest(
285 "{{source_out_dir}}/{{target_output_name}}.{{source_name_part}}.o"));
286 cxx_tool->set_precompiled_header_type(CTool::PCH_MSVC);
287 pch_toolchain.SetTool(std::move(cxx));
288
289 // Add a C compiler as well.
290 std::unique_ptr<Tool> cc = Tool::CreateTool(CTool::kCToolCc);
291 CTool* cc_tool = cc->AsC();
292 TestWithScope::SetCommandForTool(
293 "cc {{source}} {{cflags}} {{cflags_c}} {{defines}} {{include_dirs}} "
294 "-o {{output}}",
295 cc_tool);
296 cc_tool->set_outputs(SubstitutionList::MakeForTest(
297 "{{source_out_dir}}/{{target_output_name}}.{{source_name_part}}.o"));
298 cc_tool->set_precompiled_header_type(CTool::PCH_MSVC);
299 pch_toolchain.SetTool(std::move(cc));
300 pch_toolchain.ToolchainSetupComplete();
301
302 // This target doesn't specify precompiled headers.
303 {
304 std::vector<const Target*> targets;
305 Target no_pch_target(&pch_settings,
306 Label(SourceDir("//foo/"), "no_pch_target"));
307 no_pch_target.set_output_type(Target::SOURCE_SET);
308 no_pch_target.visibility().SetPublic();
309 no_pch_target.sources().push_back(SourceFile("//foo/input1.cc"));
310 no_pch_target.sources().push_back(SourceFile("//foo/input2.c"));
311 no_pch_target.config_values().cflags_c().push_back("-std=c99");
312 no_pch_target.SetToolchain(&pch_toolchain);
313 ASSERT_TRUE(no_pch_target.OnResolved(&err));
314 targets.push_back(&no_pch_target);
315
316 CompileCommandsWriter writer;
317 std::string out = writer.RenderJSON(build_settings(), targets);
318
319 #if defined(OS_WIN)
320 const char no_pch_expected[] =
321 "[\r\n"
322 " {\r\n"
323 " \"file\": \"../../foo/input1.cc\",\r\n"
324 " \"directory\": \"out/Debug\",\r\n"
325 " \"command\": \"c++ ../../foo/input1.cc -o "
326 "withpch/obj/foo/no_pch_target.input1.o\"\r\n"
327 " },\r\n"
328 " {\r\n"
329 " \"file\": \"../../foo/input2.c\",\r\n"
330 " \"directory\": \"out/Debug\",\r\n"
331 " \"command\": \"cc ../../foo/input2.c -std=c99 -o "
332 "withpch/obj/foo/no_pch_target.input2.o\"\r\n"
333 " }\r\n"
334 "]\r\n";
335 #else
336 const char no_pch_expected[] =
337 "[\n"
338 " {\n"
339 " \"file\": \"../../foo/input1.cc\",\n"
340 " \"directory\": \"out/Debug\",\n"
341 " \"command\": \"c++ ../../foo/input1.cc -o "
342 "withpch/obj/foo/no_pch_target.input1.o\"\n"
343 " },\n"
344 " {\n"
345 " \"file\": \"../../foo/input2.c\",\n"
346 " \"directory\": \"out/Debug\",\n"
347 " \"command\": \"cc ../../foo/input2.c -std=c99 -o "
348 "withpch/obj/foo/no_pch_target.input2.o\"\n"
349 " }\n"
350 "]\n";
351 #endif
352 EXPECT_EQ(no_pch_expected, out);
353 }
354
355 // This target specifies PCH.
356 {
357 std::vector<const Target*> targets;
358 Target pch_target(&pch_settings, Label(SourceDir("//foo/"), "pch_target"));
359 pch_target.config_values().set_precompiled_header("build/precompile.h");
360 pch_target.config_values().set_precompiled_source(
361 SourceFile("//build/precompile.cc"));
362 pch_target.set_output_type(Target::SOURCE_SET);
363 pch_target.visibility().SetPublic();
364 pch_target.sources().push_back(SourceFile("//foo/input1.cc"));
365 pch_target.sources().push_back(SourceFile("//foo/input2.c"));
366 pch_target.SetToolchain(&pch_toolchain);
367 ASSERT_TRUE(pch_target.OnResolved(&err));
368 targets.push_back(&pch_target);
369
370 CompileCommandsWriter writer;
371 std::string out = writer.RenderJSON(build_settings(), targets);
372
373 #if defined(OS_WIN)
374 const char pch_win_expected[] =
375 "[\r\n"
376 " {\r\n"
377 " \"file\": \"../../foo/input1.cc\",\r\n"
378 " \"directory\": \"out/Debug\",\r\n"
379 " \"command\": \"c++ ../../foo/input1.cc "
380 "/Fpwithpch/obj/foo/pch_target_cc.pch /Yubuild/precompile.h -o "
381 "withpch/obj/foo/pch_target.input1.o\"\r\n"
382 " },\r\n"
383 " {\r\n"
384 " \"file\": \"../../foo/input2.c\",\r\n"
385 " \"directory\": \"out/Debug\",\r\n"
386 " \"command\": \"cc ../../foo/input2.c "
387 "/Fpwithpch/obj/foo/pch_target_c.pch /Yubuild/precompile.h -o "
388 "withpch/obj/foo/pch_target.input2.o\"\r\n"
389 " }\r\n"
390 "]\r\n";
391 #else
392 const char pch_win_expected[] =
393 "[\n"
394 " {\n"
395 " \"file\": \"../../foo/input1.cc\",\n"
396 " \"directory\": \"out/Debug\",\n"
397 " \"command\": \"c++ ../../foo/input1.cc "
398 "/Fpwithpch/obj/foo/pch_target_cc.pch /Yubuild/precompile.h -o "
399 "withpch/obj/foo/pch_target.input1.o\"\n"
400 " },\n"
401 " {\n"
402 " \"file\": \"../../foo/input2.c\",\n"
403 " \"directory\": \"out/Debug\",\n"
404 " \"command\": \"cc ../../foo/input2.c "
405 "/Fpwithpch/obj/foo/pch_target_c.pch /Yubuild/precompile.h -o "
406 "withpch/obj/foo/pch_target.input2.o\"\n"
407 " }\n"
408 "]\n";
409 #endif
410 EXPECT_EQ(pch_win_expected, out);
411 }
412 }
413
TEST_F(CompileCommandsTest, GCCPrecompiledHeaders)414 TEST_F(CompileCommandsTest, GCCPrecompiledHeaders) {
415 Err err;
416
417 // This setup's toolchain does not have precompiled headers defined.
418 // A precompiled header toolchain.
419 Settings pch_settings(build_settings(), "withpch/");
420 Toolchain pch_toolchain(&pch_settings,
421 Label(SourceDir("//toolchain/"), "withpch"));
422 pch_settings.set_toolchain_label(pch_toolchain.label());
423 pch_settings.set_default_toolchain_label(toolchain()->label());
424
425 // Declare a C++ compiler that supports PCH.
426 std::unique_ptr<Tool> cxx = Tool::CreateTool(CTool::kCToolCxx);
427 CTool* cxx_tool = cxx->AsC();
428 TestWithScope::SetCommandForTool(
429 "c++ {{source}} {{cflags}} {{cflags_cc}} {{defines}} {{include_dirs}} "
430 "-o {{output}}",
431 cxx_tool);
432 cxx_tool->set_outputs(SubstitutionList::MakeForTest(
433 "{{source_out_dir}}/{{target_output_name}}.{{source_name_part}}.o"));
434 cxx_tool->set_precompiled_header_type(CTool::PCH_GCC);
435 pch_toolchain.SetTool(std::move(cxx));
436 pch_toolchain.ToolchainSetupComplete();
437
438 // Add a C compiler as well.
439 std::unique_ptr<Tool> cc = Tool::CreateTool(CTool::kCToolCc);
440 CTool* cc_tool = cc->AsC();
441 TestWithScope::SetCommandForTool(
442 "cc {{source}} {{cflags}} {{cflags_c}} {{defines}} {{include_dirs}} "
443 "-o {{output}}",
444 cc_tool);
445 cc_tool->set_outputs(SubstitutionList::MakeForTest(
446 "{{source_out_dir}}/{{target_output_name}}.{{source_name_part}}.o"));
447 cc_tool->set_precompiled_header_type(CTool::PCH_GCC);
448 pch_toolchain.SetTool(std::move(cc));
449 pch_toolchain.ToolchainSetupComplete();
450
451 // This target doesn't specify precompiled headers.
452 {
453 std::vector<const Target*> targets;
454 Target no_pch_target(&pch_settings,
455 Label(SourceDir("//foo/"), "no_pch_target"));
456 no_pch_target.set_output_type(Target::SOURCE_SET);
457 no_pch_target.visibility().SetPublic();
458 no_pch_target.sources().push_back(SourceFile("//foo/input1.cc"));
459 no_pch_target.sources().push_back(SourceFile("//foo/input2.c"));
460 no_pch_target.config_values().cflags_c().push_back("-std=c99");
461 no_pch_target.SetToolchain(&pch_toolchain);
462 ASSERT_TRUE(no_pch_target.OnResolved(&err));
463 targets.push_back(&no_pch_target);
464
465 CompileCommandsWriter writer;
466 std::string out = writer.RenderJSON(build_settings(), targets);
467
468 #if defined(OS_WIN)
469 const char no_pch_expected[] =
470 "[\r\n"
471 " {\r\n"
472 " \"file\": \"../../foo/input1.cc\",\r\n"
473 " \"directory\": \"out/Debug\",\r\n"
474 " \"command\": \"c++ ../../foo/input1.cc -o "
475 "withpch/obj/foo/no_pch_target.input1.o\"\r\n"
476 " },\r\n"
477 " {\r\n"
478 " \"file\": \"../../foo/input2.c\",\r\n"
479 " \"directory\": \"out/Debug\",\r\n"
480 " \"command\": \"cc ../../foo/input2.c -std=c99 -o "
481 "withpch/obj/foo/no_pch_target.input2.o\"\r\n"
482 " }\r\n"
483 "]\r\n";
484 #else
485 const char no_pch_expected[] =
486 "[\n"
487 " {\n"
488 " \"file\": \"../../foo/input1.cc\",\n"
489 " \"directory\": \"out/Debug\",\n"
490 " \"command\": \"c++ ../../foo/input1.cc -o "
491 "withpch/obj/foo/no_pch_target.input1.o\"\n"
492 " },\n"
493 " {\n"
494 " \"file\": \"../../foo/input2.c\",\n"
495 " \"directory\": \"out/Debug\",\n"
496 " \"command\": \"cc ../../foo/input2.c -std=c99 -o "
497 "withpch/obj/foo/no_pch_target.input2.o\"\n"
498 " }\n"
499 "]\n";
500 #endif
501 EXPECT_EQ(no_pch_expected, out);
502 }
503
504 // This target specifies PCH.
505 {
506 std::vector<const Target*> targets;
507 Target pch_target(&pch_settings, Label(SourceDir("//foo/"), "pch_target"));
508 pch_target.config_values().set_precompiled_source(
509 SourceFile("//build/precompile.h"));
510 pch_target.config_values().cflags_c().push_back("-std=c99");
511 pch_target.set_output_type(Target::SOURCE_SET);
512 pch_target.visibility().SetPublic();
513 pch_target.sources().push_back(SourceFile("//foo/input1.cc"));
514 pch_target.sources().push_back(SourceFile("//foo/input2.c"));
515 pch_target.SetToolchain(&pch_toolchain);
516 ASSERT_TRUE(pch_target.OnResolved(&err));
517 targets.push_back(&pch_target);
518
519 CompileCommandsWriter writer;
520 std::string out = writer.RenderJSON(build_settings(), targets);
521
522 #if defined(OS_WIN)
523 const char pch_gcc_expected[] =
524 "[\r\n"
525 " {\r\n"
526 " \"file\": \"../../foo/input1.cc\",\r\n"
527 " \"directory\": \"out/Debug\",\r\n"
528 " \"command\": \"c++ ../../foo/input1.cc -include "
529 "withpch/obj/build/pch_target.precompile.h-cc -o "
530 "withpch/obj/foo/pch_target.input1.o\"\r\n"
531 " },\r\n"
532 " {\r\n"
533 " \"file\": \"../../foo/input2.c\",\r\n"
534 " \"directory\": \"out/Debug\",\r\n"
535 " \"command\": \"cc ../../foo/input2.c -std=c99 -include "
536 "withpch/obj/build/pch_target.precompile.h-c -o "
537 "withpch/obj/foo/pch_target.input2.o\"\r\n"
538 " }\r\n"
539 "]\r\n";
540 #else
541 const char pch_gcc_expected[] =
542 "[\n"
543 " {\n"
544 " \"file\": \"../../foo/input1.cc\",\n"
545 " \"directory\": \"out/Debug\",\n"
546 " \"command\": \"c++ ../../foo/input1.cc -include "
547 "withpch/obj/build/pch_target.precompile.h-cc -o "
548 "withpch/obj/foo/pch_target.input1.o\"\n"
549 " },\n"
550 " {\n"
551 " \"file\": \"../../foo/input2.c\",\n"
552 " \"directory\": \"out/Debug\",\n"
553 " \"command\": \"cc ../../foo/input2.c -std=c99 -include "
554 "withpch/obj/build/pch_target.precompile.h-c -o "
555 "withpch/obj/foo/pch_target.input2.o\"\n"
556 " }\n"
557 "]\n";
558 #endif
559 EXPECT_EQ(pch_gcc_expected, out);
560 }
561 }
562
TEST_F(CompileCommandsTest, EscapedFlags)563 TEST_F(CompileCommandsTest, EscapedFlags) {
564 Err err;
565
566 std::vector<const Target*> targets;
567 Target target(settings(), Label(SourceDir("//foo/"), "bar"));
568 target.set_output_type(Target::SOURCE_SET);
569 target.sources().push_back(SourceFile("//foo/input1.c"));
570 target.config_values().cflags_c().push_back("-DCONFIG=\"/config\"");
571 target.SetToolchain(toolchain());
572 ASSERT_TRUE(target.OnResolved(&err));
573 targets.push_back(&target);
574
575 CompileCommandsWriter writer;
576 std::string out = writer.RenderJSON(build_settings(), targets);
577
578 #if defined(OS_WIN)
579 const char expected[] =
580 "[\r\n"
581 " {\r\n"
582 " \"file\": \"../../foo/input1.c\",\r\n"
583 " \"directory\": \"out/Debug\",\r\n"
584 " \"command\": \"cc ../../foo/input1.c -DCONFIG=\\\"/config\\\" "
585 "-o obj/foo/bar.input1.o\"\r\n"
586 " }\r\n"
587 "]\r\n";
588 #else
589 const char expected[] =
590 "[\n"
591 " {\n"
592 " \"file\": \"../../foo/input1.c\",\n"
593 " \"directory\": \"out/Debug\",\n"
594 " \"command\": \"cc ../../foo/input1.c -DCONFIG=\\\"/config\\\" "
595 "-o obj/foo/bar.input1.o\"\n"
596 " }\n"
597 "]\n";
598 #endif
599 EXPECT_EQ(expected, out);
600 }
601
TEST_F(CompileCommandsTest, CollectTargets)602 TEST_F(CompileCommandsTest, CollectTargets) {
603 // Contruct the dependency tree:
604 //
605 // //foo:bar1
606 // //base:base
607 // //foo:bar2
608 // //base:i18n
609 // //base:base
610 // //third_party:icu
611 // //random:random
612 Err err;
613 std::vector<const Target*> targets;
614
615 Target icu_target(settings(), Label(SourceDir("//third_party/"), "icu"));
616 icu_target.set_output_type(Target::SOURCE_SET);
617 icu_target.visibility().SetPublic();
618 icu_target.SetToolchain(toolchain());
619 ASSERT_TRUE(icu_target.OnResolved(&err));
620 targets.push_back(&icu_target);
621
622 Target base_target(settings(), Label(SourceDir("//base/"), "base"));
623 base_target.set_output_type(Target::SOURCE_SET);
624 base_target.visibility().SetPublic();
625 base_target.SetToolchain(toolchain());
626 ASSERT_TRUE(base_target.OnResolved(&err));
627 targets.push_back(&base_target);
628
629 Target base_i18n(settings(), Label(SourceDir("//base/"), "i18n"));
630 base_i18n.set_output_type(Target::SOURCE_SET);
631 base_i18n.visibility().SetPublic();
632 base_i18n.private_deps().push_back(LabelTargetPair(&icu_target));
633 base_i18n.public_deps().push_back(LabelTargetPair(&base_target));
634 base_i18n.SetToolchain(toolchain());
635 ASSERT_TRUE(base_i18n.OnResolved(&err))
636 << err.message() << " " << err.help_text();
637 targets.push_back(&base_i18n);
638
639 Target target1(settings(), Label(SourceDir("//foo/"), "bar1"));
640 target1.set_output_type(Target::SOURCE_SET);
641 target1.public_deps().push_back(LabelTargetPair(&base_target));
642 target1.SetToolchain(toolchain());
643 ASSERT_TRUE(target1.OnResolved(&err));
644 targets.push_back(&target1);
645
646 Target target2(settings(), Label(SourceDir("//foo/"), "bar2"));
647 target2.set_output_type(Target::SOURCE_SET);
648 target2.public_deps().push_back(LabelTargetPair(&base_i18n));
649 target2.SetToolchain(toolchain());
650 ASSERT_TRUE(target2.OnResolved(&err));
651 targets.push_back(&target2);
652
653 Target random_target(settings(), Label(SourceDir("//random/"), "random"));
654 random_target.set_output_type(Target::SOURCE_SET);
655 random_target.SetToolchain(toolchain());
656 ASSERT_TRUE(random_target.OnResolved(&err));
657 targets.push_back(&random_target);
658
659 // Collect everything, the result should match the input.
660 const std::string source_root("/home/me/build/");
661 LabelPattern wildcard_pattern = LabelPattern::GetPattern(
662 SourceDir(), source_root, Value(nullptr, "//*"), &err);
663 ASSERT_FALSE(err.has_error());
664 std::vector<const Target*> output = CompileCommandsWriter::CollectTargets(
665 build_settings(), targets, std::vector<LabelPattern>{wildcard_pattern},
666 std::nullopt, &err);
667 EXPECT_TRUE(VectorsEqual(output, targets));
668
669 // Collect nothing.
670 output = CompileCommandsWriter::CollectTargets(build_settings(), targets,
671 std::vector<LabelPattern>(),
672 std::nullopt, &err);
673 EXPECT_TRUE(output.empty());
674
675 // Collect all deps of "//foo/*".
676 LabelPattern foo_wildcard = LabelPattern::GetPattern(
677 SourceDir(), source_root, Value(nullptr, "//foo/*"), &err);
678 ASSERT_FALSE(err.has_error());
679 output = CompileCommandsWriter::CollectTargets(
680 build_settings(), targets, std::vector<LabelPattern>{foo_wildcard},
681 std::nullopt, &err);
682
683 // The result should be everything except "random".
684 std::sort(output.begin(), output.end(), &CompareLabel);
685 ASSERT_EQ(5u, output.size());
686 EXPECT_EQ(&base_target, output[0]);
687 EXPECT_EQ(&base_i18n, output[1]);
688 EXPECT_EQ(&target1, output[2]);
689 EXPECT_EQ(&target2, output[3]);
690 EXPECT_EQ(&icu_target, output[4]);
691
692 // Collect everything using the legacy filter (present string but empty).
693 output = CompileCommandsWriter::CollectTargets(build_settings(), targets,
694 std::vector<LabelPattern>{},
695 std::string(), &err);
696 EXPECT_TRUE(VectorsEqual(output, targets));
697
698 // Collect all deps of "bar2" using the legacy filter.
699 output = CompileCommandsWriter::CollectTargets(build_settings(), targets,
700 std::vector<LabelPattern>{},
701 std::string("bar2"), &err);
702 std::sort(output.begin(), output.end(), &CompareLabel);
703 ASSERT_EQ(4u, output.size());
704 EXPECT_EQ(&base_target, output[0]);
705 EXPECT_EQ(&base_i18n, output[1]);
706 EXPECT_EQ(&target2, output[2]);
707 EXPECT_EQ(&icu_target, output[3]);
708
709 // Collect all deps of "bar1" and "bar2" using the legacy filter.
710 output = CompileCommandsWriter::CollectTargets(
711 build_settings(), targets, std::vector<LabelPattern>{},
712 std::string("bar2,bar1"), &err);
713 std::sort(output.begin(), output.end(), &CompareLabel);
714 ASSERT_EQ(5u, output.size());
715 EXPECT_EQ(&base_target, output[0]);
716 EXPECT_EQ(&base_i18n, output[1]);
717 EXPECT_EQ(&target1, output[2]);
718 EXPECT_EQ(&target2, output[3]);
719 EXPECT_EQ(&icu_target, output[4]);
720
721 // Combine the legacy (bar1) and pattern (bar2) filters, we should get the
722 // union.
723 LabelPattern foo_bar2 = LabelPattern::GetPattern(
724 SourceDir(), source_root, Value(nullptr, "//foo:bar2"), &err);
725 ASSERT_FALSE(err.has_error());
726 output = CompileCommandsWriter::CollectTargets(
727 build_settings(), targets, std::vector<LabelPattern>{foo_bar2},
728 std::string("bar1"), &err);
729 std::sort(output.begin(), output.end(), &CompareLabel);
730 ASSERT_EQ(5u, output.size());
731 EXPECT_EQ(&base_target, output[0]);
732 EXPECT_EQ(&base_i18n, output[1]);
733 EXPECT_EQ(&target1, output[2]);
734 EXPECT_EQ(&target2, output[3]);
735 EXPECT_EQ(&icu_target, output[4]);
736 }
737