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