Update parsing of dumpsys batterystats
[chromium-blink-merge.git] / tools / gn / ninja_binary_target_writer.cc
blobc5a7ad16b5a7581e18d4cbc43baf7a47784c4936
1 // Copyright (c) 2013 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/ninja_binary_target_writer.h"
7 #include <set>
9 #include "base/strings/string_util.h"
10 #include "tools/gn/config_values_extractors.h"
11 #include "tools/gn/err.h"
12 #include "tools/gn/escape.h"
13 #include "tools/gn/string_utils.h"
15 namespace {
17 // Returns the proper escape options for writing compiler and linker flags.
18 EscapeOptions GetFlagOptions() {
19 EscapeOptions opts;
20 opts.mode = ESCAPE_NINJA_COMMAND;
22 // Some flag strings are actually multiple flags that expect to be just
23 // added to the command line. We assume that quoting is done by the
24 // buildfiles if it wants such things quoted.
25 opts.inhibit_quoting = true;
27 return opts;
30 struct DefineWriter {
31 DefineWriter() {
32 options.mode = ESCAPE_NINJA_COMMAND;
35 void operator()(const std::string& s, std::ostream& out) const {
36 out << " -D";
37 EscapeStringToStream(out, s, options);
40 EscapeOptions options;
43 struct IncludeWriter {
44 IncludeWriter(PathOutput& path_output, const NinjaHelper& h)
45 : helper(h),
46 path_output_(path_output) {
48 ~IncludeWriter() {
51 void operator()(const SourceDir& d, std::ostream& out) const {
52 out << " -I";
53 path_output_.WriteDir(out, d, PathOutput::DIR_NO_LAST_SLASH);
56 const NinjaHelper& helper;
57 PathOutput& path_output_;
60 Toolchain::ToolType GetToolTypeForTarget(const Target* target) {
61 switch (target->output_type()) {
62 case Target::STATIC_LIBRARY:
63 return Toolchain::TYPE_ALINK;
64 case Target::SHARED_LIBRARY:
65 return Toolchain::TYPE_SOLINK;
66 case Target::EXECUTABLE:
67 return Toolchain::TYPE_LINK;
68 default:
69 return Toolchain::TYPE_NONE;
73 } // namespace
75 NinjaBinaryTargetWriter::NinjaBinaryTargetWriter(const Target* target,
76 const Toolchain* toolchain,
77 std::ostream& out)
78 : NinjaTargetWriter(target, toolchain, out),
79 tool_type_(GetToolTypeForTarget(target)){
82 NinjaBinaryTargetWriter::~NinjaBinaryTargetWriter() {
85 void NinjaBinaryTargetWriter::Run() {
86 WriteCompilerVars();
88 std::vector<OutputFile> obj_files;
89 WriteSources(&obj_files);
91 if (target_->output_type() == Target::SOURCE_SET)
92 WriteSourceSetStamp(obj_files);
93 else
94 WriteLinkerStuff(obj_files);
97 void NinjaBinaryTargetWriter::WriteCompilerVars() {
98 // Defines.
99 out_ << "defines =";
100 RecursiveTargetConfigToStream<std::string>(target_, &ConfigValues::defines,
101 DefineWriter(), out_);
102 out_ << std::endl;
104 // Include directories.
105 out_ << "includes =";
106 RecursiveTargetConfigToStream<SourceDir>(target_, &ConfigValues::include_dirs,
107 IncludeWriter(path_output_, helper_),
108 out_);
110 out_ << std::endl;
112 // C flags and friends.
113 EscapeOptions flag_escape_options = GetFlagOptions();
114 #define WRITE_FLAGS(name) \
115 out_ << #name " ="; \
116 RecursiveTargetConfigStringsToStream(target_, &ConfigValues::name, \
117 flag_escape_options, out_); \
118 out_ << std::endl;
120 WRITE_FLAGS(cflags)
121 WRITE_FLAGS(cflags_c)
122 WRITE_FLAGS(cflags_cc)
123 WRITE_FLAGS(cflags_objc)
124 WRITE_FLAGS(cflags_objcc)
126 #undef WRITE_FLAGS
128 // Write some variables about the target for the toolchain definition to use.
129 out_ << "target_name = " << target_->label().name() << std::endl;
130 out_ << "target_out_dir = ";
131 path_output_.WriteDir(out_, helper_.GetTargetOutputDir(target_),
132 PathOutput::DIR_NO_LAST_SLASH);
133 out_ << std::endl;
134 out_ << "root_out_dir = ";
135 path_output_.WriteDir(out_, target_->settings()->toolchain_output_subdir(),
136 PathOutput::DIR_NO_LAST_SLASH);
137 out_ << std::endl << std::endl;
140 void NinjaBinaryTargetWriter::WriteSources(
141 std::vector<OutputFile>* object_files) {
142 const Target::FileList& sources = target_->sources();
143 object_files->reserve(sources.size());
145 std::string implicit_deps =
146 WriteInputDepsStampAndGetDep(std::vector<const Target*>());
148 for (size_t i = 0; i < sources.size(); i++) {
149 const SourceFile& input_file = sources[i];
151 SourceFileType input_file_type = GetSourceFileType(input_file);
152 if (input_file_type == SOURCE_UNKNOWN)
153 continue; // Skip unknown file types.
154 if (input_file_type == SOURCE_O) {
155 // Object files just get passed to the output and not compiled.
156 object_files->push_back(helper_.GetOutputFileForSource(
157 target_, input_file, input_file_type));
158 continue;
160 std::string command =
161 helper_.GetRuleForSourceType(settings_, input_file_type);
162 if (command.empty())
163 continue; // Skip files not needing compilation.
165 OutputFile output_file = helper_.GetOutputFileForSource(
166 target_, input_file, input_file_type);
167 object_files->push_back(output_file);
169 out_ << "build ";
170 path_output_.WriteFile(out_, output_file);
171 out_ << ": " << command << " ";
172 path_output_.WriteFile(out_, input_file);
173 out_ << implicit_deps << std::endl;
175 out_ << std::endl;
178 void NinjaBinaryTargetWriter::WriteLinkerStuff(
179 const std::vector<OutputFile>& object_files) {
180 // Manifest file on Windows.
181 // TODO(brettw) this seems not to be necessary for static libs, skip in
182 // that case?
183 OutputFile windows_manifest;
184 if (settings_->IsWin()) {
185 windows_manifest = helper_.GetTargetOutputDir(target_);
186 windows_manifest.value().append(target_->label().name());
187 windows_manifest.value().append(".intermediate.manifest");
188 out_ << "manifests = ";
189 path_output_.WriteFile(out_, windows_manifest);
190 out_ << std::endl;
193 const Toolchain::Tool& tool = toolchain_->GetTool(tool_type_);
194 WriteLinkerFlags(tool, windows_manifest);
195 WriteLibs(tool);
197 // The external output file is the one that other libs depend on.
198 OutputFile external_output_file = helper_.GetTargetOutputFile(target_);
200 // The internal output file is the "main thing" we think we're making. In
201 // the case of shared libraries, this is the shared library and the external
202 // output file is the import library. In other cases, the internal one and
203 // the external one are the same.
204 OutputFile internal_output_file;
205 if (target_->output_type() == Target::SHARED_LIBRARY) {
206 if (settings_->IsWin()) {
207 internal_output_file.value() =
208 target_->settings()->toolchain_output_subdir().value();
209 internal_output_file.value().append(target_->label().name());
210 internal_output_file.value().append(".dll");
211 } else {
212 internal_output_file = external_output_file;
214 } else {
215 internal_output_file = external_output_file;
218 // In Python see "self.ninja.build(output, command, input,"
219 WriteLinkCommand(external_output_file, internal_output_file, object_files);
221 if (target_->output_type() == Target::SHARED_LIBRARY) {
222 // The shared object name doesn't include a path.
223 out_ << " soname = ";
224 out_ << FindFilename(&internal_output_file.value());
225 out_ << std::endl;
227 out_ << " lib = ";
228 path_output_.WriteFile(out_, internal_output_file);
229 out_ << std::endl;
231 if (settings_->IsWin()) {
232 out_ << " dll = ";
233 path_output_.WriteFile(out_, internal_output_file);
234 out_ << std::endl;
237 if (settings_->IsWin()) {
238 out_ << " implibflag = /IMPLIB:";
239 path_output_.WriteFile(out_, external_output_file);
240 out_ << std::endl;
243 // TODO(brettw) postbuild steps.
244 if (settings_->IsMac())
245 out_ << " postbuilds = $ && (export BUILT_PRODUCTS_DIR=/Users/brettw/prj/src/out/gn; export CONFIGURATION=Debug; export DYLIB_INSTALL_NAME_BASE=@rpath; export EXECUTABLE_NAME=libbase.dylib; export EXECUTABLE_PATH=libbase.dylib; export FULL_PRODUCT_NAME=libbase.dylib; export LD_DYLIB_INSTALL_NAME=@rpath/libbase.dylib; export MACH_O_TYPE=mh_dylib; export PRODUCT_NAME=base; export PRODUCT_TYPE=com.apple.product-type.library.dynamic; export SDKROOT=/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.7.sdk; export SRCROOT=/Users/brettw/prj/src/out/gn/../../base; export SOURCE_ROOT=\"$${SRCROOT}\"; export TARGET_BUILD_DIR=/Users/brettw/prj/src/out/gn; export TEMP_DIR=\"$${TMPDIR}\"; (cd ../../base && ../build/mac/strip_from_xcode); G=$$?; ((exit $$G) || rm -rf libbase.dylib) && exit $$G)";
248 out_ << std::endl;
251 void NinjaBinaryTargetWriter::WriteLinkerFlags(
252 const Toolchain::Tool& tool,
253 const OutputFile& windows_manifest) {
254 out_ << "ldflags =";
256 // First the ldflags from the target and its config.
257 EscapeOptions flag_options = GetFlagOptions();
258 RecursiveTargetConfigStringsToStream(target_, &ConfigValues::ldflags,
259 flag_options, out_);
261 // Followed by library search paths that have been recursively pushed
262 // through the dependency tree.
263 const OrderedSet<SourceDir> all_lib_dirs = target_->all_lib_dirs();
264 if (!all_lib_dirs.empty()) {
265 // Since we're passing these on the command line to the linker and not
266 // to Ninja, we need to do shell escaping.
267 PathOutput lib_path_output(path_output_.current_dir(),
268 ESCAPE_NINJA_COMMAND);
269 for (size_t i = 0; i < all_lib_dirs.size(); i++) {
270 out_ << " " << tool.lib_dir_prefix;
271 lib_path_output.WriteDir(out_, all_lib_dirs[i],
272 PathOutput::DIR_NO_LAST_SLASH);
276 // Append manifest flag on Windows to reference our file.
277 // HACK ERASEME BRETTW FIXME
278 if (settings_->IsWin()) {
279 out_ << " /MANIFEST /ManifestFile:";
280 path_output_.WriteFile(out_, windows_manifest);
282 out_ << std::endl;
285 void NinjaBinaryTargetWriter::WriteLibs(const Toolchain::Tool& tool) {
286 out_ << "libs =";
288 // Libraries that have been recursively pushed through the dependency tree.
289 EscapeOptions lib_escape_opts;
290 lib_escape_opts.mode = ESCAPE_NINJA_COMMAND;
291 const OrderedSet<std::string> all_libs = target_->all_libs();
292 const std::string framework_ending(".framework");
293 for (size_t i = 0; i < all_libs.size(); i++) {
294 if (settings_->IsMac() && EndsWith(all_libs[i], framework_ending, false)) {
295 // Special-case libraries ending in ".framework" on Mac. Add the
296 // -framework switch and don't add the extension to the output.
297 out_ << " -framework ";
298 EscapeStringToStream(out_,
299 all_libs[i].substr(0, all_libs[i].size() - framework_ending.size()),
300 lib_escape_opts);
301 } else {
302 out_ << " " << tool.lib_prefix;
303 EscapeStringToStream(out_, all_libs[i], lib_escape_opts);
306 out_ << std::endl;
309 void NinjaBinaryTargetWriter::WriteLinkCommand(
310 const OutputFile& external_output_file,
311 const OutputFile& internal_output_file,
312 const std::vector<OutputFile>& object_files) {
313 out_ << "build ";
314 path_output_.WriteFile(out_, internal_output_file);
315 if (external_output_file != internal_output_file) {
316 out_ << " ";
317 path_output_.WriteFile(out_, external_output_file);
319 out_ << ": "
320 << helper_.GetRulePrefix(target_->settings())
321 << Toolchain::ToolTypeToName(tool_type_);
323 std::set<OutputFile> extra_object_files;
324 std::vector<const Target*> linkable_deps;
325 std::vector<const Target*> non_linkable_deps;
326 GetDeps(&extra_object_files, &linkable_deps, &non_linkable_deps);
328 // Object files.
329 for (size_t i = 0; i < object_files.size(); i++) {
330 out_ << " ";
331 path_output_.WriteFile(out_, object_files[i]);
333 for (std::set<OutputFile>::iterator i = extra_object_files.begin();
334 i != extra_object_files.end(); ++i) {
335 out_ << " ";
336 path_output_.WriteFile(out_, *i);
339 // Libs.
340 for (size_t i = 0; i < linkable_deps.size(); i++) {
341 out_ << " ";
342 path_output_.WriteFile(out_, helper_.GetTargetOutputFile(linkable_deps[i]));
345 // Append data dependencies as implicit dependencies.
346 WriteImplicitDependencies(non_linkable_deps);
348 out_ << std::endl;
351 void NinjaBinaryTargetWriter::WriteSourceSetStamp(
352 const std::vector<OutputFile>& object_files) {
353 // The stamp rule for source sets is generally not used, since targets that
354 // depend on this will reference the object files directly. However, writing
355 // this rule allows the user to type the name of the target and get a build
356 // which can be convenient for development.
357 out_ << "build ";
358 path_output_.WriteFile(out_, helper_.GetTargetOutputFile(target_));
359 out_ << ": "
360 << helper_.GetRulePrefix(target_->settings())
361 << "stamp";
363 std::set<OutputFile> extra_object_files;
364 std::vector<const Target*> linkable_deps;
365 std::vector<const Target*> non_linkable_deps;
366 GetDeps(&extra_object_files, &linkable_deps, &non_linkable_deps);
368 // The classifier should never put extra object files in a source set:
369 // any source sets that we depend on should appear in our non-linkable
370 // deps instead.
371 DCHECK(extra_object_files.empty());
373 for (size_t i = 0; i < object_files.size(); i++) {
374 out_ << " ";
375 path_output_.WriteFile(out_, object_files[i]);
378 // Append data dependencies as implicit dependencies.
379 WriteImplicitDependencies(non_linkable_deps);
381 out_ << std::endl;
384 void NinjaBinaryTargetWriter::GetDeps(
385 std::set<OutputFile>* extra_object_files,
386 std::vector<const Target*>* linkable_deps,
387 std::vector<const Target*>* non_linkable_deps) const {
388 const LabelTargetVector& deps = target_->deps();
389 const std::set<const Target*>& inherited = target_->inherited_libraries();
391 // Normal deps.
392 for (size_t i = 0; i < deps.size(); i++) {
393 if (inherited.find(deps[i].ptr) != inherited.end())
394 continue; // Don't add dupes.
395 ClassifyDependency(deps[i].ptr, extra_object_files,
396 linkable_deps, non_linkable_deps);
399 // Inherited libraries.
400 for (std::set<const Target*>::const_iterator i = inherited.begin();
401 i != inherited.end(); ++i) {
402 ClassifyDependency(*i, extra_object_files,
403 linkable_deps, non_linkable_deps);
406 // Data deps.
407 const LabelTargetVector& datadeps = target_->datadeps();
408 for (size_t i = 0; i < datadeps.size(); i++)
409 non_linkable_deps->push_back(datadeps[i].ptr);
412 void NinjaBinaryTargetWriter::ClassifyDependency(
413 const Target* dep,
414 std::set<OutputFile>* extra_object_files,
415 std::vector<const Target*>* linkable_deps,
416 std::vector<const Target*>* non_linkable_deps) const {
417 // Only these types of outputs have libraries linked into them. Child deps of
418 // static libraries get pushed up the dependency tree until one of these is
419 // reached, and source sets don't link at all.
420 bool can_link_libs =
421 (target_->output_type() == Target::EXECUTABLE ||
422 target_->output_type() == Target::SHARED_LIBRARY);
424 if (dep->output_type() == Target::SOURCE_SET) {
425 // Source sets have their object files linked into final targets (shared
426 // libraries and executables). Intermediate static libraries and other
427 // source sets just forward the dependency, otherwise the files in the
428 // source set can easily get linked more than once which will cause
429 // multiple definition errors.
431 // In the future, we may need a way to specify a "complete" static library
432 // for cases where you want a static library that includes all source sets
433 // (like if you're shipping that to customers to link against).
434 if (target_->output_type() != Target::SOURCE_SET &&
435 target_->output_type() != Target::STATIC_LIBRARY) {
436 // Linking in a source set to an executable or shared library, copy its
437 // object files.
438 for (size_t i = 0; i < dep->sources().size(); i++) {
439 SourceFileType input_file_type = GetSourceFileType(dep->sources()[i]);
440 if (input_file_type != SOURCE_UNKNOWN &&
441 input_file_type != SOURCE_H) {
442 // Note we need to specify the target as the source_set target
443 // itself, since this is used to prefix the object file name.
444 extra_object_files->insert(helper_.GetOutputFileForSource(
445 dep, dep->sources()[i], input_file_type));
449 } else if (can_link_libs && dep->IsLinkable()) {
450 linkable_deps->push_back(dep);
451 } else {
452 non_linkable_deps->push_back(dep);
456 void NinjaBinaryTargetWriter::WriteImplicitDependencies(
457 const std::vector<const Target*>& non_linkable_deps) {
458 const std::vector<SourceFile>& data = target_->data();
459 if (!non_linkable_deps.empty() || !data.empty()) {
460 out_ << " ||";
462 // Non-linkable targets.
463 for (size_t i = 0; i < non_linkable_deps.size(); i++) {
464 out_ << " ";
465 path_output_.WriteFile(out_,
466 helper_.GetTargetOutputFile(non_linkable_deps[i]));
469 // Data files.
470 const std::vector<SourceFile>& data = target_->data();
471 for (size_t i = 0; i < data.size(); i++) {
472 out_ << " ";
473 path_output_.WriteFile(out_, data[i]);