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