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