• 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,const std::string_view & source_root,EscapingMode escaping)13 PathOutput::PathOutput(const SourceDir& current_dir,
14                        const 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<OutputFile> & files) const77 void PathOutput::WriteFiles(std::ostream& out,
78                             const std::vector<OutputFile>& files) const {
79   for (const auto& file : files) {
80     out << " ";
81     WriteFile(out, file);
82   }
83 }
84 
WriteFiles(std::ostream & out,const UniqueVector<OutputFile> & files) const85 void PathOutput::WriteFiles(std::ostream& out,
86                             const UniqueVector<OutputFile>& files) const {
87   for (const auto& file : files) {
88     out << " ";
89     WriteFile(out, file);
90   }
91 }
92 
WriteDir(std::ostream & out,const OutputFile & file,DirSlashEnding slash_ending) const93 void PathOutput::WriteDir(std::ostream& out,
94                           const OutputFile& file,
95                           DirSlashEnding slash_ending) const {
96   DCHECK(file.value().empty() || file.value()[file.value().size() - 1] == '/');
97 
98   switch (slash_ending) {
99     case DIR_INCLUDE_LAST_SLASH:
100       EscapeStringToStream(out, file.value(), options_);
101       break;
102     case DIR_NO_LAST_SLASH:
103       if (!file.value().empty() &&
104           file.value()[file.value().size() - 1] == '/') {
105         // Trim trailing slash.
106         EscapeStringToStream(
107             out, std::string_view(file.value().data(), file.value().size() - 1),
108             options_);
109       } else {
110         // Doesn't end with a slash, write the whole thing.
111         EscapeStringToStream(out, file.value(), options_);
112       }
113       break;
114   }
115 }
116 
WriteFile(std::ostream & out,const base::FilePath & file) const117 void PathOutput::WriteFile(std::ostream& out,
118                            const base::FilePath& file) const {
119   // Assume native file paths are always absolute.
120   EscapeStringToStream(out, FilePathToUTF8(file), options_);
121 }
122 
WriteSourceRelativeString(std::ostream & out,const std::string_view & str) const123 void PathOutput::WriteSourceRelativeString(std::ostream& out,
124                                            const std::string_view& str) const {
125   if (options_.mode == ESCAPE_NINJA_COMMAND) {
126     // Shell escaping needs an intermediate string since it may end up
127     // quoting the whole thing.
128     std::string intermediate;
129     intermediate.reserve(inverse_current_dir_.size() + str.size());
130     intermediate.assign(inverse_current_dir_.c_str(),
131                         inverse_current_dir_.size());
132     intermediate.append(str.data(), str.size());
133 
134     EscapeStringToStream(
135         out, std::string_view(intermediate.c_str(), intermediate.size()),
136         options_);
137   } else {
138     // Ninja (and none) escaping can avoid the intermediate string and
139     // reprocessing of the inverse_current_dir_.
140     out << inverse_current_dir_;
141     EscapeStringToStream(out, str, options_);
142   }
143 }
144 
WritePathStr(std::ostream & out,const std::string_view & str) const145 void PathOutput::WritePathStr(std::ostream& out,
146                               const std::string_view& str) const {
147   DCHECK(str.size() > 0 && str[0] == '/');
148 
149   if (str.substr(0, current_dir_.value().size()) ==
150       std::string_view(current_dir_.value())) {
151     // The current dir is a prefix of the output file, so we can strip the
152     // prefix and write out the result.
153     EscapeStringToStream(out, str.substr(current_dir_.value().size()),
154                          options_);
155   } else if (str.size() >= 2 && str[1] == '/') {
156     WriteSourceRelativeString(out, str.substr(2));
157   } else {
158 // Input begins with one slash, don't write the current directory since
159 // it's system-absolute.
160 #if defined(OS_WIN)
161     // On Windows, trim the leading slash, since the input for absolute
162     // paths will look like "/C:/foo/bar.txt".
163     EscapeStringToStream(out, str.substr(1), options_);
164 #else
165     EscapeStringToStream(out, str, options_);
166 #endif
167   }
168 }
169