Upstream version 5.34.92.0
[platform/framework/web/crosswalk.git] / src / tools / gn / ninja_binary_target_writer.cc
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.
4
5 #include "tools/gn/ninja_binary_target_writer.h"
6
7 #include <set>
8
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"
14
15 namespace {
16
17 // Returns the proper escape options for writing compiler and linker flags.
18 EscapeOptions GetFlagOptions() {
19   EscapeOptions opts;
20   opts.mode = ESCAPE_NINJA;
21
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;
26
27   return opts;
28 }
29
30 struct DefineWriter {
31   DefineWriter() {
32     options.mode = ESCAPE_SHELL;
33   }
34
35   void operator()(const std::string& s, std::ostream& out) const {
36     out << " -D";
37     EscapeStringToStream(out, s, options);
38   }
39
40   EscapeOptions options;
41 };
42
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);
54   }
55   ~IncludeWriter() {
56     path_output_.set_inhibit_quoting(old_inhibit_quoting_);
57   }
58
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 << "\"";
66   }
67
68   const NinjaHelper& helper;
69   PathOutput& path_output_;
70   bool old_inhibit_quoting_;  // So we can put the PathOutput back.
71 };
72
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;
83   }
84 }
85
86 }  // namespace
87
88 NinjaBinaryTargetWriter::NinjaBinaryTargetWriter(const Target* target,
89                                                  const Toolchain* toolchain,
90                                                  std::ostream& out)
91     : NinjaTargetWriter(target, toolchain, out),
92       tool_type_(GetToolTypeForTarget(target)){
93 }
94
95 NinjaBinaryTargetWriter::~NinjaBinaryTargetWriter() {
96 }
97
98 void NinjaBinaryTargetWriter::Run() {
99   WriteCompilerVars();
100
101   std::vector<OutputFile> obj_files;
102   WriteSources(&obj_files);
103
104   if (target_->output_type() == Target::SOURCE_SET)
105     WriteSourceSetStamp(obj_files);
106   else
107     WriteLinkerStuff(obj_files);
108 }
109
110 void NinjaBinaryTargetWriter::WriteCompilerVars() {
111   // Defines.
112   out_ << "defines =";
113   RecursiveTargetConfigToStream<std::string>(target_, &ConfigValues::defines,
114                                              DefineWriter(), out_);
115   out_ << std::endl;
116
117   // Include directories.
118   out_ << "includes =";
119   RecursiveTargetConfigToStream<SourceDir>(target_, &ConfigValues::include_dirs,
120                                            IncludeWriter(path_output_, helper_),
121                                            out_);
122
123   out_ << std::endl;
124
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;
132
133   WRITE_FLAGS(cflags)
134   WRITE_FLAGS(cflags_c)
135   WRITE_FLAGS(cflags_cc)
136   WRITE_FLAGS(cflags_objc)
137   WRITE_FLAGS(cflags_objcc)
138
139 #undef WRITE_FLAGS
140
141   out_ << std::endl;
142 }
143
144 void NinjaBinaryTargetWriter::WriteSources(
145     std::vector<OutputFile>* object_files) {
146   const Target::FileList& sources = target_->sources();
147   object_files->reserve(sources.size());
148
149   std::string implicit_deps = GetSourcesImplicitDeps();
150
151   for (size_t i = 0; i < sources.size(); i++) {
152     const SourceFile& input_file = sources[i];
153
154     SourceFileType input_file_type = GetSourceFileType(input_file,
155                                                        settings_->target_os());
156     if (input_file_type == SOURCE_UNKNOWN)
157       continue;  // Skip unknown file types.
158     std::string command =
159         helper_.GetRuleForSourceType(settings_, input_file_type);
160     if (command.empty())
161       continue;  // Skip files not needing compilation.
162
163     OutputFile output_file = helper_.GetOutputFileForSource(
164         target_, input_file, input_file_type);
165     object_files->push_back(output_file);
166
167     out_ << "build ";
168     path_output_.WriteFile(out_, output_file);
169     out_ << ": " << command << " ";
170     path_output_.WriteFile(out_, input_file);
171     out_ << implicit_deps << std::endl;
172   }
173   out_ << std::endl;
174 }
175
176 void NinjaBinaryTargetWriter::WriteLinkerStuff(
177     const std::vector<OutputFile>& object_files) {
178   // Manifest file on Windows.
179   // TODO(brettw) this seems not to be necessary for static libs, skip in
180   // that case?
181   OutputFile windows_manifest;
182   if (settings_->IsWin()) {
183     windows_manifest.value().assign(helper_.GetTargetOutputDir(target_));
184     windows_manifest.value().append(target_->label().name());
185     windows_manifest.value().append(".intermediate.manifest");
186     out_ << "manifests = ";
187     path_output_.WriteFile(out_, windows_manifest);
188     out_ << std::endl;
189   }
190
191   const Toolchain::Tool& tool = toolchain_->GetTool(tool_type_);
192   WriteLinkerFlags(tool, windows_manifest);
193   WriteLibs(tool);
194
195   // The external output file is the one that other libs depend on.
196   OutputFile external_output_file = helper_.GetTargetOutputFile(target_);
197
198   // The internal output file is the "main thing" we think we're making. In
199   // the case of shared libraries, this is the shared library and the external
200   // output file is the import library. In other cases, the internal one and
201   // the external one are the same.
202   OutputFile internal_output_file;
203   if (target_->output_type() == Target::SHARED_LIBRARY) {
204     if (settings_->IsWin()) {
205       internal_output_file.value() =
206           target_->settings()->toolchain_output_subdir().value();
207       internal_output_file.value().append(target_->label().name());
208       internal_output_file.value().append(".dll");
209     } else {
210       internal_output_file = external_output_file;
211     }
212   } else {
213     internal_output_file = external_output_file;
214   }
215
216   // In Python see "self.ninja.build(output, command, input,"
217   WriteLinkCommand(external_output_file, internal_output_file, object_files);
218
219   if (target_->output_type() == Target::SHARED_LIBRARY) {
220     // The shared object name doesn't include a path.
221     out_ << "  soname = ";
222     out_ << FindFilename(&internal_output_file.value());
223     out_ << std::endl;
224
225     out_ << "  lib = ";
226     path_output_.WriteFile(out_, internal_output_file);
227     out_ << std::endl;
228
229     if (settings_->IsWin()) {
230       out_ << "  dll = ";
231       path_output_.WriteFile(out_, internal_output_file);
232       out_ << std::endl;
233     }
234
235     if (settings_->IsWin()) {
236       out_ << "  implibflag = /IMPLIB:";
237       path_output_.WriteFile(out_, external_output_file);
238       out_ << std::endl;
239     }
240
241     // TODO(brettw) postbuild steps.
242     if (settings_->IsMac())
243       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)";
244   }
245
246   out_ << std::endl;
247 }
248
249 void NinjaBinaryTargetWriter::WriteLinkerFlags(
250     const Toolchain::Tool& tool,
251     const OutputFile& windows_manifest) {
252   out_ << "ldflags =";
253
254   // First the ldflags from the target and its config.
255   EscapeOptions flag_options = GetFlagOptions();
256   RecursiveTargetConfigStringsToStream(target_, &ConfigValues::ldflags,
257                                        flag_options, out_);
258
259   // Followed by library search paths that have been recursively pushed
260   // through the dependency tree.
261   const OrderedSet<SourceDir> all_lib_dirs = target_->all_lib_dirs();
262   if (!all_lib_dirs.empty()) {
263     // Since we're passing these on the command line to the linker and not
264     // to Ninja, we need to do shell escaping.
265     PathOutput lib_path_output(path_output_.current_dir(), ESCAPE_NINJA_SHELL,
266                                true);
267     for (size_t i = 0; i < all_lib_dirs.size(); i++) {
268       out_ << " " << tool.lib_dir_prefix;
269       lib_path_output.WriteDir(out_, all_lib_dirs[i],
270                                PathOutput::DIR_NO_LAST_SLASH);
271     }
272   }
273
274   // Append manifest flag on Windows to reference our file.
275   // HACK ERASEME BRETTW FIXME
276   if (settings_->IsWin()) {
277     out_ << " /MANIFEST /ManifestFile:";
278     path_output_.WriteFile(out_, windows_manifest);
279   }
280   out_ << std::endl;
281 }
282
283 void NinjaBinaryTargetWriter::WriteLibs(const Toolchain::Tool& tool) {
284   out_ << "libs =";
285
286   // Libraries that have been recursively pushed through the dependency tree.
287   EscapeOptions lib_escape_opts;
288   lib_escape_opts.mode = ESCAPE_NINJA_SHELL;
289   const OrderedSet<std::string> all_libs = target_->all_libs();
290   const std::string framework_ending(".framework");
291   for (size_t i = 0; i < all_libs.size(); i++) {
292     if (settings_->IsMac() && EndsWith(all_libs[i], framework_ending, false)) {
293       // Special-case libraries ending in ".framework" on Mac. Add the
294       // -framework switch and don't add the extension to the output.
295       out_ << " -framework ";
296       EscapeStringToStream(out_,
297           all_libs[i].substr(0, all_libs[i].size() - framework_ending.size()),
298           lib_escape_opts);
299     } else {
300       out_ << " " << tool.lib_prefix;
301       EscapeStringToStream(out_, all_libs[i], lib_escape_opts);
302     }
303   }
304   out_ << std::endl;
305 }
306
307 void NinjaBinaryTargetWriter::WriteLinkCommand(
308     const OutputFile& external_output_file,
309     const OutputFile& internal_output_file,
310     const std::vector<OutputFile>& object_files) {
311   out_ << "build ";
312   path_output_.WriteFile(out_, internal_output_file);
313   if (external_output_file != internal_output_file) {
314     out_ << " ";
315     path_output_.WriteFile(out_, external_output_file);
316   }
317   out_ << ": "
318        << helper_.GetRulePrefix(target_->settings())
319        << Toolchain::ToolTypeToName(tool_type_);
320
321   std::set<OutputFile> extra_object_files;
322   std::vector<const Target*> linkable_deps;
323   std::vector<const Target*> non_linkable_deps;
324   GetDeps(&extra_object_files, &linkable_deps, &non_linkable_deps);
325
326   // Object files.
327   for (size_t i = 0; i < object_files.size(); i++) {
328     out_ << " ";
329     path_output_.WriteFile(out_, object_files[i]);
330   }
331   for (std::set<OutputFile>::iterator i = extra_object_files.begin();
332        i != extra_object_files.end(); ++i) {
333     out_ << " ";
334     path_output_.WriteFile(out_, *i);
335   }
336
337   // Libs.
338   for (size_t i = 0; i < linkable_deps.size(); i++) {
339     out_ << " ";
340     path_output_.WriteFile(out_, helper_.GetTargetOutputFile(linkable_deps[i]));
341   }
342
343   // Append data dependencies as implicit dependencies.
344   WriteImplicitDependencies(non_linkable_deps);
345
346   out_ << std::endl;
347 }
348
349 void NinjaBinaryTargetWriter::WriteSourceSetStamp(
350     const std::vector<OutputFile>& object_files) {
351   // The stamp rule for source sets is generally not used, since targets that
352   // depend on this will reference the object files directly. However, writing
353   // this rule allows the user to type the name of the target and get a build
354   // which can be convenient for development.
355   out_ << "build ";
356   path_output_.WriteFile(out_, helper_.GetTargetOutputFile(target_));
357   out_ << ": "
358        << helper_.GetRulePrefix(target_->settings())
359        << "stamp";
360
361   std::set<OutputFile> extra_object_files;
362   std::vector<const Target*> linkable_deps;
363   std::vector<const Target*> non_linkable_deps;
364   GetDeps(&extra_object_files, &linkable_deps, &non_linkable_deps);
365
366   // The classifier should never put extra object files in a source set:
367   // any source sets that we depend on should appear in our non-linkable
368   // deps instead.
369   DCHECK(extra_object_files.empty());
370
371   for (size_t i = 0; i < object_files.size(); i++) {
372     out_ << " ";
373     path_output_.WriteFile(out_, object_files[i]);
374   }
375
376   // Append data dependencies as implicit dependencies.
377   WriteImplicitDependencies(non_linkable_deps);
378
379   out_ << std::endl;
380 }
381
382 void NinjaBinaryTargetWriter::GetDeps(
383     std::set<OutputFile>* extra_object_files,
384     std::vector<const Target*>* linkable_deps,
385     std::vector<const Target*>* non_linkable_deps) const {
386   const LabelTargetVector& deps = target_->deps();
387   const std::set<const Target*>& inherited = target_->inherited_libraries();
388
389   // Normal deps.
390   for (size_t i = 0; i < deps.size(); i++) {
391     if (inherited.find(deps[i].ptr) != inherited.end())
392       continue;  // Don't add dupes.
393     ClassifyDependency(deps[i].ptr, extra_object_files,
394                        linkable_deps, non_linkable_deps);
395   }
396
397   // Inherited libraries.
398   for (std::set<const Target*>::const_iterator i = inherited.begin();
399        i != inherited.end(); ++i) {
400     ClassifyDependency(*i, extra_object_files,
401                        linkable_deps, non_linkable_deps);
402   }
403
404   // Data deps.
405   const LabelTargetVector& datadeps = target_->datadeps();
406   for (size_t i = 0; i < datadeps.size(); i++)
407     non_linkable_deps->push_back(datadeps[i].ptr);
408 }
409
410 void NinjaBinaryTargetWriter::ClassifyDependency(
411     const Target* dep,
412     std::set<OutputFile>* extra_object_files,
413     std::vector<const Target*>* linkable_deps,
414     std::vector<const Target*>* non_linkable_deps) const {
415   // Only these types of outputs have libraries linked into them. Child deps of
416   // static libraries get pushed up the dependency tree until one of these is
417   // reached, and source sets don't link at all.
418   bool can_link_libs =
419       (target_->output_type() == Target::EXECUTABLE ||
420        target_->output_type() == Target::SHARED_LIBRARY);
421
422   if (dep->output_type() == Target::SOURCE_SET) {
423     if (target_->output_type() == Target::SOURCE_SET) {
424       // When a source set depends on another source set, add it as a data
425       // dependency so if the user says "ninja second_source_set" it will
426       // also compile the first (what you would expect) even though we'll
427       // never do anything with the first one's files.
428       non_linkable_deps->push_back(dep);
429     } else {
430       // Linking in a source set, copy its object files.
431       for (size_t i = 0; i < dep->sources().size(); i++) {
432         SourceFileType input_file_type = GetSourceFileType(
433             dep->sources()[i], dep->settings()->target_os());
434         if (input_file_type != SOURCE_UNKNOWN &&
435             input_file_type != SOURCE_H) {
436           // Note we need to specify the target as the source_set target
437           // itself, since this is used to prefix the object file name.
438           extra_object_files->insert(helper_.GetOutputFileForSource(
439               dep, dep->sources()[i], input_file_type));
440         }
441       }
442     }
443   } else if (can_link_libs && dep->IsLinkable()) {
444     linkable_deps->push_back(dep);
445   } else {
446     non_linkable_deps->push_back(dep);
447   }
448 }
449
450 void NinjaBinaryTargetWriter::WriteImplicitDependencies(
451     const std::vector<const Target*>& non_linkable_deps) {
452   const std::vector<SourceFile>& data = target_->data();
453   if (!non_linkable_deps.empty() || !data.empty()) {
454     out_ << " ||";
455
456     // Non-linkable targets.
457     for (size_t i = 0; i < non_linkable_deps.size(); i++) {
458       out_ << " ";
459       path_output_.WriteFile(out_,
460                              helper_.GetTargetOutputFile(non_linkable_deps[i]));
461     }
462
463     // Data files.
464     const std::vector<SourceFile>& data = target_->data();
465     for (size_t i = 0; i < data.size(); i++) {
466       out_ << " ";
467       path_output_.WriteFile(out_, data[i]);
468     }
469   }
470 }