1 // Copyright 2015 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 <stddef.h>
6 
7 #include "base/stl_util.h"
8 #include "gn/runtime_deps.h"
9 #include "gn/scheduler.h"
10 #include "gn/target.h"
11 #include "gn/test_with_scheduler.h"
12 #include "gn/test_with_scope.h"
13 #include "util/test/test.h"
14 
15 namespace {
16 
InitTargetWithType(TestWithScope & setup,Target * target,Target::OutputType type)17 void InitTargetWithType(TestWithScope& setup,
18                         Target* target,
19                         Target::OutputType type) {
20   target->set_output_type(type);
21   target->visibility().SetPublic();
22   target->SetToolchain(setup.toolchain());
23 }
24 
25 // Convenience function to make the correct kind of pair.
MakePair(const char * str,const Target * t)26 std::pair<OutputFile, const Target*> MakePair(const char* str,
27                                               const Target* t) {
28   return std::pair<OutputFile, const Target*>(OutputFile(str), t);
29 }
30 
GetVectorDescription(const std::vector<std::pair<OutputFile,const Target * >> & v)31 std::string GetVectorDescription(
32     const std::vector<std::pair<OutputFile, const Target*>>& v) {
33   std::string result;
34   for (size_t i = 0; i < v.size(); i++) {
35     if (i != 0)
36       result.append(", ");
37     result.append("\"" + v[i].first.value() + "\"");
38   }
39   return result;
40 }
41 
42 }  // namespace
43 
44 using RuntimeDeps = TestWithScheduler;
45 
46 // Tests an exe depending on different types of libraries.
TEST_F(RuntimeDeps,Libs)47 TEST_F(RuntimeDeps, Libs) {
48   TestWithScope setup;
49   Err err;
50 
51   // Dependency hierarchy: main(exe) -> static library
52   //                                 -> shared library
53   //                                 -> loadable module
54   //                                 -> source set
55 
56   Target stat(setup.settings(), Label(SourceDir("//"), "stat"));
57   InitTargetWithType(setup, &stat, Target::STATIC_LIBRARY);
58   stat.data().push_back("//stat.dat");
59   ASSERT_TRUE(stat.OnResolved(&err));
60 
61   Target shared(setup.settings(), Label(SourceDir("//"), "shared"));
62   InitTargetWithType(setup, &shared, Target::SHARED_LIBRARY);
63   shared.data().push_back("//shared.dat");
64   ASSERT_TRUE(shared.OnResolved(&err));
65 
66   Target loadable(setup.settings(), Label(SourceDir("//"), "loadable"));
67   InitTargetWithType(setup, &loadable, Target::LOADABLE_MODULE);
68   loadable.data().push_back("//loadable.dat");
69   ASSERT_TRUE(loadable.OnResolved(&err));
70 
71   Target set(setup.settings(), Label(SourceDir("//"), "set"));
72   InitTargetWithType(setup, &set, Target::SOURCE_SET);
73   set.data().push_back("//set.dat");
74   ASSERT_TRUE(set.OnResolved(&err));
75 
76   Target main(setup.settings(), Label(SourceDir("//"), "main"));
77   InitTargetWithType(setup, &main, Target::EXECUTABLE);
78   main.private_deps().push_back(LabelTargetPair(&stat));
79   main.private_deps().push_back(LabelTargetPair(&shared));
80   main.private_deps().push_back(LabelTargetPair(&loadable));
81   main.private_deps().push_back(LabelTargetPair(&set));
82   main.data().push_back("//main.dat");
83   ASSERT_TRUE(main.OnResolved(&err));
84 
85   std::vector<std::pair<OutputFile, const Target*>> result =
86       ComputeRuntimeDeps(&main);
87 
88   // The result should have deps of main, all 5 dat files, libshared.so, and
89   // libloadable.so.
90   ASSERT_EQ(8u, result.size()) << GetVectorDescription(result);
91 
92   // The first one should always be the main exe.
93   EXPECT_TRUE(MakePair("./main", &main) == result[0]);
94 
95   // The rest of the ordering is undefined. First the data files.
96   EXPECT_TRUE(base::ContainsValue(result, MakePair("../../stat.dat", &stat)))
97       << GetVectorDescription(result);
98   EXPECT_TRUE(
99       base::ContainsValue(result, MakePair("../../shared.dat", &shared)))
100       << GetVectorDescription(result);
101   EXPECT_TRUE(
102       base::ContainsValue(result, MakePair("../../loadable.dat", &loadable)))
103       << GetVectorDescription(result);
104   EXPECT_TRUE(base::ContainsValue(result, MakePair("../../set.dat", &set)))
105       << GetVectorDescription(result);
106   EXPECT_TRUE(base::ContainsValue(result, MakePair("../../main.dat", &main)))
107       << GetVectorDescription(result);
108 
109   // Check the static library and loadable module.
110   EXPECT_TRUE(base::ContainsValue(result, MakePair("./libshared.so", &shared)))
111       << GetVectorDescription(result);
112   EXPECT_TRUE(
113       base::ContainsValue(result, MakePair("./libloadable.so", &loadable)))
114       << GetVectorDescription(result);
115 }
116 
117 // Tests that executables that aren't listed as data deps aren't included in
118 // the output, but executables that are data deps are included.
TEST_F(RuntimeDeps,ExeDataDep)119 TEST_F(RuntimeDeps, ExeDataDep) {
120   TestWithScope setup;
121   Err err;
122 
123   // Dependency hierarchy: main(exe) -> datadep(exe) -> final_in(source set)
124   //                                 -> dep(exe) -> final_out(source set)
125   // The final_in/out targets each have data files. final_in's should be
126   // included, final_out's should not be.
127 
128   Target final_in(setup.settings(), Label(SourceDir("//"), "final_in"));
129   InitTargetWithType(setup, &final_in, Target::SOURCE_SET);
130   final_in.data().push_back("//final_in.dat");
131   ASSERT_TRUE(final_in.OnResolved(&err));
132 
133   Target datadep(setup.settings(), Label(SourceDir("//"), "datadep"));
134   InitTargetWithType(setup, &datadep, Target::EXECUTABLE);
135   datadep.private_deps().push_back(LabelTargetPair(&final_in));
136   ASSERT_TRUE(datadep.OnResolved(&err));
137 
138   Target final_out(setup.settings(), Label(SourceDir("//"), "final_out"));
139   InitTargetWithType(setup, &final_out, Target::SOURCE_SET);
140   final_out.data().push_back("//final_out.dat");
141   ASSERT_TRUE(final_out.OnResolved(&err));
142 
143   Target dep(setup.settings(), Label(SourceDir("//"), "dep"));
144   InitTargetWithType(setup, &dep, Target::EXECUTABLE);
145   dep.private_deps().push_back(LabelTargetPair(&final_out));
146   ASSERT_TRUE(dep.OnResolved(&err));
147 
148   Target main(setup.settings(), Label(SourceDir("//"), "main"));
149   InitTargetWithType(setup, &main, Target::EXECUTABLE);
150   main.private_deps().push_back(LabelTargetPair(&dep));
151   main.data_deps().push_back(LabelTargetPair(&datadep));
152   ASSERT_TRUE(main.OnResolved(&err));
153 
154   std::vector<std::pair<OutputFile, const Target*>> result =
155       ComputeRuntimeDeps(&main);
156 
157   // The result should have deps of main, datadep, final_in.dat
158   ASSERT_EQ(3u, result.size()) << GetVectorDescription(result);
159 
160   // The first one should always be the main exe.
161   EXPECT_TRUE(MakePair("./main", &main) == result[0]);
162 
163   // The rest of the ordering is undefined.
164   EXPECT_TRUE(base::ContainsValue(result, MakePair("./datadep", &datadep)))
165       << GetVectorDescription(result);
166   EXPECT_TRUE(
167       base::ContainsValue(result, MakePair("../../final_in.dat", &final_in)))
168       << GetVectorDescription(result);
169 }
170 
TEST_F(RuntimeDeps,ActionSharedLib)171 TEST_F(RuntimeDeps, ActionSharedLib) {
172   TestWithScope setup;
173   Err err;
174 
175   // Dependency hierarchy: main(exe) -> action -> datadep(shared library)
176   //                                           -> dep(shared library)
177   // Datadep should be included, dep should not be.
178 
179   Target dep(setup.settings(), Label(SourceDir("//"), "dep"));
180   InitTargetWithType(setup, &dep, Target::SHARED_LIBRARY);
181   ASSERT_TRUE(dep.OnResolved(&err));
182 
183   Target datadep(setup.settings(), Label(SourceDir("//"), "datadep"));
184   InitTargetWithType(setup, &datadep, Target::SHARED_LIBRARY);
185   ASSERT_TRUE(datadep.OnResolved(&err));
186 
187   Target action(setup.settings(), Label(SourceDir("//"), "action"));
188   InitTargetWithType(setup, &action, Target::ACTION);
189   action.private_deps().push_back(LabelTargetPair(&dep));
190   action.data_deps().push_back(LabelTargetPair(&datadep));
191   action.action_values().outputs() =
192       SubstitutionList::MakeForTest("//action.output");
193   ASSERT_TRUE(action.OnResolved(&err));
194 
195   Target main(setup.settings(), Label(SourceDir("//"), "main"));
196   InitTargetWithType(setup, &main, Target::EXECUTABLE);
197   main.private_deps().push_back(LabelTargetPair(&action));
198   ASSERT_TRUE(main.OnResolved(&err));
199 
200   std::vector<std::pair<OutputFile, const Target*>> result =
201       ComputeRuntimeDeps(&main);
202 
203   // The result should have deps of main and data_dep.
204   ASSERT_EQ(2u, result.size()) << GetVectorDescription(result);
205 
206   // The first one should always be the main exe.
207   EXPECT_TRUE(MakePair("./main", &main) == result[0]);
208   EXPECT_TRUE(MakePair("./libdatadep.so", &datadep) == result[1]);
209 }
210 
211 // Tests that action and copy outputs are considered if they're data deps, but
212 // not if they're regular deps. Action and copy "data" files are always
213 // included.
TEST_F(RuntimeDeps,ActionOutputs)214 TEST_F(RuntimeDeps, ActionOutputs) {
215   TestWithScope setup;
216   Err err;
217 
218   // Dependency hierarchy: main(exe) -> datadep (action)
219   //                                 -> datadep_copy (copy)
220   //                                 -> dep (action)
221   //                                 -> dep_copy (copy)
222 
223   Target datadep(setup.settings(), Label(SourceDir("//"), "datadep"));
224   InitTargetWithType(setup, &datadep, Target::ACTION);
225   datadep.data().push_back("//datadep.data");
226   datadep.action_values().outputs() =
227       SubstitutionList::MakeForTest("//datadep.output");
228   ASSERT_TRUE(datadep.OnResolved(&err));
229 
230   Target datadep_copy(setup.settings(), Label(SourceDir("//"), "datadep_copy"));
231   InitTargetWithType(setup, &datadep_copy, Target::COPY_FILES);
232   datadep_copy.sources().push_back(SourceFile("//input"));
233   datadep_copy.data().push_back("//datadep_copy.data");
234   datadep_copy.action_values().outputs() =
235       SubstitutionList::MakeForTest("//datadep_copy.output");
236   ASSERT_TRUE(datadep_copy.OnResolved(&err));
237 
238   Target dep(setup.settings(), Label(SourceDir("//"), "dep"));
239   InitTargetWithType(setup, &dep, Target::ACTION);
240   dep.data().push_back("//dep.data");
241   dep.action_values().outputs() = SubstitutionList::MakeForTest("//dep.output");
242   ASSERT_TRUE(dep.OnResolved(&err));
243 
244   Target dep_copy(setup.settings(), Label(SourceDir("//"), "dep_copy"));
245   InitTargetWithType(setup, &dep_copy, Target::COPY_FILES);
246   dep_copy.sources().push_back(SourceFile("//input"));
247   dep_copy.data().push_back("//dep_copy/data/");  // Tests a directory.
248   dep_copy.action_values().outputs() =
249       SubstitutionList::MakeForTest("//dep_copy.output");
250   ASSERT_TRUE(dep_copy.OnResolved(&err));
251 
252   Target main(setup.settings(), Label(SourceDir("//"), "main"));
253   InitTargetWithType(setup, &main, Target::EXECUTABLE);
254   main.private_deps().push_back(LabelTargetPair(&dep));
255   main.private_deps().push_back(LabelTargetPair(&dep_copy));
256   main.data_deps().push_back(LabelTargetPair(&datadep));
257   main.data_deps().push_back(LabelTargetPair(&datadep_copy));
258   ASSERT_TRUE(main.OnResolved(&err));
259 
260   std::vector<std::pair<OutputFile, const Target*>> result =
261       ComputeRuntimeDeps(&main);
262 
263   // The result should have deps of main, both datadeps files, but only
264   // the data file from dep.
265   ASSERT_EQ(7u, result.size()) << GetVectorDescription(result);
266 
267   // The first one should always be the main exe.
268   EXPECT_TRUE(MakePair("./main", &main) == result[0]);
269 
270   // The rest of the ordering is undefined.
271   EXPECT_TRUE(
272       base::ContainsValue(result, MakePair("../../datadep.data", &datadep)))
273       << GetVectorDescription(result);
274   EXPECT_TRUE(base::ContainsValue(
275       result, MakePair("../../datadep_copy.data", &datadep_copy)))
276       << GetVectorDescription(result);
277   EXPECT_TRUE(
278       base::ContainsValue(result, MakePair("../../datadep.output", &datadep)))
279       << GetVectorDescription(result);
280   EXPECT_TRUE(base::ContainsValue(
281       result, MakePair("../../datadep_copy.output", &datadep_copy)))
282       << GetVectorDescription(result);
283   EXPECT_TRUE(base::ContainsValue(result, MakePair("../../dep.data", &dep)))
284       << GetVectorDescription(result);
285   EXPECT_TRUE(
286       base::ContainsValue(result, MakePair("../../dep_copy/data/", &dep_copy)))
287       << GetVectorDescription(result);
288 
289   // Explicitly asking for the runtime deps of an action target only includes
290   // the data and not all outputs.
291   result = ComputeRuntimeDeps(&dep);
292   ASSERT_EQ(1u, result.size());
293   EXPECT_TRUE(MakePair("../../dep.data", &dep) == result[0]);
294 }
295 
296 // Tests that the search for dependencies terminates at a bundle target,
297 // ignoring any shared libraries or loadable modules that get copied into the
298 // bundle.
TEST_F(RuntimeDeps,CreateBundle)299 TEST_F(RuntimeDeps, CreateBundle) {
300   TestWithScope setup;
301   Err err;
302 
303   // Dependency hierarchy:
304   // main(exe) -> dep(bundle) -> dep(shared_library) -> dep(source set)
305   //                          -> dep(bundle_data) -> dep(loadable_module)
306   //                                                      -> data(lm.data)
307   //                          -> datadep(datadep) -> data(dd.data)
308 
309   const SourceDir source_dir("//");
310   const std::string& build_dir = setup.build_settings()->build_dir().value();
311 
312   Target loadable_module(setup.settings(),
313                          Label(source_dir, "loadable_module"));
314   InitTargetWithType(setup, &loadable_module, Target::LOADABLE_MODULE);
315   loadable_module.data().push_back("//lm.data");
316   ASSERT_TRUE(loadable_module.OnResolved(&err));
317 
318   Target module_data(setup.settings(), Label(source_dir, "module_data"));
319   InitTargetWithType(setup, &module_data, Target::BUNDLE_DATA);
320   module_data.private_deps().push_back(LabelTargetPair(&loadable_module));
321   module_data.bundle_data().file_rules().push_back(BundleFileRule(
322       nullptr,
323       std::vector<SourceFile>{SourceFile(build_dir + "loadable_module.so")},
324       SubstitutionPattern::MakeForTest("{{bundle_resources_dir}}")));
325   ASSERT_TRUE(module_data.OnResolved(&err));
326 
327   Target source_set(setup.settings(), Label(source_dir, "sources"));
328   InitTargetWithType(setup, &source_set, Target::SOURCE_SET);
329   source_set.sources().push_back(SourceFile(source_dir.value() + "foo.cc"));
330   ASSERT_TRUE(source_set.OnResolved(&err));
331 
332   Target dylib(setup.settings(), Label(source_dir, "dylib"));
333   dylib.set_output_prefix_override(true);
334   dylib.set_output_extension("");
335   dylib.set_output_name("Bundle");
336   InitTargetWithType(setup, &dylib, Target::SHARED_LIBRARY);
337   dylib.private_deps().push_back(LabelTargetPair(&source_set));
338   ASSERT_TRUE(dylib.OnResolved(&err));
339 
340   Target dylib_data(setup.settings(), Label(source_dir, "dylib_data"));
341   InitTargetWithType(setup, &dylib_data, Target::BUNDLE_DATA);
342   dylib_data.private_deps().push_back(LabelTargetPair(&dylib));
343   dylib_data.bundle_data().file_rules().push_back(BundleFileRule(
344       nullptr, std::vector<SourceFile>{SourceFile(build_dir + "dylib")},
345       SubstitutionPattern::MakeForTest("{{bundle_executable_dir}}")));
346   ASSERT_TRUE(dylib_data.OnResolved(&err));
347 
348   Target data_dep(setup.settings(), Label(source_dir, "datadep"));
349   InitTargetWithType(setup, &data_dep, Target::EXECUTABLE);
350   data_dep.data().push_back("//dd.data");
351   ASSERT_TRUE(data_dep.OnResolved(&err));
352 
353   Target bundle(setup.settings(), Label(source_dir, "bundle"));
354   InitTargetWithType(setup, &bundle, Target::CREATE_BUNDLE);
355   const std::string root_dir(build_dir + "Bundle.framework/");
356   const std::string contents_dir(root_dir + "Versions/A/");
357   bundle.bundle_data().root_dir() = SourceDir(root_dir);
358   bundle.bundle_data().contents_dir() = SourceDir(contents_dir);
359   bundle.bundle_data().resources_dir() = SourceDir(contents_dir + "Resources");
360   bundle.bundle_data().executable_dir() = SourceDir(contents_dir + "MacOS");
361   bundle.private_deps().push_back(LabelTargetPair(&dylib_data));
362   bundle.private_deps().push_back(LabelTargetPair(&module_data));
363   bundle.data_deps().push_back(LabelTargetPair(&data_dep));
364   bundle.data().push_back("//b.data");
365   ASSERT_TRUE(bundle.OnResolved(&err));
366 
367   Target main(setup.settings(), Label(source_dir, "main"));
368   InitTargetWithType(setup, &main, Target::EXECUTABLE);
369   main.data_deps().push_back(LabelTargetPair(&bundle));
370   ASSERT_TRUE(main.OnResolved(&err));
371 
372   std::vector<std::pair<OutputFile, const Target*>> result =
373       ComputeRuntimeDeps(&main);
374 
375   // The result should have deps of main, datadep, final_in.dat
376   ASSERT_EQ(5u, result.size()) << GetVectorDescription(result);
377 
378   // The first one should always be the main exe.
379   EXPECT_EQ(MakePair("./main", &main), result[0]);
380 
381   // The rest of the ordering is undefined.
382 
383   // The framework bundle's internal dependencies should not be included.
384   EXPECT_TRUE(
385       base::ContainsValue(result, MakePair("Bundle.framework/", &bundle)))
386       << GetVectorDescription(result);
387   // But direct data and data dependencies should be.
388   EXPECT_TRUE(base::ContainsValue(result, MakePair("./datadep", &data_dep)))
389       << GetVectorDescription(result);
390   EXPECT_TRUE(base::ContainsValue(result, MakePair("../../dd.data", &data_dep)))
391       << GetVectorDescription(result);
392   EXPECT_TRUE(base::ContainsValue(result, MakePair("../../b.data", &bundle)))
393       << GetVectorDescription(result);
394 }
395 
396 // Tests that a dependency duplicated in regular and data deps is processed
397 // as a data dep.
TEST_F(RuntimeDeps,Dupe)398 TEST_F(RuntimeDeps, Dupe) {
399   TestWithScope setup;
400   Err err;
401 
402   Target action(setup.settings(), Label(SourceDir("//"), "action"));
403   InitTargetWithType(setup, &action, Target::ACTION);
404   action.action_values().outputs() =
405       SubstitutionList::MakeForTest("//action.output");
406   ASSERT_TRUE(action.OnResolved(&err));
407 
408   Target target(setup.settings(), Label(SourceDir("//"), "foo"));
409   InitTargetWithType(setup, &target, Target::EXECUTABLE);
410   target.private_deps().push_back(LabelTargetPair(&action));
411   target.data_deps().push_back(LabelTargetPair(&action));
412   ASSERT_TRUE(target.OnResolved(&err));
413 
414   // The results should be the executable and the copy output.
415   std::vector<std::pair<OutputFile, const Target*>> result =
416       ComputeRuntimeDeps(&target);
417   EXPECT_TRUE(
418       base::ContainsValue(result, MakePair("../../action.output", &action)))
419       << GetVectorDescription(result);
420 }
421 
422 // Tests that actions can't have output substitutions.
TEST_F(RuntimeDeps,WriteRuntimeDepsVariable)423 TEST_F(RuntimeDeps, WriteRuntimeDepsVariable) {
424   TestWithScope setup;
425   Err err;
426 
427   // Should refuse to write files outside of the output dir.
428   EXPECT_FALSE(setup.ExecuteSnippet(
429       "group(\"foo\") { write_runtime_deps = \"//foo.txt\" }", &err));
430 
431   // Should fail for garbage inputs.
432   err = Err();
433   EXPECT_FALSE(
434       setup.ExecuteSnippet("group(\"foo\") { write_runtime_deps = 0 }", &err));
435 
436   // Should be able to write inside the out dir, and shouldn't write the one
437   // in the else clause.
438   err = Err();
439   EXPECT_TRUE(setup.ExecuteSnippet(
440       "if (true) {\n"
441       "  group(\"foo\") { write_runtime_deps = \"//out/Debug/foo.txt\" }\n"
442       "} else {\n"
443       "  group(\"bar\") { write_runtime_deps = \"//out/Debug/bar.txt\" }\n"
444       "}",
445       &err));
446   EXPECT_EQ(1U, setup.items().size());
447   EXPECT_EQ(1U, scheduler().GetWriteRuntimeDepsTargets().size());
448 }
449