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"
10 #include "base/command_line.h"
11 #include "base/files/file_util.h"
12 #include "base/strings/string_split.h"
13 #include "tools/gn/build_settings.h"
14 #include "tools/gn/builder.h"
15 #include "tools/gn/deps_iterator.h"
16 #include "tools/gn/filesystem_utils.h"
17 #include "tools/gn/loader.h"
18 #include "tools/gn/output_file.h"
19 #include "tools/gn/settings.h"
20 #include "tools/gn/switches.h"
21 #include "tools/gn/target.h"
22 #include "tools/gn/trace.h"
26 using RuntimeDepsVector
= std::vector
<std::pair
<OutputFile
, const Target
*>>;
28 // Adds the given file to the deps list if it hasn't already been listed in
29 // the found_files list. Updates the list.
30 void AddIfNew(const OutputFile
& output_file
,
32 RuntimeDepsVector
* deps
,
33 std::set
<OutputFile
>* found_file
) {
34 if (found_file
->find(output_file
) != found_file
->end())
35 return; // Already there.
36 deps
->push_back(std::make_pair(output_file
, source
));
39 // Automatically converts a string that looks like a source to an OutputFile.
40 void AddIfNew(const std::string
& str
,
42 RuntimeDepsVector
* deps
,
43 std::set
<OutputFile
>* found_file
) {
44 OutputFile
output_file(RebasePath(
46 source
->settings()->build_settings()->build_dir(),
47 source
->settings()->build_settings()->root_path_utf8()));
48 AddIfNew(output_file
, source
, deps
, found_file
);
51 // Returns the output file that the runtime deps considers for the given
52 // targets. This is weird only for shared libraries.
53 const OutputFile
& GetMainOutput(const Target
* target
) {
54 if (target
->output_type() == Target::SHARED_LIBRARY
)
55 return target
->link_output_file();
56 return target
->dependency_output_file();
59 // To avoid duplicate traversals of targets, or duplicating output files that
60 // might be listed by more than one target, the set of targets and output files
61 // that have been found so far is passed.
62 void RecursiveCollectRuntimeDeps(const Target
* target
,
63 bool is_target_data_dep
,
64 RuntimeDepsVector
* deps
,
65 std::set
<const Target
*>* seen_targets
,
66 std::set
<OutputFile
>* found_files
) {
67 if (seen_targets
->find(target
) != seen_targets
->end())
68 return; // Already checked.
69 seen_targets
->insert(target
);
71 // Add the main output file for executables and shared libraries.
72 if (target
->output_type() == Target::EXECUTABLE
||
73 target
->output_type() == Target::SHARED_LIBRARY
)
74 AddIfNew(GetMainOutput(target
), target
, deps
, found_files
);
76 // Add all data files.
77 for (const auto& file
: target
->data())
78 AddIfNew(file
, target
, deps
, found_files
);
80 // Actions/copy have all outputs considered when the're a data dep.
81 if (is_target_data_dep
&&
82 (target
->output_type() == Target::ACTION
||
83 target
->output_type() == Target::ACTION_FOREACH
||
84 target
->output_type() == Target::COPY_FILES
)) {
85 std::vector
<SourceFile
> outputs
;
86 target
->action_values().GetOutputsAsSourceFiles(target
, &outputs
);
87 for (const auto& output_file
: outputs
)
88 AddIfNew(output_file
.value(), target
, deps
, found_files
);
91 // Non-data dependencies (both public and private).
92 for (const auto& dep_pair
: target
->GetDeps(Target::DEPS_LINKED
)) {
93 if (dep_pair
.ptr
->output_type() == Target::EXECUTABLE
)
94 continue; // Skip executables that aren't data deps.
95 RecursiveCollectRuntimeDeps(dep_pair
.ptr
, false,
96 deps
, seen_targets
, found_files
);
100 for (const auto& dep_pair
: target
->data_deps()) {
101 RecursiveCollectRuntimeDeps(dep_pair
.ptr
, true,
102 deps
, seen_targets
, found_files
);
106 bool WriteRuntimeDepsFile(const Target
* target
) {
107 SourceFile target_output_as_source
=
108 GetMainOutput(target
).AsSourceFile(target
->settings()->build_settings());
109 std::string data_deps_file_as_str
= target_output_as_source
.value();
110 data_deps_file_as_str
.append(".runtime_deps");
111 base::FilePath data_deps_file
=
112 target
->settings()->build_settings()->GetFullPath(
113 SourceFile(SourceFile::SwapIn(), &data_deps_file_as_str
));
115 std::stringstream contents
;
116 for (const auto& pair
: ComputeRuntimeDeps(target
))
117 contents
<< pair
.first
.value() << std::endl
;
119 ScopedTrace
trace(TraceItem::TRACE_FILE_WRITE
, data_deps_file_as_str
);
120 base::CreateDirectory(data_deps_file
.DirName());
122 std::string contents_str
= contents
.str();
123 return base::WriteFile(data_deps_file
, contents_str
.c_str(),
124 static_cast<int>(contents_str
.size())) > -1;
129 const char kRuntimeDeps_Help
[] =
130 "Runtime dependencies\n"
132 " Runtime dependencies of a target are exposed via the \"runtime_deps\"\n"
133 " category of \"gn desc\" (see \"gn help desc\") or they can be written\n"
134 " at build generation time via \"--runtime-deps-list-file\"\n"
135 " (see \"gn help --runtime-deps-list-file\").\n"
137 " To a first approximation, the runtime dependencies of a target are\n"
138 " the set of \"data\" files, data directories, and the shared libraries\n"
139 " from all transitive dependencies. Executables and shared libraries are\n"
140 " considered runtime dependencies of themselves.\n"
144 " Executable targets and those executable targets' transitive\n"
145 " dependencies are not considered unless that executable is listed in\n"
146 " \"data_deps\". Otherwise, GN assumes that the executable (and\n"
147 " everything it requires) is a build-time dependency only.\n"
149 " Action and copy targets that are listed as \"data_deps\" will have all\n"
150 " of their outputs and data files considered as runtime dependencies.\n"
151 " Action and copy targets that are \"deps\" or \"public_deps\" will have\n"
152 " only their data files considered as runtime dependencies. These\n"
153 " targets can list an output file in both the \"outputs\" and \"data\"\n"
154 " lists to force an output file as a runtime dependency in all cases.\n"
156 " The results of static_library or source_set targets are not considered\n"
157 " runtime dependencies since these are assumed to be intermediate\n"
158 " targets only. If you need to list a static library as a runtime\n"
159 " dependency, you can manually compute the .a/.lib file name for the\n"
160 " current platform and list it in the \"data\" list of a target\n"
161 " (possibly on the static library target itself).\n"
163 " When a tool produces more than one output, only the first output\n"
164 " is considered. For example, a shared library target may produce a\n"
165 " .dll and a .lib file on Windows. Only the .dll file will be considered\n"
166 " a runtime dependency.\n";
168 RuntimeDepsVector
ComputeRuntimeDeps(const Target
* target
) {
169 RuntimeDepsVector result
;
170 std::set
<const Target
*> seen_targets
;
171 std::set
<OutputFile
> found_files
;
173 // The initial target is not considered a data dependency so that actions's
174 // outputs (if the current target is an action) are not automatically
175 // considered data deps.
176 RecursiveCollectRuntimeDeps(target
, false,
177 &result
, &seen_targets
, &found_files
);
181 bool WriteRuntimeDepsFilesIfNecessary(const Builder
& builder
, Err
* err
) {
182 std::string deps_target_list_file
=
183 base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
184 switches::kRuntimeDepsListFile
);
185 if (deps_target_list_file
.empty())
186 return true; // Nothing to do.
188 std::string list_contents
;
189 ScopedTrace
load_trace(TraceItem::TRACE_FILE_LOAD
, deps_target_list_file
);
190 if (!base::ReadFileToString(UTF8ToFilePath(deps_target_list_file
),
192 *err
= Err(Location(),
193 std::string("File for --") + switches::kRuntimeDepsListFile
+
195 "The file given was \"" + deps_target_list_file
+ "\"");
200 std::vector
<std::string
> lines
;
201 base::SplitString(list_contents
, '\n', &lines
);
203 SourceDir
root_dir("//");
204 Label default_toolchain_label
= builder
.loader()->GetDefaultToolchain();
205 for (const auto& line
: lines
) {
208 Label label
= Label::Resolve(root_dir
, default_toolchain_label
,
209 Value(nullptr, line
), err
);
210 if (err
->has_error())
213 const Item
* item
= builder
.GetItem(label
);
214 const Target
* target
= item
? item
->AsTarget() : nullptr;
216 *err
= Err(Location(), "The label \"" + label
.GetUserVisibleName(true) +
217 "\" isn't a target.",
218 "When reading the line:\n " + line
+ "\n"
219 "from the --" + switches::kRuntimeDepsListFile
+ "=" +
220 deps_target_list_file
);
224 // Currently this writes all runtime deps files sequentially. We generally
225 // expect few of these. We can run this on the worker pool if it looks
226 // like it's talking a long time.
227 WriteRuntimeDepsFile(target
);