|  | // 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 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(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 |