#include "tools/gn/ninja_binary_target_writer.h"
#include <set>
+#include <sstream>
#include "base/strings/string_util.h"
#include "tools/gn/config_values_extractors.h"
+#include "tools/gn/deps_iterator.h"
#include "tools/gn/err.h"
#include "tools/gn/escape.h"
+#include "tools/gn/ninja_utils.h"
+#include "tools/gn/settings.h"
#include "tools/gn/string_utils.h"
+#include "tools/gn/substitution_writer.h"
+#include "tools/gn/target.h"
namespace {
// Returns the proper escape options for writing compiler and linker flags.
EscapeOptions GetFlagOptions() {
EscapeOptions opts;
- opts.mode = ESCAPE_NINJA;
+ opts.mode = ESCAPE_NINJA_COMMAND;
// Some flag strings are actually multiple flags that expect to be just
// added to the command line. We assume that quoting is done by the
struct DefineWriter {
DefineWriter() {
- options.mode = ESCAPE_SHELL;
+ options.mode = ESCAPE_NINJA_COMMAND;
}
void operator()(const std::string& s, std::ostream& out) const {
};
struct IncludeWriter {
- IncludeWriter(PathOutput& path_output,
- const NinjaHelper& h)
- : helper(h),
- path_output_(path_output),
- old_inhibit_quoting_(path_output.inhibit_quoting()) {
- // Inhibit quoting since we'll put quotes around the whole thing ourselves.
- // Since we're writing in NINJA escaping mode, this won't actually do
- // anything, but I think we may need to change to shell-and-then-ninja
- // escaping for this in the future.
- path_output_.set_inhibit_quoting(true);
+ IncludeWriter(PathOutput& path_output) : path_output_(path_output) {
}
~IncludeWriter() {
- path_output_.set_inhibit_quoting(old_inhibit_quoting_);
}
void operator()(const SourceDir& d, std::ostream& out) const {
- out << " \"-I";
- // It's important not to include the trailing slash on directories or on
- // Windows it will be a backslash and the compiler might think we're
- // escaping the quote!
- path_output_.WriteDir(out, d, PathOutput::DIR_NO_LAST_SLASH);
- out << "\"";
+ std::ostringstream path_out;
+ path_output_.WriteDir(path_out, d, PathOutput::DIR_NO_LAST_SLASH);
+ const std::string& path = path_out.str();
+ if (path[0] == '"')
+ out << " \"-I" << path.substr(1);
+ else
+ out << " -I" << path;
}
- const NinjaHelper& helper;
PathOutput& path_output_;
- bool old_inhibit_quoting_; // So we can put the PathOutput back.
};
-Toolchain::ToolType GetToolTypeForTarget(const Target* target) {
- switch (target->output_type()) {
- case Target::STATIC_LIBRARY:
- return Toolchain::TYPE_ALINK;
- case Target::SHARED_LIBRARY:
- return Toolchain::TYPE_SOLINK;
- case Target::EXECUTABLE:
- return Toolchain::TYPE_LINK;
- default:
- return Toolchain::TYPE_NONE;
- }
-}
-
} // namespace
NinjaBinaryTargetWriter::NinjaBinaryTargetWriter(const Target* target,
- const Toolchain* toolchain,
std::ostream& out)
- : NinjaTargetWriter(target, toolchain, out),
- tool_type_(GetToolTypeForTarget(target)){
+ : NinjaTargetWriter(target, out),
+ tool_(target->toolchain()->GetToolForTargetFinalOutput(target)) {
}
NinjaBinaryTargetWriter::~NinjaBinaryTargetWriter() {
}
void NinjaBinaryTargetWriter::WriteCompilerVars() {
+ const SubstitutionBits& subst = target_->toolchain()->substitution_bits();
+
// Defines.
- out_ << "defines =";
- RecursiveTargetConfigToStream<std::string>(target_, &ConfigValues::defines,
- DefineWriter(), out_);
- out_ << std::endl;
+ if (subst.used[SUBSTITUTION_DEFINES]) {
+ out_ << kSubstitutionNinjaNames[SUBSTITUTION_DEFINES] << " =";
+ RecursiveTargetConfigToStream<std::string>(
+ target_, &ConfigValues::defines, DefineWriter(), out_);
+ out_ << std::endl;
+ }
// Include directories.
- out_ << "includes =";
- RecursiveTargetConfigToStream<SourceDir>(target_, &ConfigValues::include_dirs,
- IncludeWriter(path_output_, helper_),
- out_);
-
- out_ << std::endl;
+ if (subst.used[SUBSTITUTION_INCLUDE_DIRS]) {
+ out_ << kSubstitutionNinjaNames[SUBSTITUTION_INCLUDE_DIRS] << " =";
+ PathOutput include_path_output(path_output_.current_dir(),
+ ESCAPE_NINJA_COMMAND);
+ RecursiveTargetConfigToStream<SourceDir>(
+ target_, &ConfigValues::include_dirs,
+ IncludeWriter(include_path_output), out_);
+ out_ << std::endl;
+ }
// C flags and friends.
EscapeOptions flag_escape_options = GetFlagOptions();
-#define WRITE_FLAGS(name) \
- out_ << #name " ="; \
- RecursiveTargetConfigStringsToStream(target_, &ConfigValues::name, \
- flag_escape_options, out_); \
- out_ << std::endl;
+#define WRITE_FLAGS(name, subst_enum) \
+ if (subst.used[subst_enum]) { \
+ out_ << kSubstitutionNinjaNames[subst_enum] << " ="; \
+ RecursiveTargetConfigStringsToStream(target_, &ConfigValues::name, \
+ flag_escape_options, out_); \
+ out_ << std::endl; \
+ }
- WRITE_FLAGS(cflags)
- WRITE_FLAGS(cflags_c)
- WRITE_FLAGS(cflags_cc)
- WRITE_FLAGS(cflags_objc)
- WRITE_FLAGS(cflags_objcc)
+ WRITE_FLAGS(cflags, SUBSTITUTION_CFLAGS)
+ WRITE_FLAGS(cflags_c, SUBSTITUTION_CFLAGS_C)
+ WRITE_FLAGS(cflags_cc, SUBSTITUTION_CFLAGS_CC)
+ WRITE_FLAGS(cflags_objc, SUBSTITUTION_CFLAGS_OBJC)
+ WRITE_FLAGS(cflags_objcc, SUBSTITUTION_CFLAGS_OBJCC)
#undef WRITE_FLAGS
- out_ << std::endl;
+ WriteSharedVars(subst);
}
void NinjaBinaryTargetWriter::WriteSources(
const Target::FileList& sources = target_->sources();
object_files->reserve(sources.size());
- std::string implicit_deps = GetSourcesImplicitDeps();
+ OutputFile input_dep =
+ WriteInputDepsStampAndGetDep(std::vector<const Target*>());
+ std::string rule_prefix = GetNinjaRulePrefixForToolchain(settings_);
+
+ std::vector<OutputFile> tool_outputs; // Prevent reallocation in loop.
for (size_t i = 0; i < sources.size(); i++) {
- const SourceFile& input_file = sources[i];
-
- SourceFileType input_file_type = GetSourceFileType(input_file,
- settings_->target_os());
- if (input_file_type == SOURCE_UNKNOWN)
- continue; // Skip unknown file types.
- std::string command =
- helper_.GetRuleForSourceType(settings_, input_file_type);
- if (command.empty())
- continue; // Skip files not needing compilation.
-
- OutputFile output_file = helper_.GetOutputFileForSource(
- target_, input_file, input_file_type);
- object_files->push_back(output_file);
-
- out_ << "build ";
- path_output_.WriteFile(out_, output_file);
- out_ << ": " << command << " ";
- path_output_.WriteFile(out_, input_file);
- out_ << implicit_deps << std::endl;
+ Toolchain::ToolType tool_type = Toolchain::TYPE_NONE;
+ if (!GetOutputFilesForSource(target_, sources[i],
+ &tool_type, &tool_outputs))
+ continue; // No output for this source.
+
+ if (tool_type != Toolchain::TYPE_NONE) {
+ out_ << "build";
+ path_output_.WriteFiles(out_, tool_outputs);
+ out_ << ": " << rule_prefix << Toolchain::ToolTypeToName(tool_type);
+ out_ << " ";
+ path_output_.WriteFile(out_, sources[i]);
+ if (!input_dep.value().empty()) {
+ // Write out the input dependencies as an order-only dependency. This
+ // will cause Ninja to make sure the inputs are up-to-date before
+ // compiling this source, but changes in the inputs deps won't cause
+ // the file to be recompiled.
+ //
+ // This is important to prevent changes in unrelated actions that
+ // are upstream of this target from causing everything to be recompiled.
+ //
+ // Why can we get away with this rather than using implicit deps ("|",
+ // which will force rebuilds when the inputs change)? For source code,
+ // the computed dependencies of all headers will be computed by the
+ // compiler, which will cause source rebuilds if any "real" upstream
+ // dependencies change.
+ //
+ // If a .cc file is generated by an input dependency, Ninja will see
+ // the input to the build rule doesn't exist, and that it is an output
+ // from a previous step, and build the previous step first. This is a
+ // "real" dependency and doesn't need | or || to express.
+ //
+ // The only case where this rule matters is for the first build where
+ // no .d files exist, and Ninja doesn't know what that source file
+ // depends on. In this case it's sufficient to ensure that the upstream
+ // dependencies are built first. This is exactly what Ninja's order-
+ // only dependencies expresses.
+ out_ << " || ";
+ path_output_.WriteFile(out_, input_dep);
+ }
+ out_ << std::endl;
+ }
+
+ // It's theoretically possible for a compiler to produce more than one
+ // output, but we'll only link to the first output.
+ object_files->push_back(tool_outputs[0]);
}
out_ << std::endl;
}
void NinjaBinaryTargetWriter::WriteLinkerStuff(
const std::vector<OutputFile>& object_files) {
- // Manifest file on Windows.
- // TODO(brettw) this seems not to be necessary for static libs, skip in
- // that case?
- OutputFile windows_manifest;
- if (settings_->IsWin()) {
- windows_manifest.value().assign(helper_.GetTargetOutputDir(target_));
- windows_manifest.value().append(target_->label().name());
- windows_manifest.value().append(".intermediate.manifest");
- out_ << "manifests = ";
- path_output_.WriteFile(out_, windows_manifest);
- out_ << std::endl;
- }
+ std::vector<OutputFile> output_files;
+ SubstitutionWriter::ApplyListToLinkerAsOutputFile(
+ target_, tool_, tool_->outputs(), &output_files);
- const Toolchain::Tool& tool = toolchain_->GetTool(tool_type_);
- WriteLinkerFlags(tool, windows_manifest);
- WriteLibs(tool);
-
- // The external output file is the one that other libs depend on.
- OutputFile external_output_file = helper_.GetTargetOutputFile(target_);
-
- // The internal output file is the "main thing" we think we're making. In
- // the case of shared libraries, this is the shared library and the external
- // output file is the import library. In other cases, the internal one and
- // the external one are the same.
- OutputFile internal_output_file;
- if (target_->output_type() == Target::SHARED_LIBRARY) {
- if (settings_->IsWin()) {
- internal_output_file.value() =
- target_->settings()->toolchain_output_subdir().value();
- internal_output_file.value().append(target_->label().name());
- internal_output_file.value().append(".dll");
- } else {
- internal_output_file = external_output_file;
- }
- } else {
- internal_output_file = external_output_file;
- }
+ out_ << "build";
+ path_output_.WriteFiles(out_, output_files);
- // In Python see "self.ninja.build(output, command, input,"
- WriteLinkCommand(external_output_file, internal_output_file, object_files);
+ out_ << ": "
+ << GetNinjaRulePrefixForToolchain(settings_)
+ << Toolchain::ToolTypeToName(
+ target_->toolchain()->GetToolTypeForTargetFinalOutput(target_));
- if (target_->output_type() == Target::SHARED_LIBRARY) {
- // The shared object name doesn't include a path.
- out_ << " soname = ";
- out_ << FindFilename(&internal_output_file.value());
- out_ << std::endl;
+ UniqueVector<OutputFile> extra_object_files;
+ UniqueVector<const Target*> linkable_deps;
+ UniqueVector<const Target*> non_linkable_deps;
+ GetDeps(&extra_object_files, &linkable_deps, &non_linkable_deps);
- out_ << " lib = ";
- path_output_.WriteFile(out_, internal_output_file);
- out_ << std::endl;
+ // Object files.
+ for (size_t i = 0; i < object_files.size(); i++) {
+ out_ << " ";
+ path_output_.WriteFile(out_, object_files[i]);
+ }
+ for (size_t i = 0; i < extra_object_files.size(); i++) {
+ out_ << " ";
+ path_output_.WriteFile(out_, extra_object_files[i]);
+ }
- if (settings_->IsWin()) {
- out_ << " dll = ";
- path_output_.WriteFile(out_, internal_output_file);
- out_ << std::endl;
- }
+ std::vector<OutputFile> implicit_deps;
+ std::vector<OutputFile> solibs;
- if (settings_->IsWin()) {
- out_ << " implibflag = /IMPLIB:";
- path_output_.WriteFile(out_, external_output_file);
- out_ << std::endl;
+ for (size_t i = 0; i < linkable_deps.size(); i++) {
+ const Target* cur = linkable_deps[i];
+
+ // All linkable deps should have a link output file.
+ DCHECK(!cur->link_output_file().value().empty())
+ << "No link output file for "
+ << target_->label().GetUserVisibleName(false);
+
+ if (cur->dependency_output_file().value() !=
+ cur->link_output_file().value()) {
+ // This is a shared library with separate link and deps files. Save for
+ // later.
+ implicit_deps.push_back(cur->dependency_output_file());
+ solibs.push_back(cur->link_output_file());
+ } else {
+ // Normal case, just link to this target.
+ out_ << " ";
+ path_output_.WriteFile(out_, cur->link_output_file());
}
+ }
- // TODO(brettw) postbuild steps.
- if (settings_->IsMac())
- 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)";
+ // Append implicit dependencies collected above.
+ if (!implicit_deps.empty()) {
+ out_ << " |";
+ path_output_.WriteFiles(out_, implicit_deps);
}
+ // Append data dependencies as order-only dependencies.
+ //
+ // This will include data dependencies and input dependencies (like when
+ // this target depends on an action). Having the data dependencies in this
+ // list ensures that the data is available at runtime when the user builds
+ // this target.
+ //
+ // The action dependencies are not strictly necessary in this case. They
+ // should also have been collected via the input deps stamp that each source
+ // file has for an order-only dependency, and since this target depends on
+ // the sources, there is already an implicit order-only dependency. However,
+ // it's extra work to separate these out and there's no disadvantage to
+ // listing them again.
+ WriteOrderOnlyDependencies(non_linkable_deps);
+
+ // End of the link "build" line.
out_ << std::endl;
+
+ // These go in the inner scope of the link line.
+ WriteLinkerFlags();
+ WriteLibs();
+ WriteOutputExtension();
+ WriteSolibs(solibs);
}
-void NinjaBinaryTargetWriter::WriteLinkerFlags(
- const Toolchain::Tool& tool,
- const OutputFile& windows_manifest) {
- out_ << "ldflags =";
+void NinjaBinaryTargetWriter::WriteLinkerFlags() {
+ out_ << " ldflags =";
// First the ldflags from the target and its config.
EscapeOptions flag_options = GetFlagOptions();
if (!all_lib_dirs.empty()) {
// Since we're passing these on the command line to the linker and not
// to Ninja, we need to do shell escaping.
- PathOutput lib_path_output(path_output_.current_dir(), ESCAPE_NINJA_SHELL,
- true);
+ PathOutput lib_path_output(path_output_.current_dir(),
+ ESCAPE_NINJA_COMMAND);
for (size_t i = 0; i < all_lib_dirs.size(); i++) {
- out_ << " " << tool.lib_dir_prefix;
+ out_ << " " << tool_->lib_dir_switch();
lib_path_output.WriteDir(out_, all_lib_dirs[i],
PathOutput::DIR_NO_LAST_SLASH);
}
}
-
- // Append manifest flag on Windows to reference our file.
- // HACK ERASEME BRETTW FIXME
- if (settings_->IsWin()) {
- out_ << " /MANIFEST /ManifestFile:";
- path_output_.WriteFile(out_, windows_manifest);
- }
out_ << std::endl;
}
-void NinjaBinaryTargetWriter::WriteLibs(const Toolchain::Tool& tool) {
- out_ << "libs =";
+void NinjaBinaryTargetWriter::WriteLibs() {
+ out_ << " libs =";
// Libraries that have been recursively pushed through the dependency tree.
EscapeOptions lib_escape_opts;
- lib_escape_opts.mode = ESCAPE_NINJA_SHELL;
+ lib_escape_opts.mode = ESCAPE_NINJA_COMMAND;
const OrderedSet<std::string> all_libs = target_->all_libs();
const std::string framework_ending(".framework");
for (size_t i = 0; i < all_libs.size(); i++) {
all_libs[i].substr(0, all_libs[i].size() - framework_ending.size()),
lib_escape_opts);
} else {
- out_ << " " << tool.lib_prefix;
+ out_ << " " << tool_->lib_switch();
EscapeStringToStream(out_, all_libs[i], lib_escape_opts);
}
}
out_ << std::endl;
}
-void NinjaBinaryTargetWriter::WriteLinkCommand(
- const OutputFile& external_output_file,
- const OutputFile& internal_output_file,
- const std::vector<OutputFile>& object_files) {
- out_ << "build ";
- path_output_.WriteFile(out_, internal_output_file);
- if (external_output_file != internal_output_file) {
- out_ << " ";
- path_output_.WriteFile(out_, external_output_file);
- }
- out_ << ": "
- << helper_.GetRulePrefix(target_->settings())
- << Toolchain::ToolTypeToName(tool_type_);
-
- std::set<OutputFile> extra_object_files;
- std::vector<const Target*> linkable_deps;
- std::vector<const Target*> non_linkable_deps;
- GetDeps(&extra_object_files, &linkable_deps, &non_linkable_deps);
-
- // Object files.
- for (size_t i = 0; i < object_files.size(); i++) {
- out_ << " ";
- path_output_.WriteFile(out_, object_files[i]);
- }
- for (std::set<OutputFile>::iterator i = extra_object_files.begin();
- i != extra_object_files.end(); ++i) {
- out_ << " ";
- path_output_.WriteFile(out_, *i);
- }
-
- // Libs.
- for (size_t i = 0; i < linkable_deps.size(); i++) {
- out_ << " ";
- path_output_.WriteFile(out_, helper_.GetTargetOutputFile(linkable_deps[i]));
+void NinjaBinaryTargetWriter::WriteOutputExtension() {
+ out_ << " output_extension = ";
+ if (target_->output_extension().empty()) {
+ // Use the default from the tool.
+ out_ << tool_->default_output_extension();
+ } else {
+ // Use the one specified in the target. Note that the one in the target
+ // does not include the leading dot, so add that.
+ out_ << "." << target_->output_extension();
}
+ out_ << std::endl;
+}
- // Append data dependencies as implicit dependencies.
- WriteImplicitDependencies(non_linkable_deps);
+void NinjaBinaryTargetWriter::WriteSolibs(
+ const std::vector<OutputFile>& solibs) {
+ if (solibs.empty())
+ return;
+ out_ << " solibs =";
+ path_output_.WriteFiles(out_, solibs);
out_ << std::endl;
}
// depend on this will reference the object files directly. However, writing
// this rule allows the user to type the name of the target and get a build
// which can be convenient for development.
- out_ << "build ";
- path_output_.WriteFile(out_, helper_.GetTargetOutputFile(target_));
- out_ << ": "
- << helper_.GetRulePrefix(target_->settings())
- << "stamp";
-
- std::set<OutputFile> extra_object_files;
- std::vector<const Target*> linkable_deps;
- std::vector<const Target*> non_linkable_deps;
+ UniqueVector<OutputFile> extra_object_files;
+ UniqueVector<const Target*> linkable_deps;
+ UniqueVector<const Target*> non_linkable_deps;
GetDeps(&extra_object_files, &linkable_deps, &non_linkable_deps);
// The classifier should never put extra object files in a source set:
// deps instead.
DCHECK(extra_object_files.empty());
- for (size_t i = 0; i < object_files.size(); i++) {
- out_ << " ";
- path_output_.WriteFile(out_, object_files[i]);
- }
-
- // Append data dependencies as implicit dependencies.
- WriteImplicitDependencies(non_linkable_deps);
+ std::vector<OutputFile> order_only_deps;
+ for (size_t i = 0; i < non_linkable_deps.size(); i++)
+ order_only_deps.push_back(non_linkable_deps[i]->dependency_output_file());
- out_ << std::endl;
+ WriteStampForTarget(object_files, order_only_deps);
}
void NinjaBinaryTargetWriter::GetDeps(
- std::set<OutputFile>* extra_object_files,
- std::vector<const Target*>* linkable_deps,
- std::vector<const Target*>* non_linkable_deps) const {
- const LabelTargetVector& deps = target_->deps();
- const std::set<const Target*>& inherited = target_->inherited_libraries();
-
- // Normal deps.
- for (size_t i = 0; i < deps.size(); i++) {
- if (inherited.find(deps[i].ptr) != inherited.end())
- continue; // Don't add dupes.
- ClassifyDependency(deps[i].ptr, extra_object_files,
+ UniqueVector<OutputFile>* extra_object_files,
+ UniqueVector<const Target*>* linkable_deps,
+ UniqueVector<const Target*>* non_linkable_deps) const {
+ const UniqueVector<const Target*>& inherited =
+ target_->inherited_libraries();
+
+ // Normal public/private deps.
+ for (DepsIterator iter(target_, DepsIterator::LINKED_ONLY); !iter.done();
+ iter.Advance()) {
+ ClassifyDependency(iter.target(), extra_object_files,
linkable_deps, non_linkable_deps);
}
// Inherited libraries.
- for (std::set<const Target*>::const_iterator i = inherited.begin();
- i != inherited.end(); ++i) {
- ClassifyDependency(*i, extra_object_files,
+ for (size_t i = 0; i < inherited.size(); i++) {
+ ClassifyDependency(inherited[i], extra_object_files,
linkable_deps, non_linkable_deps);
}
// Data deps.
- const LabelTargetVector& datadeps = target_->datadeps();
- for (size_t i = 0; i < datadeps.size(); i++)
- non_linkable_deps->push_back(datadeps[i].ptr);
+ const LabelTargetVector& data_deps = target_->data_deps();
+ for (size_t i = 0; i < data_deps.size(); i++)
+ non_linkable_deps->push_back(data_deps[i].ptr);
}
void NinjaBinaryTargetWriter::ClassifyDependency(
const Target* dep,
- std::set<OutputFile>* extra_object_files,
- std::vector<const Target*>* linkable_deps,
- std::vector<const Target*>* non_linkable_deps) const {
- // Only these types of outputs have libraries linked into them. Child deps of
- // static libraries get pushed up the dependency tree until one of these is
- // reached, and source sets don't link at all.
- bool can_link_libs =
- (target_->output_type() == Target::EXECUTABLE ||
- target_->output_type() == Target::SHARED_LIBRARY);
+ UniqueVector<OutputFile>* extra_object_files,
+ UniqueVector<const Target*>* linkable_deps,
+ UniqueVector<const Target*>* non_linkable_deps) const {
+ // Only the following types of outputs have libraries linked into them:
+ // EXECUTABLE
+ // SHARED_LIBRARY
+ // _complete_ STATIC_LIBRARY
+ //
+ // Child deps of intermediate static libraries get pushed up the
+ // dependency tree until one of these is reached, and source sets
+ // don't link at all.
+ bool can_link_libs = target_->IsFinal();
if (dep->output_type() == Target::SOURCE_SET) {
- if (target_->output_type() == Target::SOURCE_SET) {
- // When a source set depends on another source set, add it as a data
- // dependency so if the user says "ninja second_source_set" it will
- // also compile the first (what you would expect) even though we'll
- // never do anything with the first one's files.
- non_linkable_deps->push_back(dep);
- } else {
- // Linking in a source set, copy its object files.
+ // Source sets have their object files linked into final targets
+ // (shared libraries, executables, and complete static
+ // libraries). Intermediate static libraries and other source sets
+ // just forward the dependency, otherwise the files in the source
+ // set can easily get linked more than once which will cause
+ // multiple definition errors.
+ if (can_link_libs) {
+ // Linking in a source set to an executable, shared library, or
+ // complete static library, so copy its object files.
+ std::vector<OutputFile> tool_outputs; // Prevent allocation in loop.
for (size_t i = 0; i < dep->sources().size(); i++) {
- SourceFileType input_file_type = GetSourceFileType(
- dep->sources()[i], dep->settings()->target_os());
- if (input_file_type != SOURCE_UNKNOWN &&
- input_file_type != SOURCE_H) {
- // Note we need to specify the target as the source_set target
- // itself, since this is used to prefix the object file name.
- extra_object_files->insert(helper_.GetOutputFileForSource(
- dep, dep->sources()[i], input_file_type));
+ Toolchain::ToolType tool_type = Toolchain::TYPE_NONE;
+ if (GetOutputFilesForSource(dep, dep->sources()[i], &tool_type,
+ &tool_outputs)) {
+ // Only link the first output if there are more than one.
+ extra_object_files->push_back(tool_outputs[0]);
}
}
}
}
}
-void NinjaBinaryTargetWriter::WriteImplicitDependencies(
- const std::vector<const Target*>& non_linkable_deps) {
+void NinjaBinaryTargetWriter::WriteOrderOnlyDependencies(
+ const UniqueVector<const Target*>& non_linkable_deps) {
const std::vector<SourceFile>& data = target_->data();
if (!non_linkable_deps.empty() || !data.empty()) {
out_ << " ||";
// Non-linkable targets.
for (size_t i = 0; i < non_linkable_deps.size(); i++) {
out_ << " ";
- path_output_.WriteFile(out_,
- helper_.GetTargetOutputFile(non_linkable_deps[i]));
+ path_output_.WriteFile(
+ out_, non_linkable_deps[i]->dependency_output_file());
}
+ }
+}
- // Data files.
- const std::vector<SourceFile>& data = target_->data();
- for (size_t i = 0; i < data.size(); i++) {
- out_ << " ";
- path_output_.WriteFile(out_, data[i]);
- }
+bool NinjaBinaryTargetWriter::GetOutputFilesForSource(
+ const Target* target,
+ const SourceFile& source,
+ Toolchain::ToolType* computed_tool_type,
+ std::vector<OutputFile>* outputs) const {
+ outputs->clear();
+ *computed_tool_type = Toolchain::TYPE_NONE;
+
+ SourceFileType file_type = GetSourceFileType(source);
+ if (file_type == SOURCE_UNKNOWN)
+ return false;
+ if (file_type == SOURCE_O) {
+ // Object files just get passed to the output and not compiled.
+ outputs->push_back(OutputFile(settings_->build_settings(), source));
+ return true;
}
+
+ *computed_tool_type =
+ target->toolchain()->GetToolTypeForSourceType(file_type);
+ if (*computed_tool_type == Toolchain::TYPE_NONE)
+ return false; // No tool for this file (it's a header file or something).
+ const Tool* tool = target->toolchain()->GetTool(*computed_tool_type);
+ if (!tool)
+ return false; // Tool does not apply for this toolchain.file.
+
+ // Figure out what output(s) this compiler produces.
+ SubstitutionWriter::ApplyListToCompilerAsOutputFile(
+ target, source, tool->outputs(), outputs);
+ return !outputs->empty();
}