// Copyright (c) 2013 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "tools/gn/path_output.h" #include "base/strings/string_util.h" #include "build/build_config.h" #include "tools/gn/filesystem_utils.h" #include "tools/gn/output_file.h" #include "tools/gn/string_utils.h" PathOutput::PathOutput(const SourceDir& current_dir, const base::StringPiece& source_root, EscapingMode escaping) : current_dir_(current_dir) { inverse_current_dir_ = RebasePath("//", current_dir, source_root); if (!EndsWithSlash(inverse_current_dir_)) inverse_current_dir_.push_back('/'); options_.mode = escaping; } PathOutput::~PathOutput() { } void PathOutput::WriteFile(std::ostream& out, const SourceFile& file) const { WritePathStr(out, file.value()); } void PathOutput::WriteDir(std::ostream& out, const SourceDir& dir, DirSlashEnding slash_ending) const { if (dir.value() == "/") { // Writing system root is always a slash (this will normally only come up // on Posix systems). if (slash_ending == DIR_NO_LAST_SLASH) out << "/."; else out << "/"; } else if (dir.value() == "//") { // Writing out the source root. if (slash_ending == DIR_NO_LAST_SLASH) { // The inverse_current_dir_ will contain a [back]slash at the end, so we // can't just write it out. if (inverse_current_dir_.empty()) { out << "."; } else { out.write(inverse_current_dir_.c_str(), inverse_current_dir_.size() - 1); } } else { if (inverse_current_dir_.empty()) out << "./"; else out << inverse_current_dir_; } } else if (dir == current_dir_) { // Writing the same directory. This needs special handling here since // we need to output something else other than the input. if (slash_ending == DIR_INCLUDE_LAST_SLASH) out << "./"; else out << "."; } else if (slash_ending == DIR_INCLUDE_LAST_SLASH) { WritePathStr(out, dir.value()); } else { // DIR_NO_LAST_SLASH mode, just trim the last char. WritePathStr(out, base::StringPiece(dir.value().data(), dir.value().size() - 1)); } } void PathOutput::WriteFile(std::ostream& out, const OutputFile& file) const { // Here we assume that the path is already preprocessed. EscapeStringToStream(out, file.value(), options_); } void PathOutput::WriteFiles(std::ostream& out, const std::vector& files) const { for (const auto& file : files) { out << " "; WriteFile(out, file); } } void PathOutput::WriteFiles(std::ostream& out, const UniqueVector& files) const { for (const auto& file : files) { out << " "; WriteFile(out, file); } } void PathOutput::WriteDir(std::ostream& out, const OutputFile& file, DirSlashEnding slash_ending) const { DCHECK(file.value().empty() || file.value()[file.value().size() - 1] == '/'); switch (slash_ending) { case DIR_INCLUDE_LAST_SLASH: EscapeStringToStream(out, file.value(), options_); break; case DIR_NO_LAST_SLASH: if (!file.value().empty() && file.value()[file.value().size() - 1] == '/') { // Trim trailing slash. EscapeStringToStream( out, base::StringPiece(file.value().data(), file.value().size() - 1), options_); } else { // Doesn't end with a slash, write the whole thing. EscapeStringToStream(out, file.value(), options_); } break; } } void PathOutput::WriteFile(std::ostream& out, const base::FilePath& file) const { // Assume native file paths are always absolute. EscapeStringToStream(out, FilePathToUTF8(file), options_); } void PathOutput::WriteSourceRelativeString( std::ostream& out, const base::StringPiece& str) const { if (options_.mode == ESCAPE_NINJA_COMMAND) { // Shell escaping needs an intermediate string since it may end up // quoting the whole thing. std::string intermediate; intermediate.reserve(inverse_current_dir_.size() + str.size()); intermediate.assign(inverse_current_dir_.c_str(), inverse_current_dir_.size()); intermediate.append(str.data(), str.size()); EscapeStringToStream(out, base::StringPiece(intermediate.c_str(), intermediate.size()), options_); } else { // Ninja (and none) escaping can avoid the intermediate string and // reprocessing of the inverse_current_dir_. out << inverse_current_dir_; EscapeStringToStream(out, str, options_); } } void PathOutput::WritePathStr(std::ostream& out, const base::StringPiece& str) const { DCHECK(str.size() > 0 && str[0] == '/'); if (str.substr(0, current_dir_.value().size()) == base::StringPiece(current_dir_.value())) { // The current dir is a prefix of the output file, so we can strip the // prefix and write out the result. EscapeStringToStream(out, str.substr(current_dir_.value().size()), options_); } else if (str.size() >= 2 && str[1] == '/') { WriteSourceRelativeString(out, str.substr(2)); } else { // Input begins with one slash, don't write the current directory since // it's system-absolute. #if defined(OS_WIN) // On Windows, trim the leading slash, since the input for absolute // paths will look like "/C:/foo/bar.txt". EscapeStringToStream(out, str.substr(1), options_); #else EscapeStringToStream(out, str, options_); #endif } }