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
20namespace {
21
22bool CompareLabel(const Target* a, const Target* b) {
23  return a->label() < b->label();
24}
25
26// InputConversion needs a global scheduler object.
27class CompileCommandsTest : public TestWithScheduler {
28 public:
29  CompileCommandsTest() = default;
30
31  const BuildSettings* build_settings() { return setup_.build_settings(); }
32  const Settings* settings() { return setup_.settings(); }
33  const TestWithScope& setup() { return setup_; }
34  const Toolchain* toolchain() { return setup_.toolchain(); }
35
36  // Returns true if the two target vectors contain the same targets, order
37  // independent.
38  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
51TEST_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
244TEST_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
266TEST_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
414TEST_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
563TEST_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
602TEST_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