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