1 // Copyright 2014 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/err.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/value.h"
16 // Corresponds to the various values of "what" in the function call.
27 // Returns the directory containing the input (resolving it against the
28 // |current_dir|), regardless of whether the input is a directory or a file.
29 SourceDir
DirForInput(const Settings
* settings
,
30 const SourceDir
& current_dir
,
33 // Input should already have been validated as a string.
34 const std::string
& input_string
= input
.string_value();
36 if (!input_string
.empty() && input_string
[input_string
.size() - 1] == '/') {
37 // Input is a directory.
38 return current_dir
.ResolveRelativeDir(input
, err
,
39 settings
->build_settings()->root_path_utf8());
42 // Input is a directory.
43 return current_dir
.ResolveRelativeFile(input
, err
,
44 settings
->build_settings()->root_path_utf8()).GetDir();
47 std::string
GetOnePathInfo(const Settings
* settings
,
48 const SourceDir
& current_dir
,
52 if (!input
.VerifyTypeIs(Value::STRING
, err
))
54 const std::string
& input_string
= input
.string_value();
55 if (input_string
.empty()) {
56 *err
= Err(input
, "Calling get_path_info on an empty string.");
62 return FindFilename(&input_string
).as_string();
65 std::string file
= FindFilename(&input_string
).as_string();
66 size_t extension_offset
= FindExtensionOffset(file
);
67 if (extension_offset
== std::string::npos
)
69 // Trim extension and dot.
70 return file
.substr(0, extension_offset
- 1);
72 case WHAT_EXTENSION
: {
73 return FindExtension(&input_string
).as_string();
76 base::StringPiece dir_incl_slash
= FindDir(&input_string
);
77 if (dir_incl_slash
.empty())
78 return std::string(".");
79 // Trim slash since this function doesn't return trailing slashes. The
80 // times we don't do this are if the result is "/" and "//" since those
81 // slashes can't be trimmed.
82 if (dir_incl_slash
== "/")
83 return std::string("/.");
84 if (dir_incl_slash
== "//")
85 return std::string("//.");
86 return dir_incl_slash
.substr(0, dir_incl_slash
.size() - 1).as_string();
89 return DirectoryWithNoLastSlash(
90 GetGenDirForSourceDir(settings
,
91 DirForInput(settings
, current_dir
,
95 return DirectoryWithNoLastSlash(
96 GetOutputDirForSourceDir(settings
,
97 DirForInput(settings
, current_dir
,
101 if (!input_string
.empty() &&
102 input_string
[input_string
.size() - 1] == '/') {
103 return current_dir
.ResolveRelativeDir(input
, err
,
104 settings
->build_settings()->root_path_utf8()).value();
106 return current_dir
.ResolveRelativeFile(input
, err
,
107 settings
->build_settings()->root_path_utf8()).value();
112 return std::string();
118 const char kGetPathInfo
[] = "get_path_info";
119 const char kGetPathInfo_HelpShort
[] =
120 "get_path_info: Extract parts of a file or directory name.";
121 const char kGetPathInfo_Help
[] =
122 "get_path_info: Extract parts of a file or directory name.\n"
124 " get_path_info(input, what)\n"
126 " The first argument is either a string representing a file or\n"
127 " directory name, or a list of such strings. If the input is a list\n"
128 " the return value will be a list containing the result of applying the\n"
129 " rule to each item in the input.\n"
131 "Possible values for the \"what\" parameter\n"
134 " The substring after the last slash in the path, including the name\n"
135 " and extension. If the input ends in a slash, the empty string will\n"
137 " \"foo/bar.txt\" => \"bar.txt\"\n"
138 " \"bar.txt\" => \"bar.txt\"\n"
139 " \"foo/\" => \"\"\n"
143 " The substring of the file name not including the extension.\n"
144 " \"foo/bar.txt\" => \"bar\"\n"
145 " \"foo/bar\" => \"bar\"\n"
146 " \"foo/\" => \"\"\n"
149 " The substring following the last period following the last slash,\n"
150 " or the empty string if not found. The period is not included.\n"
151 " \"foo/bar.txt\" => \"txt\"\n"
152 " \"foo/bar\" => \"\"\n"
155 " The directory portion of the name, not including the slash.\n"
156 " \"foo/bar.txt\" => \"foo\"\n"
157 " \"//foo/bar\" => \"//foo\"\n"
158 " \"foo\" => \".\"\n"
160 " The result will never end in a slash, so if the resulting\n"
161 " is empty, the system (\"/\") or source (\"//\") roots, a \".\"\n"
162 " will be appended such that it is always legal to append a slash\n"
163 " and a filename and get a valid path.\n"
166 " The output file directory corresponding to the path of the\n"
167 " given file, not including a trailing slash.\n"
168 " \"//foo/bar/baz.txt\" => \"//out/Default/obj/foo/bar\"\n"
171 " The generated file directory corresponding to the path of the\n"
172 " given file, not including a trailing slash.\n"
173 " \"//foo/bar/baz.txt\" => \"//out/Default/gen/foo/bar\"\n"
176 " The full absolute path name to the file or directory. It will be\n"
177 " resolved relative to the currebt directory, and then the source-\n"
178 " absolute version will be returned. If the input is system-\n"
179 " absolute, the same input will be returned.\n"
180 " \"foo/bar.txt\" => \"//mydir/foo/bar.txt\"\n"
181 " \"foo/\" => \"//mydir/foo/\"\n"
182 " \"//foo/bar\" => \"//foo/bar\" (already absolute)\n"
183 " \"/usr/include\" => \"/usr/include\" (already absolute)\n"
185 " If you want to make the path relative to another directory, or to\n"
186 " be system-absolute, see rebase_path().\n"
189 " sources = [ \"foo.cc\", \"foo.h\" ]\n"
190 " result = get_path_info(source, \"abspath\")\n"
191 " # result will be [ \"//mydir/foo.cc\", \"//mydir/foo.h\" ]\n"
193 " result = get_path_info(\"//foo/bar/baz.cc\", \"dir\")\n"
194 " # result will be \"//foo/bar\"\n"
196 " # Extract the source-absolute directory name,\n"
197 " result = get_path_info(get_path_info(path, \"dir\"), \"abspath\")\n";
199 Value
RunGetPathInfo(Scope
* scope
,
200 const FunctionCallNode
* function
,
201 const std::vector
<Value
>& args
,
203 if (args
.size() != 2) {
204 *err
= Err(function
, "Expecting two arguments to get_path_info.");
208 // Extract the "what".
209 if (!args
[1].VerifyTypeIs(Value::STRING
, err
))
212 if (args
[1].string_value() == "file") {
214 } else if (args
[1].string_value() == "name") {
216 } else if (args
[1].string_value() == "extension") {
217 what
= WHAT_EXTENSION
;
218 } else if (args
[1].string_value() == "dir") {
220 } else if (args
[1].string_value() == "out_dir") {
222 } else if (args
[1].string_value() == "gen_dir") {
224 } else if (args
[1].string_value() == "abspath") {
227 *err
= Err(args
[1], "Unknown value for 'what'.");
231 const SourceDir
& current_dir
= scope
->GetSourceDir();
232 if (args
[0].type() == Value::STRING
) {
233 return Value(function
, GetOnePathInfo(scope
->settings(), current_dir
, what
,
235 } else if (args
[0].type() == Value::LIST
) {
236 const std::vector
<Value
>& input_list
= args
[0].list_value();
237 Value
result(function
, Value::LIST
);
238 for (const auto& cur
: input_list
) {
239 result
.list_value().push_back(Value(function
,
240 GetOnePathInfo(scope
->settings(), current_dir
, what
, cur
, err
)));
241 if (err
->has_error())
247 *err
= Err(args
[0], "Path must be a string or a list of strings.");
251 } // namespace functions