Add ICU message format support
[chromium-blink-merge.git] / tools / gn / function_rebase_path.cc
blobffe4eae565f6d49253bfde38d8af2f4de40041f5
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/build_settings.h"
6 #include "tools/gn/filesystem_utils.h"
7 #include "tools/gn/functions.h"
8 #include "tools/gn/parse_tree.h"
9 #include "tools/gn/scope.h"
10 #include "tools/gn/settings.h"
11 #include "tools/gn/source_dir.h"
12 #include "tools/gn/source_file.h"
13 #include "tools/gn/value.h"
15 namespace functions {
17 namespace {
19 // We want the output to match the input in terms of ending in a slash or not.
20 // Through all the transformations, these can get added or removed in various
21 // cases.
22 void MakeSlashEndingMatchInput(const std::string& input, std::string* output) {
23 if (EndsWithSlash(input)) {
24 if (!EndsWithSlash(*output)) // Preserve same slash type as input.
25 output->push_back(input[input.size() - 1]);
26 } else {
27 if (EndsWithSlash(*output))
28 output->resize(output->size() - 1);
32 // Returns true if the given value looks like a directory, otherwise we'll
33 // assume it's a file.
34 bool ValueLooksLikeDir(const std::string& value) {
35 if (value.empty())
36 return true;
37 size_t value_size = value.size();
39 // Count the number of dots at the end of the string.
40 size_t num_dots = 0;
41 while (num_dots < value_size && value[value_size - num_dots - 1] == '.')
42 num_dots++;
44 if (num_dots == value.size())
45 return true; // String is all dots.
47 if (IsSlash(value[value_size - num_dots - 1]))
48 return true; // String is a [back]slash followed by 0 or more dots.
50 // Anything else.
51 return false;
54 Value ConvertOnePath(const Scope* scope,
55 const FunctionCallNode* function,
56 const Value& value,
57 const SourceDir& from_dir,
58 const SourceDir& to_dir,
59 bool convert_to_system_absolute,
60 Err* err) {
61 Value result; // Ensure return value optimization.
63 if (!value.VerifyTypeIs(Value::STRING, err))
64 return result;
65 const std::string& string_value = value.string_value();
67 bool looks_like_dir = ValueLooksLikeDir(string_value);
69 // System-absolute output special case.
70 if (convert_to_system_absolute) {
71 base::FilePath system_path;
72 if (looks_like_dir) {
73 system_path = scope->settings()->build_settings()->GetFullPath(
74 from_dir.ResolveRelativeDir(value, err,
75 scope->settings()->build_settings()->root_path_utf8()));
76 } else {
77 system_path = scope->settings()->build_settings()->GetFullPath(
78 from_dir.ResolveRelativeFile(value, err,
79 scope->settings()->build_settings()->root_path_utf8()));
81 if (err->has_error())
82 return Value();
84 result = Value(function, FilePathToUTF8(system_path));
85 if (looks_like_dir)
86 MakeSlashEndingMatchInput(string_value, &result.string_value());
87 return result;
90 result = Value(function, Value::STRING);
91 if (looks_like_dir) {
92 result.string_value() = RebasePath(
93 from_dir.ResolveRelativeDir(value, err,
94 scope->settings()->build_settings()->root_path_utf8()).value(),
95 to_dir,
96 scope->settings()->build_settings()->root_path_utf8());
97 MakeSlashEndingMatchInput(string_value, &result.string_value());
98 } else {
99 result.string_value() = RebasePath(
100 from_dir.ResolveRelativeFile(value, err,
101 scope->settings()->build_settings()->root_path_utf8()).value(),
102 to_dir,
103 scope->settings()->build_settings()->root_path_utf8());
104 if (err->has_error())
105 return Value();
108 return result;
111 } // namespace
113 const char kRebasePath[] = "rebase_path";
114 const char kRebasePath_HelpShort[] =
115 "rebase_path: Rebase a file or directory to another location.";
116 const char kRebasePath_Help[] =
117 "rebase_path: Rebase a file or directory to another location.\n"
118 "\n"
119 " converted = rebase_path(input,\n"
120 " new_base = \"\",\n"
121 " current_base = \".\")\n"
122 "\n"
123 " Takes a string argument representing a file name, or a list of such\n"
124 " strings and converts it/them to be relative to a different base\n"
125 " directory.\n"
126 "\n"
127 " When invoking the compiler or scripts, GN will automatically convert\n"
128 " sources and include directories to be relative to the build directory.\n"
129 " However, if you're passing files directly in the \"args\" array or\n"
130 " doing other manual manipulations where GN doesn't know something is\n"
131 " a file name, you will need to convert paths to be relative to what\n"
132 " your tool is expecting.\n"
133 "\n"
134 " The common case is to use this to convert paths relative to the\n"
135 " current directory to be relative to the build directory (which will\n"
136 " be the current directory when executing scripts).\n"
137 "\n"
138 " If you want to convert a file path to be source-absolute (that is,\n"
139 " beginning with a double slash like \"//foo/bar\"), you should use\n"
140 " the get_path_info() function. This function won't work because it will\n"
141 " always make relative paths, and it needs to support making paths\n"
142 " relative to the source root, so can't also generate source-absolute\n"
143 " paths without more special-cases.\n"
144 "\n"
145 "Arguments:\n"
146 "\n"
147 " input\n"
148 " A string or list of strings representing file or directory names\n"
149 " These can be relative paths (\"foo/bar.txt\"), system absolute\n"
150 " paths (\"/foo/bar.txt\"), or source absolute paths\n"
151 " (\"//foo/bar.txt\").\n"
152 "\n"
153 " new_base\n"
154 " The directory to convert the paths to be relative to. This can be\n"
155 " an absolute path or a relative path (which will be treated\n"
156 " as being relative to the current BUILD-file's directory).\n"
157 "\n"
158 " As a special case, if new_base is the empty string (the default),\n"
159 " all paths will be converted to system-absolute native style paths\n"
160 " with system path separators. This is useful for invoking external\n"
161 " programs.\n"
162 "\n"
163 " current_base\n"
164 " Directory representing the base for relative paths in the input.\n"
165 " If this is not an absolute path, it will be treated as being\n"
166 " relative to the current build file. Use \".\" (the default) to\n"
167 " convert paths from the current BUILD-file's directory.\n"
168 "\n"
169 "Return value\n"
170 "\n"
171 " The return value will be the same type as the input value (either a\n"
172 " string or a list of strings). All relative and source-absolute file\n"
173 " names will be converted to be relative to the requested output\n"
174 " System-absolute paths will be unchanged.\n"
175 "\n"
176 "Example\n"
177 "\n"
178 " # Convert a file in the current directory to be relative to the build\n"
179 " # directory (the current dir when executing compilers and scripts).\n"
180 " foo = rebase_path(\"myfile.txt\", root_build_dir)\n"
181 " # might produce \"../../project/myfile.txt\".\n"
182 "\n"
183 " # Convert a file to be system absolute:\n"
184 " foo = rebase_path(\"myfile.txt\")\n"
185 " # Might produce \"D:\\source\\project\\myfile.txt\" on Windows or\n"
186 " # \"/home/you/source/project/myfile.txt\" on Linux.\n"
187 "\n"
188 " # Typical usage for converting to the build directory for a script.\n"
189 " action(\"myscript\") {\n"
190 " # Don't convert sources, GN will automatically convert these to be\n"
191 " # relative to the build directory when it constructs the command\n"
192 " # line for your script.\n"
193 " sources = [ \"foo.txt\", \"bar.txt\" ]\n"
194 "\n"
195 " # Extra file args passed manually need to be explicitly converted\n"
196 " # to be relative to the build directory:\n"
197 " args = [\n"
198 " \"--data\",\n"
199 " rebase_path(\"//mything/data/input.dat\", root_build_dir),\n"
200 " \"--rel\",\n"
201 " rebase_path(\"relative_path.txt\", root_build_dir)\n"
202 " ] + rebase_path(sources, root_build_dir)\n"
203 " }\n";
205 Value RunRebasePath(Scope* scope,
206 const FunctionCallNode* function,
207 const std::vector<Value>& args,
208 Err* err) {
209 Value result;
211 // Argument indices.
212 static const size_t kArgIndexInputs = 0;
213 static const size_t kArgIndexDest = 1;
214 static const size_t kArgIndexFrom = 2;
216 // Inputs.
217 if (args.size() < 1 || args.size() > 3) {
218 *err = Err(function->function(), "Wrong # of arguments for rebase_path.");
219 return result;
221 const Value& inputs = args[kArgIndexInputs];
223 // To path.
224 bool convert_to_system_absolute = true;
225 SourceDir to_dir;
226 const SourceDir& current_dir = scope->GetSourceDir();
227 if (args.size() > kArgIndexDest) {
228 if (!args[kArgIndexDest].VerifyTypeIs(Value::STRING, err))
229 return result;
230 if (!args[kArgIndexDest].string_value().empty()) {
231 to_dir = current_dir.ResolveRelativeDir(
232 args[kArgIndexDest], err,
233 scope->settings()->build_settings()->root_path_utf8());
234 if (err->has_error())
235 return Value();
236 convert_to_system_absolute = false;
240 // From path.
241 SourceDir from_dir;
242 if (args.size() > kArgIndexFrom) {
243 if (!args[kArgIndexFrom].VerifyTypeIs(Value::STRING, err))
244 return result;
245 from_dir = current_dir.ResolveRelativeDir(
246 args[kArgIndexFrom], err,
247 scope->settings()->build_settings()->root_path_utf8());
248 if (err->has_error())
249 return Value();
250 } else {
251 // Default to current directory if unspecified.
252 from_dir = current_dir;
255 // Path conversion.
256 if (inputs.type() == Value::STRING) {
257 return ConvertOnePath(scope, function, inputs,
258 from_dir, to_dir, convert_to_system_absolute, err);
260 } else if (inputs.type() == Value::LIST) {
261 result = Value(function, Value::LIST);
262 result.list_value().reserve(inputs.list_value().size());
264 for (const auto& input : inputs.list_value()) {
265 result.list_value().push_back(
266 ConvertOnePath(scope, function, input,
267 from_dir, to_dir, convert_to_system_absolute, err));
268 if (err->has_error()) {
269 result = Value();
270 return result;
273 return result;
276 *err = Err(function->function(),
277 "rebase_path requires a list or a string.");
278 return result;
281 } // namespace functions