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"
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
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]);
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
) {
37 size_t value_size
= value
.size();
39 // Count the number of dots at the end of the string.
41 while (num_dots
< value_size
&& value
[value_size
- num_dots
- 1] == '.')
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.
54 Value
ConvertOnePath(const Scope
* scope
,
55 const FunctionCallNode
* function
,
57 const SourceDir
& from_dir
,
58 const SourceDir
& to_dir
,
59 bool convert_to_system_absolute
,
61 Value result
; // Ensure return value optimization.
63 if (!value
.VerifyTypeIs(Value::STRING
, err
))
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
;
73 system_path
= scope
->settings()->build_settings()->GetFullPath(
74 from_dir
.ResolveRelativeDir(string_value
));
76 system_path
= scope
->settings()->build_settings()->GetFullPath(
77 from_dir
.ResolveRelativeFile(string_value
));
79 result
= Value(function
, FilePathToUTF8(system_path
));
81 MakeSlashEndingMatchInput(string_value
, &result
.string_value());
85 if (from_dir
.is_system_absolute() || to_dir
.is_system_absolute()) {
86 *err
= Err(function
, "System-absolute directories are not supported for "
87 "the source or dest dir for rebase_path. It would be nice to add this "
88 "if you're so inclined!");
92 result
= Value(function
, Value::STRING
);
94 result
.string_value() = RebaseSourceAbsolutePath(
95 from_dir
.ResolveRelativeDir(string_value
).value(),
97 MakeSlashEndingMatchInput(string_value
, &result
.string_value());
99 result
.string_value() = RebaseSourceAbsolutePath(
100 from_dir
.ResolveRelativeFile(string_value
).value(),
109 const char kRebasePath
[] = "rebase_path";
110 const char kRebasePath_HelpShort
[] =
111 "rebase_path: Rebase a file or directory to another location.";
112 const char kRebasePath_Help
[] =
113 "rebase_path: Rebase a file or directory to another location.\n"
115 " converted = rebase_path(input,\n"
116 " new_base = \"\",\n"
117 " current_base = \".\")\n"
119 " Takes a string argument representing a file name, or a list of such\n"
120 " strings and converts it/them to be relative to a different base\n"
123 " When invoking the compiler or scripts, GN will automatically convert\n"
124 " sources and include directories to be relative to the build directory.\n"
125 " However, if you're passing files directly in the \"args\" array or\n"
126 " doing other manual manipulations where GN doesn't know something is\n"
127 " a file name, you will need to convert paths to be relative to what\n"
128 " your tool is expecting.\n"
130 " The common case is to use this to convert paths relative to the\n"
131 " current directory to be relative to the build directory (which will\n"
132 " be the current directory when executing scripts).\n"
134 " If you want to convert a file path to be source-absolute (that is,\n"
135 " beginning with a double slash like \"//foo/bar\"), you should use\n"
136 " the get_path_info() function. This function won't work because it will\n"
137 " always make relative paths, and it needs to support making paths\n"
138 " relative to the source root, so can't also generate source-absolute\n"
139 " paths without more special-cases.\n"
144 " A string or list of strings representing file or directory names\n"
145 " These can be relative paths (\"foo/bar.txt\"), system absolute\n"
146 " paths (\"/foo/bar.txt\"), or source absolute paths\n"
147 " (\"//foo/bar.txt\").\n"
150 " The directory to convert the paths to be relative to. This can be\n"
151 " an absolute path or a relative path (which will be treated\n"
152 " as being relative to the current BUILD-file's directory).\n"
154 " As a special case, if new_base is the empty string (the default),\n"
155 " all paths will be converted to system-absolute native style paths\n"
156 " with system path separators. This is useful for invoking external\n"
160 " Directory representing the base for relative paths in the input.\n"
161 " If this is not an absolute path, it will be treated as being\n"
162 " relative to the current build file. Use \".\" (the default) to\n"
163 " convert paths from the current BUILD-file's directory.\n"
165 " On Posix systems there are no path separator transformations\n"
166 " applied. If the new_base is empty (specifying absolute output)\n"
167 " this parameter should not be supplied since paths will always be\n"
172 " The return value will be the same type as the input value (either a\n"
173 " string or a list of strings). All relative and source-absolute file\n"
174 " names will be converted to be relative to the requested output\n"
175 " System-absolute paths will be unchanged.\n"
179 " # Convert a file in the current directory to be relative to the build\n"
180 " # directory (the current dir when executing compilers and scripts).\n"
181 " foo = rebase_path(\"myfile.txt\", root_build_dir)\n"
182 " # might produce \"../../project/myfile.txt\".\n"
184 " # Convert a file to be system absolute:\n"
185 " foo = rebase_path(\"myfile.txt\")\n"
186 " # Might produce \"D:\\source\\project\\myfile.txt\" on Windows or\n"
187 " # \"/home/you/source/project/myfile.txt\" on Linux.\n"
189 " # Convert a file's path separators from forward slashes to system\n"
191 " foo = rebase_path(\"source/myfile.txt\", \".\", \".\", \"to_system\")\n"
193 " # Typical usage for converting to the build directory for a script.\n"
194 " action(\"myscript\") {\n"
195 " # Don't convert sources, GN will automatically convert these to be\n"
196 " # relative to the build directory when it contructs the command\n"
197 " # line for your script.\n"
198 " sources = [ \"foo.txt\", \"bar.txt\" ]\n"
200 " # Extra file args passed manually need to be explicitly converted\n"
201 " # to be relative to the build directory:\n"
204 " rebase_path(\"//mything/data/input.dat\", root_build_dir),\n"
206 " rebase_path(\"relative_path.txt\", root_build_dir)\n"
210 Value
RunRebasePath(Scope
* scope
,
211 const FunctionCallNode
* function
,
212 const std::vector
<Value
>& args
,
217 static const size_t kArgIndexInputs
= 0;
218 static const size_t kArgIndexDest
= 1;
219 static const size_t kArgIndexFrom
= 2;
222 if (args
.size() < 1 || args
.size() > 3) {
223 *err
= Err(function
->function(), "Wrong # of arguments for rebase_path.");
226 const Value
& inputs
= args
[kArgIndexInputs
];
229 bool convert_to_system_absolute
= true;
231 const SourceDir
& current_dir
= scope
->GetSourceDir();
232 if (args
.size() > kArgIndexDest
) {
233 if (!args
[kArgIndexDest
].VerifyTypeIs(Value::STRING
, err
))
235 if (!args
[kArgIndexDest
].string_value().empty()) {
237 current_dir
.ResolveRelativeDir(args
[kArgIndexDest
].string_value());
238 convert_to_system_absolute
= false;
244 if (args
.size() > kArgIndexFrom
) {
245 if (!args
[kArgIndexFrom
].VerifyTypeIs(Value::STRING
, err
))
248 current_dir
.ResolveRelativeDir(args
[kArgIndexFrom
].string_value());
250 // Default to current directory if unspecified.
251 from_dir
= current_dir
;
255 if (inputs
.type() == Value::STRING
) {
256 if (inputs
.string_value() == "//foo")
258 return ConvertOnePath(scope
, function
, inputs
,
259 from_dir
, to_dir
, convert_to_system_absolute
, err
);
261 } else if (inputs
.type() == Value::LIST
) {
262 result
= Value(function
, Value::LIST
);
263 result
.list_value().reserve(inputs
.list_value().size());
265 for (size_t i
= 0; i
< inputs
.list_value().size(); i
++) {
266 result
.list_value().push_back(
267 ConvertOnePath(scope
, function
, inputs
.list_value()[i
],
268 from_dir
, to_dir
, convert_to_system_absolute
, err
));
269 if (err
->has_error()) {
277 *err
= Err(function
->function(),
278 "rebase_path requires a list or a string.");
282 } // namespace functions