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.
5 #include "tools/gn/path_output.h"
7 #include "base/strings/string_util.h"
8 #include "build/build_config.h"
9 #include "tools/gn/filesystem_utils.h"
10 #include "tools/gn/output_file.h"
11 #include "tools/gn/string_utils.h"
13 PathOutput::PathOutput(const SourceDir
& current_dir
,
14 const base::StringPiece
& 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
;
23 PathOutput::~PathOutput() {
26 void PathOutput::WriteFile(std::ostream
& out
, const SourceFile
& file
) const {
27 WritePathStr(out
, file
.value());
30 void PathOutput::WriteDir(std::ostream
& out
,
32 DirSlashEnding slash_ending
) const {
33 if (dir
.value() == "/") {
34 // Writing system root is always a slash (this will normally only come up
36 if (slash_ending
== DIR_NO_LAST_SLASH
)
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()) {
48 out
.write(inverse_current_dir_
.c_str(),
49 inverse_current_dir_
.size() - 1);
52 if (inverse_current_dir_
.empty())
55 out
<< inverse_current_dir_
;
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
)
64 } else if (slash_ending
== DIR_INCLUDE_LAST_SLASH
) {
65 WritePathStr(out
, dir
.value());
67 // DIR_NO_LAST_SLASH mode, just trim the last char.
68 WritePathStr(out
, base::StringPiece(dir
.value().data(),
69 dir
.value().size() - 1));
73 void 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_
);
78 void PathOutput::WriteFiles(std::ostream
& out
,
79 const std::vector
<OutputFile
>& files
) const {
80 for (const auto& file
: files
) {
86 void PathOutput::WriteFiles(std::ostream
& out
,
87 const UniqueVector
<OutputFile
>& files
) const {
88 for (const auto& file
: files
) {
94 void PathOutput::WriteDir(std::ostream
& out
,
95 const OutputFile
& file
,
96 DirSlashEnding slash_ending
) const {
97 DCHECK(file
.value().empty() ||
98 file
.value()[file
.value().size() - 1] == '/');
100 switch (slash_ending
) {
101 case DIR_INCLUDE_LAST_SLASH
:
102 EscapeStringToStream(out
, file
.value(), options_
);
104 case DIR_NO_LAST_SLASH
:
105 if (!file
.value().empty() &&
106 file
.value()[file
.value().size() - 1] == '/') {
107 // Trim trailing slash.
108 EscapeStringToStream(
110 base::StringPiece(file
.value().data(), file
.value().size() - 1),
113 // Doesn't end with a slash, write the whole thing.
114 EscapeStringToStream(out
, file
.value(), options_
);
120 void PathOutput::WriteFile(std::ostream
& out
,
121 const base::FilePath
& file
) const {
122 // Assume native file paths are always absolute.
123 EscapeStringToStream(out
, FilePathToUTF8(file
), options_
);
126 void PathOutput::WriteSourceRelativeString(
128 const base::StringPiece
& str
) const {
129 if (options_
.mode
== ESCAPE_NINJA_COMMAND
) {
130 // Shell escaping needs an intermediate string since it may end up
131 // quoting the whole thing.
132 std::string intermediate
;
133 intermediate
.reserve(inverse_current_dir_
.size() + str
.size());
134 intermediate
.assign(inverse_current_dir_
.c_str(),
135 inverse_current_dir_
.size());
136 intermediate
.append(str
.data(), str
.size());
138 EscapeStringToStream(out
,
139 base::StringPiece(intermediate
.c_str(), intermediate
.size()),
142 // Ninja (and none) escaping can avoid the intermediate string and
143 // reprocessing of the inverse_current_dir_.
144 out
<< inverse_current_dir_
;
145 EscapeStringToStream(out
, str
, options_
);
149 void PathOutput::WritePathStr(std::ostream
& out
,
150 const base::StringPiece
& str
) const {
151 DCHECK(str
.size() > 0 && str
[0] == '/');
153 if (str
.substr(0, current_dir_
.value().size()) ==
154 base::StringPiece(current_dir_
.value())) {
155 // The current dir is a prefix of the output file, so we can strip the
156 // prefix and write out the result.
157 EscapeStringToStream(out
, str
.substr(current_dir_
.value().size()),
159 } else if (str
.size() >= 2 && str
[1] == '/') {
160 WriteSourceRelativeString(out
, str
.substr(2));
162 // Input begins with one slash, don't write the current directory since
163 // it's system-absolute.
165 // On Windows, trim the leading slash, since the input for absolute
166 // paths will look like "/C:/foo/bar.txt".
167 EscapeStringToStream(out
, str
.substr(1), options_
);
169 EscapeStringToStream(out
, str
, options_
);