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. 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 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 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