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/path_output.h"
      6 
      7 #include "build/build_config.h"
      8 #include "tools/gn/filesystem_utils.h"
      9 #include "tools/gn/output_file.h"
     10 #include "tools/gn/string_utils.h"
     11 
     12 PathOutput::PathOutput(const SourceDir& current_dir, EscapingMode escaping)
     13     : current_dir_(current_dir) {
     14   CHECK(current_dir.is_source_absolute())
     15       << "Currently this only supports writing to output directories inside "
     16          "the source root. There needs to be some tweaks to PathOutput to make "
     17          "doing this work correctly.";
     18   inverse_current_dir_ = InvertDir(current_dir_);
     19 
     20   options_.mode = escaping;
     21 }
     22 
     23 PathOutput::~PathOutput() {
     24 }
     25 
     26 void PathOutput::WriteFile(std::ostream& out, const SourceFile& file) const {
     27   WritePathStr(out, file.value());
     28 }
     29 
     30 void PathOutput::WriteDir(std::ostream& out,
     31                           const SourceDir& dir,
     32                           DirSlashEnding slash_ending) const {
     33   if (dir.value() == "/") {
     34     // Writing system root is always a slash (this will normally only come up
     35     // on Posix systems).
     36     if (slash_ending == DIR_NO_LAST_SLASH)
     37       out << "/.";
     38     else
     39       out << "/";
     40   } else if (dir.value() == "//") {
     41     // Writing out the source root.
     42     if (slash_ending == DIR_NO_LAST_SLASH) {
     43       // The inverse_current_dir_ will contain a [back]slash at the end, so we
     44       // can't just write it out.
     45       if (inverse_current_dir_.empty()) {
     46         out << ".";
     47       } else {
     48         out.write(inverse_current_dir_.c_str(),
     49                   inverse_current_dir_.size() - 1);
     50       }
     51     } else {
     52       if (inverse_current_dir_.empty())
     53         out << "./";
     54       else
     55         out << inverse_current_dir_;
     56     }
     57   } else if (dir == current_dir_) {
     58     // Writing the same directory. This needs special handling here since
     59     // we need to output something else other than the input.
     60     if (slash_ending == DIR_INCLUDE_LAST_SLASH)
     61       out << "./";
     62     else
     63       out << ".";
     64   } else if (slash_ending == DIR_INCLUDE_LAST_SLASH) {
     65     WritePathStr(out, dir.value());
     66   } else {
     67     // DIR_NO_LAST_SLASH mode, just trim the last char.
     68     WritePathStr(out, base::StringPiece(dir.value().data(),
     69                                         dir.value().size() - 1));
     70   }
     71 }
     72 
     73 void PathOutput::WriteFile(std::ostream& out, const OutputFile& file) const {
     74   // Here we assume that the path is already preprocessed.
     75   EscapeStringToStream(out, file.value(), options_);
     76 }
     77 
     78 void PathOutput::WriteFiles(std::ostream& out,
     79                             const std::vector<OutputFile>& files) const {
     80   for (size_t i = 0; i < files.size(); i++) {
     81     out << " ";
     82     WriteFile(out, files[i]);
     83   }
     84 }
     85 
     86 void PathOutput::WriteDir(std::ostream& out,
     87                           const OutputFile& file,
     88                           DirSlashEnding slash_ending) const {
     89   DCHECK(file.value().empty() ||
     90          file.value()[file.value().size() - 1] == '/');
     91 
     92   switch (slash_ending) {
     93     case DIR_INCLUDE_LAST_SLASH:
     94       EscapeStringToStream(out, file.value(), options_);
     95       break;
     96     case DIR_NO_LAST_SLASH:
     97       if (!file.value().empty() &&
     98           file.value()[file.value().size() - 1] == '/') {
     99         // Trim trailing slash.
    100         EscapeStringToStream(
    101             out,
    102             base::StringPiece(file.value().data(), file.value().size() - 1),
    103             options_);
    104       } else {
    105         // Doesn't end with a slash, write the whole thing.
    106         EscapeStringToStream(out, file.value(), options_);
    107       }
    108       break;
    109   }
    110 }
    111 
    112 void PathOutput::WriteFile(std::ostream& out,
    113                            const base::FilePath& file) const {
    114   // Assume native file paths are always absolute.
    115   EscapeStringToStream(out, FilePathToUTF8(file), options_);
    116 }
    117 
    118 void PathOutput::WriteSourceRelativeString(
    119     std::ostream& out,
    120     const base::StringPiece& str) const {
    121   if (options_.mode == ESCAPE_NINJA_COMMAND) {
    122     // Shell escaping needs an intermediate string since it may end up
    123     // quoting the whole thing.
    124     std::string intermediate;
    125     intermediate.reserve(inverse_current_dir_.size() + str.size());
    126     intermediate.assign(inverse_current_dir_.c_str(),
    127                         inverse_current_dir_.size());
    128     intermediate.append(str.data(), str.size());
    129 
    130     EscapeStringToStream(out,
    131         base::StringPiece(intermediate.c_str(), intermediate.size()),
    132         options_);
    133   } else {
    134     // Ninja (and none) escaping can avoid the intermediate string and
    135     // reprocessing of the inverse_current_dir_.
    136     out << inverse_current_dir_;
    137     EscapeStringToStream(out, str, options_);
    138   }
    139 }
    140 
    141 void PathOutput::WritePathStr(std::ostream& out,
    142                               const base::StringPiece& str) const {
    143   DCHECK(str.size() > 0 && str[0] == '/');
    144 
    145   if (str.substr(0, current_dir_.value().size()) ==
    146       base::StringPiece(current_dir_.value())) {
    147     // The current dir is a prefix of the output file, so we can strip the
    148     // prefix and write out the result.
    149     EscapeStringToStream(out, str.substr(current_dir_.value().size()),
    150                          options_);
    151   } else if (str.size() >= 2 && str[1] == '/') {
    152     WriteSourceRelativeString(out, str.substr(2));
    153   } else {
    154     // Input begins with one slash, don't write the current directory since
    155     // it's system-absolute.
    156 #if defined(OS_WIN)
    157     // On Windows, trim the leading slash, since the input for absolute
    158     // paths will look like "/C:/foo/bar.txt".
    159     EscapeStringToStream(out, str.substr(1), options_);
    160 #else
    161     EscapeStringToStream(out, str, options_);
    162 #endif
    163   }
    164 }
    165