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"));
379 // Declare a C++ compiler that supports PCH.
380 scoped_ptr
<Tool
> cxx_tool(new Tool
);
381 TestWithScope::SetCommandForTool(
382 "c++ {{source}} {{cflags}} {{cflags_cc}} {{defines}} {{include_dirs}} "
385 cxx_tool
->set_outputs(SubstitutionList::MakeForTest(
386 "{{source_out_dir}}/{{target_output_name}}.{{source_name_part}}.o"));
387 cxx_tool
->set_precompiled_header_type(Tool::PCH_MSVC
);
388 pch_toolchain
.SetTool(Toolchain::TYPE_CXX
, cxx_tool
.Pass());
389 pch_toolchain
.ToolchainSetupComplete();
391 // This target doesn't specify precompiled headers.
393 Target
no_pch_target(&pch_settings
,
394 Label(SourceDir("//foo/"), "no_pch_target"));
395 no_pch_target
.set_output_type(Target::SOURCE_SET
);
396 no_pch_target
.visibility().SetPublic();
397 no_pch_target
.sources().push_back(SourceFile("//foo/input1.cc"));
398 no_pch_target
.SetToolchain(&pch_toolchain
);
399 ASSERT_TRUE(no_pch_target
.OnResolved(&err
));
401 std::ostringstream out
;
402 NinjaBinaryTargetWriter
writer(&no_pch_target
, out
);
405 const char no_pch_expected
[] =
410 "target_output_name = no_pch_target\n"
412 "build withpch/obj/foo/no_pch_target.input1.o: "
413 "cxx ../../foo/input1.cc\n"
415 "build withpch/obj/foo/no_pch_target.stamp: "
416 "stamp withpch/obj/foo/no_pch_target.input1.o\n";
417 EXPECT_EQ(no_pch_expected
, out
.str());
420 // This target specifies PCH.
422 Target
pch_target(&pch_settings
,
423 Label(SourceDir("//foo/"), "pch_target"));
424 pch_target
.config_values().set_precompiled_header("build/precompile.h");
425 pch_target
.config_values().set_precompiled_source(
426 SourceFile("//build/precompile.cc"));
427 pch_target
.set_output_type(Target::SOURCE_SET
);
428 pch_target
.visibility().SetPublic();
429 pch_target
.sources().push_back(SourceFile("//foo/input1.cc"));
430 pch_target
.SetToolchain(&pch_toolchain
);
431 ASSERT_TRUE(pch_target
.OnResolved(&err
));
433 std::ostringstream out
;
434 NinjaBinaryTargetWriter
writer(&pch_target
, out
);
437 const char pch_win_expected
[] =
441 // There should only be one .pch file created, for C++ files.
442 "cflags_cc = /Fpwithpch/obj/foo/pch_target_cc.pch "
443 "/Yubuild/precompile.h\n"
444 "target_output_name = pch_target\n"
446 // Compile the precompiled source file with /Yc.
447 "build withpch/obj/build/pch_target.precompile.cc.o: "
448 "cxx ../../build/precompile.cc\n"
449 " cflags_cc = ${cflags_cc} /Ycbuild/precompile.h\n"
451 "build withpch/obj/foo/pch_target.input1.o: "
452 "cxx ../../foo/input1.cc | "
453 // Explicit dependency on the PCH build step.
454 "withpch/obj/build/pch_target.precompile.cc.o\n"
456 "build withpch/obj/foo/pch_target.stamp: "
457 "stamp withpch/obj/foo/pch_target.input1.o "
458 // The precompiled object file was added to the outputs.
459 "withpch/obj/build/pch_target.precompile.cc.o\n";
460 EXPECT_EQ(pch_win_expected
, out
.str());
464 // Should throw an error with the scheduler if a duplicate object file exists.
465 // This is dependent on the toolchain's object file mapping.
466 TEST(NinjaBinaryTargetWriter
, DupeObjFileError
) {
470 TestTarget
target(setup
, "//foo:bar", Target::EXECUTABLE
);
471 target
.sources().push_back(SourceFile("//a.cc"));
472 target
.sources().push_back(SourceFile("//a.cc"));
474 EXPECT_FALSE(scheduler
.is_failed());
476 std::ostringstream out
;
477 NinjaBinaryTargetWriter
writer(&target
, out
);
480 // Should have issued an error.
481 EXPECT_TRUE(scheduler
.is_failed());