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