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. 4 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" 14 15 namespace functions { 16 17 namespace { 18 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); 29 } 30 } 31 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(); 38 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++; 43 44 if (num_dots == value.size()) 45 return true; // String is all dots. 46 47 if (IsSlash(value[value_size - num_dots - 1])) 48 return true; // String is a [back]slash followed by 0 or more dots. 49 50 // Anything else. 51 return false; 52 } 53 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. 62 63 if (!value.VerifyTypeIs(Value::STRING, err)) 64 return result; 65 const std::string& string_value = value.string_value(); 66 67 bool looks_like_dir = ValueLooksLikeDir(string_value); 68 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(string_value)); 75 } else { 76 system_path = scope->settings()->build_settings()->GetFullPath( 77 from_dir.ResolveRelativeFile(string_value)); 78 } 79 result = Value(function, FilePathToUTF8(system_path)); 80 if (looks_like_dir) 81 MakeSlashEndingMatchInput(string_value, &result.string_value()); 82 return result; 83 } 84 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!"); 89 return result; 90 } 91 92 result = Value(function, Value::STRING); 93 if (looks_like_dir) { 94 result.string_value() = RebaseSourceAbsolutePath( 95 from_dir.ResolveRelativeDir(string_value).value(), 96 to_dir); 97 MakeSlashEndingMatchInput(string_value, &result.string_value()); 98 } else { 99 result.string_value() = RebaseSourceAbsolutePath( 100 from_dir.ResolveRelativeFile(string_value).value(), 101 to_dir); 102 } 103 104 return result; 105 } 106 107 } // namespace 108 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" 114 "\n" 115 " converted = rebase_path(input,\n" 116 " new_base = \"\",\n" 117 " current_base = \".\")\n" 118 "\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" 121 " directory.\n" 122 "\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" 129 "\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" 133 "\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" 140 "\n" 141 "Arguments:\n" 142 "\n" 143 " input\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" 148 "\n" 149 " new_base\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" 153 "\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" 157 " programs.\n" 158 "\n" 159 " current_base\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" 164 "\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" 168 " converted,\n" 169 "\n" 170 "Return value\n" 171 "\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" 176 "\n" 177 "Example\n" 178 "\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" 183 "\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" 188 "\n" 189 " # Convert a file's path separators from forward slashes to system\n" 190 " # slashes.\n" 191 " foo = rebase_path(\"source/myfile.txt\", \".\", \".\", \"to_system\")\n" 192 "\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" 199 "\n" 200 " # Extra file args passed manually need to be explicitly converted\n" 201 " # to be relative to the build directory:\n" 202 " args = [\n" 203 " \"--data\",\n" 204 " rebase_path(\"//mything/data/input.dat\", root_build_dir),\n" 205 " \"--rel\",\n" 206 " rebase_path(\"relative_path.txt\", root_build_dir)\n" 207 " ] + sources\n" 208 " }\n"; 209 210 Value RunRebasePath(Scope* scope, 211 const FunctionCallNode* function, 212 const std::vector<Value>& args, 213 Err* err) { 214 Value result; 215 216 // Argument indices. 217 static const size_t kArgIndexInputs = 0; 218 static const size_t kArgIndexDest = 1; 219 static const size_t kArgIndexFrom = 2; 220 221 // Inputs. 222 if (args.size() < 1 || args.size() > 3) { 223 *err = Err(function->function(), "Wrong # of arguments for rebase_path."); 224 return result; 225 } 226 const Value& inputs = args[kArgIndexInputs]; 227 228 // To path. 229 bool convert_to_system_absolute = true; 230 SourceDir to_dir; 231 const SourceDir& current_dir = scope->GetSourceDir(); 232 if (args.size() > kArgIndexDest) { 233 if (!args[kArgIndexDest].VerifyTypeIs(Value::STRING, err)) 234 return result; 235 if (!args[kArgIndexDest].string_value().empty()) { 236 to_dir = 237 current_dir.ResolveRelativeDir(args[kArgIndexDest].string_value()); 238 convert_to_system_absolute = false; 239 } 240 } 241 242 // From path. 243 SourceDir from_dir; 244 if (args.size() > kArgIndexFrom) { 245 if (!args[kArgIndexFrom].VerifyTypeIs(Value::STRING, err)) 246 return result; 247 from_dir = 248 current_dir.ResolveRelativeDir(args[kArgIndexFrom].string_value()); 249 } else { 250 // Default to current directory if unspecified. 251 from_dir = current_dir; 252 } 253 254 // Path conversion. 255 if (inputs.type() == Value::STRING) { 256 return ConvertOnePath(scope, function, inputs, 257 from_dir, to_dir, convert_to_system_absolute, err); 258 259 } else if (inputs.type() == Value::LIST) { 260 result = Value(function, Value::LIST); 261 result.list_value().reserve(inputs.list_value().size()); 262 263 for (size_t i = 0; i < inputs.list_value().size(); i++) { 264 result.list_value().push_back( 265 ConvertOnePath(scope, function, inputs.list_value()[i], 266 from_dir, to_dir, convert_to_system_absolute, err)); 267 if (err->has_error()) { 268 result = Value(); 269 return result; 270 } 271 } 272 return result; 273 } 274 275 *err = Err(function->function(), 276 "rebase_path requires a list or a string."); 277 return result; 278 } 279 280 } // namespace functions 281