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