1 // Copyright (c) 2013 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.
7 #include "testing/gtest/include/gtest/gtest.h"
8 #include "tools/gn/ninja_binary_target_writer.h"
9 #include "tools/gn/scheduler.h"
10 #include "tools/gn/target.h"
11 #include "tools/gn/test_with_scope.h"
13 TEST(NinjaBinaryTargetWriter
, SourceSet
) {
17 setup
.build_settings()->SetBuildDir(SourceDir("//out/Debug/"));
18 setup
.settings()->set_target_os(Settings::WIN
);
20 Target
target(setup
.settings(), Label(SourceDir("//foo/"), "bar"));
21 target
.set_output_type(Target::SOURCE_SET
);
22 target
.visibility().SetPublic();
23 target
.sources().push_back(SourceFile("//foo/input1.cc"));
24 target
.sources().push_back(SourceFile("//foo/input2.cc"));
25 // Also test object files, which should be just passed through to the
26 // dependents to link.
27 target
.sources().push_back(SourceFile("//foo/input3.o"));
28 target
.sources().push_back(SourceFile("//foo/input4.obj"));
29 target
.SetToolchain(setup
.toolchain());
30 ASSERT_TRUE(target
.OnResolved(&err
));
34 std::ostringstream out
;
35 NinjaBinaryTargetWriter
writer(&target
, out
);
38 const char expected
[] =
44 "target_out_dir = obj/foo\n"
45 "target_output_name = bar\n"
47 "build obj/foo/bar.input1.o: cxx ../../foo/input1.cc\n"
48 "build obj/foo/bar.input2.o: cxx ../../foo/input2.cc\n"
50 "build obj/foo/bar.stamp: stamp obj/foo/bar.input1.o "
51 "obj/foo/bar.input2.o ../../foo/input3.o ../../foo/input4.obj\n";
52 std::string out_str
= out
.str();
53 EXPECT_EQ(expected
, out_str
);
56 // A shared library that depends on the source set.
57 Target
shlib_target(setup
.settings(), Label(SourceDir("//foo/"), "shlib"));
58 shlib_target
.set_output_type(Target::SHARED_LIBRARY
);
59 shlib_target
.public_deps().push_back(LabelTargetPair(&target
));
60 shlib_target
.SetToolchain(setup
.toolchain());
61 ASSERT_TRUE(shlib_target
.OnResolved(&err
));
64 std::ostringstream out
;
65 NinjaBinaryTargetWriter
writer(&shlib_target
, out
);
68 const char expected
[] =
72 "target_out_dir = obj/foo\n"
73 "target_output_name = libshlib\n"
76 // Ordering of the obj files here should come out in the order
77 // specified, with the target's first, followed by the source set's, in
79 "build ./libshlib.so: solink obj/foo/bar.input1.o "
80 "obj/foo/bar.input2.o ../../foo/input3.o ../../foo/input4.obj "
81 "|| obj/foo/bar.stamp\n"
84 " output_extension = .so\n";
85 std::string out_str
= out
.str();
86 EXPECT_EQ(expected
, out_str
);
89 // A static library that depends on the source set (should not link it).
90 Target
stlib_target(setup
.settings(), Label(SourceDir("//foo/"), "stlib"));
91 stlib_target
.set_output_type(Target::STATIC_LIBRARY
);
92 stlib_target
.public_deps().push_back(LabelTargetPair(&target
));
93 stlib_target
.SetToolchain(setup
.toolchain());
94 ASSERT_TRUE(stlib_target
.OnResolved(&err
));
97 std::ostringstream out
;
98 NinjaBinaryTargetWriter
writer(&stlib_target
, out
);
101 const char expected
[] =
105 "target_out_dir = obj/foo\n"
106 "target_output_name = libstlib\n"
109 // There are no sources so there are no params to alink. (In practice
110 // this will probably fail in the archive tool.)
111 "build obj/foo/libstlib.a: alink || obj/foo/bar.stamp\n"
114 " output_extension = \n";
115 std::string out_str
= out
.str();
116 EXPECT_EQ(expected
, out_str
);
119 // Make the static library 'complete', which means it should be linked.
120 stlib_target
.set_complete_static_lib(true);
122 std::ostringstream out
;
123 NinjaBinaryTargetWriter
writer(&stlib_target
, out
);
126 const char expected
[] =
130 "target_out_dir = obj/foo\n"
131 "target_output_name = libstlib\n"
134 // Ordering of the obj files here should come out in the order
135 // specified, with the target's first, followed by the source set's, in
137 "build obj/foo/libstlib.a: alink obj/foo/bar.input1.o "
138 "obj/foo/bar.input2.o ../../foo/input3.o ../../foo/input4.obj "
139 "|| obj/foo/bar.stamp\n"
142 " output_extension = \n";
143 std::string out_str
= out
.str();
144 EXPECT_EQ(expected
, out_str
);
148 // This tests that output extension overrides apply, and input dependencies
150 TEST(NinjaBinaryTargetWriter
, ProductExtensionAndInputDeps
) {
154 setup
.build_settings()->SetBuildDir(SourceDir("//out/Debug/"));
155 setup
.settings()->set_target_os(Settings::LINUX
);
157 // An action for our library to depend on.
158 Target
action(setup
.settings(), Label(SourceDir("//foo/"), "action"));
159 action
.set_output_type(Target::ACTION_FOREACH
);
160 action
.visibility().SetPublic();
161 action
.SetToolchain(setup
.toolchain());
162 ASSERT_TRUE(action
.OnResolved(&err
));
164 // A shared library w/ the product_extension set to a custom value.
165 Target
target(setup
.settings(), Label(SourceDir("//foo/"), "shlib"));
166 target
.set_output_type(Target::SHARED_LIBRARY
);
167 target
.set_output_extension(std::string("so.6"));
168 target
.sources().push_back(SourceFile("//foo/input1.cc"));
169 target
.sources().push_back(SourceFile("//foo/input2.cc"));
170 target
.public_deps().push_back(LabelTargetPair(&action
));
171 target
.SetToolchain(setup
.toolchain());
172 ASSERT_TRUE(target
.OnResolved(&err
));
174 std::ostringstream out
;
175 NinjaBinaryTargetWriter
writer(&target
, out
);
178 const char expected
[] =
184 "target_out_dir = obj/foo\n"
185 "target_output_name = libshlib\n"
187 "build obj/foo/shlib.inputdeps.stamp: stamp obj/foo/action.stamp\n"
188 "build obj/foo/libshlib.input1.o: cxx ../../foo/input1.cc"
189 " || obj/foo/shlib.inputdeps.stamp\n"
190 "build obj/foo/libshlib.input2.o: cxx ../../foo/input2.cc"
191 " || obj/foo/shlib.inputdeps.stamp\n"
193 "build ./libshlib.so.6: solink obj/foo/libshlib.input1.o "
194 // The order-only dependency here is stricly unnecessary since the
195 // sources list this as an order-only dep. See discussion in the code
197 "obj/foo/libshlib.input2.o || obj/foo/action.stamp\n"
200 " output_extension = .so.6\n";
202 std::string out_str
= out
.str();
203 EXPECT_EQ(expected
, out_str
);
206 TEST(NinjaBinaryTargetWriter
, EmptyProductExtension
) {
210 setup
.build_settings()->SetBuildDir(SourceDir("//out/Debug/"));
211 setup
.settings()->set_target_os(Settings::LINUX
);
213 // This test is the same as ProductExtension, except that
214 // we call set_output_extension("") and ensure that we still get the default.
215 Target
target(setup
.settings(), Label(SourceDir("//foo/"), "shlib"));
216 target
.set_output_type(Target::SHARED_LIBRARY
);
217 target
.set_output_extension(std::string());
218 target
.sources().push_back(SourceFile("//foo/input1.cc"));
219 target
.sources().push_back(SourceFile("//foo/input2.cc"));
221 target
.SetToolchain(setup
.toolchain());
222 ASSERT_TRUE(target
.OnResolved(&err
));
224 std::ostringstream out
;
225 NinjaBinaryTargetWriter
writer(&target
, out
);
228 const char expected
[] =
234 "target_out_dir = obj/foo\n"
235 "target_output_name = libshlib\n"
237 "build obj/foo/libshlib.input1.o: cxx ../../foo/input1.cc\n"
238 "build obj/foo/libshlib.input2.o: cxx ../../foo/input2.cc\n"
240 "build ./libshlib.so: solink obj/foo/libshlib.input1.o "
241 "obj/foo/libshlib.input2.o\n"
244 " output_extension = .so\n";
246 std::string out_str
= out
.str();
247 EXPECT_EQ(expected
, out_str
);
250 TEST(NinjaBinaryTargetWriter
, SourceSetDataDeps
) {
252 setup
.build_settings()->SetBuildDir(SourceDir("//out/Debug/"));
253 setup
.settings()->set_target_os(Settings::LINUX
);
257 // This target is a data (runtime) dependency of the intermediate target.
258 Target
data(setup
.settings(), Label(SourceDir("//foo/"), "data_target"));
259 data
.set_output_type(Target::EXECUTABLE
);
260 data
.visibility().SetPublic();
261 data
.SetToolchain(setup
.toolchain());
262 ASSERT_TRUE(data
.OnResolved(&err
));
264 // Intermediate source set target.
265 Target
inter(setup
.settings(), Label(SourceDir("//foo/"), "inter"));
266 inter
.set_output_type(Target::SOURCE_SET
);
267 inter
.visibility().SetPublic();
268 inter
.data_deps().push_back(LabelTargetPair(&data
));
269 inter
.SetToolchain(setup
.toolchain());
270 inter
.sources().push_back(SourceFile("//foo/inter.cc"));
271 ASSERT_TRUE(inter
.OnResolved(&err
)) << err
.message();
273 // Write out the intermediate target.
274 std::ostringstream inter_out
;
275 NinjaBinaryTargetWriter
inter_writer(&inter
, inter_out
);
278 // The intermediate source set will be a stamp file that depends on the
279 // object files, and will have an order-only dependency on its data dep and
281 const char inter_expected
[] =
287 "target_out_dir = obj/foo\n"
288 "target_output_name = inter\n"
290 "build obj/foo/inter.inter.o: cxx ../../foo/inter.cc\n"
292 "build obj/foo/inter.stamp: stamp obj/foo/inter.inter.o || "
294 EXPECT_EQ(inter_expected
, inter_out
.str());
297 Target
exe(setup
.settings(), Label(SourceDir("//foo/"), "exe"));
298 exe
.set_output_type(Target::EXECUTABLE
);
299 exe
.public_deps().push_back(LabelTargetPair(&inter
));
300 exe
.SetToolchain(setup
.toolchain());
301 exe
.sources().push_back(SourceFile("//foo/final.cc"));
302 ASSERT_TRUE(exe
.OnResolved(&err
));
304 std::ostringstream final_out
;
305 NinjaBinaryTargetWriter
final_writer(&exe
, final_out
);
308 // The final output depends on both object files (one from the final target,
309 // one from the source set) and has an order-only dependency on the source
310 // set's stamp file and the final target's data file. The source set stamp
311 // dependency will create an implicit order-only dependency on the data
313 const char final_expected
[] =
319 "target_out_dir = obj/foo\n"
320 "target_output_name = exe\n"
322 "build obj/foo/exe.final.o: cxx ../../foo/final.cc\n"
324 "build ./exe: link obj/foo/exe.final.o obj/foo/inter.inter.o || "
325 "obj/foo/inter.stamp\n"
328 " output_extension = \n";
329 EXPECT_EQ(final_expected
, final_out
.str());
332 TEST(NinjaBinaryTargetWriter
, SharedLibraryModuleDefinitionFile
) {
334 setup
.build_settings()->SetBuildDir(SourceDir("//out/Debug/"));
335 setup
.settings()->set_target_os(Settings::WIN
);
337 Target
shared_lib(setup
.settings(), Label(SourceDir("//foo/"), "bar"));
338 shared_lib
.set_output_type(Target::SHARED_LIBRARY
);
339 shared_lib
.SetToolchain(setup
.toolchain());
340 shared_lib
.sources().push_back(SourceFile("//foo/sources.cc"));
341 shared_lib
.sources().push_back(SourceFile("//foo/bar.def"));
344 ASSERT_TRUE(shared_lib
.OnResolved(&err
));
346 std::ostringstream out
;
347 NinjaBinaryTargetWriter
writer(&shared_lib
, out
);
350 const char expected
[] =
356 "target_out_dir = obj/foo\n"
357 "target_output_name = libbar\n"
359 "build obj/foo/libbar.sources.o: cxx ../../foo/sources.cc\n"
361 "build ./libbar.so: solink obj/foo/libbar.sources.o | ../../foo/bar.def\n"
362 " ldflags = /DEF:../../foo/bar.def\n"
364 " output_extension = .so\n";
365 EXPECT_EQ(expected
, out
.str());
368 TEST(NinjaBinaryTargetWriter
, WinPrecompiledHeaders
) {
371 // This setup's toolchain does not have precompiled headers defined.
374 // A precompiled header toolchain.
375 Settings
pch_settings(setup
.build_settings(), "withpch/");
376 Toolchain
pch_toolchain(&pch_settings
,
377 Label(SourceDir("//toolchain/"), "withpch"));
378 pch_settings
.set_toolchain_label(pch_toolchain
.label());
379 pch_settings
.set_default_toolchain_label(setup
.toolchain()->label());
381 // Declare a C++ compiler that supports PCH.
382 scoped_ptr
<Tool
> cxx_tool(new Tool
);
383 TestWithScope::SetCommandForTool(
384 "c++ {{source}} {{cflags}} {{cflags_cc}} {{defines}} {{include_dirs}} "
387 cxx_tool
->set_outputs(SubstitutionList::MakeForTest(
388 "{{source_out_dir}}/{{target_output_name}}.{{source_name_part}}.o"));
389 cxx_tool
->set_precompiled_header_type(Tool::PCH_MSVC
);
390 pch_toolchain
.SetTool(Toolchain::TYPE_CXX
, cxx_tool
.Pass());
392 // Add a C compiler as well.
393 scoped_ptr
<Tool
> cc_tool(new Tool
);
394 TestWithScope::SetCommandForTool(
395 "cc {{source}} {{cflags}} {{cflags_c}} {{defines}} {{include_dirs}} "
398 cc_tool
->set_outputs(SubstitutionList::MakeForTest(
399 "{{source_out_dir}}/{{target_output_name}}.{{source_name_part}}.o"));
400 cc_tool
->set_precompiled_header_type(Tool::PCH_MSVC
);
401 pch_toolchain
.SetTool(Toolchain::TYPE_CC
, cc_tool
.Pass());
402 pch_toolchain
.ToolchainSetupComplete();
404 // This target doesn't specify precompiled headers.
406 Target
no_pch_target(&pch_settings
,
407 Label(SourceDir("//foo/"), "no_pch_target"));
408 no_pch_target
.set_output_type(Target::SOURCE_SET
);
409 no_pch_target
.visibility().SetPublic();
410 no_pch_target
.sources().push_back(SourceFile("//foo/input1.cc"));
411 no_pch_target
.sources().push_back(SourceFile("//foo/input2.c"));
412 no_pch_target
.SetToolchain(&pch_toolchain
);
413 ASSERT_TRUE(no_pch_target
.OnResolved(&err
));
415 std::ostringstream out
;
416 NinjaBinaryTargetWriter
writer(&no_pch_target
, out
);
419 const char no_pch_expected
[] =
425 "target_output_name = no_pch_target\n"
427 "build withpch/obj/foo/no_pch_target.input1.o: "
428 "withpch_cxx ../../foo/input1.cc\n"
429 "build withpch/obj/foo/no_pch_target.input2.o: "
430 "withpch_cc ../../foo/input2.c\n"
432 "build withpch/obj/foo/no_pch_target.stamp: "
433 "withpch_stamp withpch/obj/foo/no_pch_target.input1.o "
434 "withpch/obj/foo/no_pch_target.input2.o\n";
435 EXPECT_EQ(no_pch_expected
, out
.str());
438 // This target specifies PCH.
440 Target
pch_target(&pch_settings
,
441 Label(SourceDir("//foo/"), "pch_target"));
442 pch_target
.config_values().set_precompiled_header("build/precompile.h");
443 pch_target
.config_values().set_precompiled_source(
444 SourceFile("//build/precompile.cc"));
445 pch_target
.set_output_type(Target::SOURCE_SET
);
446 pch_target
.visibility().SetPublic();
447 pch_target
.sources().push_back(SourceFile("//foo/input1.cc"));
448 pch_target
.sources().push_back(SourceFile("//foo/input2.c"));
449 pch_target
.SetToolchain(&pch_toolchain
);
450 ASSERT_TRUE(pch_target
.OnResolved(&err
));
452 std::ostringstream out
;
453 NinjaBinaryTargetWriter
writer(&pch_target
, out
);
456 const char pch_win_expected
[] =
460 // It should output language-specific pch files.
461 "cflags_c = /Fpwithpch/obj/foo/pch_target_c.pch "
462 "/Yubuild/precompile.h\n"
463 "cflags_cc = /Fpwithpch/obj/foo/pch_target_cc.pch "
464 "/Yubuild/precompile.h\n"
465 "target_output_name = pch_target\n"
467 // Compile the precompiled source files with /Yc.
468 "build withpch/obj/build/pch_target.precompile.c.o: "
469 "withpch_cc ../../build/precompile.cc\n"
470 " cflags_c = ${cflags_c} /Ycbuild/precompile.h\n"
472 "build withpch/obj/build/pch_target.precompile.cc.o: "
473 "withpch_cxx ../../build/precompile.cc\n"
474 " cflags_cc = ${cflags_cc} /Ycbuild/precompile.h\n"
476 "build withpch/obj/foo/pch_target.input1.o: "
477 "withpch_cxx ../../foo/input1.cc | "
478 // Explicit dependency on the PCH build step.
479 "withpch/obj/build/pch_target.precompile.cc.o\n"
480 "build withpch/obj/foo/pch_target.input2.o: "
481 "withpch_cc ../../foo/input2.c | "
482 // Explicit dependency on the PCH build step.
483 "withpch/obj/build/pch_target.precompile.c.o\n"
485 "build withpch/obj/foo/pch_target.stamp: withpch_stamp "
486 "withpch/obj/foo/pch_target.input1.o "
487 "withpch/obj/foo/pch_target.input2.o "
488 // The precompiled object files were added to the outputs.
489 "withpch/obj/build/pch_target.precompile.c.o "
490 "withpch/obj/build/pch_target.precompile.cc.o\n";
491 EXPECT_EQ(pch_win_expected
, out
.str());
495 TEST(NinjaBinaryTargetWriter
, GCCPrecompiledHeaders
) {
498 // This setup's toolchain does not have precompiled headers defined.
501 // A precompiled header toolchain.
502 Settings
pch_settings(setup
.build_settings(), "withpch/");
503 Toolchain
pch_toolchain(&pch_settings
,
504 Label(SourceDir("//toolchain/"), "withpch"));
505 pch_settings
.set_toolchain_label(pch_toolchain
.label());
506 pch_settings
.set_default_toolchain_label(setup
.toolchain()->label());
508 // Declare a C++ compiler that supports PCH.
509 scoped_ptr
<Tool
> cxx_tool(new Tool
);
510 TestWithScope::SetCommandForTool(
511 "c++ {{source}} {{cflags}} {{cflags_cc}} {{defines}} {{include_dirs}} "
514 cxx_tool
->set_outputs(SubstitutionList::MakeForTest(
515 "{{source_out_dir}}/{{target_output_name}}.{{source_name_part}}.o"));
516 cxx_tool
->set_precompiled_header_type(Tool::PCH_GCC
);
517 pch_toolchain
.SetTool(Toolchain::TYPE_CXX
, cxx_tool
.Pass());
518 pch_toolchain
.ToolchainSetupComplete();
520 // Add a C compiler as well.
521 scoped_ptr
<Tool
> cc_tool(new Tool
);
522 TestWithScope::SetCommandForTool(
523 "cc {{source}} {{cflags}} {{cflags_c}} {{defines}} {{include_dirs}} "
526 cc_tool
->set_outputs(SubstitutionList::MakeForTest(
527 "{{source_out_dir}}/{{target_output_name}}.{{source_name_part}}.o"));
528 cc_tool
->set_precompiled_header_type(Tool::PCH_GCC
);
529 pch_toolchain
.SetTool(Toolchain::TYPE_CC
, cc_tool
.Pass());
530 pch_toolchain
.ToolchainSetupComplete();
532 // This target doesn't specify precompiled headers.
534 Target
no_pch_target(&pch_settings
,
535 Label(SourceDir("//foo/"), "no_pch_target"));
536 no_pch_target
.set_output_type(Target::SOURCE_SET
);
537 no_pch_target
.visibility().SetPublic();
538 no_pch_target
.sources().push_back(SourceFile("//foo/input1.cc"));
539 no_pch_target
.sources().push_back(SourceFile("//foo/input2.c"));
540 no_pch_target
.SetToolchain(&pch_toolchain
);
541 ASSERT_TRUE(no_pch_target
.OnResolved(&err
));
543 std::ostringstream out
;
544 NinjaBinaryTargetWriter
writer(&no_pch_target
, out
);
547 const char no_pch_expected
[] =
553 "target_output_name = no_pch_target\n"
555 "build withpch/obj/foo/no_pch_target.input1.o: "
556 "withpch_cxx ../../foo/input1.cc\n"
557 "build withpch/obj/foo/no_pch_target.input2.o: "
558 "withpch_cc ../../foo/input2.c\n"
560 "build withpch/obj/foo/no_pch_target.stamp: "
561 "withpch_stamp withpch/obj/foo/no_pch_target.input1.o "
562 "withpch/obj/foo/no_pch_target.input2.o\n";
563 EXPECT_EQ(no_pch_expected
, out
.str());
566 // This target specifies PCH.
568 Target
pch_target(&pch_settings
,
569 Label(SourceDir("//foo/"), "pch_target"));
570 pch_target
.config_values().set_precompiled_header("build/precompile.h");
571 pch_target
.config_values().set_precompiled_source(
572 SourceFile("//build/precompile.h"));
573 pch_target
.config_values().cflags_c().push_back("-std=c99");
574 pch_target
.set_output_type(Target::SOURCE_SET
);
575 pch_target
.visibility().SetPublic();
576 pch_target
.sources().push_back(SourceFile("//foo/input1.cc"));
577 pch_target
.sources().push_back(SourceFile("//foo/input2.c"));
578 pch_target
.SetToolchain(&pch_toolchain
);
579 ASSERT_TRUE(pch_target
.OnResolved(&err
));
581 std::ostringstream out
;
582 NinjaBinaryTargetWriter
writer(&pch_target
, out
);
585 const char pch_gcc_expected
[] =
589 "cflags_c = -std=c99 "
590 "-include withpch/obj/build/pch_target.precompile.h-c\n"
591 "cflags_cc = -include withpch/obj/build/pch_target.precompile.h-cc\n"
592 "target_output_name = pch_target\n"
594 // Compile the precompiled sources with -x <lang>.
595 "build withpch/obj/build/pch_target.precompile.h-c.gch: "
596 "withpch_cc ../../build/precompile.h\n"
597 " cflags_c = -std=c99 -x c-header\n"
599 "build withpch/obj/build/pch_target.precompile.h-cc.gch: "
600 "withpch_cxx ../../build/precompile.h\n"
601 " cflags_cc = -x c++-header\n"
603 "build withpch/obj/foo/pch_target.input1.o: "
604 "withpch_cxx ../../foo/input1.cc | "
605 // Explicit dependency on the PCH build step.
606 "withpch/obj/build/pch_target.precompile.h-cc.gch\n"
607 "build withpch/obj/foo/pch_target.input2.o: "
608 "withpch_cc ../../foo/input2.c | "
609 // Explicit dependency on the PCH build step.
610 "withpch/obj/build/pch_target.precompile.h-c.gch\n"
612 "build withpch/obj/foo/pch_target.stamp: "
613 "withpch_stamp withpch/obj/foo/pch_target.input1.o "
614 "withpch/obj/foo/pch_target.input2.o\n";
615 EXPECT_EQ(pch_gcc_expected
, out
.str());
619 // Should throw an error with the scheduler if a duplicate object file exists.
620 // This is dependent on the toolchain's object file mapping.
621 TEST(NinjaBinaryTargetWriter
, DupeObjFileError
) {
625 TestTarget
target(setup
, "//foo:bar", Target::EXECUTABLE
);
626 target
.sources().push_back(SourceFile("//a.cc"));
627 target
.sources().push_back(SourceFile("//a.cc"));
629 EXPECT_FALSE(scheduler
.is_failed());
631 std::ostringstream out
;
632 NinjaBinaryTargetWriter
writer(&target
, out
);
635 // Should have issued an error.
636 EXPECT_TRUE(scheduler
.is_failed());