| // 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 <stddef.h> |
| |
| #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 Settings* settings, |
| const SourceDir& current_dir, |
| const Value& input, |
| Err* err) { |
| // Input should already have been validated as a string. |
| const std::string& input_string = input.string_value(); |
| |
| if (!input_string.empty() && input_string[input_string.size() - 1] == '/') { |
| // Input is a directory. |
| return current_dir.ResolveRelativeDir( |
| input, err, settings->build_settings()->root_path_utf8()); |
| } |
| |
| // Input is a file. |
| return current_dir |
| .ResolveRelativeFile(input, err, |
| settings->build_settings()->root_path_utf8()) |
| .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 std::string(FindFilename(&input_string)); |
| } |
| case WHAT_NAME: { |
| std::string file(FindFilename(&input_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 std::string(FindExtension(&input_string)); |
| } |
| case WHAT_DIR: { |
| std::string_view 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 std::string(dir_incl_slash.substr(0, dir_incl_slash.size() - 1)); |
| } |
| case WHAT_GEN_DIR: { |
| return DirectoryWithNoLastSlash(GetSubBuildDirAsSourceDir( |
| BuildDirContext(settings), |
| DirForInput(settings, current_dir, input, err), BuildDirType::GEN)); |
| } |
| case WHAT_OUT_DIR: { |
| return DirectoryWithNoLastSlash(GetSubBuildDirAsSourceDir( |
| BuildDirContext(settings), |
| DirForInput(settings, current_dir, input, err), BuildDirType::OBJ)); |
| } |
| case WHAT_ABSPATH: { |
| bool as_dir = |
| !input_string.empty() && input_string[input_string.size() - 1] == '/'; |
| |
| return current_dir.ResolveRelativeAs( |
| !as_dir, input, err, settings->build_settings()->root_path_utf8(), |
| &input_string); |
| } |
| 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[] = |
| R"(get_path_info: Extract parts of a file or directory name. |
| |
| get_path_info(input, what) |
| |
| The first argument is either a string representing a file or directory name, |
| or a list of such strings. If the input is a list the return value will be a |
| list containing the result of applying the rule to each item in the input. |
| |
| Possible values for the "what" parameter |
| |
| "file" |
| The substring after the last slash in the path, including the name and |
| extension. If the input ends in a slash, the empty string will be |
| returned. |
| "foo/bar.txt" => "bar.txt" |
| "bar.txt" => "bar.txt" |
| "foo/" => "" |
| "" => "" |
| |
| "name" |
| The substring of the file name not including the extension. |
| "foo/bar.txt" => "bar" |
| "foo/bar" => "bar" |
| "foo/" => "" |
| |
| "extension" |
| The substring following the last period following the last slash, or the |
| empty string if not found. The period is not included. |
| "foo/bar.txt" => "txt" |
| "foo/bar" => "" |
| |
| "dir" |
| The directory portion of the name, not including the slash. |
| "foo/bar.txt" => "foo" |
| "//foo/bar" => "//foo" |
| "foo" => "." |
| |
| The result will never end in a slash, so if the resulting is empty, the |
| system ("/") or source ("//") roots, a "." will be appended such that it |
| is always legal to append a slash and a filename and get a valid path. |
| |
| "out_dir" |
| The output file directory corresponding to the path of the given file, |
| not including a trailing slash. |
| "//foo/bar/baz.txt" => "//out/Default/obj/foo/bar" |
| |
| "gen_dir" |
| The generated file directory corresponding to the path of the given file, |
| not including a trailing slash. |
| "//foo/bar/baz.txt" => "//out/Default/gen/foo/bar" |
| |
| "abspath" |
| The full absolute path name to the file or directory. It will be resolved |
| relative to the current directory, and then the source- absolute version |
| will be returned. If the input is system- absolute, the same input will |
| be returned. |
| "foo/bar.txt" => "//mydir/foo/bar.txt" |
| "foo/" => "//mydir/foo/" |
| "//foo/bar" => "//foo/bar" (already absolute) |
| "/usr/include" => "/usr/include" (already absolute) |
| |
| If you want to make the path relative to another directory, or to be |
| system-absolute, see rebase_path(). |
| |
| Examples |
| sources = [ "foo.cc", "foo.h" ] |
| result = get_path_info(source, "abspath") |
| # result will be [ "//mydir/foo.cc", "//mydir/foo.h" ] |
| |
| result = get_path_info("//foo/bar/baz.cc", "dir") |
| # result will be "//foo/bar" |
| |
| # Extract the source-absolute directory name, |
| result = get_path_info(get_path_info(path, "dir"), "abspath") |
| )"; |
| |
| 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 (const auto& cur : input_list) { |
| result.list_value().push_back(Value( |
| function, |
| GetOnePathInfo(scope->settings(), current_dir, what, cur, 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 |