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"
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"
17 // Returns the proper escape options for writing compiler and linker flags.
18 EscapeOptions
GetFlagOptions() {
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;
32 options
.mode
= ESCAPE_SHELL
;
35 void operator()(const std::string
& s
, std::ostream
& out
) const {
37 EscapeStringToStream(out
, s
, options
);
40 EscapeOptions options
;
43 struct IncludeWriter
{
44 IncludeWriter(PathOutput
& path_output
,
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);
56 path_output_
.set_inhibit_quoting(old_inhibit_quoting_
);
59 void operator()(const SourceDir
& d
, std::ostream
& out
) const {
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
);
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
;
82 return Toolchain::TYPE_NONE
;
88 NinjaBinaryTargetWriter::NinjaBinaryTargetWriter(const Target
* target
,
89 const Toolchain
* toolchain
,
91 : NinjaTargetWriter(target
, toolchain
, out
),
92 tool_type_(GetToolTypeForTarget(target
)){
95 NinjaBinaryTargetWriter::~NinjaBinaryTargetWriter() {
98 void NinjaBinaryTargetWriter::Run() {
101 std::vector
<OutputFile
> obj_files
;
102 WriteSources(&obj_files
);
104 if (target_
->output_type() == Target::SOURCE_SET
)
105 WriteSourceSetStamp(obj_files
);
107 WriteLinkerStuff(obj_files
);
110 void NinjaBinaryTargetWriter::WriteCompilerVars() {
113 RecursiveTargetConfigToStream
<std::string
>(target_
, &ConfigValues::defines
,
114 DefineWriter(), out_
);
117 // Include directories.
118 out_
<< "includes =";
119 RecursiveTargetConfigToStream
<SourceDir
>(target_
, &ConfigValues::include_dirs
,
120 IncludeWriter(path_output_
, helper_
),
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_); \
134 WRITE_FLAGS(cflags_c
)
135 WRITE_FLAGS(cflags_cc
)
136 WRITE_FLAGS(cflags_objc
)
137 WRITE_FLAGS(cflags_objcc
)
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
);
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
);
167 path_output_
.WriteFile(out_
, output_file
);
168 out_
<< ": " << command
<< " ";
169 path_output_
.WriteFile(out_
, input_file
);
170 out_
<< implicit_deps
<< 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
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
);
190 const Toolchain::Tool
& tool
= toolchain_
->GetTool(tool_type_
);
191 WriteLinkerFlags(tool
, windows_manifest
);
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");
209 internal_output_file
= external_output_file
;
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());
225 path_output_
.WriteFile(out_
, internal_output_file
);
228 if (settings_
->IsWin()) {
230 path_output_
.WriteFile(out_
, internal_output_file
);
234 if (settings_
->IsWin()) {
235 out_
<< " implibflag = /IMPLIB:";
236 path_output_
.WriteFile(out_
, external_output_file
);
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)";
248 void NinjaBinaryTargetWriter::WriteLinkerFlags(
249 const Toolchain::Tool
& tool
,
250 const OutputFile
& windows_manifest
) {
253 // First the ldflags from the target and its config.
254 EscapeOptions flag_options
= GetFlagOptions();
255 RecursiveTargetConfigStringsToStream(target_
, &ConfigValues::ldflags
,
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
);
282 void NinjaBinaryTargetWriter::WriteLibs(const Toolchain::Tool
& tool
) {
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()),
299 out_
<< " " << tool
.lib_prefix
;
300 EscapeStringToStream(out_
, all_libs
[i
], lib_escape_opts
);
306 void NinjaBinaryTargetWriter::WriteLinkCommand(
307 const OutputFile
& external_output_file
,
308 const OutputFile
& internal_output_file
,
309 const std::vector
<OutputFile
>& object_files
) {
311 path_output_
.WriteFile(out_
, internal_output_file
);
312 if (external_output_file
!= internal_output_file
) {
314 path_output_
.WriteFile(out_
, external_output_file
);
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
);
326 for (size_t i
= 0; i
< object_files
.size(); i
++) {
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
) {
333 path_output_
.WriteFile(out_
, *i
);
337 for (size_t i
= 0; i
< linkable_deps
.size(); i
++) {
339 path_output_
.WriteFile(out_
, helper_
.GetTargetOutputFile(linkable_deps
[i
]));
342 // Append data dependencies as implicit dependencies.
343 WriteImplicitDependencies(non_linkable_deps
);
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.
355 path_output_
.WriteFile(out_
, helper_
.GetTargetOutputFile(target_
));
357 << helper_
.GetRulePrefix(target_
->settings())
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
368 DCHECK(extra_object_files
.empty());
370 for (size_t i
= 0; i
< object_files
.size(); i
++) {
372 path_output_
.WriteFile(out_
, object_files
[i
]);
375 // Append data dependencies as implicit dependencies.
376 WriteImplicitDependencies(non_linkable_deps
);
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();
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
);
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(
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.
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
);
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
);
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()) {
454 // Non-linkable targets.
455 for (size_t i
= 0; i
< non_linkable_deps
.size(); i
++) {
457 path_output_
.WriteFile(out_
,
458 helper_
.GetTargetOutputFile(non_linkable_deps
[i
]));
462 const std::vector
<SourceFile
>& data
= target_
->data();
463 for (size_t i
= 0; i
< data
.size(); i
++) {
465 path_output_
.WriteFile(out_
, data
[i
]);