• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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