| // Copyright 2014 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "tools/gn/err.h" |
| #include "tools/gn/filesystem_utils.h" |
| #include "tools/gn/functions.h" |
| #include "tools/gn/parse_tree.h" |
| #include "tools/gn/scope.h" |
| #include "tools/gn/value.h" |
| |
| namespace functions { |
| |
| namespace { |
| |
| // Corresponds to the various values of "what" in the function call. |
| enum What { |
| WHAT_FILE, |
| WHAT_NAME, |
| WHAT_EXTENSION, |
| WHAT_DIR, |
| WHAT_ABSPATH, |
| WHAT_GEN_DIR, |
| WHAT_OUT_DIR, |
| }; |
| |
| // Returns the directory containing the input (resolving it against the |
| // |current_dir|), regardless of whether the input is a directory or a file. |
| SourceDir DirForInput(const SourceDir& current_dir, |
| const std::string& input_string) { |
| if (!input_string.empty() && input_string[input_string.size() - 1] == '/') { |
| // Input is a directory. |
| return current_dir.ResolveRelativeDir(input_string); |
| } |
| |
| // Input is a directory. |
| return current_dir.ResolveRelativeFile(input_string).GetDir(); |
| } |
| |
| std::string GetOnePathInfo(const Settings* settings, |
| const SourceDir& current_dir, |
| What what, |
| const Value& input, |
| Err* err) { |
| if (!input.VerifyTypeIs(Value::STRING, err)) |
| return std::string(); |
| const std::string& input_string = input.string_value(); |
| if (input_string.empty()) { |
| *err = Err(input, "Calling get_path_info on an empty string."); |
| return std::string(); |
| } |
| |
| switch (what) { |
| case WHAT_FILE: { |
| return FindFilename(&input_string).as_string(); |
| } |
| case WHAT_NAME: { |
| std::string file = FindFilename(&input_string).as_string(); |
| size_t extension_offset = FindExtensionOffset(file); |
| if (extension_offset == std::string::npos) |
| return file; |
| // Trim extension and dot. |
| return file.substr(0, extension_offset - 1); |
| } |
| case WHAT_EXTENSION: { |
| return FindExtension(&input_string).as_string(); |
| } |
| case WHAT_DIR: { |
| base::StringPiece dir_incl_slash = FindDir(&input_string); |
| if (dir_incl_slash.empty()) |
| return std::string("."); |
| // Trim slash since this function doesn't return trailing slashes. The |
| // times we don't do this are if the result is "/" and "//" since those |
| // slashes can't be trimmed. |
| if (dir_incl_slash == "/") |
| return std::string("/."); |
| if (dir_incl_slash == "//") |
| return std::string("//."); |
| return dir_incl_slash.substr(0, dir_incl_slash.size() - 1).as_string(); |
| } |
| case WHAT_GEN_DIR: { |
| return DirectoryWithNoLastSlash( |
| GetGenDirForSourceDir(settings, |
| DirForInput(current_dir, input_string))); |
| } |
| case WHAT_OUT_DIR: { |
| return DirectoryWithNoLastSlash( |
| GetOutputDirForSourceDir(settings, |
| DirForInput(current_dir, input_string))); |
| } |
| case WHAT_ABSPATH: { |
| if (!input_string.empty() && input_string[input_string.size() - 1] == '/') |
| return current_dir.ResolveRelativeDir(input_string).value(); |
| else |
| return current_dir.ResolveRelativeFile(input_string).value(); |
| } |
| default: |
| NOTREACHED(); |
| return std::string(); |
| } |
| } |
| |
| } // namespace |
| |
| const char kGetPathInfo[] = "get_path_info"; |
| const char kGetPathInfo_HelpShort[] = |
| "get_path_info: Extract parts of a file or directory name."; |
| const char kGetPathInfo_Help[] = |
| "get_path_info: Extract parts of a file or directory name.\n" |
| "\n" |
| " get_path_info(input, what)\n" |
| "\n" |
| " The first argument is either a string representing a file or\n" |
| " directory name, or a list of such strings. If the input is a list\n" |
| " the return value will be a list containing the result of applying the\n" |
| " rule to each item in the input.\n" |
| "\n" |
| "Possible values for the \"what\" parameter\n" |
| "\n" |
| " \"file\"\n" |
| " The substring after the last slash in the path, including the name\n" |
| " and extension. If the input ends in a slash, the empty string will\n" |
| " be returned.\n" |
| " \"foo/bar.txt\" => \"bar.txt\"\n" |
| " \"bar.txt\" => \"bar.txt\"\n" |
| " \"foo/\" => \"\"\n" |
| " \"\" => \"\"\n" |
| "\n" |
| " \"name\"\n" |
| " The substring of the file name not including the extension.\n" |
| " \"foo/bar.txt\" => \"bar\"\n" |
| " \"foo/bar\" => \"bar\"\n" |
| " \"foo/\" => \"\"\n" |
| "\n" |
| " \"extension\"\n" |
| " The substring following the last period following the last slash,\n" |
| " or the empty string if not found. The period is not included.\n" |
| " \"foo/bar.txt\" => \"txt\"\n" |
| " \"foo/bar\" => \"\"\n" |
| "\n" |
| " \"dir\"\n" |
| " The directory portion of the name, not including the slash.\n" |
| " \"foo/bar.txt\" => \"foo\"\n" |
| " \"//foo/bar\" => \"//foo\"\n" |
| " \"foo\" => \".\"\n" |
| "\n" |
| " The result will never end in a slash, so if the resulting\n" |
| " is empty, the system (\"/\") or source (\"//\") roots, a \".\"\n" |
| " will be appended such that it is always legal to append a slash\n" |
| " and a filename and get a valid path.\n" |
| "\n" |
| " \"out_dir\"\n" |
| " The output file directory corresponding to the path of the\n" |
| " given file, not including a trailing slash.\n" |
| " \"//foo/bar/baz.txt\" => \"//out/Default/obj/foo/bar\"\n" |
| |
| " \"gen_dir\"\n" |
| " The generated file directory corresponding to the path of the\n" |
| " given file, not including a trailing slash.\n" |
| " \"//foo/bar/baz.txt\" => \"//out/Default/gen/foo/bar\"\n" |
| "\n" |
| " \"abspath\"\n" |
| " The full absolute path name to the file or directory. It will be\n" |
| " resolved relative to the currebt directory, and then the source-\n" |
| " absolute version will be returned. If the input is system-\n" |
| " absolute, the same input will be returned.\n" |
| " \"foo/bar.txt\" => \"//mydir/foo/bar.txt\"\n" |
| " \"foo/\" => \"//mydir/foo/\"\n" |
| " \"//foo/bar\" => \"//foo/bar\" (already absolute)\n" |
| " \"/usr/include\" => \"/usr/include\" (already absolute)\n" |
| "\n" |
| " If you want to make the path relative to another directory, or to\n" |
| " be system-absolute, see rebase_path().\n" |
| "\n" |
| "Examples\n" |
| " sources = [ \"foo.cc\", \"foo.h\" ]\n" |
| " result = get_path_info(source, \"abspath\")\n" |
| " # result will be [ \"//mydir/foo.cc\", \"//mydir/foo.h\" ]\n" |
| "\n" |
| " result = get_path_info(\"//foo/bar/baz.cc\", \"dir\")\n" |
| " # result will be \"//foo/bar\"\n" |
| "\n" |
| " # Extract the source-absolute directory name,\n" |
| " result = get_path_info(get_path_info(path, \"dir\"), \"abspath\")\n"; |
| |
| Value RunGetPathInfo(Scope* scope, |
| const FunctionCallNode* function, |
| const std::vector<Value>& args, |
| Err* err) { |
| if (args.size() != 2) { |
| *err = Err(function, "Expecting two arguments to get_path_info."); |
| return Value(); |
| } |
| |
| // Extract the "what". |
| if (!args[1].VerifyTypeIs(Value::STRING, err)) |
| return Value(); |
| What what; |
| if (args[1].string_value() == "file") { |
| what = WHAT_FILE; |
| } else if (args[1].string_value() == "name") { |
| what = WHAT_NAME; |
| } else if (args[1].string_value() == "extension") { |
| what = WHAT_EXTENSION; |
| } else if (args[1].string_value() == "dir") { |
| what = WHAT_DIR; |
| } else if (args[1].string_value() == "out_dir") { |
| what = WHAT_OUT_DIR; |
| } else if (args[1].string_value() == "gen_dir") { |
| what = WHAT_GEN_DIR; |
| } else if (args[1].string_value() == "abspath") { |
| what = WHAT_ABSPATH; |
| } else { |
| *err = Err(args[1], "Unknown value for 'what'."); |
| return Value(); |
| } |
| |
| const SourceDir& current_dir = scope->GetSourceDir(); |
| if (args[0].type() == Value::STRING) { |
| return Value(function, GetOnePathInfo(scope->settings(), current_dir, what, |
| args[0], err)); |
| } else if (args[0].type() == Value::LIST) { |
| const std::vector<Value>& input_list = args[0].list_value(); |
| Value result(function, Value::LIST); |
| for (size_t i = 0; i < input_list.size(); i++) { |
| result.list_value().push_back(Value(function, |
| GetOnePathInfo(scope->settings(), current_dir, what, |
| input_list[i], err))); |
| if (err->has_error()) |
| return Value(); |
| } |
| return result; |
| } |
| |
| *err = Err(args[0], "Path must be a string or a list of strings."); |
| return Value(); |
| } |
| |
| } // namespace functions |