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_helper.h"
      6 
      7 #include "base/logging.h"
      8 #include "base/strings/string_util.h"
      9 #include "tools/gn/filesystem_utils.h"
     10 #include "tools/gn/string_utils.h"
     11 #include "tools/gn/target.h"
     12 
     13 namespace {
     14 
     15 const char kLibDirWithSlash[] = "lib/";
     16 const char kObjectDirNoSlash[] = "obj";
     17 
     18 }  // namespace
     19 
     20 NinjaHelper::NinjaHelper(const BuildSettings* build_settings)
     21     : build_settings_(build_settings) {
     22   build_to_src_no_last_slash_ = build_settings->build_to_source_dir_string();
     23   if (!build_to_src_no_last_slash_.empty() &&
     24       build_to_src_no_last_slash_[build_to_src_no_last_slash_.size() - 1] ==
     25           '/')
     26     build_to_src_no_last_slash_.resize(build_to_src_no_last_slash_.size() - 1);
     27 
     28   build_to_src_system_no_last_slash_ = build_to_src_no_last_slash_;
     29 }
     30 
     31 NinjaHelper::~NinjaHelper() {
     32 }
     33 
     34 std::string NinjaHelper::GetTopleveOutputDir() const {
     35   return kObjectDirNoSlash;
     36 }
     37 
     38 OutputFile NinjaHelper::GetTargetOutputDir(const Target* target) const {
     39   OutputFile ret(target->settings()->toolchain_output_subdir());
     40   ret.value().append(kObjectDirNoSlash);
     41   AppendStringPiece(&ret.value(),
     42                     target->label().dir().SourceAbsoluteWithOneSlash());
     43   return ret;
     44 }
     45 
     46 OutputFile NinjaHelper::GetNinjaFileForTarget(const Target* target) const {
     47   OutputFile ret = GetTargetOutputDir(target);
     48   ret.value().append(target->label().name());
     49   ret.value().append(".ninja");
     50   return ret;
     51 }
     52 
     53 OutputFile NinjaHelper::GetNinjaFileForToolchain(
     54     const Settings* settings) const {
     55   OutputFile ret;
     56   ret.value().append(settings->toolchain_output_subdir().value());
     57   ret.value().append("toolchain.ninja");
     58   return ret;
     59 }
     60 
     61 // In Python, GypPathToUniqueOutput does the qualification. The only case where
     62 // the Python version doesn't qualify the name is for target outputs, which we
     63 // handle in a separate function.
     64 OutputFile NinjaHelper::GetOutputFileForSource(
     65     const Target* target,
     66     const SourceFile& source,
     67     SourceFileType type) const {
     68   // Extract the filename and remove the extension (keep the dot).
     69   base::StringPiece filename = FindFilename(&source.value());
     70   std::string name(filename.data(), filename.size());
     71   size_t extension_offset = FindExtensionOffset(name);
     72   CHECK(extension_offset != std::string::npos);
     73   name.resize(extension_offset);
     74 
     75   // Append the new extension.
     76   switch (type) {
     77     case SOURCE_ASM:
     78     case SOURCE_C:
     79     case SOURCE_CC:
     80     case SOURCE_M:
     81     case SOURCE_MM:
     82     case SOURCE_S:
     83       name.append(target->settings()->IsWin() ? "obj" : "o");
     84       break;
     85 
     86     case SOURCE_RC:
     87       name.append("res");
     88       break;
     89 
     90     // Pass .o/.obj files through unchanged.
     91     case SOURCE_O: {
     92       // System-absolute file names get preserved (they don't need to be
     93       // rebased relative to the build dir).
     94       if (source.is_system_absolute())
     95         return OutputFile(source.value());
     96 
     97       // Files that are already inside the build dir should not be made
     98       // relative to the source tree. Doing so will insert an unnecessary
     99       // "../.." into the path which won't match the corresponding target
    100       // name in ninja.
    101       CHECK(build_settings_->build_dir().is_source_absolute());
    102       CHECK(source.is_source_absolute());
    103       if (StartsWithASCII(source.value(),
    104                           build_settings_->build_dir().value(),
    105                           true)) {
    106         return OutputFile(
    107             source.value().substr(
    108                 build_settings_->build_dir().value().size()));
    109       }
    110 
    111       // Construct the relative location of the file from the build dir.
    112       OutputFile ret(build_to_src_no_last_slash());
    113       source.SourceAbsoluteWithOneSlash().AppendToString(&ret.value());
    114       return ret;
    115     }
    116 
    117     case SOURCE_H:
    118     case SOURCE_UNKNOWN:
    119       NOTREACHED();
    120       return OutputFile();
    121   }
    122 
    123   // Use the scheme <path>/<target>.<name>.<extension> so that all output
    124   // names are unique to different targets.
    125 
    126   // This will look like "obj" or "toolchain_name/obj".
    127   OutputFile ret(target->settings()->toolchain_output_subdir());
    128   ret.value().append(kObjectDirNoSlash);
    129 
    130   // Find the directory, assume it starts with two slashes, and trim to one.
    131   base::StringPiece dir = FindDir(&source.value());
    132   CHECK(dir.size() >= 2 && dir[0] == '/' && dir[1] == '/')
    133       << "Source file isn't in the source repo: " << dir;
    134   AppendStringPiece(&ret.value(), dir.substr(1));
    135 
    136   ret.value().append(target->label().name());
    137   ret.value().append(".");
    138   ret.value().append(name);
    139   return ret;
    140 }
    141 
    142 OutputFile NinjaHelper::GetTargetOutputFile(const Target* target) const {
    143   OutputFile ret;
    144 
    145   // Use the output name if given, fall back to target name if not.
    146   const std::string& name = target->output_name().empty() ?
    147       target->label().name() : target->output_name();
    148 
    149   // This is prepended to the output file name. Some platforms get "lib"
    150   // prepended to library names. but be careful not to make a duplicate (e.g.
    151   // some targets like "libxml" already have the "lib" in the name).
    152   const char* prefix;
    153   if (!target->settings()->IsWin() &&
    154       (target->output_type() == Target::SHARED_LIBRARY ||
    155        target->output_type() == Target::STATIC_LIBRARY) &&
    156       name.compare(0, 3, "lib") != 0)
    157     prefix = "lib";
    158   else
    159     prefix = "";
    160 
    161   const char* extension;
    162   if (target->output_extension().empty()) {
    163     if (target->output_type() == Target::GROUP ||
    164         target->output_type() == Target::SOURCE_SET ||
    165         target->output_type() == Target::COPY_FILES ||
    166         target->output_type() == Target::ACTION ||
    167         target->output_type() == Target::ACTION_FOREACH) {
    168       extension = "stamp";
    169     } else {
    170       extension = GetExtensionForOutputType(target->output_type(),
    171                                             target->settings()->target_os());
    172     }
    173   } else {
    174     extension = target->output_extension().c_str();
    175   }
    176 
    177   // Everything goes into the toolchain directory (which will be empty for the
    178   // default toolchain, and will end in a slash otherwise).
    179   ret.value().append(target->settings()->toolchain_output_subdir().value());
    180 
    181   // Binaries and loadable libraries go into the toolchain root.
    182   if (target->output_type() == Target::EXECUTABLE ||
    183       (target->settings()->IsMac() &&
    184           (target->output_type() == Target::SHARED_LIBRARY ||
    185            target->output_type() == Target::STATIC_LIBRARY)) ||
    186       (target->settings()->IsWin() &&
    187        target->output_type() == Target::SHARED_LIBRARY)) {
    188     // Generate a name like "<toolchain>/<prefix><name>.<extension>".
    189     ret.value().append(prefix);
    190     ret.value().append(name);
    191     if (extension[0]) {
    192       ret.value().push_back('.');
    193       ret.value().append(extension);
    194     }
    195     return ret;
    196   }
    197 
    198   // Libraries go into the library subdirectory like
    199   // "<toolchain>/lib/<prefix><name>.<extension>".
    200   if (target->output_type() == Target::SHARED_LIBRARY) {
    201     ret.value().append(kLibDirWithSlash);
    202     ret.value().append(prefix);
    203     ret.value().append(name);
    204     if (extension[0]) {
    205       ret.value().push_back('.');
    206       ret.value().append(extension);
    207     }
    208     return ret;
    209   }
    210 
    211   // Everything else goes next to the target's .ninja file like
    212   // "<toolchain>/obj/<path>/<name>.<extension>".
    213   ret.value().append(kObjectDirNoSlash);
    214   AppendStringPiece(&ret.value(),
    215                     target->label().dir().SourceAbsoluteWithOneSlash());
    216   ret.value().append(prefix);
    217   ret.value().append(name);
    218   if (extension[0]) {
    219     ret.value().push_back('.');
    220     ret.value().append(extension);
    221   }
    222   return ret;
    223 }
    224 
    225 std::string NinjaHelper::GetRulePrefix(const Settings* settings) const {
    226   // Don't prefix the default toolchain so it looks prettier, prefix everything
    227   // else.
    228   if (settings->is_default())
    229     return std::string();  // Default toolchain has no prefix.
    230   return settings->toolchain_label().name() + "_";
    231 }
    232 
    233 std::string NinjaHelper::GetRuleForSourceType(const Settings* settings,
    234                                               SourceFileType type) const {
    235   // This function may be hot since it will be called for every source file
    236   // in the tree. We could cache the results to avoid making a string for
    237   // every invocation.
    238   std::string prefix = GetRulePrefix(settings);
    239 
    240   if (type == SOURCE_C)
    241     return prefix + "cc";
    242   if (type == SOURCE_CC)
    243     return prefix + "cxx";
    244   if (type == SOURCE_M)
    245     return prefix + "objc";
    246   if (type == SOURCE_MM)
    247     return prefix + "objcxx";
    248   if (type == SOURCE_RC)
    249     return prefix + "rc";
    250   if (type == SOURCE_S)
    251     return prefix + "cc";  // Assembly files just get compiled by CC.
    252 
    253   // TODO(brettw) asm files.
    254 
    255   // .obj files have no rules to make them (they're already built) so we return
    256   // the enpty string for SOURCE_O.
    257   return std::string();
    258 }
    259