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