Home | History | Annotate | Download | only in gn
      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