Home | History | Annotate | Download | only in gn
      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_target_writer.h"
      6 
      7 #include <fstream>
      8 #include <sstream>
      9 
     10 #include "base/file_util.h"
     11 #include "base/logging.h"
     12 #include "base/strings/string_util.h"
     13 #include "tools/gn/config_values_extractors.h"
     14 #include "tools/gn/err.h"
     15 #include "tools/gn/escape.h"
     16 #include "tools/gn/file_template.h"
     17 #include "tools/gn/filesystem_utils.h"
     18 #include "tools/gn/location.h"
     19 #include "tools/gn/path_output.h"
     20 #include "tools/gn/scheduler.h"
     21 #include "tools/gn/string_utils.h"
     22 #include "tools/gn/target.h"
     23 
     24 namespace {
     25 
     26 static const char kCustomTargetSourceKey[] = "{{source}}";
     27 static const char kCustomTargetSourceNamePartKey[] = "{{source_name_part}}";
     28 
     29 struct DefineWriter {
     30   void operator()(const std::string& s, std::ostream& out) const {
     31     out << " -D" << s;
     32   }
     33 };
     34 
     35 struct IncludeWriter {
     36   IncludeWriter(PathOutput& path_output,
     37                 const NinjaHelper& h)
     38       : helper(h),
     39         path_output_(path_output),
     40         old_inhibit_quoting_(path_output.inhibit_quoting()) {
     41     // Inhibit quoting since we'll put quotes around the whole thing ourselves.
     42     // Since we're writing in NINJA escaping mode, this won't actually do
     43     // anything, but I think we may need to change to shell-and-then-ninja
     44     // escaping for this in the future.
     45     path_output_.set_inhibit_quoting(true);
     46   }
     47   ~IncludeWriter() {
     48     path_output_.set_inhibit_quoting(old_inhibit_quoting_);
     49   }
     50 
     51   void operator()(const SourceDir& d, std::ostream& out) const {
     52     out << " \"-I";
     53     // It's important not to include the trailing slash on directories or on
     54     // Windows it will be a backslash and the compiler might think we're
     55     // escaping the quote!
     56     path_output_.WriteDir(out, d, PathOutput::DIR_NO_LAST_SLASH);
     57     out << "\"";
     58   }
     59 
     60   const NinjaHelper& helper;
     61   PathOutput& path_output_;
     62   bool old_inhibit_quoting_;  // So we can put the PathOutput back.
     63 };
     64 
     65 }  // namespace
     66 
     67 NinjaTargetWriter::NinjaTargetWriter(const Target* target, std::ostream& out)
     68     : settings_(target->settings()),
     69       target_(target),
     70       out_(out),
     71       path_output_(settings_->build_settings()->build_dir(),
     72                    ESCAPE_NINJA, true),
     73       helper_(settings_->build_settings()) {
     74 }
     75 
     76 NinjaTargetWriter::~NinjaTargetWriter() {
     77 }
     78 
     79 void NinjaTargetWriter::Run() {
     80   // TODO(brettw) have a better way to do the environment setup on Windows.
     81   if (target_->settings()->IsWin())
     82     out_ << "arch = environment.x86\n";
     83 
     84   if (target_->output_type() == Target::COPY_FILES) {
     85     WriteCopyRules();
     86   } else if (target_->output_type() == Target::CUSTOM) {
     87     WriteCustomRules();
     88   } else {
     89     WriteCompilerVars();
     90 
     91     std::vector<OutputFile> obj_files;
     92     WriteSources(&obj_files);
     93 
     94     WriteLinkerStuff(obj_files);
     95   }
     96 }
     97 
     98 // static
     99 void NinjaTargetWriter::RunAndWriteFile(const Target* target) {
    100   if (target->output_type() == Target::NONE)
    101     return;
    102 
    103   const Settings* settings = target->settings();
    104   NinjaHelper helper(settings->build_settings());
    105 
    106   base::FilePath ninja_file(settings->build_settings()->GetFullPath(
    107       helper.GetNinjaFileForTarget(target).GetSourceFile(
    108           settings->build_settings())));
    109 
    110   if (g_scheduler->verbose_logging())
    111     g_scheduler->Log("Writing", FilePathToUTF8(ninja_file));
    112 
    113   file_util::CreateDirectory(ninja_file.DirName());
    114 
    115   // It's rediculously faster to write to a string and then write that to
    116   // disk in one operation than to use an fstream here.
    117   std::stringstream file;
    118   if (file.fail()) {
    119     g_scheduler->FailWithError(
    120         Err(Location(), "Error writing ninja file.",
    121             "Unable to open \"" + FilePathToUTF8(ninja_file) + "\"\n"
    122             "for writing."));
    123     return;
    124   }
    125 
    126   NinjaTargetWriter gen(target, file);
    127   gen.Run();
    128 
    129   std::string contents = file.str();
    130   file_util::WriteFile(ninja_file, contents.c_str(), contents.size());
    131 }
    132 
    133 void NinjaTargetWriter::WriteCopyRules() {
    134   // The dest dir should be inside the output dir so we can just remove the
    135   // prefix and get ninja-relative paths.
    136   const std::string& output_dir =
    137       settings_->build_settings()->build_dir().value();
    138   const std::string& dest_dir = target_->destdir().value();
    139   DCHECK(StartsWithASCII(dest_dir, output_dir, true));
    140   std::string relative_dest_dir(&dest_dir[output_dir.size()],
    141                                 dest_dir.size() - output_dir.size());
    142 
    143   const Target::FileList& sources = target_->sources();
    144   std::vector<OutputFile> dest_files;
    145   dest_files.reserve(sources.size());
    146 
    147   // Write out rules for each file copied.
    148   for (size_t i = 0; i < sources.size(); i++) {
    149     const SourceFile& input_file = sources[i];
    150 
    151     // The files should have the same name but in the dest dir.
    152     base::StringPiece name_part = FindFilename(&input_file.value());
    153     OutputFile dest_file(relative_dest_dir);
    154     AppendStringPiece(&dest_file.value(), name_part);
    155 
    156     dest_files.push_back(dest_file);
    157 
    158     out_ << "build ";
    159     path_output_.WriteFile(out_, dest_file);
    160     out_ << ": copy ";
    161     path_output_.WriteFile(out_, input_file);
    162     out_ << std::endl;
    163   }
    164 
    165   // Write out the rule for the target to copy all of them.
    166   out_ << std::endl << "build ";
    167   path_output_.WriteFile(out_, helper_.GetTargetOutputFile(target_));
    168   out_ << ": stamp";
    169   for (size_t i = 0; i < dest_files.size(); i++) {
    170     out_ << " ";
    171     path_output_.WriteFile(out_, dest_files[i]);
    172   }
    173   out_ << std::endl;
    174 
    175   // TODO(brettw) need some kind of stamp file for depending on this, as well
    176   // as order_only=prebuild.
    177 }
    178 
    179 void NinjaTargetWriter::WriteCustomRules() {
    180   // Make a unique name for this rule.
    181   std::string target_label = target_->label().GetUserVisibleName(true);
    182   std::string custom_rule_name(target_label);
    183   ReplaceChars(custom_rule_name, ":/()", "_", &custom_rule_name);
    184   custom_rule_name.append("_rule");
    185 
    186   // Run the script from the dir of the BUILD file. This has no trailing
    187   // slash.
    188   const SourceDir& script_cd = target_->label().dir();
    189   std::string script_cd_to_root = InvertDir(script_cd);
    190   if (script_cd_to_root.empty()) {
    191     script_cd_to_root = ".";
    192   } else {
    193     // Remove trailing slash
    194     DCHECK(script_cd_to_root[script_cd_to_root.size() - 1] == '/');
    195     script_cd_to_root.resize(script_cd_to_root.size() - 1);
    196   }
    197 
    198   std::string script_relative_to_cd =
    199       script_cd_to_root + target_->script().value();
    200 
    201   bool no_sources = target_->sources().empty();
    202 
    203   // Use a unique name for the response file when there are multiple build
    204   // steps so that they don't stomp on each other.
    205   std::string rspfile = custom_rule_name;
    206   if (!no_sources)
    207     rspfile += ".$unique_name";
    208   rspfile += ".rsp";
    209 
    210   // First write the custom rule.
    211   out_ << "rule " << custom_rule_name << std::endl;
    212   out_ << "  command = $pythonpath gyp-win-tool action-wrapper $arch "
    213        << rspfile << " ";
    214   path_output_.WriteDir(out_, script_cd, PathOutput::DIR_NO_LAST_SLASH);
    215   out_ << std::endl;
    216   out_ << "  description = CUSTOM " << target_label << std::endl;
    217   out_ << "  restat = 1" << std::endl;
    218   out_ << "  rspfile = " << rspfile << std::endl;
    219 
    220   // The build command goes in the rsp file.
    221   out_ << "  rspfile_content = $pythonpath " << script_relative_to_cd;
    222   for (size_t i = 0; i < target_->script_args().size(); i++) {
    223     const std::string& arg = target_->script_args()[i];
    224     out_ << " ";
    225     WriteCustomArg(arg);
    226   }
    227   out_ << std::endl << std::endl;
    228 
    229   // Precompute the common dependencies for each step. This includes the
    230   // script itself (changing the script should force a rebuild) and any data
    231   // files.
    232   //
    233   // TODO(brettW) this needs to be re-thought. "data" is supposed to be runtime
    234   // data (i.e. for tests and such) rather than compile-time dependencies for
    235   // each target. If we really need this, we need to have a different way to
    236   // express it.
    237   //
    238   // One idea: add an "inputs" variable to specify this kind of thing. We
    239   // should probably make it an error to specify data but no inputs for a
    240   // script as a way to catch people doing the wrong way.
    241   std::ostringstream common_deps_stream;
    242   path_output_.WriteFile(common_deps_stream, target_->script());
    243   const Target::FileList& datas = target_->data();
    244   for (size_t i = 0; i < datas.size(); i++) {
    245     common_deps_stream << " ";
    246     path_output_.WriteFile(common_deps_stream, datas[i]);
    247   }
    248   const std::string& common_deps = common_deps_stream.str();
    249 
    250   // Collects all output files for writing below.
    251   std::vector<OutputFile> output_files;
    252 
    253   if (no_sources) {
    254     // No sources, write a rule that invokes the script once with the
    255     // outputs as outputs, and the data as inputs.
    256     out_ << "build";
    257     const Target::FileList& outputs = target_->outputs();
    258     for (size_t i = 0; i < outputs.size(); i++) {
    259       OutputFile output_path(
    260           RemovePrefix(outputs[i].value(),
    261                        settings_->build_settings()->build_dir().value()));
    262       output_files.push_back(output_path);
    263       out_ << " ";
    264       path_output_.WriteFile(out_, output_path);
    265     }
    266     out_ << ": " << custom_rule_name << " " << common_deps << std::endl;
    267   } else {
    268     // Write separate rules for each input source file.
    269     WriteCustomSourceRules(custom_rule_name, common_deps, script_cd,
    270                            script_cd_to_root, &output_files);
    271   }
    272   out_ << std::endl;
    273 
    274   // Last write a stamp rule to collect all outputs.
    275   out_ << "build ";
    276   path_output_.WriteFile(out_, helper_.GetTargetOutputFile(target_));
    277   out_ << ": stamp";
    278   for (size_t i = 0; i < output_files.size(); i++) {
    279     out_ << " ";
    280     path_output_.WriteFile(out_, output_files[i]);
    281   }
    282   out_ << std::endl;
    283 }
    284 
    285 void NinjaTargetWriter::WriteCustomArg(const std::string& arg) {
    286   // This can be optimized if it's called a lot.
    287   EscapeOptions options;
    288   options.mode = ESCAPE_NINJA;
    289   std::string output_str = EscapeString(arg, options);
    290 
    291   // Do this substitution after escaping our our $ will be escaped (which we
    292   // don't want).
    293   ReplaceSubstringsAfterOffset(&output_str, 0, FileTemplate::kSource,
    294                                "${source}");
    295   ReplaceSubstringsAfterOffset(&output_str, 0, FileTemplate::kSourceNamePart,
    296                                "${source_name_part}");
    297   out_ << output_str;
    298 }
    299 
    300 void NinjaTargetWriter::WriteCustomSourceRules(
    301     const std::string& custom_rule_name,
    302     const std::string& common_deps,
    303     const SourceDir& script_cd,
    304     const std::string& script_cd_to_root,
    305     std::vector<OutputFile>* output_files) {
    306   // Construct the template for generating the output files from each source.
    307   const Target::FileList& outputs = target_->outputs();
    308   std::vector<std::string> output_template_args;
    309   for (size_t i = 0; i < outputs.size(); i++) {
    310     // All outputs should be in the output dir.
    311     output_template_args.push_back(
    312         RemovePrefix(outputs[i].value(),
    313                      settings_->build_settings()->build_dir().value()));
    314   }
    315   FileTemplate output_template(output_template_args);
    316 
    317   // Prevent re-allocating each time by initializing outside the loop.
    318   std::vector<std::string> output_template_result;
    319 
    320   // Path output formatter for wrigin source paths passed to the script.
    321   PathOutput script_source_path_output(script_cd, ESCAPE_SHELL, true);
    322 
    323   const Target::FileList& sources = target_->sources();
    324   for (size_t i = 0; i < sources.size(); i++) {
    325     // Write outputs for this source file computed by the template.
    326     out_ << "build";
    327     output_template.ApplyString(sources[i].value(), &output_template_result);
    328     for (size_t out_i = 0; out_i < output_template_result.size(); out_i++) {
    329       OutputFile output_path(output_template_result[out_i]);
    330       output_files->push_back(output_path);
    331       out_ << " ";
    332       path_output_.WriteFile(out_, output_path);
    333     }
    334 
    335     out_ << ": " << custom_rule_name
    336          << " " << common_deps
    337          << " ";
    338     path_output_.WriteFile(out_, sources[i]);
    339     out_ << std::endl;
    340 
    341     out_ << "  unique_name = " << i << std::endl;
    342 
    343     // The source file here should be relative to the script directory since
    344     // this is the variable passed to the script. Here we slightly abuse the
    345     // OutputFile object by putting a non-output-relative path in it to signal
    346     // that the PathWriter should not prepend directories.
    347     out_ << "  source = ";
    348     script_source_path_output.WriteFile(out_, sources[i]);
    349     out_ << std::endl;
    350 
    351     out_ << "  source_name_part = "
    352          << FindFilenameNoExtension(&sources[i].value()).as_string()
    353          << std::endl;
    354   }
    355 }
    356 
    357 void NinjaTargetWriter::WriteCompilerVars() {
    358   // Defines.
    359   out_ << "defines =";
    360   RecursiveTargetConfigToStream(target_, &ConfigValues::defines,
    361                                 DefineWriter(), out_);
    362   out_ << std::endl;
    363 
    364   // Includes.
    365   out_ << "includes =";
    366   RecursiveTargetConfigToStream(target_, &ConfigValues::includes,
    367                                 IncludeWriter(path_output_, helper_), out_);
    368 
    369   out_ << std::endl;
    370 
    371   // C flags and friends.
    372 #define WRITE_FLAGS(name) \
    373     out_ << #name " ="; \
    374     RecursiveTargetConfigStringsToStream(target_, &ConfigValues::name, out_); \
    375     out_ << std::endl;
    376 
    377   WRITE_FLAGS(cflags)
    378   WRITE_FLAGS(cflags_c)
    379   WRITE_FLAGS(cflags_cc)
    380   WRITE_FLAGS(cflags_objc)
    381   WRITE_FLAGS(cflags_objcc)
    382 
    383 #undef WRITE_FLAGS
    384 
    385   out_ << std::endl;
    386 }
    387 
    388 void NinjaTargetWriter::WriteSources(
    389     std::vector<OutputFile>* object_files) {
    390   const Target::FileList& sources = target_->sources();
    391   object_files->reserve(sources.size());
    392 
    393   for (size_t i = 0; i < sources.size(); i++) {
    394     const SourceFile& input_file = sources[i];
    395 
    396     SourceFileType input_file_type = GetSourceFileType(input_file,
    397                                                        settings_->target_os());
    398     if (input_file_type == SOURCE_UNKNOWN)
    399       continue;  // Skip unknown file types.
    400     const char* command = GetCommandForSourceType(input_file_type);
    401     if (!command)
    402       continue;  // Skip files not needing compilation.
    403 
    404     OutputFile output_file = helper_.GetOutputFileForSource(
    405         target_, input_file, input_file_type);
    406     object_files->push_back(output_file);
    407 
    408     out_ << "build ";
    409     path_output_.WriteFile(out_, output_file);
    410     out_ << ": " << command << " ";
    411     path_output_.WriteFile(out_, input_file);
    412     out_ << std::endl;
    413   }
    414   out_ << std::endl;
    415 }
    416 
    417 void NinjaTargetWriter::WriteLinkerStuff(
    418     const std::vector<OutputFile>& object_files) {
    419   // Manifest file on Windows.
    420   // TODO(brettw) this seems not to be necessary for static libs, skip in
    421   // that case?
    422   OutputFile windows_manifest;
    423   if (settings_->IsWin()) {
    424     windows_manifest.value().assign(helper_.GetTargetOutputDir(target_));
    425     windows_manifest.value().append(target_->label().name());
    426     windows_manifest.value().append(".intermediate.manifest");
    427     out_ << "manifests = ";
    428     path_output_.WriteFile(out_, windows_manifest);
    429     out_ << std::endl;
    430   }
    431 
    432   // Linker flags, append manifest flag on Windows to reference our file.
    433   out_ << "ldflags =";
    434   RecursiveTargetConfigStringsToStream(target_, &ConfigValues::ldflags, out_);
    435   // HACK ERASEME BRETTW FIXME
    436   if (settings_->IsWin()) {
    437     out_ << " /MANIFEST /ManifestFile:";
    438     path_output_.WriteFile(out_, windows_manifest);
    439     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";
    440   }
    441   out_ << std::endl;
    442 
    443   // Libraries to link.
    444   out_ << "libs =";
    445   if (settings_->IsMac()) {
    446     // TODO(brettw) fix this.
    447     out_ << " -framework AppKit -framework ApplicationServices -framework Carbon -framework CoreFoundation -framework Foundation -framework IOKit -framework Security";
    448   }
    449   out_ << std::endl;
    450 
    451   // The external output file is the one that other libs depend on.
    452   OutputFile external_output_file = helper_.GetTargetOutputFile(target_);
    453 
    454   // The internal output file is the "main thing" we think we're making. In
    455   // the case of shared libraries, this is the shared library and the external
    456   // output file is the import library. In other cases, the internal one and
    457   // the external one are the same.
    458   OutputFile internal_output_file;
    459   if (target_->output_type() == Target::SHARED_LIBRARY) {
    460     if (settings_->IsWin()) {
    461       internal_output_file = OutputFile(target_->label().name() + ".dll");
    462     } else {
    463       internal_output_file = external_output_file;
    464     }
    465   } else {
    466     internal_output_file = external_output_file;
    467   }
    468 
    469   // In Python see "self.ninja.build(output, command, input,"
    470   WriteLinkCommand(external_output_file, internal_output_file, object_files);
    471 
    472   if (target_->output_type() == Target::SHARED_LIBRARY) {
    473     out_ << "  soname = ";
    474     path_output_.WriteFile(out_, internal_output_file);
    475     out_ << std::endl;
    476 
    477     out_ << "  lib = ";
    478     path_output_.WriteFile(out_, internal_output_file);
    479     out_ << std::endl;
    480 
    481     out_ << "  dll = ";
    482     path_output_.WriteFile(out_, internal_output_file);
    483     out_ << std::endl;
    484 
    485     if (settings_->IsWin()) {
    486       out_ << "  implibflag = /IMPLIB:";
    487       path_output_.WriteFile(out_, external_output_file);
    488       out_ << std::endl;
    489     }
    490 
    491     // TODO(brettw) postbuild steps.
    492     if (settings_->IsMac())
    493       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)";
    494   }
    495 
    496   out_ << std::endl;
    497 }
    498 
    499 void NinjaTargetWriter::WriteLinkCommand(
    500     const OutputFile& external_output_file,
    501     const OutputFile& internal_output_file,
    502     const std::vector<OutputFile>& object_files) {
    503   out_ << "build ";
    504   path_output_.WriteFile(out_, internal_output_file);
    505   if (external_output_file != internal_output_file) {
    506     out_ << " ";
    507     path_output_.WriteFile(out_, external_output_file);
    508   }
    509   out_ << ": " << GetCommandForTargetType();
    510 
    511   // Object files.
    512   for (size_t i = 0; i < object_files.size(); i++) {
    513     out_ << " ";
    514     path_output_.WriteFile(out_, object_files[i]);
    515   }
    516 
    517   // Library inputs (deps and inherited static libraries).
    518   //
    519   // Static libraries since they're just a collection of the object files so
    520   // don't need libraries linked with them, but we still need to go through
    521   // the list and find non-linkable data deps in the "deps" section. We'll
    522   // collect all non-linkable deps and put it in the order-only deps below.
    523   std::vector<const Target*> extra_data_deps;
    524   const std::vector<const Target*>& deps = target_->deps();
    525   const std::set<const Target*>& inherited = target_->inherited_libraries();
    526   for (size_t i = 0; i < deps.size(); i++) {
    527     if (inherited.find(deps[i]) != inherited.end())
    528       continue;
    529     if (target_->output_type() != Target::STATIC_LIBRARY &&
    530         deps[i]->IsLinkable()) {
    531       out_ << " ";
    532       path_output_.WriteFile(out_, helper_.GetTargetOutputFile(deps[i]));
    533     } else {
    534       extra_data_deps.push_back(deps[i]);
    535     }
    536   }
    537   for (std::set<const Target*>::const_iterator i = inherited.begin();
    538        i != inherited.end(); ++i) {
    539     if (target_->output_type() == Target::STATIC_LIBRARY) {
    540       extra_data_deps.push_back(*i);
    541     } else {
    542       out_ << " ";
    543       path_output_.WriteFile(out_, helper_.GetTargetOutputFile(*i));
    544     }
    545   }
    546 
    547   // Append data dependencies as order-only dependencies.
    548   const std::vector<const Target*>& datadeps = target_->datadeps();
    549   const std::vector<SourceFile>& data = target_->data();
    550   if (!extra_data_deps.empty() || !datadeps.empty() || !data.empty()) {
    551     out_ << " ||";
    552 
    553     // Non-linkable deps in the deps section above.
    554     for (size_t i = 0; i < extra_data_deps.size(); i++) {
    555       out_ << " ";
    556       path_output_.WriteFile(out_,
    557                              helper_.GetTargetOutputFile(extra_data_deps[i]));
    558     }
    559 
    560     // Data deps.
    561     for (size_t i = 0; i < datadeps.size(); i++) {
    562       out_ << " ";
    563       path_output_.WriteFile(out_, helper_.GetTargetOutputFile(datadeps[i]));
    564     }
    565 
    566     // Data files.
    567     const std::vector<SourceFile>& data = target_->data();
    568     for (size_t i = 0; i < data.size(); i++) {
    569       out_ << " ";
    570       path_output_.WriteFile(out_, data[i]);
    571     }
    572   }
    573 
    574   out_ << std::endl;
    575 }
    576 
    577 const char* NinjaTargetWriter::GetCommandForSourceType(
    578     SourceFileType type) const {
    579   if (type == SOURCE_C)
    580     return "cc";
    581   if (type == SOURCE_CC)
    582     return "cxx";
    583 
    584   // TODO(brettw) asm files.
    585 
    586   if (settings_->IsMac()) {
    587     if (type == SOURCE_M)
    588       return "objc";
    589     if (type == SOURCE_MM)
    590       return "objcxx";
    591   }
    592 
    593   if (settings_->IsWin()) {
    594     if (type == SOURCE_RC)
    595       return "rc";
    596   }
    597 
    598   // TODO(brettw) stuff about "S" files on non-Windows.
    599   return NULL;
    600 }
    601 
    602 const char* NinjaTargetWriter::GetCommandForTargetType() const {
    603   if (target_->output_type() == Target::NONE) {
    604     NOTREACHED();
    605     return "";
    606   }
    607 
    608   if (target_->output_type() == Target::STATIC_LIBRARY) {
    609     // TODO(brettw) stuff about standalong static libraryes on Unix in
    610     // WriteTarget in the Python one, and lots of postbuild steps.
    611     return "alink";
    612   }
    613 
    614   if (target_->output_type() == Target::SHARED_LIBRARY)
    615     return "solink";
    616 
    617   return "link";
    618 }
    619