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 "gn/runtime_deps.h"
6
7 #include <map>
8 #include <set>
9 #include <sstream>
10
11 #include "base/command_line.h"
12 #include "base/files/file_util.h"
13 #include "base/strings/string_split.h"
14 #include "gn/build_settings.h"
15 #include "gn/builder.h"
16 #include "gn/deps_iterator.h"
17 #include "gn/filesystem_utils.h"
18 #include "gn/loader.h"
19 #include "gn/output_file.h"
20 #include "gn/scheduler.h"
21 #include "gn/settings.h"
22 #include "gn/switches.h"
23 #include "gn/target.h"
24 #include "gn/trace.h"
25
26 namespace {
27
28 using RuntimeDepsVector = std::vector<std::pair<OutputFile, const Target*>>;
29
30 // Adds the given file to the deps list if it hasn't already been listed in
31 // the found_files list. Updates the list.
AddIfNew(const OutputFile & output_file,const Target * source,RuntimeDepsVector * deps,std::set<OutputFile> * found_file)32 void AddIfNew(const OutputFile& output_file,
33 const Target* source,
34 RuntimeDepsVector* deps,
35 std::set<OutputFile>* found_file) {
36 if (found_file->find(output_file) != found_file->end())
37 return; // Already there.
38 deps->push_back(std::make_pair(output_file, source));
39 }
40
41 // Automatically converts a string that looks like a source to an OutputFile.
AddIfNew(const std::string & str,const Target * source,RuntimeDepsVector * deps,std::set<OutputFile> * found_file)42 void AddIfNew(const std::string& str,
43 const Target* source,
44 RuntimeDepsVector* deps,
45 std::set<OutputFile>* found_file) {
46 OutputFile output_file(
47 RebasePath(str, source->settings()->build_settings()->build_dir(),
48 source->settings()->build_settings()->root_path_utf8()));
49 AddIfNew(output_file, source, deps, found_file);
50 }
51
52 // To avoid duplicate traversals of targets, or duplicating output files that
53 // might be listed by more than one target, the set of targets and output files
54 // that have been found so far is passed. The "value" of the seen_targets map
55 // is a boolean indicating if the seen dep was a data dep (true = data_dep).
56 // data deps add more stuff, so we will want to revisit a target if it's a
57 // data dependency and we've previously only seen it as a regular dep.
RecursiveCollectRuntimeDeps(const Target * target,bool is_target_data_dep,RuntimeDepsVector * deps,std::map<const Target *,bool> * seen_targets,std::set<OutputFile> * found_files)58 void RecursiveCollectRuntimeDeps(const Target* target,
59 bool is_target_data_dep,
60 RuntimeDepsVector* deps,
61 std::map<const Target*, bool>* seen_targets,
62 std::set<OutputFile>* found_files) {
63 const auto& found_seen_target = seen_targets->find(target);
64 if (found_seen_target != seen_targets->end()) {
65 // Already visited.
66 if (found_seen_target->second || !is_target_data_dep) {
67 // Already visited as a data dep, or the current dep is not a data
68 // dep so visiting again will be a no-op.
69 return;
70 }
71 // In the else case, the previously seen target was a regular dependency
72 // and we'll now process it as a data dependency.
73 }
74 (*seen_targets)[target] = is_target_data_dep;
75
76 // Add the main output file for executables, shared libraries, and
77 // loadable modules.
78 if (target->output_type() == Target::EXECUTABLE ||
79 target->output_type() == Target::LOADABLE_MODULE ||
80 target->output_type() == Target::SHARED_LIBRARY) {
81 for (const auto& runtime_output : target->runtime_outputs())
82 AddIfNew(runtime_output, target, deps, found_files);
83 }
84
85 // Add all data files.
86 for (const auto& file : target->data())
87 AddIfNew(file, target, deps, found_files);
88
89 // Actions/copy have all outputs considered when the're a data dep.
90 if (is_target_data_dep && (target->output_type() == Target::ACTION ||
91 target->output_type() == Target::ACTION_FOREACH ||
92 target->output_type() == Target::COPY_FILES)) {
93 std::vector<SourceFile> outputs;
94 target->action_values().GetOutputsAsSourceFiles(target, &outputs);
95 for (const auto& output_file : outputs)
96 AddIfNew(output_file.value(), target, deps, found_files);
97 }
98
99 // Data dependencies.
100 for (const auto& dep_pair : target->data_deps()) {
101 RecursiveCollectRuntimeDeps(dep_pair.ptr, true, deps, seen_targets,
102 found_files);
103 }
104
105 // Do not recurse into bundle targets. A bundle's dependencies should be
106 // copied into the bundle itself for run-time access.
107 if (target->output_type() == Target::CREATE_BUNDLE) {
108 SourceDir bundle_root_dir =
109 target->bundle_data().GetBundleRootDirOutputAsDir(target->settings());
110 AddIfNew(bundle_root_dir.value(), target, deps, found_files);
111 return;
112 }
113
114 // Non-data dependencies (both public and private).
115 for (const auto& dep_pair : target->GetDeps(Target::DEPS_LINKED)) {
116 if (dep_pair.ptr->output_type() == Target::EXECUTABLE)
117 continue; // Skip executables that aren't data deps.
118 if (dep_pair.ptr->output_type() == Target::SHARED_LIBRARY &&
119 (target->output_type() == Target::ACTION ||
120 target->output_type() == Target::ACTION_FOREACH)) {
121 // Skip shared libraries that action depends on,
122 // unless it were listed in data deps.
123 continue;
124 }
125 RecursiveCollectRuntimeDeps(dep_pair.ptr, false, deps, seen_targets,
126 found_files);
127 }
128 }
129
CollectRuntimeDepsFromFlag(const BuildSettings * build_settings,const Builder & builder,RuntimeDepsVector * files_to_write,Err * err)130 bool CollectRuntimeDepsFromFlag(const BuildSettings* build_settings,
131 const Builder& builder,
132 RuntimeDepsVector* files_to_write,
133 Err* err) {
134 std::string deps_target_list_file =
135 base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
136 switches::kRuntimeDepsListFile);
137
138 if (deps_target_list_file.empty())
139 return true;
140
141 std::string list_contents;
142 ScopedTrace load_trace(TraceItem::TRACE_FILE_LOAD, deps_target_list_file);
143 if (!base::ReadFileToString(UTF8ToFilePath(deps_target_list_file),
144 &list_contents)) {
145 *err = Err(Location(),
146 std::string("File for --") + switches::kRuntimeDepsListFile +
147 " doesn't exist.",
148 "The file given was \"" + deps_target_list_file + "\"");
149 return false;
150 }
151 load_trace.Done();
152
153 SourceDir root_dir("//");
154 Label default_toolchain_label = builder.loader()->GetDefaultToolchain();
155 for (const auto& line : base::SplitString(
156 list_contents, "\n", base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL)) {
157 if (line.empty())
158 continue;
159 Label label =
160 Label::Resolve(root_dir, build_settings->root_path_utf8(),
161 default_toolchain_label, Value(nullptr, line), err);
162 if (err->has_error())
163 return false;
164
165 const Item* item = builder.GetItem(label);
166 const Target* target = item ? item->AsTarget() : nullptr;
167 if (!target) {
168 *err =
169 Err(Location(),
170 "The label \"" + label.GetUserVisibleName(true) +
171 "\" isn't a target.",
172 "When reading the line:\n " + line +
173 "\n"
174 "from the --" +
175 switches::kRuntimeDepsListFile + "=" + deps_target_list_file);
176 return false;
177 }
178
179 OutputFile output_file;
180 const char extension[] = ".runtime_deps";
181 if (target->output_type() == Target::SHARED_LIBRARY ||
182 target->output_type() == Target::LOADABLE_MODULE) {
183 // Force the first output for shared-library-type linker outputs since
184 // the dependency output files might not be the main output.
185 CHECK(!target->computed_outputs().empty());
186 output_file =
187 OutputFile(target->computed_outputs()[0].value() + extension);
188 } else {
189 output_file =
190 OutputFile(target->dependency_output_file().value() + extension);
191 }
192 files_to_write->push_back(std::make_pair(output_file, target));
193 }
194 return true;
195 }
196
WriteRuntimeDepsFile(const OutputFile & output_file,const Target * target,Err * err)197 bool WriteRuntimeDepsFile(const OutputFile& output_file,
198 const Target* target,
199 Err* err) {
200 SourceFile output_as_source =
201 output_file.AsSourceFile(target->settings()->build_settings());
202 base::FilePath data_deps_file =
203 target->settings()->build_settings()->GetFullPath(output_as_source);
204
205 std::stringstream contents;
206 for (const auto& pair : ComputeRuntimeDeps(target))
207 contents << pair.first.value() << std::endl;
208
209 ScopedTrace trace(TraceItem::TRACE_FILE_WRITE, output_as_source.value());
210 return WriteFileIfChanged(data_deps_file, contents.str(), err);
211 }
212
213 } // namespace
214
215 const char kRuntimeDeps_Help[] =
216 R"(Runtime dependencies
217
218 Runtime dependencies of a target are exposed via the "runtime_deps" category
219 of "gn desc" (see "gn help desc") or they can be written at build generation
220 time via write_runtime_deps(), or --runtime-deps-list-file (see "gn help
221 --runtime-deps-list-file").
222
223 To a first approximation, the runtime dependencies of a target are the set of
224 "data" files, data directories, and the shared libraries from all transitive
225 dependencies. Executables, shared libraries, and loadable modules are
226 considered runtime dependencies of themselves.
227
228 Executables
229
230 Executable targets and those executable targets' transitive dependencies are
231 not considered unless that executable is listed in "data_deps". Otherwise, GN
232 assumes that the executable (and everything it requires) is a build-time
233 dependency only.
234
235 Actions and copies
236
237 Action and copy targets that are listed as "data_deps" will have all of their
238 outputs and data files considered as runtime dependencies. Action and copy
239 targets that are "deps" or "public_deps" will have only their data files
240 considered as runtime dependencies. These targets can list an output file in
241 both the "outputs" and "data" lists to force an output file as a runtime
242 dependency in all cases.
243
244 The different rules for deps and data_deps are to express build-time (deps)
245 vs. run-time (data_deps) outputs. If GN counted all build-time copy steps as
246 data dependencies, there would be a lot of extra stuff, and if GN counted all
247 run-time dependencies as regular deps, the build's parallelism would be
248 unnecessarily constrained.
249
250 This rule can sometimes lead to unintuitive results. For example, given the
251 three targets:
252 A --[data_deps]--> B --[deps]--> ACTION
253 GN would say that A does not have runtime deps on the result of the ACTION,
254 which is often correct. But the purpose of the B target might be to collect
255 many actions into one logic unit, and the "data"-ness of A's dependency is
256 lost. Solutions:
257
258 - List the outputs of the action in its data section (if the results of
259 that action are always runtime files).
260 - Have B list the action in data_deps (if the outputs of the actions are
261 always runtime files).
262 - Have B list the action in both deps and data deps (if the outputs might be
263 used in both contexts and you don't care about unnecessary entries in the
264 list of files required at runtime).
265 - Split B into run-time and build-time versions with the appropriate "deps"
266 for each.
267
268 Static libraries and source sets
269
270 The results of static_library or source_set targets are not considered
271 runtime dependencies since these are assumed to be intermediate targets only.
272 If you need to list a static library as a runtime dependency, you can
273 manually compute the .a/.lib file name for the current platform and list it
274 in the "data" list of a target (possibly on the static library target
275 itself).
276
277 Multiple outputs
278
279 Linker tools can specify which of their outputs should be considered when
280 computing the runtime deps by setting runtime_outputs. If this is unset on
281 the tool, the default will be the first output only.
282 )";
283
ComputeRuntimeDeps(const Target * target)284 RuntimeDepsVector ComputeRuntimeDeps(const Target* target) {
285 RuntimeDepsVector result;
286 std::map<const Target*, bool> seen_targets;
287 std::set<OutputFile> found_files;
288
289 // The initial target is not considered a data dependency so that actions's
290 // outputs (if the current target is an action) are not automatically
291 // considered data deps.
292 RecursiveCollectRuntimeDeps(target, false, &result, &seen_targets,
293 &found_files);
294 return result;
295 }
296
WriteRuntimeDepsFilesIfNecessary(const BuildSettings * build_settings,const Builder & builder,Err * err)297 bool WriteRuntimeDepsFilesIfNecessary(const BuildSettings* build_settings,
298 const Builder& builder,
299 Err* err) {
300 RuntimeDepsVector files_to_write;
301 if (!CollectRuntimeDepsFromFlag(build_settings, builder, &files_to_write,
302 err))
303 return false;
304
305 // Files scheduled by write_runtime_deps.
306 for (const Target* target : g_scheduler->GetWriteRuntimeDepsTargets()) {
307 files_to_write.push_back(
308 std::make_pair(target->write_runtime_deps_output(), target));
309 }
310
311 for (const auto& entry : files_to_write) {
312 // Currently this writes all runtime deps files sequentially. We generally
313 // expect few of these. We can run this on the worker pool if it looks
314 // like it's talking a long time.
315 if (!WriteRuntimeDepsFile(entry.first, entry.second, err))
316 return false;
317 }
318 return true;
319 }
320