347 lines
12 KiB
C++
347 lines
12 KiB
C++
|
// Copyright (c) 2013 The Chromium Authors. All rights reserved.
|
||
|
// Use of this source code is governed by a BSD-style license that can be
|
||
|
// found in the LICENSE file.
|
||
|
|
||
|
#include "tools/gn/ninja_binary_target_writer.h"
|
||
|
|
||
|
#include "tools/gn/config_values_extractors.h"
|
||
|
#include "tools/gn/err.h"
|
||
|
#include "tools/gn/escape.h"
|
||
|
#include "tools/gn/string_utils.h"
|
||
|
|
||
|
namespace {
|
||
|
|
||
|
// Returns the proper escape options for writing compiler and linker flags.
|
||
|
EscapeOptions GetFlagOptions() {
|
||
|
EscapeOptions opts;
|
||
|
opts.mode = ESCAPE_NINJA;
|
||
|
|
||
|
// 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
|
||
|
// buildfiles if it wants such things quoted.
|
||
|
opts.inhibit_quoting = true;
|
||
|
|
||
|
return opts;
|
||
|
}
|
||
|
|
||
|
struct DefineWriter {
|
||
|
void operator()(const std::string& s, std::ostream& out) const {
|
||
|
out << " -D" << s;
|
||
|
}
|
||
|
};
|
||
|
|
||
|
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() {
|
||
|
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 << "\"";
|
||
|
}
|
||
|
|
||
|
const NinjaHelper& helper;
|
||
|
PathOutput& path_output_;
|
||
|
bool old_inhibit_quoting_; // So we can put the PathOutput back.
|
||
|
};
|
||
|
|
||
|
} // namespace
|
||
|
|
||
|
NinjaBinaryTargetWriter::NinjaBinaryTargetWriter(const Target* target,
|
||
|
std::ostream& out)
|
||
|
: NinjaTargetWriter(target, out) {
|
||
|
}
|
||
|
|
||
|
NinjaBinaryTargetWriter::~NinjaBinaryTargetWriter() {
|
||
|
}
|
||
|
|
||
|
void NinjaBinaryTargetWriter::Run() {
|
||
|
WriteEnvironment();
|
||
|
|
||
|
WriteCompilerVars();
|
||
|
|
||
|
std::vector<OutputFile> obj_files;
|
||
|
WriteSources(&obj_files);
|
||
|
|
||
|
WriteLinkerStuff(obj_files);
|
||
|
}
|
||
|
|
||
|
void NinjaBinaryTargetWriter::WriteCompilerVars() {
|
||
|
// Defines.
|
||
|
out_ << "defines =";
|
||
|
RecursiveTargetConfigToStream(target_, &ConfigValues::defines,
|
||
|
DefineWriter(), out_);
|
||
|
out_ << std::endl;
|
||
|
|
||
|
// Includes.
|
||
|
out_ << "includes =";
|
||
|
RecursiveTargetConfigToStream(target_, &ConfigValues::includes,
|
||
|
IncludeWriter(path_output_, helper_), 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;
|
||
|
|
||
|
WRITE_FLAGS(cflags)
|
||
|
WRITE_FLAGS(cflags_c)
|
||
|
WRITE_FLAGS(cflags_cc)
|
||
|
WRITE_FLAGS(cflags_objc)
|
||
|
WRITE_FLAGS(cflags_objcc)
|
||
|
|
||
|
#undef WRITE_FLAGS
|
||
|
|
||
|
out_ << std::endl;
|
||
|
}
|
||
|
|
||
|
void NinjaBinaryTargetWriter::WriteSources(
|
||
|
std::vector<OutputFile>* object_files) {
|
||
|
const Target::FileList& sources = target_->sources();
|
||
|
object_files->reserve(sources.size());
|
||
|
|
||
|
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.
|
||
|
const char* command = GetCommandForSourceType(input_file_type);
|
||
|
if (!command)
|
||
|
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_ << std::endl;
|
||
|
}
|
||
|
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;
|
||
|
}
|
||
|
|
||
|
// Linker flags, append manifest flag on Windows to reference our file.
|
||
|
out_ << "ldflags =";
|
||
|
RecursiveTargetConfigStringsToStream(target_, &ConfigValues::ldflags,
|
||
|
GetFlagOptions(), out_);
|
||
|
// HACK ERASEME BRETTW FIXME
|
||
|
if (settings_->IsWin()) {
|
||
|
out_ << " /MANIFEST /ManifestFile:";
|
||
|
path_output_.WriteFile(out_, windows_manifest);
|
||
|
out_ << " /DEBUG /MACHINE:X86 /LIBPATH:\"C:\\Program Files (x86)\\Windows Kits\\8.0\\Lib\\win8\\um\\x86\" /DELAYLOAD:dbghelp.dll /DELAYLOAD:dwmapi.dll /DELAYLOAD:shell32.dll /DELAYLOAD:uxtheme.dll /safeseh /dynamicbase /ignore:4199 /ignore:4221 /nxcompat /SUBSYSTEM:CONSOLE /INCREMENTAL /FIXED:NO /DYNAMICBASE:NO wininet.lib dnsapi.lib version.lib msimg32.lib ws2_32.lib usp10.lib psapi.lib dbghelp.lib winmm.lib shlwapi.lib kernel32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib user32.lib uuid.lib odbc32.lib odbccp32.lib delayimp.lib /NXCOMPAT";
|
||
|
}
|
||
|
out_ << std::endl;
|
||
|
|
||
|
// Libraries to link.
|
||
|
out_ << "libs =";
|
||
|
if (settings_->IsMac()) {
|
||
|
// TODO(brettw) fix this.
|
||
|
out_ << " -framework AppKit -framework ApplicationServices -framework Carbon -framework CoreFoundation -framework Foundation -framework IOKit -framework Security";
|
||
|
}
|
||
|
out_ << std::endl;
|
||
|
|
||
|
// 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 = OutputFile(target_->label().name() + ".dll");
|
||
|
} else {
|
||
|
internal_output_file = external_output_file;
|
||
|
}
|
||
|
} else {
|
||
|
internal_output_file = external_output_file;
|
||
|
}
|
||
|
|
||
|
// In Python see "self.ninja.build(output, command, input,"
|
||
|
WriteLinkCommand(external_output_file, internal_output_file, object_files);
|
||
|
|
||
|
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;
|
||
|
|
||
|
out_ << " lib = ";
|
||
|
path_output_.WriteFile(out_, internal_output_file);
|
||
|
out_ << std::endl;
|
||
|
|
||
|
if (settings_->IsWin()) {
|
||
|
out_ << " dll = ";
|
||
|
path_output_.WriteFile(out_, internal_output_file);
|
||
|
out_ << std::endl;
|
||
|
}
|
||
|
|
||
|
if (settings_->IsWin()) {
|
||
|
out_ << " implibflag = /IMPLIB:";
|
||
|
path_output_.WriteFile(out_, external_output_file);
|
||
|
out_ << std::endl;
|
||
|
}
|
||
|
|
||
|
// 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)";
|
||
|
}
|
||
|
|
||
|
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_ << ": " << GetCommandForTargetType();
|
||
|
|
||
|
// Object files.
|
||
|
for (size_t i = 0; i < object_files.size(); i++) {
|
||
|
out_ << " ";
|
||
|
path_output_.WriteFile(out_, object_files[i]);
|
||
|
}
|
||
|
|
||
|
// Library inputs (deps and inherited static libraries).
|
||
|
//
|
||
|
// Static libraries since they're just a collection of the object files so
|
||
|
// don't need libraries linked with them, but we still need to go through
|
||
|
// the list and find non-linkable data deps in the "deps" section. We'll
|
||
|
// collect all non-linkable deps and put it in the order-only deps below.
|
||
|
std::vector<const Target*> extra_data_deps;
|
||
|
const std::vector<const Target*>& deps = target_->deps();
|
||
|
const std::set<const Target*>& inherited = target_->inherited_libraries();
|
||
|
for (size_t i = 0; i < deps.size(); i++) {
|
||
|
if (inherited.find(deps[i]) != inherited.end())
|
||
|
continue;
|
||
|
if (target_->output_type() != Target::STATIC_LIBRARY &&
|
||
|
deps[i]->IsLinkable()) {
|
||
|
out_ << " ";
|
||
|
path_output_.WriteFile(out_, helper_.GetTargetOutputFile(deps[i]));
|
||
|
} else {
|
||
|
extra_data_deps.push_back(deps[i]);
|
||
|
}
|
||
|
}
|
||
|
for (std::set<const Target*>::const_iterator i = inherited.begin();
|
||
|
i != inherited.end(); ++i) {
|
||
|
if (target_->output_type() == Target::STATIC_LIBRARY) {
|
||
|
extra_data_deps.push_back(*i);
|
||
|
} else {
|
||
|
out_ << " ";
|
||
|
path_output_.WriteFile(out_, helper_.GetTargetOutputFile(*i));
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Append data dependencies as order-only dependencies.
|
||
|
const std::vector<const Target*>& datadeps = target_->datadeps();
|
||
|
const std::vector<SourceFile>& data = target_->data();
|
||
|
if (!extra_data_deps.empty() || !datadeps.empty() || !data.empty()) {
|
||
|
out_ << " ||";
|
||
|
|
||
|
// Non-linkable deps in the deps section above.
|
||
|
for (size_t i = 0; i < extra_data_deps.size(); i++) {
|
||
|
out_ << " ";
|
||
|
path_output_.WriteFile(out_,
|
||
|
helper_.GetTargetOutputFile(extra_data_deps[i]));
|
||
|
}
|
||
|
|
||
|
// Data deps.
|
||
|
for (size_t i = 0; i < datadeps.size(); i++) {
|
||
|
out_ << " ";
|
||
|
path_output_.WriteFile(out_, helper_.GetTargetOutputFile(datadeps[i]));
|
||
|
}
|
||
|
|
||
|
// 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]);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
out_ << std::endl;
|
||
|
}
|
||
|
|
||
|
const char* NinjaBinaryTargetWriter::GetCommandForSourceType(
|
||
|
SourceFileType type) const {
|
||
|
if (type == SOURCE_C)
|
||
|
return "cc";
|
||
|
if (type == SOURCE_CC)
|
||
|
return "cxx";
|
||
|
|
||
|
// TODO(brettw) asm files.
|
||
|
|
||
|
if (settings_->IsMac()) {
|
||
|
if (type == SOURCE_M)
|
||
|
return "objc";
|
||
|
if (type == SOURCE_MM)
|
||
|
return "objcxx";
|
||
|
}
|
||
|
|
||
|
if (settings_->IsWin()) {
|
||
|
if (type == SOURCE_RC)
|
||
|
return "rc";
|
||
|
}
|
||
|
|
||
|
// TODO(brettw) stuff about "S" files on non-Windows.
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
const char* NinjaBinaryTargetWriter::GetCommandForTargetType() const {
|
||
|
if (target_->output_type() == Target::STATIC_LIBRARY) {
|
||
|
// TODO(brettw) stuff about standalong static libraryes on Unix in
|
||
|
// WriteTarget in the Python one, and lots of postbuild steps.
|
||
|
return "alink";
|
||
|
}
|
||
|
|
||
|
if (target_->output_type() == Target::SHARED_LIBRARY)
|
||
|
return "solink";
|
||
|
|
||
|
return "link";
|
||
|
}
|