Revert 264226 "Reduce dependency of TiclInvalidationService on P..."
[chromium-blink-merge.git] / tools / gn / ninja_binary_target_writer.cc
blob392bc54b84fd643d00431fb7dbefe5c9ff94e55c
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;
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_SHELL;
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,
45 const NinjaHelper& h)
46 : helper(h),
47 path_output_(path_output),
48 old_inhibit_quoting_(path_output.inhibit_quoting()) {
49 // Inhibit quoting since we'll put quotes around the whole thing ourselves.
50 // Since we're writing in NINJA escaping mode, this won't actually do
51 // anything, but I think we may need to change to shell-and-then-ninja
52 // escaping for this in the future.
53 path_output_.set_inhibit_quoting(true);
55 ~IncludeWriter() {
56 path_output_.set_inhibit_quoting(old_inhibit_quoting_);
59 void operator()(const SourceDir& d, std::ostream& out) const {
60 out << " \"-I";
61 // It's important not to include the trailing slash on directories or on
62 // Windows it will be a backslash and the compiler might think we're
63 // escaping the quote!
64 path_output_.WriteDir(out, d, PathOutput::DIR_NO_LAST_SLASH);
65 out << "\"";
68 const NinjaHelper& helper;
69 PathOutput& path_output_;
70 bool old_inhibit_quoting_; // So we can put the PathOutput back.
73 Toolchain::ToolType GetToolTypeForTarget(const Target* target) {
74 switch (target->output_type()) {
75 case Target::STATIC_LIBRARY:
76 return Toolchain::TYPE_ALINK;
77 case Target::SHARED_LIBRARY:
78 return Toolchain::TYPE_SOLINK;
79 case Target::EXECUTABLE:
80 return Toolchain::TYPE_LINK;
81 default:
82 return Toolchain::TYPE_NONE;
86 } // namespace
88 NinjaBinaryTargetWriter::NinjaBinaryTargetWriter(const Target* target,
89 const Toolchain* toolchain,
90 std::ostream& out)
91 : NinjaTargetWriter(target, toolchain, out),
92 tool_type_(GetToolTypeForTarget(target)){
95 NinjaBinaryTargetWriter::~NinjaBinaryTargetWriter() {
98 void NinjaBinaryTargetWriter::Run() {
99 WriteCompilerVars();
101 std::vector<OutputFile> obj_files;
102 WriteSources(&obj_files);
104 if (target_->output_type() == Target::SOURCE_SET)
105 WriteSourceSetStamp(obj_files);
106 else
107 WriteLinkerStuff(obj_files);
110 void NinjaBinaryTargetWriter::WriteCompilerVars() {
111 // Defines.
112 out_ << "defines =";
113 RecursiveTargetConfigToStream<std::string>(target_, &ConfigValues::defines,
114 DefineWriter(), out_);
115 out_ << std::endl;
117 // Include directories.
118 out_ << "includes =";
119 RecursiveTargetConfigToStream<SourceDir>(target_, &ConfigValues::include_dirs,
120 IncludeWriter(path_output_, helper_),
121 out_);
123 out_ << std::endl;
125 // C flags and friends.
126 EscapeOptions flag_escape_options = GetFlagOptions();
127 #define WRITE_FLAGS(name) \
128 out_ << #name " ="; \
129 RecursiveTargetConfigStringsToStream(target_, &ConfigValues::name, \
130 flag_escape_options, out_); \
131 out_ << std::endl;
133 WRITE_FLAGS(cflags)
134 WRITE_FLAGS(cflags_c)
135 WRITE_FLAGS(cflags_cc)
136 WRITE_FLAGS(cflags_objc)
137 WRITE_FLAGS(cflags_objcc)
139 #undef WRITE_FLAGS
141 out_ << std::endl;
144 void NinjaBinaryTargetWriter::WriteSources(
145 std::vector<OutputFile>* object_files) {
146 const Target::FileList& sources = target_->sources();
147 object_files->reserve(sources.size());
149 std::string implicit_deps = GetSourcesImplicitDeps();
151 for (size_t i = 0; i < sources.size(); i++) {
152 const SourceFile& input_file = sources[i];
154 SourceFileType input_file_type = GetSourceFileType(input_file);
155 if (input_file_type == SOURCE_UNKNOWN)
156 continue; // Skip unknown file types.
157 std::string command =
158 helper_.GetRuleForSourceType(settings_, input_file_type);
159 if (command.empty())
160 continue; // Skip files not needing compilation.
162 OutputFile output_file = helper_.GetOutputFileForSource(
163 target_, input_file, input_file_type);
164 object_files->push_back(output_file);
166 out_ << "build ";
167 path_output_.WriteFile(out_, output_file);
168 out_ << ": " << command << " ";
169 path_output_.WriteFile(out_, input_file);
170 out_ << implicit_deps << std::endl;
172 out_ << std::endl;
175 void NinjaBinaryTargetWriter::WriteLinkerStuff(
176 const std::vector<OutputFile>& object_files) {
177 // Manifest file on Windows.
178 // TODO(brettw) this seems not to be necessary for static libs, skip in
179 // that case?
180 OutputFile windows_manifest;
181 if (settings_->IsWin()) {
182 windows_manifest.value().assign(helper_.GetTargetOutputDir(target_));
183 windows_manifest.value().append(target_->label().name());
184 windows_manifest.value().append(".intermediate.manifest");
185 out_ << "manifests = ";
186 path_output_.WriteFile(out_, windows_manifest);
187 out_ << std::endl;
190 const Toolchain::Tool& tool = toolchain_->GetTool(tool_type_);
191 WriteLinkerFlags(tool, windows_manifest);
192 WriteLibs(tool);
194 // The external output file is the one that other libs depend on.
195 OutputFile external_output_file = helper_.GetTargetOutputFile(target_);
197 // The internal output file is the "main thing" we think we're making. In
198 // the case of shared libraries, this is the shared library and the external
199 // output file is the import library. In other cases, the internal one and
200 // the external one are the same.
201 OutputFile internal_output_file;
202 if (target_->output_type() == Target::SHARED_LIBRARY) {
203 if (settings_->IsWin()) {
204 internal_output_file.value() =
205 target_->settings()->toolchain_output_subdir().value();
206 internal_output_file.value().append(target_->label().name());
207 internal_output_file.value().append(".dll");
208 } else {
209 internal_output_file = external_output_file;
211 } else {
212 internal_output_file = external_output_file;
215 // In Python see "self.ninja.build(output, command, input,"
216 WriteLinkCommand(external_output_file, internal_output_file, object_files);
218 if (target_->output_type() == Target::SHARED_LIBRARY) {
219 // The shared object name doesn't include a path.
220 out_ << " soname = ";
221 out_ << FindFilename(&internal_output_file.value());
222 out_ << std::endl;
224 out_ << " lib = ";
225 path_output_.WriteFile(out_, internal_output_file);
226 out_ << std::endl;
228 if (settings_->IsWin()) {
229 out_ << " dll = ";
230 path_output_.WriteFile(out_, internal_output_file);
231 out_ << std::endl;
234 if (settings_->IsWin()) {
235 out_ << " implibflag = /IMPLIB:";
236 path_output_.WriteFile(out_, external_output_file);
237 out_ << std::endl;
240 // TODO(brettw) postbuild steps.
241 if (settings_->IsMac())
242 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)";
245 out_ << std::endl;
248 void NinjaBinaryTargetWriter::WriteLinkerFlags(
249 const Toolchain::Tool& tool,
250 const OutputFile& windows_manifest) {
251 out_ << "ldflags =";
253 // First the ldflags from the target and its config.
254 EscapeOptions flag_options = GetFlagOptions();
255 RecursiveTargetConfigStringsToStream(target_, &ConfigValues::ldflags,
256 flag_options, out_);
258 // Followed by library search paths that have been recursively pushed
259 // through the dependency tree.
260 const OrderedSet<SourceDir> all_lib_dirs = target_->all_lib_dirs();
261 if (!all_lib_dirs.empty()) {
262 // Since we're passing these on the command line to the linker and not
263 // to Ninja, we need to do shell escaping.
264 PathOutput lib_path_output(
265 path_output_.current_dir(), ESCAPE_NINJA_SHELL, false);
266 for (size_t i = 0; i < all_lib_dirs.size(); i++) {
267 out_ << " " << tool.lib_dir_prefix;
268 lib_path_output.WriteDir(out_, all_lib_dirs[i],
269 PathOutput::DIR_NO_LAST_SLASH);
273 // Append manifest flag on Windows to reference our file.
274 // HACK ERASEME BRETTW FIXME
275 if (settings_->IsWin()) {
276 out_ << " /MANIFEST /ManifestFile:";
277 path_output_.WriteFile(out_, windows_manifest);
279 out_ << std::endl;
282 void NinjaBinaryTargetWriter::WriteLibs(const Toolchain::Tool& tool) {
283 out_ << "libs =";
285 // Libraries that have been recursively pushed through the dependency tree.
286 EscapeOptions lib_escape_opts;
287 lib_escape_opts.mode = ESCAPE_NINJA_SHELL;
288 const OrderedSet<std::string> all_libs = target_->all_libs();
289 const std::string framework_ending(".framework");
290 for (size_t i = 0; i < all_libs.size(); i++) {
291 if (settings_->IsMac() && EndsWith(all_libs[i], framework_ending, false)) {
292 // Special-case libraries ending in ".framework" on Mac. Add the
293 // -framework switch and don't add the extension to the output.
294 out_ << " -framework ";
295 EscapeStringToStream(out_,
296 all_libs[i].substr(0, all_libs[i].size() - framework_ending.size()),
297 lib_escape_opts);
298 } else {
299 out_ << " " << tool.lib_prefix;
300 EscapeStringToStream(out_, all_libs[i], lib_escape_opts);
303 out_ << std::endl;
306 void NinjaBinaryTargetWriter::WriteLinkCommand(
307 const OutputFile& external_output_file,
308 const OutputFile& internal_output_file,
309 const std::vector<OutputFile>& object_files) {
310 out_ << "build ";
311 path_output_.WriteFile(out_, internal_output_file);
312 if (external_output_file != internal_output_file) {
313 out_ << " ";
314 path_output_.WriteFile(out_, external_output_file);
316 out_ << ": "
317 << helper_.GetRulePrefix(target_->settings())
318 << Toolchain::ToolTypeToName(tool_type_);
320 std::set<OutputFile> extra_object_files;
321 std::vector<const Target*> linkable_deps;
322 std::vector<const Target*> non_linkable_deps;
323 GetDeps(&extra_object_files, &linkable_deps, &non_linkable_deps);
325 // Object files.
326 for (size_t i = 0; i < object_files.size(); i++) {
327 out_ << " ";
328 path_output_.WriteFile(out_, object_files[i]);
330 for (std::set<OutputFile>::iterator i = extra_object_files.begin();
331 i != extra_object_files.end(); ++i) {
332 out_ << " ";
333 path_output_.WriteFile(out_, *i);
336 // Libs.
337 for (size_t i = 0; i < linkable_deps.size(); i++) {
338 out_ << " ";
339 path_output_.WriteFile(out_, helper_.GetTargetOutputFile(linkable_deps[i]));
342 // Append data dependencies as implicit dependencies.
343 WriteImplicitDependencies(non_linkable_deps);
345 out_ << std::endl;
348 void NinjaBinaryTargetWriter::WriteSourceSetStamp(
349 const std::vector<OutputFile>& object_files) {
350 // The stamp rule for source sets is generally not used, since targets that
351 // depend on this will reference the object files directly. However, writing
352 // this rule allows the user to type the name of the target and get a build
353 // which can be convenient for development.
354 out_ << "build ";
355 path_output_.WriteFile(out_, helper_.GetTargetOutputFile(target_));
356 out_ << ": "
357 << helper_.GetRulePrefix(target_->settings())
358 << "stamp";
360 std::set<OutputFile> extra_object_files;
361 std::vector<const Target*> linkable_deps;
362 std::vector<const Target*> non_linkable_deps;
363 GetDeps(&extra_object_files, &linkable_deps, &non_linkable_deps);
365 // The classifier should never put extra object files in a source set:
366 // any source sets that we depend on should appear in our non-linkable
367 // deps instead.
368 DCHECK(extra_object_files.empty());
370 for (size_t i = 0; i < object_files.size(); i++) {
371 out_ << " ";
372 path_output_.WriteFile(out_, object_files[i]);
375 // Append data dependencies as implicit dependencies.
376 WriteImplicitDependencies(non_linkable_deps);
378 out_ << std::endl;
381 void NinjaBinaryTargetWriter::GetDeps(
382 std::set<OutputFile>* extra_object_files,
383 std::vector<const Target*>* linkable_deps,
384 std::vector<const Target*>* non_linkable_deps) const {
385 const LabelTargetVector& deps = target_->deps();
386 const std::set<const Target*>& inherited = target_->inherited_libraries();
388 // Normal deps.
389 for (size_t i = 0; i < deps.size(); i++) {
390 if (inherited.find(deps[i].ptr) != inherited.end())
391 continue; // Don't add dupes.
392 ClassifyDependency(deps[i].ptr, extra_object_files,
393 linkable_deps, non_linkable_deps);
396 // Inherited libraries.
397 for (std::set<const Target*>::const_iterator i = inherited.begin();
398 i != inherited.end(); ++i) {
399 ClassifyDependency(*i, extra_object_files,
400 linkable_deps, non_linkable_deps);
403 // Data deps.
404 const LabelTargetVector& datadeps = target_->datadeps();
405 for (size_t i = 0; i < datadeps.size(); i++)
406 non_linkable_deps->push_back(datadeps[i].ptr);
409 void NinjaBinaryTargetWriter::ClassifyDependency(
410 const Target* dep,
411 std::set<OutputFile>* extra_object_files,
412 std::vector<const Target*>* linkable_deps,
413 std::vector<const Target*>* non_linkable_deps) const {
414 // Only these types of outputs have libraries linked into them. Child deps of
415 // static libraries get pushed up the dependency tree until one of these is
416 // reached, and source sets don't link at all.
417 bool can_link_libs =
418 (target_->output_type() == Target::EXECUTABLE ||
419 target_->output_type() == Target::SHARED_LIBRARY);
421 if (dep->output_type() == Target::SOURCE_SET) {
422 if (target_->output_type() == Target::SOURCE_SET) {
423 // When a source set depends on another source set, add it as a data
424 // dependency so if the user says "ninja second_source_set" it will
425 // also compile the first (what you would expect) even though we'll
426 // never do anything with the first one's files.
427 non_linkable_deps->push_back(dep);
428 } else {
429 // Linking in a source set, copy its object files.
430 for (size_t i = 0; i < dep->sources().size(); i++) {
431 SourceFileType input_file_type = GetSourceFileType(dep->sources()[i]);
432 if (input_file_type != SOURCE_UNKNOWN &&
433 input_file_type != SOURCE_H) {
434 // Note we need to specify the target as the source_set target
435 // itself, since this is used to prefix the object file name.
436 extra_object_files->insert(helper_.GetOutputFileForSource(
437 dep, dep->sources()[i], input_file_type));
441 } else if (can_link_libs && dep->IsLinkable()) {
442 linkable_deps->push_back(dep);
443 } else {
444 non_linkable_deps->push_back(dep);
448 void NinjaBinaryTargetWriter::WriteImplicitDependencies(
449 const std::vector<const Target*>& non_linkable_deps) {
450 const std::vector<SourceFile>& data = target_->data();
451 if (!non_linkable_deps.empty() || !data.empty()) {
452 out_ << " ||";
454 // Non-linkable targets.
455 for (size_t i = 0; i < non_linkable_deps.size(); i++) {
456 out_ << " ";
457 path_output_.WriteFile(out_,
458 helper_.GetTargetOutputFile(non_linkable_deps[i]));
461 // Data files.
462 const std::vector<SourceFile>& data = target_->data();
463 for (size_t i = 0; i < data.size(); i++) {
464 out_ << " ";
465 path_output_.WriteFile(out_, data[i]);