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