• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.
4 
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"
11 
12 namespace functions {
13 
14 namespace {
15 
16 // Corresponds to the various values of "what" in the function call.
17 enum What {
18   WHAT_FILE,
19   WHAT_NAME,
20   WHAT_EXTENSION,
21   WHAT_DIR,
22   WHAT_ABSPATH,
23   WHAT_GEN_DIR,
24   WHAT_OUT_DIR,
25 };
26 
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.
DirForInput(const SourceDir & current_dir,const std::string & input_string)29 SourceDir DirForInput(const SourceDir& current_dir,
30                       const std::string& input_string) {
31   if (!input_string.empty() && input_string[input_string.size() - 1] == '/') {
32     // Input is a directory.
33     return current_dir.ResolveRelativeDir(input_string);
34   }
35 
36   // Input is a directory.
37   return current_dir.ResolveRelativeFile(input_string).GetDir();
38 }
39 
GetOnePathInfo(const Settings * settings,const SourceDir & current_dir,What what,const Value & input,Err * err)40 std::string GetOnePathInfo(const Settings* settings,
41                            const SourceDir& current_dir,
42                            What what,
43                            const Value& input,
44                            Err* err) {
45   if (!input.VerifyTypeIs(Value::STRING, err))
46     return std::string();
47   const std::string& input_string = input.string_value();
48   if (input_string.empty()) {
49     *err = Err(input, "Calling get_path_info on an empty string.");
50     return std::string();
51   }
52 
53   switch (what) {
54     case WHAT_FILE: {
55       return FindFilename(&input_string).as_string();
56     }
57     case WHAT_NAME: {
58       std::string file = FindFilename(&input_string).as_string();
59       size_t extension_offset = FindExtensionOffset(file);
60       if (extension_offset == std::string::npos)
61         return file;
62       // Trim extension and dot.
63       return file.substr(0, extension_offset - 1);
64     }
65     case WHAT_EXTENSION: {
66       return FindExtension(&input_string).as_string();
67     }
68     case WHAT_DIR: {
69       base::StringPiece dir_incl_slash = FindDir(&input_string);
70       if (dir_incl_slash.empty())
71         return std::string(".");
72       // Trim slash since this function doesn't return trailing slashes. The
73       // times we don't do this are if the result is "/" and "//" since those
74       // slashes can't be trimmed.
75       if (dir_incl_slash == "/")
76         return std::string("/.");
77       if (dir_incl_slash == "//")
78         return std::string("//.");
79       return dir_incl_slash.substr(0, dir_incl_slash.size() - 1).as_string();
80     }
81     case WHAT_GEN_DIR: {
82       return DirectoryWithNoLastSlash(
83           GetGenDirForSourceDir(settings,
84                                 DirForInput(current_dir, input_string)));
85     }
86     case WHAT_OUT_DIR: {
87       return DirectoryWithNoLastSlash(
88           GetOutputDirForSourceDir(settings,
89                                    DirForInput(current_dir, input_string)));
90     }
91     case WHAT_ABSPATH: {
92       if (!input_string.empty() && input_string[input_string.size() - 1] == '/')
93         return current_dir.ResolveRelativeDir(input_string).value();
94       else
95         return current_dir.ResolveRelativeFile(input_string).value();
96     }
97     default:
98       NOTREACHED();
99       return std::string();
100   }
101 }
102 
103 }  // namespace
104 
105 const char kGetPathInfo[] = "get_path_info";
106 const char kGetPathInfo_HelpShort[] =
107     "get_path_info: Extract parts of a file or directory name.";
108 const char kGetPathInfo_Help[] =
109     "get_path_info: Extract parts of a file or directory name.\n"
110     "\n"
111     "  get_path_info(input, what)\n"
112     "\n"
113     "  The first argument is either a string representing a file or\n"
114     "  directory name, or a list of such strings. If the input is a list\n"
115     "  the return value will be a list containing the result of applying the\n"
116     "  rule to each item in the input.\n"
117     "\n"
118     "Possible values for the \"what\" parameter\n"
119     "\n"
120     "  \"file\"\n"
121     "      The substring after the last slash in the path, including the name\n"
122     "      and extension. If the input ends in a slash, the empty string will\n"
123     "      be returned.\n"
124     "        \"foo/bar.txt\" => \"bar.txt\"\n"
125     "        \"bar.txt\" => \"bar.txt\"\n"
126     "        \"foo/\" => \"\"\n"
127     "        \"\" => \"\"\n"
128     "\n"
129     "  \"name\"\n"
130     "     The substring of the file name not including the extension.\n"
131     "        \"foo/bar.txt\" => \"bar\"\n"
132     "        \"foo/bar\" => \"bar\"\n"
133     "        \"foo/\" => \"\"\n"
134     "\n"
135     "  \"extension\"\n"
136     "      The substring following the last period following the last slash,\n"
137     "      or the empty string if not found. The period is not included.\n"
138     "        \"foo/bar.txt\" => \"txt\"\n"
139     "        \"foo/bar\" => \"\"\n"
140     "\n"
141     "  \"dir\"\n"
142     "      The directory portion of the name, not including the slash.\n"
143     "        \"foo/bar.txt\" => \"foo\"\n"
144     "        \"//foo/bar\" => \"//foo\"\n"
145     "        \"foo\" => \".\"\n"
146     "\n"
147     "      The result will never end in a slash, so if the resulting\n"
148     "      is empty, the system (\"/\") or source (\"//\") roots, a \".\"\n"
149     "      will be appended such that it is always legal to append a slash\n"
150     "      and a filename and get a valid path.\n"
151     "\n"
152     "  \"out_dir\"\n"
153     "      The output file directory corresponding to the path of the\n"
154     "      given file, not including a trailing slash.\n"
155     "        \"//foo/bar/baz.txt\" => \"//out/Default/obj/foo/bar\"\n"
156 
157     "  \"gen_dir\"\n"
158     "      The generated file directory corresponding to the path of the\n"
159     "      given file, not including a trailing slash.\n"
160     "        \"//foo/bar/baz.txt\" => \"//out/Default/gen/foo/bar\"\n"
161     "\n"
162     "  \"abspath\"\n"
163     "      The full absolute path name to the file or directory. It will be\n"
164     "      resolved relative to the currebt directory, and then the source-\n"
165     "      absolute version will be returned. If the input is system-\n"
166     "      absolute, the same input will be returned.\n"
167     "        \"foo/bar.txt\" => \"//mydir/foo/bar.txt\"\n"
168     "        \"foo/\" => \"//mydir/foo/\"\n"
169     "        \"//foo/bar\" => \"//foo/bar\"  (already absolute)\n"
170     "        \"/usr/include\" => \"/usr/include\"  (already absolute)\n"
171     "\n"
172     "      If you want to make the path relative to another directory, or to\n"
173     "      be system-absolute, see rebase_path().\n"
174     "\n"
175     "Examples\n"
176     "  sources = [ \"foo.cc\", \"foo.h\" ]\n"
177     "  result = get_path_info(source, \"abspath\")\n"
178     "  # result will be [ \"//mydir/foo.cc\", \"//mydir/foo.h\" ]\n"
179     "\n"
180     "  result = get_path_info(\"//foo/bar/baz.cc\", \"dir\")\n"
181     "  # result will be \"//foo/bar\"\n"
182     "\n"
183     "  # Extract the source-absolute directory name,\n"
184     "  result = get_path_info(get_path_info(path, \"dir\"), \"abspath\")\n";
185 
RunGetPathInfo(Scope * scope,const FunctionCallNode * function,const std::vector<Value> & args,Err * err)186 Value RunGetPathInfo(Scope* scope,
187                      const FunctionCallNode* function,
188                      const std::vector<Value>& args,
189                      Err* err) {
190   if (args.size() != 2) {
191     *err = Err(function, "Expecting two arguments to get_path_info.");
192     return Value();
193   }
194 
195   // Extract the "what".
196   if (!args[1].VerifyTypeIs(Value::STRING, err))
197     return Value();
198   What what;
199   if (args[1].string_value() == "file") {
200     what = WHAT_FILE;
201   } else if (args[1].string_value() == "name") {
202     what = WHAT_NAME;
203   } else if (args[1].string_value() == "extension") {
204     what = WHAT_EXTENSION;
205   } else if (args[1].string_value() == "dir") {
206     what = WHAT_DIR;
207   } else if (args[1].string_value() == "out_dir") {
208     what = WHAT_OUT_DIR;
209   } else if (args[1].string_value() == "gen_dir") {
210     what = WHAT_GEN_DIR;
211   } else if (args[1].string_value() == "abspath") {
212     what = WHAT_ABSPATH;
213   } else {
214     *err = Err(args[1], "Unknown value for 'what'.");
215     return Value();
216   }
217 
218   const SourceDir& current_dir = scope->GetSourceDir();
219   if (args[0].type() == Value::STRING) {
220     return Value(function, GetOnePathInfo(scope->settings(), current_dir, what,
221                                           args[0], err));
222   } else if (args[0].type() == Value::LIST) {
223     const std::vector<Value>& input_list = args[0].list_value();
224     Value result(function, Value::LIST);
225     for (size_t i = 0; i < input_list.size(); i++) {
226       result.list_value().push_back(Value(function,
227           GetOnePathInfo(scope->settings(), current_dir, what,
228                          input_list[i], err)));
229       if (err->has_error())
230         return Value();
231     }
232     return result;
233   }
234 
235   *err = Err(args[0], "Path must be a string or a list of strings.");
236   return Value();
237 }
238 
239 }  // namespace functions
240