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 12PathOutput::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 23PathOutput::~PathOutput() { 24} 25 26void PathOutput::WriteFile(std::ostream& out, const SourceFile& file) const { 27 WritePathStr(out, file.value()); 28} 29 30void 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 73void 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 78void 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 86void 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 112void 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 118void 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 141void 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