1 // Copyright 2019 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/ninja_c_binary_target_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 using NinjaCBinaryTargetWriterTest = TestWithScheduler;
21
TEST_F(NinjaCBinaryTargetWriterTest,SourceSet)22 TEST_F(NinjaCBinaryTargetWriterTest, SourceSet) {
23 Err err;
24 TestWithScope setup;
25
26 Target target(setup.settings(), Label(SourceDir("//foo/"), "bar"));
27 target.set_output_type(Target::SOURCE_SET);
28 target.visibility().SetPublic();
29 target.sources().push_back(SourceFile("//foo/input1.cc"));
30 target.sources().push_back(SourceFile("//foo/input2.cc"));
31 // Also test object files, which should be just passed through to the
32 // dependents to link.
33 target.sources().push_back(SourceFile("//foo/input3.o"));
34 target.sources().push_back(SourceFile("//foo/input4.obj"));
35 target.source_types_used().Set(SourceFile::SOURCE_CPP);
36 target.source_types_used().Set(SourceFile::SOURCE_O);
37 target.SetToolchain(setup.toolchain());
38 ASSERT_TRUE(target.OnResolved(&err));
39
40 // Source set itself.
41 {
42 std::ostringstream out;
43 NinjaCBinaryTargetWriter writer(&target, out);
44 writer.Run();
45
46 const char expected[] =
47 "defines =\n"
48 "include_dirs =\n"
49 "cflags =\n"
50 "cflags_cc =\n"
51 "root_out_dir = .\n"
52 "target_out_dir = obj/foo\n"
53 "target_output_name = bar\n"
54 "\n"
55 "build obj/foo/bar.input1.o: cxx ../../foo/input1.cc\n"
56 "build obj/foo/bar.input2.o: cxx ../../foo/input2.cc\n"
57 "\n"
58 "build obj/foo/bar.stamp: stamp obj/foo/bar.input1.o "
59 "obj/foo/bar.input2.o ../../foo/input3.o ../../foo/input4.obj\n";
60 std::string out_str = out.str();
61 EXPECT_EQ(expected, out_str);
62 }
63
64 // A shared library that depends on the source set.
65 Target shlib_target(setup.settings(), Label(SourceDir("//foo/"), "shlib"));
66 shlib_target.set_output_type(Target::SHARED_LIBRARY);
67 shlib_target.public_deps().push_back(LabelTargetPair(&target));
68 shlib_target.SetToolchain(setup.toolchain());
69 ASSERT_TRUE(shlib_target.OnResolved(&err));
70
71 {
72 std::ostringstream out;
73 NinjaCBinaryTargetWriter writer(&shlib_target, out);
74 writer.Run();
75
76 const char expected[] =
77 "defines =\n"
78 "include_dirs =\n"
79 "root_out_dir = .\n"
80 "target_out_dir = obj/foo\n"
81 "target_output_name = libshlib\n"
82 "\n"
83 "\n"
84 // Ordering of the obj files here should come out in the order
85 // specified, with the target's first, followed by the source set's, in
86 // order.
87 "build ./libshlib.so: solink obj/foo/bar.input1.o "
88 "obj/foo/bar.input2.o ../../foo/input3.o ../../foo/input4.obj "
89 "|| obj/foo/bar.stamp\n"
90 " ldflags =\n"
91 " libs =\n"
92 " frameworks =\n"
93 " output_extension = .so\n"
94 " output_dir = \n";
95 std::string out_str = out.str();
96 EXPECT_EQ(expected, out_str);
97 }
98
99 // A static library that depends on the source set (should not link it).
100 Target stlib_target(setup.settings(), Label(SourceDir("//foo/"), "stlib"));
101 stlib_target.set_output_type(Target::STATIC_LIBRARY);
102 stlib_target.public_deps().push_back(LabelTargetPair(&target));
103 stlib_target.SetToolchain(setup.toolchain());
104 ASSERT_TRUE(stlib_target.OnResolved(&err));
105
106 {
107 std::ostringstream out;
108 NinjaCBinaryTargetWriter writer(&stlib_target, out);
109 writer.Run();
110
111 const char expected[] =
112 "defines =\n"
113 "include_dirs =\n"
114 "root_out_dir = .\n"
115 "target_out_dir = obj/foo\n"
116 "target_output_name = libstlib\n"
117 "\n"
118 "\n"
119 // There are no sources so there are no params to alink. (In practice
120 // this will probably fail in the archive tool.)
121 "build obj/foo/libstlib.a: alink || obj/foo/bar.stamp\n"
122 " arflags =\n"
123 " output_extension = \n"
124 " output_dir = \n";
125 std::string out_str = out.str();
126 EXPECT_EQ(expected, out_str);
127 }
128
129 // Make the static library 'complete', which means it should be linked.
130 stlib_target.set_complete_static_lib(true);
131 {
132 std::ostringstream out;
133 NinjaCBinaryTargetWriter writer(&stlib_target, out);
134 writer.Run();
135
136 const char expected[] =
137 "defines =\n"
138 "include_dirs =\n"
139 "root_out_dir = .\n"
140 "target_out_dir = obj/foo\n"
141 "target_output_name = libstlib\n"
142 "\n"
143 "\n"
144 // Ordering of the obj files here should come out in the order
145 // specified, with the target's first, followed by the source set's, in
146 // order.
147 "build obj/foo/libstlib.a: alink obj/foo/bar.input1.o "
148 "obj/foo/bar.input2.o ../../foo/input3.o ../../foo/input4.obj "
149 "|| obj/foo/bar.stamp\n"
150 " arflags =\n"
151 " output_extension = \n"
152 " output_dir = \n";
153 std::string out_str = out.str();
154 EXPECT_EQ(expected, out_str);
155 }
156 }
157
TEST_F(NinjaCBinaryTargetWriterTest,EscapeDefines)158 TEST_F(NinjaCBinaryTargetWriterTest, EscapeDefines) {
159 TestWithScope setup;
160 Err err;
161
162 TestTarget target(setup, "//foo:bar", Target::STATIC_LIBRARY);
163 target.config_values().defines().push_back("BOOL_DEF");
164 target.config_values().defines().push_back("INT_DEF=123");
165 target.config_values().defines().push_back("STR_DEF=\"ABCD-1\"");
166 ASSERT_TRUE(target.OnResolved(&err));
167
168 std::ostringstream out;
169 NinjaCBinaryTargetWriter writer(&target, out);
170 writer.Run();
171
172 const char expectedSubstr[] =
173 #if defined(OS_WIN)
174 "defines = -DBOOL_DEF -DINT_DEF=123 \"-DSTR_DEF=\\\"ABCD-1\\\"\"";
175 #else
176 "defines = -DBOOL_DEF -DINT_DEF=123 -DSTR_DEF=\\\"ABCD-1\\\"";
177 #endif
178 std::string out_str = out.str();
179 EXPECT_TRUE(out_str.find(expectedSubstr) != std::string::npos);
180 }
181
TEST_F(NinjaCBinaryTargetWriterTest,StaticLibrary)182 TEST_F(NinjaCBinaryTargetWriterTest, StaticLibrary) {
183 TestWithScope setup;
184 Err err;
185
186 TestTarget target(setup, "//foo:bar", Target::STATIC_LIBRARY);
187 target.sources().push_back(SourceFile("//foo/input1.cc"));
188 target.source_types_used().Set(SourceFile::SOURCE_CPP);
189 target.config_values().arflags().push_back("--asdf");
190 ASSERT_TRUE(target.OnResolved(&err));
191
192 std::ostringstream out;
193 NinjaCBinaryTargetWriter writer(&target, out);
194 writer.Run();
195
196 const char expected[] =
197 "defines =\n"
198 "include_dirs =\n"
199 "cflags =\n"
200 "cflags_cc =\n"
201 "root_out_dir = .\n"
202 "target_out_dir = obj/foo\n"
203 "target_output_name = libbar\n"
204 "\n"
205 "build obj/foo/libbar.input1.o: cxx ../../foo/input1.cc\n"
206 "\n"
207 "build obj/foo/libbar.a: alink obj/foo/libbar.input1.o\n"
208 " arflags = --asdf\n"
209 " output_extension = \n"
210 " output_dir = \n";
211 std::string out_str = out.str();
212 EXPECT_EQ(expected, out_str);
213 }
214
TEST_F(NinjaCBinaryTargetWriterTest,CompleteStaticLibrary)215 TEST_F(NinjaCBinaryTargetWriterTest, CompleteStaticLibrary) {
216 TestWithScope setup;
217 Err err;
218
219 TestTarget target(setup, "//foo:bar", Target::STATIC_LIBRARY);
220 target.sources().push_back(SourceFile("//foo/input1.cc"));
221 target.source_types_used().Set(SourceFile::SOURCE_CPP);
222 target.config_values().arflags().push_back("--asdf");
223 target.set_complete_static_lib(true);
224
225 TestTarget baz(setup, "//foo:baz", Target::STATIC_LIBRARY);
226 baz.sources().push_back(SourceFile("//foo/input2.cc"));
227 baz.source_types_used().Set(SourceFile::SOURCE_CPP);
228
229 target.public_deps().push_back(LabelTargetPair(&baz));
230
231 ASSERT_TRUE(target.OnResolved(&err));
232 ASSERT_TRUE(baz.OnResolved(&err));
233
234 // A complete static library that depends on an incomplete static library
235 // should link in the dependent object files as if the dependent target
236 // were a source set.
237 {
238 std::ostringstream out;
239 NinjaCBinaryTargetWriter writer(&target, out);
240 writer.Run();
241
242 const char expected[] =
243 "defines =\n"
244 "include_dirs =\n"
245 "cflags =\n"
246 "cflags_cc =\n"
247 "root_out_dir = .\n"
248 "target_out_dir = obj/foo\n"
249 "target_output_name = libbar\n"
250 "\n"
251 "build obj/foo/libbar.input1.o: cxx ../../foo/input1.cc\n"
252 "\n"
253 "build obj/foo/libbar.a: alink obj/foo/libbar.input1.o "
254 "obj/foo/libbaz.input2.o || obj/foo/libbaz.a\n"
255 " arflags = --asdf\n"
256 " output_extension = \n"
257 " output_dir = \n";
258 std::string out_str = out.str();
259 EXPECT_EQ(expected, out_str);
260 }
261
262 // Make the dependent static library complete.
263 baz.set_complete_static_lib(true);
264
265 // Dependent complete static libraries should not be linked directly.
266 {
267 std::ostringstream out;
268 NinjaCBinaryTargetWriter writer(&target, out);
269 writer.Run();
270
271 const char expected[] =
272 "defines =\n"
273 "include_dirs =\n"
274 "cflags =\n"
275 "cflags_cc =\n"
276 "root_out_dir = .\n"
277 "target_out_dir = obj/foo\n"
278 "target_output_name = libbar\n"
279 "\n"
280 "build obj/foo/libbar.input1.o: cxx ../../foo/input1.cc\n"
281 "\n"
282 "build obj/foo/libbar.a: alink obj/foo/libbar.input1.o "
283 "|| obj/foo/libbaz.a\n"
284 " arflags = --asdf\n"
285 " output_extension = \n"
286 " output_dir = \n";
287 std::string out_str = out.str();
288 EXPECT_EQ(expected, out_str);
289 }
290 }
291
292 // This tests that output extension and output dir overrides apply, and input
293 // dependencies are applied.
TEST_F(NinjaCBinaryTargetWriterTest,OutputExtensionAndInputDeps)294 TEST_F(NinjaCBinaryTargetWriterTest, OutputExtensionAndInputDeps) {
295 Err err;
296 TestWithScope setup;
297
298 // An action for our library to depend on.
299 Target action(setup.settings(), Label(SourceDir("//foo/"), "action"));
300 action.set_output_type(Target::ACTION_FOREACH);
301 action.visibility().SetPublic();
302 action.SetToolchain(setup.toolchain());
303 ASSERT_TRUE(action.OnResolved(&err));
304
305 // A shared library w/ the output_extension set to a custom value.
306 Target target(setup.settings(), Label(SourceDir("//foo/"), "shlib"));
307 target.set_output_type(Target::SHARED_LIBRARY);
308 target.set_output_extension(std::string("so.6"));
309 target.set_output_dir(SourceDir("//out/Debug/foo/"));
310 target.sources().push_back(SourceFile("//foo/input1.cc"));
311 target.sources().push_back(SourceFile("//foo/input2.cc"));
312 target.source_types_used().Set(SourceFile::SOURCE_CPP);
313 target.public_deps().push_back(LabelTargetPair(&action));
314 target.SetToolchain(setup.toolchain());
315 ASSERT_TRUE(target.OnResolved(&err));
316
317 std::ostringstream out;
318 NinjaCBinaryTargetWriter writer(&target, out);
319 writer.Run();
320
321 const char expected[] =
322 "defines =\n"
323 "include_dirs =\n"
324 "cflags =\n"
325 "cflags_cc =\n"
326 "root_out_dir = .\n"
327 "target_out_dir = obj/foo\n"
328 "target_output_name = libshlib\n"
329 "\n"
330 "build obj/foo/libshlib.input1.o: cxx ../../foo/input1.cc"
331 " || obj/foo/action.stamp\n"
332 "build obj/foo/libshlib.input2.o: cxx ../../foo/input2.cc"
333 " || obj/foo/action.stamp\n"
334 "\n"
335 "build ./libshlib.so.6: solink obj/foo/libshlib.input1.o "
336 // The order-only dependency here is stricly unnecessary since the
337 // sources list this as an order-only dep. See discussion in the code
338 // that writes this.
339 "obj/foo/libshlib.input2.o || obj/foo/action.stamp\n"
340 " ldflags =\n"
341 " libs =\n"
342 " frameworks =\n"
343 " output_extension = .so.6\n"
344 " output_dir = foo\n";
345
346 std::string out_str = out.str();
347 EXPECT_EQ(expected, out_str);
348 }
349
TEST_F(NinjaCBinaryTargetWriterTest,NoHardDepsToNoPublicHeaderTarget)350 TEST_F(NinjaCBinaryTargetWriterTest, NoHardDepsToNoPublicHeaderTarget) {
351 Err err;
352 TestWithScope setup;
353
354 SourceFile generated_file("//out/Debug/generated.cc");
355
356 // An action does code generation.
357 Target action(setup.settings(), Label(SourceDir("//foo/"), "generate"));
358 action.set_output_type(Target::ACTION);
359 action.visibility().SetPublic();
360 action.SetToolchain(setup.toolchain());
361 action.set_output_dir(SourceDir("//out/Debug/foo/"));
362 action.action_values().outputs() =
363 SubstitutionList::MakeForTest("//out/Debug/generated.cc");
364 ASSERT_TRUE(action.OnResolved(&err));
365
366 // A source set compiling geneated code, this target does not publicize any
367 // headers.
368 Target gen_obj(setup.settings(), Label(SourceDir("//foo/"), "gen_obj"));
369 gen_obj.set_output_type(Target::SOURCE_SET);
370 gen_obj.set_output_dir(SourceDir("//out/Debug/foo/"));
371 gen_obj.sources().push_back(generated_file);
372 gen_obj.source_types_used().Set(SourceFile::SOURCE_CPP);
373 gen_obj.visibility().SetPublic();
374 gen_obj.private_deps().push_back(LabelTargetPair(&action));
375 gen_obj.set_all_headers_public(false);
376 gen_obj.SetToolchain(setup.toolchain());
377 ASSERT_TRUE(gen_obj.OnResolved(&err));
378
379 std::ostringstream obj_out;
380 NinjaCBinaryTargetWriter obj_writer(&gen_obj, obj_out);
381 obj_writer.Run();
382
383 const char obj_expected[] =
384 "defines =\n"
385 "include_dirs =\n"
386 "cflags =\n"
387 "cflags_cc =\n"
388 "root_out_dir = .\n"
389 "target_out_dir = obj/foo\n"
390 "target_output_name = gen_obj\n"
391 "\n"
392 "build obj/out/Debug/gen_obj.generated.o: cxx generated.cc"
393 " || obj/foo/generate.stamp\n"
394 "\n"
395 "build obj/foo/gen_obj.stamp: stamp obj/out/Debug/gen_obj.generated.o"
396 // The order-only dependency here is strictly unnecessary since the
397 // sources list this as an order-only dep.
398 " || obj/foo/generate.stamp\n";
399
400 std::string obj_str = obj_out.str();
401 EXPECT_EQ(obj_expected, obj_str);
402
403 // A shared library depends on gen_obj, having corresponding header for
404 // generated obj.
405 Target gen_lib(setup.settings(), Label(SourceDir("//foo/"), "gen_lib"));
406 gen_lib.set_output_type(Target::SHARED_LIBRARY);
407 gen_lib.set_output_dir(SourceDir("//out/Debug/foo/"));
408 gen_lib.sources().push_back(SourceFile("//foor/generated.h"));
409 gen_lib.source_types_used().Set(SourceFile::SOURCE_H);
410 gen_lib.visibility().SetPublic();
411 gen_lib.private_deps().push_back(LabelTargetPair(&gen_obj));
412 gen_lib.SetToolchain(setup.toolchain());
413 ASSERT_TRUE(gen_lib.OnResolved(&err));
414
415 std::ostringstream lib_out;
416 NinjaCBinaryTargetWriter lib_writer(&gen_lib, lib_out);
417 lib_writer.Run();
418
419 const char lib_expected[] =
420 "defines =\n"
421 "include_dirs =\n"
422 "root_out_dir = .\n"
423 "target_out_dir = obj/foo\n"
424 "target_output_name = libgen_lib\n"
425 "\n"
426 "\n"
427 "build ./libgen_lib.so: solink obj/out/Debug/gen_obj.generated.o"
428 // The order-only dependency here is strictly unnecessary since
429 // obj/out/Debug/gen_obj.generated.o has dependency to
430 // obj/foo/gen_obj.stamp
431 " || obj/foo/gen_obj.stamp\n"
432 " ldflags =\n"
433 " libs =\n"
434 " frameworks =\n"
435 " output_extension = .so\n"
436 " output_dir = foo\n";
437
438 std::string lib_str = lib_out.str();
439 EXPECT_EQ(lib_expected, lib_str);
440
441 // An executable depends on gen_lib.
442 Target executable(setup.settings(),
443 Label(SourceDir("//foo/"), "final_target"));
444 executable.set_output_type(Target::EXECUTABLE);
445 executable.set_output_dir(SourceDir("//out/Debug/foo/"));
446 executable.sources().push_back(SourceFile("//foo/main.cc"));
447 executable.source_types_used().Set(SourceFile::SOURCE_CPP);
448 executable.private_deps().push_back(LabelTargetPair(&gen_lib));
449 executable.SetToolchain(setup.toolchain());
450 ASSERT_TRUE(executable.OnResolved(&err)) << err.message();
451
452 std::ostringstream final_out;
453 NinjaCBinaryTargetWriter final_writer(&executable, final_out);
454 final_writer.Run();
455
456 // There is no order only dependency to action target.
457 const char final_expected[] =
458 "defines =\n"
459 "include_dirs =\n"
460 "cflags =\n"
461 "cflags_cc =\n"
462 "root_out_dir = .\n"
463 "target_out_dir = obj/foo\n"
464 "target_output_name = final_target\n"
465 "\n"
466 "build obj/foo/final_target.main.o: cxx ../../foo/main.cc\n"
467 "\n"
468 "build ./final_target: link obj/foo/final_target.main.o"
469 " ./libgen_lib.so\n"
470 " ldflags =\n"
471 " libs =\n"
472 " frameworks =\n"
473 " output_extension = \n"
474 " output_dir = foo\n";
475
476 std::string final_str = final_out.str();
477 EXPECT_EQ(final_expected, final_str);
478 }
479
480 // Tests libs are applied.
TEST_F(NinjaCBinaryTargetWriterTest,LibsAndLibDirs)481 TEST_F(NinjaCBinaryTargetWriterTest, LibsAndLibDirs) {
482 Err err;
483 TestWithScope setup;
484
485 // A shared library w/ libs and lib_dirs.
486 Target target(setup.settings(), Label(SourceDir("//foo/"), "shlib"));
487 target.set_output_type(Target::SHARED_LIBRARY);
488 target.config_values().libs().push_back(LibFile(SourceFile("//foo/lib1.a")));
489 target.config_values().libs().push_back(LibFile("foo"));
490 target.config_values().lib_dirs().push_back(SourceDir("//foo/bar/"));
491 target.SetToolchain(setup.toolchain());
492 ASSERT_TRUE(target.OnResolved(&err));
493
494 std::ostringstream out;
495 NinjaCBinaryTargetWriter writer(&target, out);
496 writer.Run();
497
498 const char expected[] =
499 "defines =\n"
500 "include_dirs =\n"
501 "root_out_dir = .\n"
502 "target_out_dir = obj/foo\n"
503 "target_output_name = libshlib\n"
504 "\n"
505 "\n"
506 "build ./libshlib.so: solink | ../../foo/lib1.a\n"
507 " ldflags = -L../../foo/bar\n"
508 " libs = ../../foo/lib1.a -lfoo\n"
509 " frameworks =\n"
510 " output_extension = .so\n"
511 " output_dir = \n";
512
513 std::string out_str = out.str();
514 EXPECT_EQ(expected, out_str);
515 }
516
517 // Tests frameworks are applied.
TEST_F(NinjaCBinaryTargetWriterTest,FrameworksAndFrameworkDirs)518 TEST_F(NinjaCBinaryTargetWriterTest, FrameworksAndFrameworkDirs) {
519 Err err;
520 TestWithScope setup;
521
522 // A config that force linking with the framework.
523 Config framework_config(setup.settings(),
524 Label(SourceDir("//bar"), "framework_config"));
525 framework_config.own_values().frameworks().push_back("Bar.framework");
526 framework_config.own_values().framework_dirs().push_back(
527 SourceDir("//out/Debug/"));
528 ASSERT_TRUE(framework_config.OnResolved(&err));
529
530 // A target creating a framework bundle.
531 Target framework(setup.settings(), Label(SourceDir("//bar"), "framework"));
532 framework.set_output_type(Target::CREATE_BUNDLE);
533 framework.bundle_data().product_type() = "com.apple.product-type.framework";
534 framework.public_configs().push_back(LabelConfigPair(&framework_config));
535 framework.SetToolchain(setup.toolchain());
536 framework.visibility().SetPublic();
537 ASSERT_TRUE(framework.OnResolved(&err));
538
539 // A shared library w/ libs and lib_dirs.
540 Target target(setup.settings(), Label(SourceDir("//foo/"), "shlib"));
541 target.set_output_type(Target::SHARED_LIBRARY);
542 target.config_values().frameworks().push_back("System.framework");
543 target.private_deps().push_back(LabelTargetPair(&framework));
544 target.SetToolchain(setup.toolchain());
545 ASSERT_TRUE(target.OnResolved(&err));
546
547 std::ostringstream out;
548 NinjaCBinaryTargetWriter writer(&target, out);
549 writer.Run();
550
551 const char expected[] =
552 "defines =\n"
553 "include_dirs =\n"
554 "root_out_dir = .\n"
555 "target_out_dir = obj/foo\n"
556 "target_output_name = libshlib\n"
557 "\n"
558 "\n"
559 "build ./libshlib.so: solink | obj/bar/framework.stamp\n"
560 " ldflags = -F.\n"
561 " libs =\n"
562 " frameworks = -framework System -framework Bar\n"
563 " output_extension = .so\n"
564 " output_dir = \n";
565
566 std::string out_str = out.str();
567 EXPECT_EQ(expected, out_str);
568 }
569
TEST_F(NinjaCBinaryTargetWriterTest,EmptyOutputExtension)570 TEST_F(NinjaCBinaryTargetWriterTest, EmptyOutputExtension) {
571 Err err;
572 TestWithScope setup;
573
574 // This test is the same as OutputExtensionAndInputDeps, except that we call
575 // set_output_extension("") and ensure that we get an empty one and override
576 // the output prefix so that the name matches the target exactly.
577 Target target(setup.settings(), Label(SourceDir("//foo/"), "shlib"));
578 target.set_output_type(Target::SHARED_LIBRARY);
579 target.set_output_prefix_override(true);
580 target.set_output_extension(std::string());
581 target.sources().push_back(SourceFile("//foo/input1.cc"));
582 target.sources().push_back(SourceFile("//foo/input2.cc"));
583 target.source_types_used().Set(SourceFile::SOURCE_CPP);
584
585 target.SetToolchain(setup.toolchain());
586 ASSERT_TRUE(target.OnResolved(&err));
587
588 std::ostringstream out;
589 NinjaCBinaryTargetWriter writer(&target, out);
590 writer.Run();
591
592 const char expected[] =
593 "defines =\n"
594 "include_dirs =\n"
595 "cflags =\n"
596 "cflags_cc =\n"
597 "root_out_dir = .\n"
598 "target_out_dir = obj/foo\n"
599 "target_output_name = shlib\n"
600 "\n"
601 "build obj/foo/shlib.input1.o: cxx ../../foo/input1.cc\n"
602 "build obj/foo/shlib.input2.o: cxx ../../foo/input2.cc\n"
603 "\n"
604 "build ./shlib: solink obj/foo/shlib.input1.o "
605 "obj/foo/shlib.input2.o\n"
606 " ldflags =\n"
607 " libs =\n"
608 " frameworks =\n"
609 " output_extension = \n"
610 " output_dir = \n";
611
612 std::string out_str = out.str();
613 EXPECT_EQ(expected, out_str);
614 }
615
TEST_F(NinjaCBinaryTargetWriterTest,SourceSetDataDeps)616 TEST_F(NinjaCBinaryTargetWriterTest, SourceSetDataDeps) {
617 Err err;
618 TestWithScope setup;
619
620 // This target is a data (runtime) dependency of the intermediate target.
621 Target data(setup.settings(), Label(SourceDir("//foo/"), "data_target"));
622 data.set_output_type(Target::EXECUTABLE);
623 data.visibility().SetPublic();
624 data.SetToolchain(setup.toolchain());
625 ASSERT_TRUE(data.OnResolved(&err));
626
627 // Intermediate source set target.
628 Target inter(setup.settings(), Label(SourceDir("//foo/"), "inter"));
629 inter.set_output_type(Target::SOURCE_SET);
630 inter.visibility().SetPublic();
631 inter.data_deps().push_back(LabelTargetPair(&data));
632 inter.SetToolchain(setup.toolchain());
633 inter.sources().push_back(SourceFile("//foo/inter.cc"));
634 inter.source_types_used().Set(SourceFile::SOURCE_CPP);
635 ASSERT_TRUE(inter.OnResolved(&err)) << err.message();
636
637 // Write out the intermediate target.
638 std::ostringstream inter_out;
639 NinjaCBinaryTargetWriter inter_writer(&inter, inter_out);
640 inter_writer.Run();
641
642 // The intermediate source set will be a stamp file that depends on the
643 // object files, and will have an order-only dependency on its data dep and
644 // data file.
645 const char inter_expected[] =
646 "defines =\n"
647 "include_dirs =\n"
648 "cflags =\n"
649 "cflags_cc =\n"
650 "root_out_dir = .\n"
651 "target_out_dir = obj/foo\n"
652 "target_output_name = inter\n"
653 "\n"
654 "build obj/foo/inter.inter.o: cxx ../../foo/inter.cc\n"
655 "\n"
656 "build obj/foo/inter.stamp: stamp obj/foo/inter.inter.o || "
657 "./data_target\n";
658 EXPECT_EQ(inter_expected, inter_out.str());
659
660 // Final target.
661 Target exe(setup.settings(), Label(SourceDir("//foo/"), "exe"));
662 exe.set_output_type(Target::EXECUTABLE);
663 exe.public_deps().push_back(LabelTargetPair(&inter));
664 exe.SetToolchain(setup.toolchain());
665 exe.sources().push_back(SourceFile("//foo/final.cc"));
666 exe.source_types_used().Set(SourceFile::SOURCE_CPP);
667 ASSERT_TRUE(exe.OnResolved(&err));
668
669 std::ostringstream final_out;
670 NinjaCBinaryTargetWriter final_writer(&exe, final_out);
671 final_writer.Run();
672
673 // The final output depends on both object files (one from the final target,
674 // one from the source set) and has an order-only dependency on the source
675 // set's stamp file and the final target's data file. The source set stamp
676 // dependency will create an implicit order-only dependency on the data
677 // target.
678 const char final_expected[] =
679 "defines =\n"
680 "include_dirs =\n"
681 "cflags =\n"
682 "cflags_cc =\n"
683 "root_out_dir = .\n"
684 "target_out_dir = obj/foo\n"
685 "target_output_name = exe\n"
686 "\n"
687 "build obj/foo/exe.final.o: cxx ../../foo/final.cc\n"
688 "\n"
689 "build ./exe: link obj/foo/exe.final.o obj/foo/inter.inter.o || "
690 "obj/foo/inter.stamp\n"
691 " ldflags =\n"
692 " libs =\n"
693 " frameworks =\n"
694 " output_extension = \n"
695 " output_dir = \n";
696 EXPECT_EQ(final_expected, final_out.str());
697 }
698
TEST_F(NinjaCBinaryTargetWriterTest,SharedLibraryModuleDefinitionFile)699 TEST_F(NinjaCBinaryTargetWriterTest, SharedLibraryModuleDefinitionFile) {
700 Err err;
701 TestWithScope setup;
702
703 Target shared_lib(setup.settings(), Label(SourceDir("//foo/"), "bar"));
704 shared_lib.set_output_type(Target::SHARED_LIBRARY);
705 shared_lib.SetToolchain(setup.toolchain());
706 shared_lib.sources().push_back(SourceFile("//foo/sources.cc"));
707 shared_lib.sources().push_back(SourceFile("//foo/bar.def"));
708 shared_lib.source_types_used().Set(SourceFile::SOURCE_CPP);
709 shared_lib.source_types_used().Set(SourceFile::SOURCE_DEF);
710 ASSERT_TRUE(shared_lib.OnResolved(&err));
711
712 std::ostringstream out;
713 NinjaCBinaryTargetWriter writer(&shared_lib, out);
714 writer.Run();
715
716 const char expected[] =
717 "defines =\n"
718 "include_dirs =\n"
719 "cflags =\n"
720 "cflags_cc =\n"
721 "root_out_dir = .\n"
722 "target_out_dir = obj/foo\n"
723 "target_output_name = libbar\n"
724 "\n"
725 "build obj/foo/libbar.sources.o: cxx ../../foo/sources.cc\n"
726 "\n"
727 "build ./libbar.so: solink obj/foo/libbar.sources.o | ../../foo/bar.def\n"
728 " ldflags = /DEF:../../foo/bar.def\n"
729 " libs =\n"
730 " frameworks =\n"
731 " output_extension = .so\n"
732 " output_dir = \n";
733 EXPECT_EQ(expected, out.str());
734 }
735
TEST_F(NinjaCBinaryTargetWriterTest,LoadableModule)736 TEST_F(NinjaCBinaryTargetWriterTest, LoadableModule) {
737 Err err;
738 TestWithScope setup;
739
740 Target loadable_module(setup.settings(), Label(SourceDir("//foo/"), "bar"));
741 loadable_module.set_output_type(Target::LOADABLE_MODULE);
742 loadable_module.visibility().SetPublic();
743 loadable_module.SetToolchain(setup.toolchain());
744 loadable_module.sources().push_back(SourceFile("//foo/sources.cc"));
745 loadable_module.source_types_used().Set(SourceFile::SOURCE_CPP);
746 ASSERT_TRUE(loadable_module.OnResolved(&err)) << err.message();
747
748 std::ostringstream out;
749 NinjaCBinaryTargetWriter writer(&loadable_module, out);
750 writer.Run();
751
752 const char loadable_expected[] =
753 "defines =\n"
754 "include_dirs =\n"
755 "cflags =\n"
756 "cflags_cc =\n"
757 "root_out_dir = .\n"
758 "target_out_dir = obj/foo\n"
759 "target_output_name = libbar\n"
760 "\n"
761 "build obj/foo/libbar.sources.o: cxx ../../foo/sources.cc\n"
762 "\n"
763 "build ./libbar.so: solink_module obj/foo/libbar.sources.o\n"
764 " ldflags =\n"
765 " libs =\n"
766 " frameworks =\n"
767 " output_extension = .so\n"
768 " output_dir = \n";
769 EXPECT_EQ(loadable_expected, out.str());
770
771 // Final target.
772 Target exe(setup.settings(), Label(SourceDir("//foo/"), "exe"));
773 exe.set_output_type(Target::EXECUTABLE);
774 exe.public_deps().push_back(LabelTargetPair(&loadable_module));
775 exe.SetToolchain(setup.toolchain());
776 exe.sources().push_back(SourceFile("//foo/final.cc"));
777 exe.source_types_used().Set(SourceFile::SOURCE_CPP);
778 ASSERT_TRUE(exe.OnResolved(&err)) << err.message();
779
780 std::ostringstream final_out;
781 NinjaCBinaryTargetWriter final_writer(&exe, final_out);
782 final_writer.Run();
783
784 // The final output depends on the loadable module so should have an
785 // order-only dependency on the loadable modules's output file.
786 const char final_expected[] =
787 "defines =\n"
788 "include_dirs =\n"
789 "cflags =\n"
790 "cflags_cc =\n"
791 "root_out_dir = .\n"
792 "target_out_dir = obj/foo\n"
793 "target_output_name = exe\n"
794 "\n"
795 "build obj/foo/exe.final.o: cxx ../../foo/final.cc\n"
796 "\n"
797 "build ./exe: link obj/foo/exe.final.o || ./libbar.so\n"
798 " ldflags =\n"
799 " libs =\n"
800 " frameworks =\n"
801 " output_extension = \n"
802 " output_dir = \n";
803 EXPECT_EQ(final_expected, final_out.str());
804 }
805
TEST_F(NinjaCBinaryTargetWriterTest,WinPrecompiledHeaders)806 TEST_F(NinjaCBinaryTargetWriterTest, WinPrecompiledHeaders) {
807 Err err;
808
809 // This setup's toolchain does not have precompiled headers defined.
810 TestWithScope setup;
811
812 // A precompiled header toolchain.
813 Settings pch_settings(setup.build_settings(), "withpch/");
814 Toolchain pch_toolchain(&pch_settings,
815 Label(SourceDir("//toolchain/"), "withpch"));
816 pch_settings.set_toolchain_label(pch_toolchain.label());
817 pch_settings.set_default_toolchain_label(setup.toolchain()->label());
818
819 // Declare a C++ compiler that supports PCH.
820 std::unique_ptr<Tool> cxx = std::make_unique<CTool>(CTool::kCToolCxx);
821 CTool* cxx_tool = cxx->AsC();
822 TestWithScope::SetCommandForTool(
823 "c++ {{source}} {{cflags}} {{cflags_cc}} {{defines}} {{include_dirs}} "
824 "-o {{output}}",
825 cxx_tool);
826 cxx_tool->set_outputs(SubstitutionList::MakeForTest(
827 "{{source_out_dir}}/{{target_output_name}}.{{source_name_part}}.o"));
828 cxx_tool->set_precompiled_header_type(CTool::PCH_MSVC);
829 pch_toolchain.SetTool(std::move(cxx));
830
831 // Add a C compiler as well.
832 std::unique_ptr<Tool> cc = std::make_unique<CTool>(CTool::kCToolCc);
833 CTool* cc_tool = cc->AsC();
834 TestWithScope::SetCommandForTool(
835 "cc {{source}} {{cflags}} {{cflags_c}} {{defines}} {{include_dirs}} "
836 "-o {{output}}",
837 cc_tool);
838 cc_tool->set_outputs(SubstitutionList::MakeForTest(
839 "{{source_out_dir}}/{{target_output_name}}.{{source_name_part}}.o"));
840 cc_tool->set_precompiled_header_type(CTool::PCH_MSVC);
841 pch_toolchain.SetTool(std::move(cc));
842 pch_toolchain.ToolchainSetupComplete();
843
844 // This target doesn't specify precompiled headers.
845 {
846 Target no_pch_target(&pch_settings,
847 Label(SourceDir("//foo/"), "no_pch_target"));
848 no_pch_target.set_output_type(Target::SOURCE_SET);
849 no_pch_target.visibility().SetPublic();
850 no_pch_target.sources().push_back(SourceFile("//foo/input1.cc"));
851 no_pch_target.sources().push_back(SourceFile("//foo/input2.c"));
852 no_pch_target.source_types_used().Set(SourceFile::SOURCE_CPP);
853 no_pch_target.source_types_used().Set(SourceFile::SOURCE_C);
854 no_pch_target.config_values().cflags_c().push_back("-std=c99");
855 no_pch_target.SetToolchain(&pch_toolchain);
856 ASSERT_TRUE(no_pch_target.OnResolved(&err));
857
858 std::ostringstream out;
859 NinjaCBinaryTargetWriter writer(&no_pch_target, out);
860 writer.Run();
861
862 const char no_pch_expected[] =
863 "defines =\n"
864 "include_dirs =\n"
865 "cflags =\n"
866 "cflags_c = -std=c99\n"
867 "cflags_cc =\n"
868 "target_output_name = no_pch_target\n"
869 "\n"
870 "build withpch/obj/foo/no_pch_target.input1.o: "
871 "withpch_cxx ../../foo/input1.cc\n"
872 "build withpch/obj/foo/no_pch_target.input2.o: "
873 "withpch_cc ../../foo/input2.c\n"
874 "\n"
875 "build withpch/obj/foo/no_pch_target.stamp: "
876 "withpch_stamp withpch/obj/foo/no_pch_target.input1.o "
877 "withpch/obj/foo/no_pch_target.input2.o\n";
878 EXPECT_EQ(no_pch_expected, out.str());
879 }
880
881 // This target specifies PCH.
882 {
883 Target pch_target(&pch_settings, Label(SourceDir("//foo/"), "pch_target"));
884 pch_target.config_values().set_precompiled_header("build/precompile.h");
885 pch_target.config_values().set_precompiled_source(
886 SourceFile("//build/precompile.cc"));
887 pch_target.set_output_type(Target::SOURCE_SET);
888 pch_target.visibility().SetPublic();
889 pch_target.sources().push_back(SourceFile("//foo/input1.cc"));
890 pch_target.sources().push_back(SourceFile("//foo/input2.c"));
891 pch_target.source_types_used().Set(SourceFile::SOURCE_CPP);
892 pch_target.source_types_used().Set(SourceFile::SOURCE_C);
893 pch_target.SetToolchain(&pch_toolchain);
894 ASSERT_TRUE(pch_target.OnResolved(&err));
895
896 std::ostringstream out;
897 NinjaCBinaryTargetWriter writer(&pch_target, out);
898 writer.Run();
899
900 const char pch_win_expected[] =
901 "defines =\n"
902 "include_dirs =\n"
903 "cflags =\n"
904 // It should output language-specific pch files.
905 "cflags_c = /Fpwithpch/obj/foo/pch_target_c.pch "
906 "/Yubuild/precompile.h\n"
907 "cflags_cc = /Fpwithpch/obj/foo/pch_target_cc.pch "
908 "/Yubuild/precompile.h\n"
909 "target_output_name = pch_target\n"
910 "\n"
911 // Compile the precompiled source files with /Yc.
912 "build withpch/obj/build/pch_target.precompile.c.o: "
913 "withpch_cc ../../build/precompile.cc\n"
914 " cflags_c = ${cflags_c} /Ycbuild/precompile.h\n"
915 "\n"
916 "build withpch/obj/build/pch_target.precompile.cc.o: "
917 "withpch_cxx ../../build/precompile.cc\n"
918 " cflags_cc = ${cflags_cc} /Ycbuild/precompile.h\n"
919 "\n"
920 "build withpch/obj/foo/pch_target.input1.o: "
921 "withpch_cxx ../../foo/input1.cc | "
922 // Explicit dependency on the PCH build step.
923 "withpch/obj/build/pch_target.precompile.cc.o\n"
924 "build withpch/obj/foo/pch_target.input2.o: "
925 "withpch_cc ../../foo/input2.c | "
926 // Explicit dependency on the PCH build step.
927 "withpch/obj/build/pch_target.precompile.c.o\n"
928 "\n"
929 "build withpch/obj/foo/pch_target.stamp: withpch_stamp "
930 "withpch/obj/foo/pch_target.input1.o "
931 "withpch/obj/foo/pch_target.input2.o "
932 // The precompiled object files were added to the outputs.
933 "withpch/obj/build/pch_target.precompile.c.o "
934 "withpch/obj/build/pch_target.precompile.cc.o\n";
935 EXPECT_EQ(pch_win_expected, out.str());
936 }
937 }
938
TEST_F(NinjaCBinaryTargetWriterTest,GCCPrecompiledHeaders)939 TEST_F(NinjaCBinaryTargetWriterTest, GCCPrecompiledHeaders) {
940 Err err;
941
942 // This setup's toolchain does not have precompiled headers defined.
943 TestWithScope setup;
944
945 // A precompiled header toolchain.
946 Settings pch_settings(setup.build_settings(), "withpch/");
947 Toolchain pch_toolchain(&pch_settings,
948 Label(SourceDir("//toolchain/"), "withpch"));
949 pch_settings.set_toolchain_label(pch_toolchain.label());
950 pch_settings.set_default_toolchain_label(setup.toolchain()->label());
951
952 // Declare a C++ compiler that supports PCH.
953 std::unique_ptr<Tool> cxx = std::make_unique<CTool>(CTool::kCToolCxx);
954 CTool* cxx_tool = cxx->AsC();
955 TestWithScope::SetCommandForTool(
956 "c++ {{source}} {{cflags}} {{cflags_cc}} {{defines}} {{include_dirs}} "
957 "-o {{output}}",
958 cxx_tool);
959 cxx_tool->set_outputs(SubstitutionList::MakeForTest(
960 "{{source_out_dir}}/{{target_output_name}}.{{source_name_part}}.o"));
961 cxx_tool->set_precompiled_header_type(CTool::PCH_GCC);
962 pch_toolchain.SetTool(std::move(cxx));
963 pch_toolchain.ToolchainSetupComplete();
964
965 // Add a C compiler as well.
966 std::unique_ptr<Tool> cc = std::make_unique<CTool>(CTool::kCToolCc);
967 CTool* cc_tool = cc->AsC();
968 TestWithScope::SetCommandForTool(
969 "cc {{source}} {{cflags}} {{cflags_c}} {{defines}} {{include_dirs}} "
970 "-o {{output}}",
971 cc_tool);
972 cc_tool->set_outputs(SubstitutionList::MakeForTest(
973 "{{source_out_dir}}/{{target_output_name}}.{{source_name_part}}.o"));
974 cc_tool->set_precompiled_header_type(CTool::PCH_GCC);
975 pch_toolchain.SetTool(std::move(cc));
976 pch_toolchain.ToolchainSetupComplete();
977
978 // This target doesn't specify precompiled headers.
979 {
980 Target no_pch_target(&pch_settings,
981 Label(SourceDir("//foo/"), "no_pch_target"));
982 no_pch_target.set_output_type(Target::SOURCE_SET);
983 no_pch_target.visibility().SetPublic();
984 no_pch_target.sources().push_back(SourceFile("//foo/input1.cc"));
985 no_pch_target.sources().push_back(SourceFile("//foo/input2.c"));
986 no_pch_target.source_types_used().Set(SourceFile::SOURCE_CPP);
987 no_pch_target.source_types_used().Set(SourceFile::SOURCE_C);
988 no_pch_target.config_values().cflags_c().push_back("-std=c99");
989 no_pch_target.SetToolchain(&pch_toolchain);
990 ASSERT_TRUE(no_pch_target.OnResolved(&err));
991
992 std::ostringstream out;
993 NinjaCBinaryTargetWriter writer(&no_pch_target, out);
994 writer.Run();
995
996 const char no_pch_expected[] =
997 "defines =\n"
998 "include_dirs =\n"
999 "cflags =\n"
1000 "cflags_c = -std=c99\n"
1001 "cflags_cc =\n"
1002 "target_output_name = no_pch_target\n"
1003 "\n"
1004 "build withpch/obj/foo/no_pch_target.input1.o: "
1005 "withpch_cxx ../../foo/input1.cc\n"
1006 "build withpch/obj/foo/no_pch_target.input2.o: "
1007 "withpch_cc ../../foo/input2.c\n"
1008 "\n"
1009 "build withpch/obj/foo/no_pch_target.stamp: "
1010 "withpch_stamp withpch/obj/foo/no_pch_target.input1.o "
1011 "withpch/obj/foo/no_pch_target.input2.o\n";
1012 EXPECT_EQ(no_pch_expected, out.str());
1013 }
1014
1015 // This target specifies PCH.
1016 {
1017 Target pch_target(&pch_settings, Label(SourceDir("//foo/"), "pch_target"));
1018 pch_target.config_values().set_precompiled_source(
1019 SourceFile("//build/precompile.h"));
1020 pch_target.config_values().cflags_c().push_back("-std=c99");
1021 pch_target.set_output_type(Target::SOURCE_SET);
1022 pch_target.visibility().SetPublic();
1023 pch_target.sources().push_back(SourceFile("//foo/input1.cc"));
1024 pch_target.sources().push_back(SourceFile("//foo/input2.c"));
1025 pch_target.source_types_used().Set(SourceFile::SOURCE_CPP);
1026 pch_target.source_types_used().Set(SourceFile::SOURCE_C);
1027 pch_target.SetToolchain(&pch_toolchain);
1028 ASSERT_TRUE(pch_target.OnResolved(&err));
1029
1030 std::ostringstream out;
1031 NinjaCBinaryTargetWriter writer(&pch_target, out);
1032 writer.Run();
1033
1034 const char pch_gcc_expected[] =
1035 "defines =\n"
1036 "include_dirs =\n"
1037 "cflags =\n"
1038 "cflags_c = -std=c99 "
1039 "-include withpch/obj/build/pch_target.precompile.h-c\n"
1040 "cflags_cc = -include withpch/obj/build/pch_target.precompile.h-cc\n"
1041 "target_output_name = pch_target\n"
1042 "\n"
1043 // Compile the precompiled sources with -x <lang>.
1044 "build withpch/obj/build/pch_target.precompile.h-c.gch: "
1045 "withpch_cc ../../build/precompile.h\n"
1046 " cflags_c = -std=c99 -x c-header\n"
1047 "\n"
1048 "build withpch/obj/build/pch_target.precompile.h-cc.gch: "
1049 "withpch_cxx ../../build/precompile.h\n"
1050 " cflags_cc = -x c++-header\n"
1051 "\n"
1052 "build withpch/obj/foo/pch_target.input1.o: "
1053 "withpch_cxx ../../foo/input1.cc | "
1054 // Explicit dependency on the PCH build step.
1055 "withpch/obj/build/pch_target.precompile.h-cc.gch\n"
1056 "build withpch/obj/foo/pch_target.input2.o: "
1057 "withpch_cc ../../foo/input2.c | "
1058 // Explicit dependency on the PCH build step.
1059 "withpch/obj/build/pch_target.precompile.h-c.gch\n"
1060 "\n"
1061 "build withpch/obj/foo/pch_target.stamp: "
1062 "withpch_stamp withpch/obj/foo/pch_target.input1.o "
1063 "withpch/obj/foo/pch_target.input2.o\n";
1064 EXPECT_EQ(pch_gcc_expected, out.str());
1065 }
1066 }
1067
1068 // Should throw an error with the scheduler if a duplicate object file exists.
1069 // This is dependent on the toolchain's object file mapping.
TEST_F(NinjaCBinaryTargetWriterTest,DupeObjFileError)1070 TEST_F(NinjaCBinaryTargetWriterTest, DupeObjFileError) {
1071 TestWithScope setup;
1072 TestTarget target(setup, "//foo:bar", Target::EXECUTABLE);
1073 target.sources().push_back(SourceFile("//a.cc"));
1074 target.sources().push_back(SourceFile("//a.cc"));
1075 target.source_types_used().Set(SourceFile::SOURCE_CPP);
1076
1077 EXPECT_FALSE(scheduler().is_failed());
1078
1079 scheduler().SuppressOutputForTesting(true);
1080
1081 std::ostringstream out;
1082 NinjaCBinaryTargetWriter writer(&target, out);
1083 writer.Run();
1084
1085 scheduler().SuppressOutputForTesting(false);
1086
1087 // Should have issued an error.
1088 EXPECT_TRUE(scheduler().is_failed());
1089 }
1090
1091 // This tests that output extension and output dir overrides apply, and input
1092 // dependencies are applied.
TEST_F(NinjaCBinaryTargetWriterTest,InputFiles)1093 TEST_F(NinjaCBinaryTargetWriterTest, InputFiles) {
1094 Err err;
1095 TestWithScope setup;
1096
1097 // This target has one input.
1098 {
1099 Target target(setup.settings(), Label(SourceDir("//foo/"), "bar"));
1100 target.set_output_type(Target::SOURCE_SET);
1101 target.visibility().SetPublic();
1102 target.sources().push_back(SourceFile("//foo/input1.cc"));
1103 target.sources().push_back(SourceFile("//foo/input2.cc"));
1104 target.source_types_used().Set(SourceFile::SOURCE_CPP);
1105 target.config_values().inputs().push_back(SourceFile("//foo/input.data"));
1106 target.SetToolchain(setup.toolchain());
1107 ASSERT_TRUE(target.OnResolved(&err));
1108
1109 std::ostringstream out;
1110 NinjaCBinaryTargetWriter writer(&target, out);
1111 writer.Run();
1112
1113 const char expected[] =
1114 "defines =\n"
1115 "include_dirs =\n"
1116 "cflags =\n"
1117 "cflags_cc =\n"
1118 "root_out_dir = .\n"
1119 "target_out_dir = obj/foo\n"
1120 "target_output_name = bar\n"
1121 "\n"
1122 "build obj/foo/bar.input1.o: cxx ../../foo/input1.cc"
1123 " | ../../foo/input.data\n"
1124 "build obj/foo/bar.input2.o: cxx ../../foo/input2.cc"
1125 " | ../../foo/input.data\n"
1126 "\n"
1127 "build obj/foo/bar.stamp: stamp obj/foo/bar.input1.o "
1128 "obj/foo/bar.input2.o\n";
1129
1130 EXPECT_EQ(expected, out.str());
1131 }
1132
1133 // This target has one input but no source files.
1134 {
1135 Target target(setup.settings(), Label(SourceDir("//foo/"), "bar"));
1136 target.set_output_type(Target::SHARED_LIBRARY);
1137 target.visibility().SetPublic();
1138 target.config_values().inputs().push_back(SourceFile("//foo/input.data"));
1139 target.SetToolchain(setup.toolchain());
1140 ASSERT_TRUE(target.OnResolved(&err));
1141
1142 std::ostringstream out;
1143 NinjaCBinaryTargetWriter writer(&target, out);
1144 writer.Run();
1145
1146 const char expected[] =
1147 "defines =\n"
1148 "include_dirs =\n"
1149 "root_out_dir = .\n"
1150 "target_out_dir = obj/foo\n"
1151 "target_output_name = libbar\n"
1152 "\n"
1153 "\n"
1154 "build ./libbar.so: solink | ../../foo/input.data\n"
1155 " ldflags =\n"
1156 " libs =\n"
1157 " frameworks =\n"
1158 " output_extension = .so\n"
1159 " output_dir = \n";
1160
1161 EXPECT_EQ(expected, out.str());
1162 }
1163
1164 // This target has multiple inputs.
1165 {
1166 Target target(setup.settings(), Label(SourceDir("//foo/"), "bar"));
1167 target.set_output_type(Target::SOURCE_SET);
1168 target.visibility().SetPublic();
1169 target.sources().push_back(SourceFile("//foo/input1.cc"));
1170 target.sources().push_back(SourceFile("//foo/input2.cc"));
1171 target.source_types_used().Set(SourceFile::SOURCE_CPP);
1172 target.config_values().inputs().push_back(SourceFile("//foo/input1.data"));
1173 target.config_values().inputs().push_back(SourceFile("//foo/input2.data"));
1174 target.SetToolchain(setup.toolchain());
1175 ASSERT_TRUE(target.OnResolved(&err));
1176
1177 std::ostringstream out;
1178 NinjaCBinaryTargetWriter writer(&target, out);
1179 writer.Run();
1180
1181 const char expected[] =
1182 "defines =\n"
1183 "include_dirs =\n"
1184 "cflags =\n"
1185 "cflags_cc =\n"
1186 "root_out_dir = .\n"
1187 "target_out_dir = obj/foo\n"
1188 "target_output_name = bar\n"
1189 "\n"
1190 "build obj/foo/bar.inputs.stamp: stamp"
1191 " ../../foo/input1.data ../../foo/input2.data\n"
1192 "build obj/foo/bar.input1.o: cxx ../../foo/input1.cc"
1193 " | obj/foo/bar.inputs.stamp\n"
1194 "build obj/foo/bar.input2.o: cxx ../../foo/input2.cc"
1195 " | obj/foo/bar.inputs.stamp\n"
1196 "\n"
1197 "build obj/foo/bar.stamp: stamp obj/foo/bar.input1.o "
1198 "obj/foo/bar.input2.o\n";
1199
1200 EXPECT_EQ(expected, out.str());
1201 }
1202
1203 // This target has one input itself, one from an immediate config, and one
1204 // from a config tacked on to said config.
1205 {
1206 Config far_config(setup.settings(), Label(SourceDir("//foo/"), "qux"));
1207 far_config.own_values().inputs().push_back(SourceFile("//foo/input3.data"));
1208 ASSERT_TRUE(far_config.OnResolved(&err));
1209
1210 Config config(setup.settings(), Label(SourceDir("//foo/"), "baz"));
1211 config.own_values().inputs().push_back(SourceFile("//foo/input2.data"));
1212 config.configs().push_back(LabelConfigPair(&far_config));
1213 ASSERT_TRUE(config.OnResolved(&err));
1214
1215 Target target(setup.settings(), Label(SourceDir("//foo/"), "bar"));
1216 target.set_output_type(Target::SOURCE_SET);
1217 target.visibility().SetPublic();
1218 target.sources().push_back(SourceFile("//foo/input1.cc"));
1219 target.sources().push_back(SourceFile("//foo/input2.cc"));
1220 target.source_types_used().Set(SourceFile::SOURCE_CPP);
1221 target.config_values().inputs().push_back(SourceFile("//foo/input1.data"));
1222 target.configs().push_back(LabelConfigPair(&config));
1223 target.SetToolchain(setup.toolchain());
1224 ASSERT_TRUE(target.OnResolved(&err));
1225
1226 std::ostringstream out;
1227 NinjaCBinaryTargetWriter writer(&target, out);
1228 writer.Run();
1229
1230 const char expected[] =
1231 "defines =\n"
1232 "include_dirs =\n"
1233 "cflags =\n"
1234 "cflags_cc =\n"
1235 "root_out_dir = .\n"
1236 "target_out_dir = obj/foo\n"
1237 "target_output_name = bar\n"
1238 "\n"
1239 "build obj/foo/bar.inputs.stamp: stamp"
1240 " ../../foo/input1.data ../../foo/input2.data ../../foo/input3.data\n"
1241 "build obj/foo/bar.input1.o: cxx ../../foo/input1.cc"
1242 " | obj/foo/bar.inputs.stamp\n"
1243 "build obj/foo/bar.input2.o: cxx ../../foo/input2.cc"
1244 " | obj/foo/bar.inputs.stamp\n"
1245 "\n"
1246 "build obj/foo/bar.stamp: stamp obj/foo/bar.input1.o "
1247 "obj/foo/bar.input2.o\n";
1248
1249 EXPECT_EQ(expected, out.str());
1250 }
1251 }
1252
1253 // Test linking of Rust dependencies into C targets.
TEST_F(NinjaCBinaryTargetWriterTest,RustDeps)1254 TEST_F(NinjaCBinaryTargetWriterTest, RustDeps) {
1255 Err err;
1256 TestWithScope setup;
1257
1258 {
1259 Target library_target(setup.settings(), Label(SourceDir("//foo/"), "foo"));
1260 library_target.set_output_type(Target::STATIC_LIBRARY);
1261 library_target.visibility().SetPublic();
1262 SourceFile lib("//foo/lib.rs");
1263 library_target.sources().push_back(lib);
1264 library_target.source_types_used().Set(SourceFile::SOURCE_RS);
1265 library_target.rust_values().set_crate_root(lib);
1266 library_target.rust_values().crate_name() = "foo";
1267 library_target.SetToolchain(setup.toolchain());
1268 ASSERT_TRUE(library_target.OnResolved(&err));
1269
1270 Target target(setup.settings(), Label(SourceDir("//bar/"), "bar"));
1271 target.set_output_type(Target::EXECUTABLE);
1272 target.visibility().SetPublic();
1273 target.sources().push_back(SourceFile("//bar/bar.cc"));
1274 target.source_types_used().Set(SourceFile::SOURCE_CPP);
1275 target.private_deps().push_back(LabelTargetPair(&library_target));
1276 target.SetToolchain(setup.toolchain());
1277 ASSERT_TRUE(target.OnResolved(&err));
1278
1279 std::ostringstream out;
1280 NinjaCBinaryTargetWriter writer(&target, out);
1281 writer.Run();
1282
1283 const char expected[] =
1284 "defines =\n"
1285 "include_dirs =\n"
1286 "cflags =\n"
1287 "cflags_cc =\n"
1288 "root_out_dir = .\n"
1289 "target_out_dir = obj/bar\n"
1290 "target_output_name = bar\n"
1291 "\n"
1292 "build obj/bar/bar.bar.o: cxx ../../bar/bar.cc\n"
1293 "\n"
1294 "build ./bar: link obj/bar/bar.bar.o obj/foo/libfoo.a\n"
1295 " ldflags =\n"
1296 " libs =\n"
1297 " frameworks =\n"
1298 " output_extension = \n"
1299 " output_dir = \n";
1300
1301 std::string out_str = out.str();
1302 EXPECT_EQ(expected, out_str) << expected << "\n" << out_str;
1303 }
1304
1305 {
1306 Target rlib_target(setup.settings(), Label(SourceDir("//baz/"), "lib"));
1307 rlib_target.set_output_type(Target::RUST_LIBRARY);
1308 rlib_target.visibility().SetPublic();
1309 SourceFile bazlib("//baz/lib.rs");
1310 rlib_target.sources().push_back(bazlib);
1311 rlib_target.source_types_used().Set(SourceFile::SOURCE_RS);
1312 rlib_target.rust_values().set_crate_root(bazlib);
1313 rlib_target.rust_values().crate_name() = "lib";
1314 rlib_target.SetToolchain(setup.toolchain());
1315 ASSERT_TRUE(rlib_target.OnResolved(&err));
1316
1317 Target library_target(setup.settings(), Label(SourceDir("//foo/"), "foo"));
1318 library_target.set_output_type(Target::STATIC_LIBRARY);
1319 library_target.visibility().SetPublic();
1320 SourceFile lib("//foo/lib.rs");
1321 library_target.sources().push_back(lib);
1322 library_target.source_types_used().Set(SourceFile::SOURCE_RS);
1323 library_target.rust_values().set_crate_root(lib);
1324 library_target.rust_values().crate_name() = "foo";
1325 library_target.public_deps().push_back(LabelTargetPair(&rlib_target));
1326 library_target.SetToolchain(setup.toolchain());
1327 ASSERT_TRUE(library_target.OnResolved(&err));
1328
1329 Target target(setup.settings(), Label(SourceDir("//bar/"), "bar"));
1330 target.set_output_type(Target::EXECUTABLE);
1331 target.visibility().SetPublic();
1332 target.sources().push_back(SourceFile("//bar/bar.cc"));
1333 target.source_types_used().Set(SourceFile::SOURCE_CPP);
1334 target.private_deps().push_back(LabelTargetPair(&library_target));
1335 target.SetToolchain(setup.toolchain());
1336 ASSERT_TRUE(target.OnResolved(&err));
1337
1338 std::ostringstream out;
1339 NinjaCBinaryTargetWriter writer(&target, out);
1340 writer.Run();
1341
1342 const char expected[] =
1343 "defines =\n"
1344 "include_dirs =\n"
1345 "cflags =\n"
1346 "cflags_cc =\n"
1347 "root_out_dir = .\n"
1348 "target_out_dir = obj/bar\n"
1349 "target_output_name = bar\n"
1350 "\n"
1351 "build obj/bar/bar.bar.o: cxx ../../bar/bar.cc\n"
1352 "\n"
1353 "build ./bar: link obj/bar/bar.bar.o obj/foo/libfoo.a\n"
1354 " ldflags =\n"
1355 " libs =\n"
1356 " frameworks =\n"
1357 " output_extension = \n"
1358 " output_dir = \n";
1359
1360 std::string out_str = out.str();
1361 EXPECT_EQ(expected, out_str) << expected << "\n" << out_str;
1362 }
1363
1364 {
1365 Target procmacro(setup.settings(), Label(SourceDir("//baz/"), "macro"));
1366 procmacro.set_output_type(Target::LOADABLE_MODULE);
1367 procmacro.visibility().SetPublic();
1368 SourceFile bazlib("//baz/lib.rs");
1369 procmacro.sources().push_back(bazlib);
1370 procmacro.source_types_used().Set(SourceFile::SOURCE_RS);
1371 procmacro.rust_values().set_crate_root(bazlib);
1372 procmacro.rust_values().crate_name() = "macro";
1373 procmacro.rust_values().set_crate_type(RustValues::CRATE_PROC_MACRO);
1374 procmacro.SetToolchain(setup.toolchain());
1375 ASSERT_TRUE(procmacro.OnResolved(&err));
1376
1377 Target library_target(setup.settings(), Label(SourceDir("//foo/"), "foo"));
1378 library_target.set_output_type(Target::STATIC_LIBRARY);
1379 library_target.visibility().SetPublic();
1380 SourceFile lib("//foo/lib.rs");
1381 library_target.sources().push_back(lib);
1382 library_target.source_types_used().Set(SourceFile::SOURCE_RS);
1383 library_target.rust_values().set_crate_root(lib);
1384 library_target.rust_values().crate_name() = "foo";
1385 library_target.public_deps().push_back(LabelTargetPair(&procmacro));
1386 library_target.SetToolchain(setup.toolchain());
1387 ASSERT_TRUE(library_target.OnResolved(&err));
1388
1389 Target target(setup.settings(), Label(SourceDir("//bar/"), "bar"));
1390 target.set_output_type(Target::EXECUTABLE);
1391 target.visibility().SetPublic();
1392 target.sources().push_back(SourceFile("//bar/bar.cc"));
1393 target.source_types_used().Set(SourceFile::SOURCE_CPP);
1394 target.private_deps().push_back(LabelTargetPair(&library_target));
1395 target.SetToolchain(setup.toolchain());
1396 ASSERT_TRUE(target.OnResolved(&err));
1397
1398 std::ostringstream out;
1399 NinjaCBinaryTargetWriter writer(&target, out);
1400 writer.Run();
1401
1402 const char expected[] =
1403 "defines =\n"
1404 "include_dirs =\n"
1405 "cflags =\n"
1406 "cflags_cc =\n"
1407 "root_out_dir = .\n"
1408 "target_out_dir = obj/bar\n"
1409 "target_output_name = bar\n"
1410 "\n"
1411 "build obj/bar/bar.bar.o: cxx ../../bar/bar.cc\n"
1412 "\n"
1413 "build ./bar: link obj/bar/bar.bar.o obj/foo/libfoo.a\n"
1414 " ldflags =\n"
1415 " libs =\n"
1416 " frameworks =\n"
1417 " output_extension = \n"
1418 " output_dir = \n";
1419
1420 std::string out_str = out.str();
1421 EXPECT_EQ(expected, out_str) << expected << "\n" << out_str;
1422 }
1423 }
1424