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_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;
32 options
.mode
= ESCAPE_NINJA_COMMAND
;
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
, const NinjaHelper
& h
)
46 path_output_(path_output
) {
51 void operator()(const SourceDir
& d
, std::ostream
& out
) const {
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
;
69 return Toolchain::TYPE_NONE
;
75 NinjaBinaryTargetWriter::NinjaBinaryTargetWriter(const Target
* target
,
76 const Toolchain
* toolchain
,
78 : NinjaTargetWriter(target
, toolchain
, out
),
79 tool_type_(GetToolTypeForTarget(target
)){
82 NinjaBinaryTargetWriter::~NinjaBinaryTargetWriter() {
85 void NinjaBinaryTargetWriter::Run() {
88 std::vector
<OutputFile
> obj_files
;
89 WriteSources(&obj_files
);
91 if (target_
->output_type() == Target::SOURCE_SET
)
92 WriteSourceSetStamp(obj_files
);
94 WriteLinkerStuff(obj_files
);
97 void NinjaBinaryTargetWriter::WriteCompilerVars() {
100 RecursiveTargetConfigToStream
<std::string
>(target_
, &ConfigValues::defines
,
101 DefineWriter(), out_
);
104 // Include directories.
105 out_
<< "includes =";
106 RecursiveTargetConfigToStream
<SourceDir
>(target_
, &ConfigValues::include_dirs
,
107 IncludeWriter(path_output_
, helper_
),
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_); \
121 WRITE_FLAGS(cflags_c
)
122 WRITE_FLAGS(cflags_cc
)
123 WRITE_FLAGS(cflags_objc
)
124 WRITE_FLAGS(cflags_objcc
)
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
);
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
));
160 std::string command
=
161 helper_
.GetRuleForSourceType(settings_
, input_file_type
);
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
);
170 path_output_
.WriteFile(out_
, output_file
);
171 out_
<< ": " << command
<< " ";
172 path_output_
.WriteFile(out_
, input_file
);
173 out_
<< implicit_deps
<< 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
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
);
193 const Toolchain::Tool
& tool
= toolchain_
->GetTool(tool_type_
);
194 WriteLinkerFlags(tool
, windows_manifest
);
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");
212 internal_output_file
= external_output_file
;
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());
228 path_output_
.WriteFile(out_
, internal_output_file
);
231 if (settings_
->IsWin()) {
233 path_output_
.WriteFile(out_
, internal_output_file
);
237 if (settings_
->IsWin()) {
238 out_
<< " implibflag = /IMPLIB:";
239 path_output_
.WriteFile(out_
, external_output_file
);
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)";
251 void NinjaBinaryTargetWriter::WriteLinkerFlags(
252 const Toolchain::Tool
& tool
,
253 const OutputFile
& windows_manifest
) {
256 // First the ldflags from the target and its config.
257 EscapeOptions flag_options
= GetFlagOptions();
258 RecursiveTargetConfigStringsToStream(target_
, &ConfigValues::ldflags
,
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
);
285 void NinjaBinaryTargetWriter::WriteLibs(const Toolchain::Tool
& tool
) {
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()),
302 out_
<< " " << tool
.lib_prefix
;
303 EscapeStringToStream(out_
, all_libs
[i
], lib_escape_opts
);
309 void NinjaBinaryTargetWriter::WriteLinkCommand(
310 const OutputFile
& external_output_file
,
311 const OutputFile
& internal_output_file
,
312 const std::vector
<OutputFile
>& object_files
) {
314 path_output_
.WriteFile(out_
, internal_output_file
);
315 if (external_output_file
!= internal_output_file
) {
317 path_output_
.WriteFile(out_
, external_output_file
);
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
);
329 for (size_t i
= 0; i
< object_files
.size(); i
++) {
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
) {
336 path_output_
.WriteFile(out_
, *i
);
340 for (size_t i
= 0; i
< linkable_deps
.size(); i
++) {
342 path_output_
.WriteFile(out_
, helper_
.GetTargetOutputFile(linkable_deps
[i
]));
345 // Append data dependencies as implicit dependencies.
346 WriteImplicitDependencies(non_linkable_deps
);
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.
358 path_output_
.WriteFile(out_
, helper_
.GetTargetOutputFile(target_
));
360 << helper_
.GetRulePrefix(target_
->settings())
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
371 DCHECK(extra_object_files
.empty());
373 for (size_t i
= 0; i
< object_files
.size(); i
++) {
375 path_output_
.WriteFile(out_
, object_files
[i
]);
378 // Append data dependencies as implicit dependencies.
379 WriteImplicitDependencies(non_linkable_deps
);
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();
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
);
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(
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.
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
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
);
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()) {
462 // Non-linkable targets.
463 for (size_t i
= 0; i
< non_linkable_deps
.size(); i
++) {
465 path_output_
.WriteFile(out_
,
466 helper_
.GetTargetOutputFile(non_linkable_deps
[i
]));
470 const std::vector
<SourceFile
>& data
= target_
->data();
471 for (size_t i
= 0; i
< data
.size(); i
++) {
473 path_output_
.WriteFile(out_
, data
[i
]);