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