[refactor] More post-NSS WebCrypto cleanups (utility functions).
[chromium-blink-merge.git] / tools / gn / runtime_deps.cc
blobf6441e8cd113b4ddd9ac96ec12e40904a782ba36
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.
5 #include "tools/gn/runtime_deps.h"
7 #include <map>
8 #include <set>
9 #include <sstream>
11 #include "base/command_line.h"
12 #include "base/files/file_util.h"
13 #include "base/strings/string_split.h"
14 #include "tools/gn/build_settings.h"
15 #include "tools/gn/builder.h"
16 #include "tools/gn/deps_iterator.h"
17 #include "tools/gn/filesystem_utils.h"
18 #include "tools/gn/loader.h"
19 #include "tools/gn/output_file.h"
20 #include "tools/gn/settings.h"
21 #include "tools/gn/switches.h"
22 #include "tools/gn/target.h"
23 #include "tools/gn/trace.h"
25 namespace {
27 using RuntimeDepsVector = std::vector<std::pair<OutputFile, const Target*>>;
29 // Adds the given file to the deps list if it hasn't already been listed in
30 // the found_files list. Updates the list.
31 void AddIfNew(const OutputFile& output_file,
32 const Target* source,
33 RuntimeDepsVector* deps,
34 std::set<OutputFile>* found_file) {
35 if (found_file->find(output_file) != found_file->end())
36 return; // Already there.
37 deps->push_back(std::make_pair(output_file, source));
40 // Automatically converts a string that looks like a source to an OutputFile.
41 void AddIfNew(const std::string& str,
42 const Target* source,
43 RuntimeDepsVector* deps,
44 std::set<OutputFile>* found_file) {
45 OutputFile output_file(RebasePath(
46 str,
47 source->settings()->build_settings()->build_dir(),
48 source->settings()->build_settings()->root_path_utf8()));
49 AddIfNew(output_file, source, deps, found_file);
52 // Returns the output file that the runtime deps considers for the given
53 // targets. This is weird only for shared libraries.
54 const OutputFile& GetMainOutput(const Target* target) {
55 if (target->output_type() == Target::SHARED_LIBRARY)
56 return target->link_output_file();
57 return target->dependency_output_file();
60 // To avoid duplicate traversals of targets, or duplicating output files that
61 // might be listed by more than one target, the set of targets and output files
62 // that have been found so far is passed. The "value" of the seen_targets map
63 // is a boolean indicating if the seen dep was a data dep (true = data_dep).
64 // data deps add more stuff, so we will want to revisit a target if it's a
65 // data dependency and we've previously only seen it as a regular dep.
66 void RecursiveCollectRuntimeDeps(const Target* target,
67 bool is_target_data_dep,
68 RuntimeDepsVector* deps,
69 std::map<const Target*, bool>* seen_targets,
70 std::set<OutputFile>* found_files) {
71 const auto& found_seen_target = seen_targets->find(target);
72 if (found_seen_target != seen_targets->end()) {
73 // Already visited.
74 if (found_seen_target->second || !is_target_data_dep) {
75 // Already visited as a data dep, or the current dep is not a data
76 // dep so visiting again will be a no-op.
77 return;
79 // In the else case, the previously seen target was a regular dependency
80 // and we'll now process it as a data dependency.
82 (*seen_targets)[target] = is_target_data_dep;
84 // Add the main output file for executables and shared libraries.
85 if (target->output_type() == Target::EXECUTABLE ||
86 target->output_type() == Target::SHARED_LIBRARY)
87 AddIfNew(GetMainOutput(target), target, deps, found_files);
89 // Add all data files.
90 for (const auto& file : target->data())
91 AddIfNew(file, target, deps, found_files);
93 // Actions/copy have all outputs considered when the're a data dep.
94 if (is_target_data_dep &&
95 (target->output_type() == Target::ACTION ||
96 target->output_type() == Target::ACTION_FOREACH ||
97 target->output_type() == Target::COPY_FILES)) {
98 std::vector<SourceFile> outputs;
99 target->action_values().GetOutputsAsSourceFiles(target, &outputs);
100 for (const auto& output_file : outputs)
101 AddIfNew(output_file.value(), target, deps, found_files);
104 // Non-data dependencies (both public and private).
105 for (const auto& dep_pair : target->GetDeps(Target::DEPS_LINKED)) {
106 if (dep_pair.ptr->output_type() == Target::EXECUTABLE)
107 continue; // Skip executables that aren't data deps.
108 RecursiveCollectRuntimeDeps(dep_pair.ptr, false,
109 deps, seen_targets, found_files);
112 // Data dependencies.
113 for (const auto& dep_pair : target->data_deps()) {
114 RecursiveCollectRuntimeDeps(dep_pair.ptr, true,
115 deps, seen_targets, found_files);
119 bool WriteRuntimeDepsFile(const Target* target) {
120 SourceFile target_output_as_source =
121 GetMainOutput(target).AsSourceFile(target->settings()->build_settings());
122 std::string data_deps_file_as_str = target_output_as_source.value();
123 data_deps_file_as_str.append(".runtime_deps");
124 base::FilePath data_deps_file =
125 target->settings()->build_settings()->GetFullPath(
126 SourceFile(SourceFile::SwapIn(), &data_deps_file_as_str));
128 std::stringstream contents;
129 for (const auto& pair : ComputeRuntimeDeps(target))
130 contents << pair.first.value() << std::endl;
132 ScopedTrace trace(TraceItem::TRACE_FILE_WRITE, data_deps_file_as_str);
133 base::CreateDirectory(data_deps_file.DirName());
135 std::string contents_str = contents.str();
136 return base::WriteFile(data_deps_file, contents_str.c_str(),
137 static_cast<int>(contents_str.size())) > -1;
140 } // namespace
142 const char kRuntimeDeps_Help[] =
143 "Runtime dependencies\n"
144 "\n"
145 " Runtime dependencies of a target are exposed via the \"runtime_deps\"\n"
146 " category of \"gn desc\" (see \"gn help desc\") or they can be written\n"
147 " at build generation time via \"--runtime-deps-list-file\"\n"
148 " (see \"gn help --runtime-deps-list-file\").\n"
149 "\n"
150 " To a first approximation, the runtime dependencies of a target are\n"
151 " the set of \"data\" files, data directories, and the shared libraries\n"
152 " from all transitive dependencies. Executables and shared libraries are\n"
153 " considered runtime dependencies of themselves.\n"
154 "\n"
155 "Executables\n"
156 "\n"
157 " Executable targets and those executable targets' transitive\n"
158 " dependencies are not considered unless that executable is listed in\n"
159 " \"data_deps\". Otherwise, GN assumes that the executable (and\n"
160 " everything it requires) is a build-time dependency only.\n"
161 "\n"
162 "Actions and copies\n"
163 "\n"
164 " Action and copy targets that are listed as \"data_deps\" will have all\n"
165 " of their outputs and data files considered as runtime dependencies.\n"
166 " Action and copy targets that are \"deps\" or \"public_deps\" will have\n"
167 " only their data files considered as runtime dependencies. These\n"
168 " targets can list an output file in both the \"outputs\" and \"data\"\n"
169 " lists to force an output file as a runtime dependency in all cases.\n"
170 "\n"
171 " The different rules for deps and data_deps are to express build-time\n"
172 " (deps) vs. run-time (data_deps) outputs. If GN counted all build-time\n"
173 " copy steps as data dependencies, there would be a lot of extra stuff,\n"
174 " and if GN counted all run-time dependencies as regular deps, the\n"
175 " build's parallelism would be unnecessarily constrained.\n"
176 "\n"
177 " This rule can sometimes lead to unintuitive results. For example,\n"
178 " given the three targets:\n"
179 " A --[data_deps]--> B --[deps]--> ACTION\n"
180 " GN would say that A does not have runtime deps on the result of the\n"
181 " ACTION, which is often correct. But the purpose of the B target might\n"
182 " be to collect many actions into one logic unit, and the \"data\"-ness\n"
183 " of A's dependency is lost. Solutions:\n"
184 "\n"
185 " - List the outputs of the action in it's data section (if the\n"
186 " results of that action are always runtime files).\n"
187 " - Have B list the action in data_deps (if the outputs of the actions\n"
188 " are always runtime files).\n"
189 " - Have B list the action in both deps and data deps (if the outputs\n"
190 " might be used in both contexts and you don't care about unnecessary\n"
191 " entries in the list of files required at runtime).\n"
192 " - Split B into run-time and build-time versions with the appropriate\n"
193 " \"deps\" for each.\n"
194 "\n"
195 "Static libraries and source sets\n"
196 "\n"
197 " The results of static_library or source_set targets are not considered\n"
198 " runtime dependencies since these are assumed to be intermediate\n"
199 " targets only. If you need to list a static library as a runtime\n"
200 " dependency, you can manually compute the .a/.lib file name for the\n"
201 " current platform and list it in the \"data\" list of a target\n"
202 " (possibly on the static library target itself).\n"
203 "\n"
204 "Multiple outputs\n"
205 "\n"
206 " When a tool produces more than one output, only the first output\n"
207 " is considered. For example, a shared library target may produce a\n"
208 " .dll and a .lib file on Windows. Only the .dll file will be considered\n"
209 " a runtime dependency. This applies only to linker tools, scripts and\n"
210 " copy steps with multiple outputs will also get all outputs listed.\n";
212 RuntimeDepsVector ComputeRuntimeDeps(const Target* target) {
213 RuntimeDepsVector result;
214 std::map<const Target*, bool> seen_targets;
215 std::set<OutputFile> found_files;
217 // The initial target is not considered a data dependency so that actions's
218 // outputs (if the current target is an action) are not automatically
219 // considered data deps.
220 RecursiveCollectRuntimeDeps(target, false,
221 &result, &seen_targets, &found_files);
222 return result;
225 bool WriteRuntimeDepsFilesIfNecessary(const Builder& builder, Err* err) {
226 std::string deps_target_list_file =
227 base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
228 switches::kRuntimeDepsListFile);
229 if (deps_target_list_file.empty())
230 return true; // Nothing to do.
232 std::string list_contents;
233 ScopedTrace load_trace(TraceItem::TRACE_FILE_LOAD, deps_target_list_file);
234 if (!base::ReadFileToString(UTF8ToFilePath(deps_target_list_file),
235 &list_contents)) {
236 *err = Err(Location(),
237 std::string("File for --") + switches::kRuntimeDepsListFile +
238 " doesn't exist.",
239 "The file given was \"" + deps_target_list_file + "\"");
240 return false;
242 load_trace.Done();
244 SourceDir root_dir("//");
245 Label default_toolchain_label = builder.loader()->GetDefaultToolchain();
246 for (const auto& line : base::SplitString(
247 list_contents, "\n", base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL)) {
248 if (line.empty())
249 continue;
250 Label label = Label::Resolve(root_dir, default_toolchain_label,
251 Value(nullptr, line), err);
252 if (err->has_error())
253 return false;
255 const Item* item = builder.GetItem(label);
256 const Target* target = item ? item->AsTarget() : nullptr;
257 if (!target) {
258 *err = Err(Location(), "The label \"" + label.GetUserVisibleName(true) +
259 "\" isn't a target.",
260 "When reading the line:\n " + line + "\n"
261 "from the --" + switches::kRuntimeDepsListFile + "=" +
262 deps_target_list_file);
263 return false;
266 // Currently this writes all runtime deps files sequentially. We generally
267 // expect few of these. We can run this on the worker pool if it looks
268 // like it's talking a long time.
269 WriteRuntimeDepsFile(target);
271 return true;