• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
22 // InputConversion needs a global scheduler object.
23 class CompileCommandsTest : public TestWithScheduler {
24  public:
25   CompileCommandsTest() = default;
26 
build_settings()27   const BuildSettings* build_settings() { return setup_.build_settings(); }
settings()28   const Settings* settings() { return setup_.settings(); }
setup()29   const TestWithScope& setup() { return setup_; }
toolchain()30   const Toolchain* toolchain() { return setup_.toolchain(); }
31 
32  private:
33   TestWithScope setup_;
34 };
35 
36 }  // namespace
37 
TEST_F(CompileCommandsTest,SourceSet)38 TEST_F(CompileCommandsTest, SourceSet) {
39   Err err;
40 
41   std::vector<const Target*> targets;
42   Target target(settings(), Label(SourceDir("//foo/"), "bar"));
43   target.set_output_type(Target::SOURCE_SET);
44   target.visibility().SetPublic();
45   target.sources().push_back(SourceFile("//foo/input1.cc"));
46   target.sources().push_back(SourceFile("//foo/input2.cc"));
47   // Also test object files, which should be just passed through to the
48   // dependents to link.
49   target.sources().push_back(SourceFile("//foo/input3.o"));
50   target.sources().push_back(SourceFile("//foo/input4.obj"));
51   target.SetToolchain(toolchain());
52   ASSERT_TRUE(target.OnResolved(&err));
53   targets.push_back(&target);
54 
55   // Source set itself.
56   {
57     CompileCommandsWriter writer;
58     std::string out = writer.RenderJSON(build_settings(), targets);
59 
60 #if defined(OS_WIN)
61     const char expected[] =
62         "[\r\n"
63         "  {\r\n"
64         "    \"file\": \"../../foo/input1.cc\",\r\n"
65         "    \"directory\": \"out/Debug\",\r\n"
66         "    \"command\": \"c++ ../../foo/input1.cc     -o  "
67         "obj/foo/bar.input1.o\"\r\n"
68         "  },\r\n"
69         "  {\r\n"
70         "    \"file\": \"../../foo/input2.cc\",\r\n"
71         "    \"directory\": \"out/Debug\",\r\n"
72         "    \"command\": \"c++ ../../foo/input2.cc     -o  "
73         "obj/foo/bar.input2.o\"\r\n"
74         "  }\r\n"
75         "]\r\n";
76 #else
77     const char expected[] =
78         "[\n"
79         "  {\n"
80         "    \"file\": \"../../foo/input1.cc\",\n"
81         "    \"directory\": \"out/Debug\",\n"
82         "    \"command\": \"c++ ../../foo/input1.cc     -o  "
83         "obj/foo/bar.input1.o\"\n"
84         "  },\n"
85         "  {\n"
86         "    \"file\": \"../../foo/input2.cc\",\n"
87         "    \"directory\": \"out/Debug\",\n"
88         "    \"command\": \"c++ ../../foo/input2.cc     -o  "
89         "obj/foo/bar.input2.o\"\n"
90         "  }\n"
91         "]\n";
92 #endif
93     EXPECT_EQ(expected, out);
94   }
95 
96   // A shared library that depends on the source set.
97   Target shlib_target(settings(), Label(SourceDir("//foo/"), "shlib"));
98   shlib_target.sources().push_back(SourceFile("//foo/input3.cc"));
99   shlib_target.set_output_type(Target::SHARED_LIBRARY);
100   shlib_target.public_deps().push_back(LabelTargetPair(&target));
101   shlib_target.SetToolchain(toolchain());
102   ASSERT_TRUE(shlib_target.OnResolved(&err));
103   targets.push_back(&shlib_target);
104 
105   {
106     CompileCommandsWriter writer;
107     std::string out = writer.RenderJSON(build_settings(), targets);
108 
109 #if defined(OS_WIN)
110     const char expected[] =
111         "[\r\n"
112         "  {\r\n"
113         "    \"file\": \"../../foo/input1.cc\",\r\n"
114         "    \"directory\": \"out/Debug\",\r\n"
115         "    \"command\": \"c++ ../../foo/input1.cc     -o  "
116         "obj/foo/bar.input1.o\"\r\n"
117         "  },\r\n"
118         "  {\r\n"
119         "    \"file\": \"../../foo/input2.cc\",\r\n"
120         "    \"directory\": \"out/Debug\",\r\n"
121         "    \"command\": \"c++ ../../foo/input2.cc     -o  "
122         "obj/foo/bar.input2.o\"\r\n"
123         "  },\r\n"
124         "  {\r\n"
125         "    \"file\": \"../../foo/input3.cc\",\r\n"
126         "    \"directory\": \"out/Debug\",\r\n"
127         "    \"command\": \"c++ ../../foo/input3.cc     -o  "
128         "obj/foo/libshlib.input3.o\"\r\n"
129         "  }\r\n"
130         "]\r\n";
131 #else
132     const char expected[] =
133         "[\n"
134         "  {\n"
135         "    \"file\": \"../../foo/input1.cc\",\n"
136         "    \"directory\": \"out/Debug\",\n"
137         "    \"command\": \"c++ ../../foo/input1.cc     -o  "
138         "obj/foo/bar.input1.o\"\n"
139         "  },\n"
140         "  {\n"
141         "    \"file\": \"../../foo/input2.cc\",\n"
142         "    \"directory\": \"out/Debug\",\n"
143         "    \"command\": \"c++ ../../foo/input2.cc     -o  "
144         "obj/foo/bar.input2.o\"\n"
145         "  },\n"
146         "  {\n"
147         "    \"file\": \"../../foo/input3.cc\",\n"
148         "    \"directory\": \"out/Debug\",\n"
149         "    \"command\": \"c++ ../../foo/input3.cc     -o  "
150         "obj/foo/libshlib.input3.o\"\n"
151         "  }\n"
152         "]\n";
153 #endif
154     EXPECT_EQ(expected, out);
155   }
156 
157   // A static library that depends on the source set (should not link it).
158   Target stlib_target(settings(), Label(SourceDir("//foo/"), "stlib"));
159   stlib_target.sources().push_back(SourceFile("//foo/input4.cc"));
160   stlib_target.set_output_type(Target::STATIC_LIBRARY);
161   stlib_target.public_deps().push_back(LabelTargetPair(&target));
162   stlib_target.SetToolchain(toolchain());
163   ASSERT_TRUE(stlib_target.OnResolved(&err));
164   targets.push_back(&stlib_target);
165 
166   {
167     CompileCommandsWriter writer;
168     std::string out = writer.RenderJSON(build_settings(), targets);
169 
170 #if defined(OS_WIN)
171     const char expected[] =
172         "[\r\n"
173         "  {\r\n"
174         "    \"file\": \"../../foo/input1.cc\",\r\n"
175         "    \"directory\": \"out/Debug\",\r\n"
176         "    \"command\": \"c++ ../../foo/input1.cc     -o  "
177         "obj/foo/bar.input1.o\"\r\n"
178         "  },\r\n"
179         "  {\r\n"
180         "    \"file\": \"../../foo/input2.cc\",\r\n"
181         "    \"directory\": \"out/Debug\",\r\n"
182         "    \"command\": \"c++ ../../foo/input2.cc     -o  "
183         "obj/foo/bar.input2.o\"\r\n"
184         "  },\r\n"
185         "  {\r\n"
186         "    \"file\": \"../../foo/input3.cc\",\r\n"
187         "    \"directory\": \"out/Debug\",\r\n"
188         "    \"command\": \"c++ ../../foo/input3.cc     -o  "
189         "obj/foo/libshlib.input3.o\"\r\n"
190         "  },\r\n"
191         "  {\r\n"
192         "    \"file\": \"../../foo/input4.cc\",\r\n"
193         "    \"directory\": \"out/Debug\",\r\n"
194         "    \"command\": \"c++ ../../foo/input4.cc     -o  "
195         "obj/foo/libstlib.input4.o\"\r\n"
196         "  }\r\n"
197         "]\r\n";
198 #else
199     const char expected[] =
200         "[\n"
201         "  {\n"
202         "    \"file\": \"../../foo/input1.cc\",\n"
203         "    \"directory\": \"out/Debug\",\n"
204         "    \"command\": \"c++ ../../foo/input1.cc     -o  "
205         "obj/foo/bar.input1.o\"\n"
206         "  },\n"
207         "  {\n"
208         "    \"file\": \"../../foo/input2.cc\",\n"
209         "    \"directory\": \"out/Debug\",\n"
210         "    \"command\": \"c++ ../../foo/input2.cc     -o  "
211         "obj/foo/bar.input2.o\"\n"
212         "  },\n"
213         "  {\n"
214         "    \"file\": \"../../foo/input3.cc\",\n"
215         "    \"directory\": \"out/Debug\",\n"
216         "    \"command\": \"c++ ../../foo/input3.cc     -o  "
217         "obj/foo/libshlib.input3.o\"\n"
218         "  },\n"
219         "  {\n"
220         "    \"file\": \"../../foo/input4.cc\",\n"
221         "    \"directory\": \"out/Debug\",\n"
222         "    \"command\": \"c++ ../../foo/input4.cc     -o  "
223         "obj/foo/libstlib.input4.o\"\n"
224         "  }\n"
225         "]\n";
226 #endif
227     EXPECT_EQ(expected, out);
228   }
229 }
230 
TEST_F(CompileCommandsTest,EscapeDefines)231 TEST_F(CompileCommandsTest, EscapeDefines) {
232   Err err;
233 
234   std::vector<const Target*> targets;
235   TestTarget target(setup(), "//foo:bar", Target::STATIC_LIBRARY);
236   target.sources().push_back(SourceFile("//foo/input.cc"));
237   target.config_values().defines().push_back("BOOL_DEF");
238   target.config_values().defines().push_back("INT_DEF=123");
239   target.config_values().defines().push_back("STR_DEF=\"ABCD 1\"");
240   target.config_values().defines().push_back("INCLUDE=<header.h>");
241   ASSERT_TRUE(target.OnResolved(&err));
242   targets.push_back(&target);
243 
244   CompileCommandsWriter writer;
245   std::string out = writer.RenderJSON(build_settings(), targets);
246 
247   const char expected[] =
248       "-DBOOL_DEF -DINT_DEF=123 \\\"-DSTR_DEF=\\\\\\\"ABCD 1\\\\\\\"\\\" "
249       "\\\"-DINCLUDE=\\u003Cheader.h>\\\"";
250   EXPECT_TRUE(out.find(expected) != std::string::npos);
251 }
252 
TEST_F(CompileCommandsTest,WinPrecompiledHeaders)253 TEST_F(CompileCommandsTest, WinPrecompiledHeaders) {
254   Err err;
255 
256   // This setup's toolchain does not have precompiled headers defined.
257   // A precompiled header toolchain.
258   Settings pch_settings(build_settings(), "withpch/");
259   Toolchain pch_toolchain(&pch_settings,
260                           Label(SourceDir("//toolchain/"), "withpch"));
261   pch_settings.set_toolchain_label(pch_toolchain.label());
262   pch_settings.set_default_toolchain_label(toolchain()->label());
263 
264   // Declare a C++ compiler that supports PCH.
265   std::unique_ptr<Tool> cxx = Tool::CreateTool(CTool::kCToolCxx);
266   CTool* cxx_tool = cxx->AsC();
267   TestWithScope::SetCommandForTool(
268       "c++ {{source}} {{cflags}} {{cflags_cc}} {{defines}} {{include_dirs}} "
269       "-o {{output}}",
270       cxx_tool);
271   cxx_tool->set_outputs(SubstitutionList::MakeForTest(
272       "{{source_out_dir}}/{{target_output_name}}.{{source_name_part}}.o"));
273   cxx_tool->set_precompiled_header_type(CTool::PCH_MSVC);
274   pch_toolchain.SetTool(std::move(cxx));
275 
276   // Add a C compiler as well.
277   std::unique_ptr<Tool> cc = Tool::CreateTool(CTool::kCToolCc);
278   CTool* cc_tool = cc->AsC();
279   TestWithScope::SetCommandForTool(
280       "cc {{source}} {{cflags}} {{cflags_c}} {{defines}} {{include_dirs}} "
281       "-o {{output}}",
282       cc_tool);
283   cc_tool->set_outputs(SubstitutionList::MakeForTest(
284       "{{source_out_dir}}/{{target_output_name}}.{{source_name_part}}.o"));
285   cc_tool->set_precompiled_header_type(CTool::PCH_MSVC);
286   pch_toolchain.SetTool(std::move(cc));
287   pch_toolchain.ToolchainSetupComplete();
288 
289   // This target doesn't specify precompiled headers.
290   {
291     std::vector<const Target*> targets;
292     Target no_pch_target(&pch_settings,
293                          Label(SourceDir("//foo/"), "no_pch_target"));
294     no_pch_target.set_output_type(Target::SOURCE_SET);
295     no_pch_target.visibility().SetPublic();
296     no_pch_target.sources().push_back(SourceFile("//foo/input1.cc"));
297     no_pch_target.sources().push_back(SourceFile("//foo/input2.c"));
298     no_pch_target.config_values().cflags_c().push_back("-std=c99");
299     no_pch_target.SetToolchain(&pch_toolchain);
300     ASSERT_TRUE(no_pch_target.OnResolved(&err));
301     targets.push_back(&no_pch_target);
302 
303     CompileCommandsWriter writer;
304     std::string out = writer.RenderJSON(build_settings(), targets);
305 
306 #if defined(OS_WIN)
307     const char no_pch_expected[] =
308         "[\r\n"
309         "  {\r\n"
310         "    \"file\": \"../../foo/input1.cc\",\r\n"
311         "    \"directory\": \"out/Debug\",\r\n"
312         "    \"command\": \"c++ ../../foo/input1.cc     -o  "
313         "withpch/obj/foo/no_pch_target.input1.o\"\r\n"
314         "  },\r\n"
315         "  {\r\n"
316         "    \"file\": \"../../foo/input2.c\",\r\n"
317         "    \"directory\": \"out/Debug\",\r\n"
318         "    \"command\": \"cc ../../foo/input2.c   -std=c99   -o  "
319         "withpch/obj/foo/no_pch_target.input2.o\"\r\n"
320         "  }\r\n"
321         "]\r\n";
322 #else
323     const char no_pch_expected[] =
324         "[\n"
325         "  {\n"
326         "    \"file\": \"../../foo/input1.cc\",\n"
327         "    \"directory\": \"out/Debug\",\n"
328         "    \"command\": \"c++ ../../foo/input1.cc     -o  "
329         "withpch/obj/foo/no_pch_target.input1.o\"\n"
330         "  },\n"
331         "  {\n"
332         "    \"file\": \"../../foo/input2.c\",\n"
333         "    \"directory\": \"out/Debug\",\n"
334         "    \"command\": \"cc ../../foo/input2.c   -std=c99   -o  "
335         "withpch/obj/foo/no_pch_target.input2.o\"\n"
336         "  }\n"
337         "]\n";
338 #endif
339     EXPECT_EQ(no_pch_expected, out);
340   }
341 
342   // This target specifies PCH.
343   {
344     std::vector<const Target*> targets;
345     Target pch_target(&pch_settings, Label(SourceDir("//foo/"), "pch_target"));
346     pch_target.config_values().set_precompiled_header("build/precompile.h");
347     pch_target.config_values().set_precompiled_source(
348         SourceFile("//build/precompile.cc"));
349     pch_target.set_output_type(Target::SOURCE_SET);
350     pch_target.visibility().SetPublic();
351     pch_target.sources().push_back(SourceFile("//foo/input1.cc"));
352     pch_target.sources().push_back(SourceFile("//foo/input2.c"));
353     pch_target.SetToolchain(&pch_toolchain);
354     ASSERT_TRUE(pch_target.OnResolved(&err));
355     targets.push_back(&pch_target);
356 
357     CompileCommandsWriter writer;
358     std::string out = writer.RenderJSON(build_settings(), targets);
359 
360 #if defined(OS_WIN)
361     const char pch_win_expected[] =
362         "[\r\n"
363         "  {\r\n"
364         "    \"file\": \"../../foo/input1.cc\",\r\n"
365         "    \"directory\": \"out/Debug\",\r\n"
366         "    \"command\": \"c++ ../../foo/input1.cc   "
367         "/Fpwithpch/obj/foo/pch_target_cc.pch /Yubuild/precompile.h   -o  "
368         "withpch/obj/foo/pch_target.input1.o\"\r\n"
369         "  },\r\n"
370         "  {\r\n"
371         "    \"file\": \"../../foo/input2.c\",\r\n"
372         "    \"directory\": \"out/Debug\",\r\n"
373         "    \"command\": \"cc ../../foo/input2.c   "
374         "/Fpwithpch/obj/foo/pch_target_c.pch /Yubuild/precompile.h   -o  "
375         "withpch/obj/foo/pch_target.input2.o\"\r\n"
376         "  }\r\n"
377         "]\r\n";
378 #else
379     const char pch_win_expected[] =
380         "[\n"
381         "  {\n"
382         "    \"file\": \"../../foo/input1.cc\",\n"
383         "    \"directory\": \"out/Debug\",\n"
384         "    \"command\": \"c++ ../../foo/input1.cc   "
385         "/Fpwithpch/obj/foo/pch_target_cc.pch /Yubuild/precompile.h   -o  "
386         "withpch/obj/foo/pch_target.input1.o\"\n"
387         "  },\n"
388         "  {\n"
389         "    \"file\": \"../../foo/input2.c\",\n"
390         "    \"directory\": \"out/Debug\",\n"
391         "    \"command\": \"cc ../../foo/input2.c   "
392         "/Fpwithpch/obj/foo/pch_target_c.pch /Yubuild/precompile.h   -o  "
393         "withpch/obj/foo/pch_target.input2.o\"\n"
394         "  }\n"
395         "]\n";
396 #endif
397     EXPECT_EQ(pch_win_expected, out);
398   }
399 }
400 
TEST_F(CompileCommandsTest,GCCPrecompiledHeaders)401 TEST_F(CompileCommandsTest, GCCPrecompiledHeaders) {
402   Err err;
403 
404   // This setup's toolchain does not have precompiled headers defined.
405   // A precompiled header toolchain.
406   Settings pch_settings(build_settings(), "withpch/");
407   Toolchain pch_toolchain(&pch_settings,
408                           Label(SourceDir("//toolchain/"), "withpch"));
409   pch_settings.set_toolchain_label(pch_toolchain.label());
410   pch_settings.set_default_toolchain_label(toolchain()->label());
411 
412   // Declare a C++ compiler that supports PCH.
413   std::unique_ptr<Tool> cxx = Tool::CreateTool(CTool::kCToolCxx);
414   CTool* cxx_tool = cxx->AsC();
415   TestWithScope::SetCommandForTool(
416       "c++ {{source}} {{cflags}} {{cflags_cc}} {{defines}} {{include_dirs}} "
417       "-o {{output}}",
418       cxx_tool);
419   cxx_tool->set_outputs(SubstitutionList::MakeForTest(
420       "{{source_out_dir}}/{{target_output_name}}.{{source_name_part}}.o"));
421   cxx_tool->set_precompiled_header_type(CTool::PCH_GCC);
422   pch_toolchain.SetTool(std::move(cxx));
423   pch_toolchain.ToolchainSetupComplete();
424 
425   // Add a C compiler as well.
426   std::unique_ptr<Tool> cc = Tool::CreateTool(CTool::kCToolCc);
427   CTool* cc_tool = cc->AsC();
428   TestWithScope::SetCommandForTool(
429       "cc {{source}} {{cflags}} {{cflags_c}} {{defines}} {{include_dirs}} "
430       "-o {{output}}",
431       cc_tool);
432   cc_tool->set_outputs(SubstitutionList::MakeForTest(
433       "{{source_out_dir}}/{{target_output_name}}.{{source_name_part}}.o"));
434   cc_tool->set_precompiled_header_type(CTool::PCH_GCC);
435   pch_toolchain.SetTool(std::move(cc));
436   pch_toolchain.ToolchainSetupComplete();
437 
438   // This target doesn't specify precompiled headers.
439   {
440     std::vector<const Target*> targets;
441     Target no_pch_target(&pch_settings,
442                          Label(SourceDir("//foo/"), "no_pch_target"));
443     no_pch_target.set_output_type(Target::SOURCE_SET);
444     no_pch_target.visibility().SetPublic();
445     no_pch_target.sources().push_back(SourceFile("//foo/input1.cc"));
446     no_pch_target.sources().push_back(SourceFile("//foo/input2.c"));
447     no_pch_target.config_values().cflags_c().push_back("-std=c99");
448     no_pch_target.SetToolchain(&pch_toolchain);
449     ASSERT_TRUE(no_pch_target.OnResolved(&err));
450     targets.push_back(&no_pch_target);
451 
452     CompileCommandsWriter writer;
453     std::string out = writer.RenderJSON(build_settings(), targets);
454 
455 #if defined(OS_WIN)
456     const char no_pch_expected[] =
457         "[\r\n"
458         "  {\r\n"
459         "    \"file\": \"../../foo/input1.cc\",\r\n"
460         "    \"directory\": \"out/Debug\",\r\n"
461         "    \"command\": \"c++ ../../foo/input1.cc     -o  "
462         "withpch/obj/foo/no_pch_target.input1.o\"\r\n"
463         "  },\r\n"
464         "  {\r\n"
465         "    \"file\": \"../../foo/input2.c\",\r\n"
466         "    \"directory\": \"out/Debug\",\r\n"
467         "    \"command\": \"cc ../../foo/input2.c   -std=c99   -o  "
468         "withpch/obj/foo/no_pch_target.input2.o\"\r\n"
469         "  }\r\n"
470         "]\r\n";
471 #else
472     const char no_pch_expected[] =
473         "[\n"
474         "  {\n"
475         "    \"file\": \"../../foo/input1.cc\",\n"
476         "    \"directory\": \"out/Debug\",\n"
477         "    \"command\": \"c++ ../../foo/input1.cc     -o  "
478         "withpch/obj/foo/no_pch_target.input1.o\"\n"
479         "  },\n"
480         "  {\n"
481         "    \"file\": \"../../foo/input2.c\",\n"
482         "    \"directory\": \"out/Debug\",\n"
483         "    \"command\": \"cc ../../foo/input2.c   -std=c99   -o  "
484         "withpch/obj/foo/no_pch_target.input2.o\"\n"
485         "  }\n"
486         "]\n";
487 #endif
488     EXPECT_EQ(no_pch_expected, out);
489   }
490 
491   // This target specifies PCH.
492   {
493     std::vector<const Target*> targets;
494     Target pch_target(&pch_settings, Label(SourceDir("//foo/"), "pch_target"));
495     pch_target.config_values().set_precompiled_source(
496         SourceFile("//build/precompile.h"));
497     pch_target.config_values().cflags_c().push_back("-std=c99");
498     pch_target.set_output_type(Target::SOURCE_SET);
499     pch_target.visibility().SetPublic();
500     pch_target.sources().push_back(SourceFile("//foo/input1.cc"));
501     pch_target.sources().push_back(SourceFile("//foo/input2.c"));
502     pch_target.SetToolchain(&pch_toolchain);
503     ASSERT_TRUE(pch_target.OnResolved(&err));
504     targets.push_back(&pch_target);
505 
506     CompileCommandsWriter writer;
507     std::string out = writer.RenderJSON(build_settings(), targets);
508 
509 #if defined(OS_WIN)
510     const char pch_gcc_expected[] =
511         "[\r\n"
512         "  {\r\n"
513         "    \"file\": \"../../foo/input1.cc\",\r\n"
514         "    \"directory\": \"out/Debug\",\r\n"
515         "    \"command\": \"c++ ../../foo/input1.cc   -include "
516         "withpch/obj/build/pch_target.precompile.h-cc   -o  "
517         "withpch/obj/foo/pch_target.input1.o\"\r\n"
518         "  },\r\n"
519         "  {\r\n"
520         "    \"file\": \"../../foo/input2.c\",\r\n"
521         "    \"directory\": \"out/Debug\",\r\n"
522         "    \"command\": \"cc ../../foo/input2.c   -std=c99 -include "
523         "withpch/obj/build/pch_target.precompile.h-c   -o  "
524         "withpch/obj/foo/pch_target.input2.o\"\r\n"
525         "  }\r\n"
526         "]\r\n";
527 #else
528     const char pch_gcc_expected[] =
529         "[\n"
530         "  {\n"
531         "    \"file\": \"../../foo/input1.cc\",\n"
532         "    \"directory\": \"out/Debug\",\n"
533         "    \"command\": \"c++ ../../foo/input1.cc   -include "
534         "withpch/obj/build/pch_target.precompile.h-cc   -o  "
535         "withpch/obj/foo/pch_target.input1.o\"\n"
536         "  },\n"
537         "  {\n"
538         "    \"file\": \"../../foo/input2.c\",\n"
539         "    \"directory\": \"out/Debug\",\n"
540         "    \"command\": \"cc ../../foo/input2.c   -std=c99 -include "
541         "withpch/obj/build/pch_target.precompile.h-c   -o  "
542         "withpch/obj/foo/pch_target.input2.o\"\n"
543         "  }\n"
544         "]\n";
545 #endif
546     EXPECT_EQ(pch_gcc_expected, out);
547   }
548 }
549 
TEST_F(CompileCommandsTest,EscapedFlags)550 TEST_F(CompileCommandsTest, EscapedFlags) {
551   Err err;
552 
553   std::vector<const Target*> targets;
554   Target target(settings(), Label(SourceDir("//foo/"), "bar"));
555   target.set_output_type(Target::SOURCE_SET);
556   target.sources().push_back(SourceFile("//foo/input1.c"));
557   target.config_values().cflags_c().push_back("-DCONFIG=\"/config\"");
558   target.SetToolchain(toolchain());
559   ASSERT_TRUE(target.OnResolved(&err));
560   targets.push_back(&target);
561 
562   CompileCommandsWriter writer;
563   std::string out = writer.RenderJSON(build_settings(), targets);
564 
565 #if defined(OS_WIN)
566   const char expected[] =
567       "[\r\n"
568       "  {\r\n"
569       "    \"file\": \"../../foo/input1.c\",\r\n"
570       "    \"directory\": \"out/Debug\",\r\n"
571       "    \"command\": \"cc ../../foo/input1.c   -DCONFIG=\\\"/config\\\"   "
572       "-o  obj/foo/bar.input1.o\"\r\n"
573       "  }\r\n"
574       "]\r\n";
575 #else
576   const char expected[] =
577       "[\n"
578       "  {\n"
579       "    \"file\": \"../../foo/input1.c\",\n"
580       "    \"directory\": \"out/Debug\",\n"
581       "    \"command\": \"cc ../../foo/input1.c   -DCONFIG=\\\"/config\\\"   "
582       "-o  obj/foo/bar.input1.o\"\n"
583       "  }\n"
584       "]\n";
585 #endif
586   EXPECT_EQ(expected, out);
587 }
588 
TEST_F(CompileCommandsTest,CompDBFilter)589 TEST_F(CompileCommandsTest, CompDBFilter) {
590   Err err;
591 
592   std::vector<const Target*> targets;
593   Target target1(settings(), Label(SourceDir("//foo/"), "bar1"));
594   target1.set_output_type(Target::SOURCE_SET);
595   target1.sources().push_back(SourceFile("//foo/input1.c"));
596   target1.config_values().cflags_c().push_back("-DCONFIG=\"/config\"");
597   target1.SetToolchain(toolchain());
598   ASSERT_TRUE(target1.OnResolved(&err));
599   targets.push_back(&target1);
600 
601   Target target2(settings(), Label(SourceDir("//foo/"), "bar2"));
602   target2.set_output_type(Target::SOURCE_SET);
603   target2.sources().push_back(SourceFile("//foo/input2.c"));
604   target2.config_values().cflags_c().push_back("-DCONFIG=\"/config\"");
605   target2.SetToolchain(toolchain());
606   ASSERT_TRUE(target2.OnResolved(&err));
607   targets.push_back(&target2);
608 
609   Target target3(settings(), Label(SourceDir("//foo/"), "bar3"));
610   target3.set_output_type(Target::SOURCE_SET);
611   target3.sources().push_back(SourceFile("//foo/input3.c"));
612   target3.config_values().cflags_c().push_back("-DCONFIG=\"/config\"");
613   target3.SetToolchain(toolchain());
614   ASSERT_TRUE(target3.OnResolved(&err));
615   targets.push_back(&target3);
616 
617   target1.private_deps().push_back(LabelTargetPair(&target2));
618   target1.private_deps().push_back(LabelTargetPair(&target3));
619 
620   CompileCommandsWriter writer;
621 
622   std::set<std::string> filter1;
623   std::vector<const Target*> test_results1 =
624       writer.FilterTargets(targets, filter1);
625   ASSERT_TRUE(test_results1.empty());
626 
627   std::set<std::string> filter2;
628   filter2.insert(target1.label().name());
629   std::vector<const Target*> test_results2 =
630       writer.FilterTargets(targets, filter2);
631   ASSERT_EQ(test_results2, targets);
632 
633   std::set<std::string> filter3;
634   filter3.insert(target2.label().name());
635   std::vector<const Target*> test_result3 =
636       writer.FilterTargets(targets, filter3);
637   std::vector<const Target*> expected_results3;
638   expected_results3.push_back(&target2);
639   ASSERT_EQ(test_result3, expected_results3);
640 }
641