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/substitution_writer.h"
      6 
      7 #include "tools/gn/build_settings.h"
      8 #include "tools/gn/escape.h"
      9 #include "tools/gn/filesystem_utils.h"
     10 #include "tools/gn/output_file.h"
     11 #include "tools/gn/settings.h"
     12 #include "tools/gn/source_file.h"
     13 #include "tools/gn/string_utils.h"
     14 #include "tools/gn/substitution_list.h"
     15 #include "tools/gn/substitution_pattern.h"
     16 #include "tools/gn/target.h"
     17 
     18 namespace {
     19 
     20 // Sets the given directory string to the destination, trimming any trailing
     21 // slash from the directory (SourceDirs and OutputFiles representing
     22 // directories will end in a trailing slash). If the directory is empty,
     23 // it will be replaced with a ".".
     24 void SetDirOrDotWithNoSlash(const std::string& dir, std::string* dest) {
     25   if (!dir.empty() && dir[dir.size() - 1] == '/')
     26     dest->assign(dir.data(), dir.size() - 1);
     27   else
     28     dest->assign(dir);
     29 
     30   if (dest->empty())
     31     dest->push_back('.');
     32 }
     33 
     34 }  // namespace
     35 
     36 const char kSourceExpansion_Help[] =
     37     "How Source Expansion Works\n"
     38     "\n"
     39     "  Source expansion is used for the action_foreach and copy target types\n"
     40     "  to map source file names to output file names or arguments.\n"
     41     "\n"
     42     "  To perform source expansion in the outputs, GN maps every entry in the\n"
     43     "  sources to every entry in the outputs list, producing the cross\n"
     44     "  product of all combinations, expanding placeholders (see below).\n"
     45     "\n"
     46     "  Source expansion in the args works similarly, but performing the\n"
     47     "  placeholder substitution produces a different set of arguments for\n"
     48     "  each invocation of the script.\n"
     49     "\n"
     50     "  If no placeholders are found, the outputs or args list will be treated\n"
     51     "  as a static list of literal file names that do not depend on the\n"
     52     "  sources.\n"
     53     "\n"
     54     "  See \"gn help copy\" and \"gn help action_foreach\" for more on how\n"
     55     "  this is applied.\n"
     56     "\n"
     57     "Placeholders\n"
     58     "\n"
     59     "  {{source}}\n"
     60     "      The name of the source file including directory (*). This will\n"
     61     "      generally be used for specifying inputs to a script in the\n"
     62     "      \"args\" variable.\n"
     63     "        \"//foo/bar/baz.txt\" => \"../../foo/bar/baz.txt\"\n"
     64     "\n"
     65     "  {{source_file_part}}\n"
     66     "      The file part of the source including the extension.\n"
     67     "        \"//foo/bar/baz.txt\" => \"baz.txt\"\n"
     68     "\n"
     69     "  {{source_name_part}}\n"
     70     "      The filename part of the source file with no directory or\n"
     71     "      extension. This will generally be used for specifying a\n"
     72     "      transformation from a soruce file to a destination file with the\n"
     73     "      same name but different extension.\n"
     74     "        \"//foo/bar/baz.txt\" => \"baz\"\n"
     75     "\n"
     76     "  {{source_dir}}\n"
     77     "      The directory (*) containing the source file with no\n"
     78     "      trailing slash.\n"
     79     "        \"//foo/bar/baz.txt\" => \"../../foo/bar\"\n"
     80     "\n"
     81     "  {{source_root_relative_dir}}\n"
     82     "      The path to the source file's directory relative to the source\n"
     83     "      root, with no leading \"//\" or trailing slashes. If the path is\n"
     84     "      system-absolute, (beginning in a single slash) this will just\n"
     85     "      return the path with no trailing slash. This value will always\n"
     86     "      be the same, regardless of whether it appears in the \"outputs\"\n"
     87     "      or \"args\" section.\n"
     88     "        \"//foo/bar/baz.txt\" => \"foo/bar\"\n"
     89     "\n"
     90     "  {{source_gen_dir}}\n"
     91     "      The generated file directory (*) corresponding to the source\n"
     92     "      file's path. This will be different than the target's generated\n"
     93     "      file directory if the source file is in a different directory\n"
     94     "      than the BUILD.gn file.\n"
     95     "        \"//foo/bar/baz.txt\" => \"gen/foo/bar\"\n"
     96     "\n"
     97     "  {{source_out_dir}}\n"
     98     "      The object file directory (*) corresponding to the source file's\n"
     99     "      path, relative to the build directory. this us be different than\n"
    100     "      the target's out directory if the source file is in a different\n"
    101     "      directory than the build.gn file.\n"
    102     "        \"//foo/bar/baz.txt\" => \"obj/foo/bar\"\n"
    103     "\n"
    104     "(*) Note on directories\n"
    105     "\n"
    106     "  Paths containing directories (except the source_root_relative_dir)\n"
    107     "  will be different depending on what context the expansion is evaluated\n"
    108     "  in. Generally it should \"just work\" but it means you can't\n"
    109     "  concatenate strings containing these values with reasonable results.\n"
    110     "\n"
    111     "  Details: source expansions can be used in the \"outputs\" variable,\n"
    112     "  the \"args\" variable, and in calls to \"process_file_template\". The\n"
    113     "  \"args\" are passed to a script which is run from the build directory,\n"
    114     "  so these directories will relative to the build directory for the\n"
    115     "  script to find. In the other cases, the directories will be source-\n"
    116     "  absolute (begin with a \"//\") because the results of those expansions\n"
    117     "  will be handled by GN internally.\n"
    118     "\n"
    119     "Examples\n"
    120     "\n"
    121     "  Non-varying outputs:\n"
    122     "    action(\"hardcoded_outputs\") {\n"
    123     "      sources = [ \"input1.idl\", \"input2.idl\" ]\n"
    124     "      outputs = [ \"$target_out_dir/output1.dat\",\n"
    125     "                  \"$target_out_dir/output2.dat\" ]\n"
    126     "    }\n"
    127     "  The outputs in this case will be the two literal files given.\n"
    128     "\n"
    129     "  Varying outputs:\n"
    130     "    action_foreach(\"varying_outputs\") {\n"
    131     "      sources = [ \"input1.idl\", \"input2.idl\" ]\n"
    132     "      outputs = [ \"{{source_gen_dir}}/{{source_name_part}}.h\",\n"
    133     "                  \"{{source_gen_dir}}/{{source_name_part}}.cc\" ]\n"
    134     "    }\n"
    135     "  Performing source expansion will result in the following output names:\n"
    136     "    //out/Debug/obj/mydirectory/input1.h\n"
    137     "    //out/Debug/obj/mydirectory/input1.cc\n"
    138     "    //out/Debug/obj/mydirectory/input2.h\n"
    139     "    //out/Debug/obj/mydirectory/input2.cc\n";
    140 
    141 // static
    142 void SubstitutionWriter::WriteWithNinjaVariables(
    143     const SubstitutionPattern& pattern,
    144     const EscapeOptions& escape_options,
    145     std::ostream& out) {
    146   // The result needs to be quoted as if it was one string, but the $ for
    147   // the inserted Ninja variables can't be escaped. So write to a buffer with
    148   // no quoting, and then quote the whole thing if necessary.
    149   EscapeOptions no_quoting(escape_options);
    150   no_quoting.inhibit_quoting = true;
    151 
    152   bool needs_quotes = false;
    153   std::string result;
    154   for (size_t i = 0; i < pattern.ranges().size(); i++) {
    155     const SubstitutionPattern::Subrange range = pattern.ranges()[i];
    156     if (range.type == SUBSTITUTION_LITERAL) {
    157       result.append(EscapeString(range.literal, no_quoting, &needs_quotes));
    158     } else {
    159       result.append("${");
    160       result.append(kSubstitutionNinjaNames[range.type]);
    161       result.append("}");
    162     }
    163   }
    164 
    165   if (needs_quotes && !escape_options.inhibit_quoting)
    166     out << "\"" << result << "\"";
    167   else
    168     out << result;
    169 }
    170 
    171 // static
    172 void SubstitutionWriter::GetListAsSourceFiles(
    173     const SubstitutionList& list,
    174     std::vector<SourceFile>* output) {
    175   for (size_t i = 0; i < list.list().size(); i++) {
    176     const SubstitutionPattern& pattern = list.list()[i];
    177     CHECK(pattern.ranges().size() == 1 &&
    178           pattern.ranges()[0].type == SUBSTITUTION_LITERAL)
    179         << "The substitution patterm \""
    180         << pattern.AsString()
    181         << "\" was expected to be a literal with no {{substitutions}}.";
    182     const std::string& literal = pattern.ranges()[0].literal;
    183     CHECK(literal.size() >= 1 && literal[0] == '/')
    184         << "The result of the pattern \""
    185         << pattern.AsString()
    186         << "\" was not an absolute path.";
    187     output->push_back(SourceFile(literal));
    188   }
    189 }
    190 
    191 // static
    192 void SubstitutionWriter::GetListAsOutputFiles(
    193     const Settings* settings,
    194     const SubstitutionList& list,
    195     std::vector<OutputFile>* output) {
    196   std::vector<SourceFile> output_as_sources;
    197   GetListAsSourceFiles(list, &output_as_sources);
    198   for (size_t i = 0; i < output_as_sources.size(); i++) {
    199     output->push_back(OutputFile(settings->build_settings(),
    200                                  output_as_sources[i]));
    201   }
    202 }
    203 
    204 // static
    205 SourceFile SubstitutionWriter::ApplyPatternToSource(
    206       const Settings* settings,
    207       const SubstitutionPattern& pattern,
    208       const SourceFile& source) {
    209   std::string result_value = ApplyPatternToSourceAsString(
    210       settings, pattern, source);
    211   CHECK(!result_value.empty() && result_value[0] == '/')
    212       << "The result of the pattern \""
    213       << pattern.AsString()
    214       << "\" was not a path beginning in \"/\" or \"//\".";
    215   return SourceFile(SourceFile::SWAP_IN, &result_value);
    216 }
    217 
    218 // static
    219 std::string SubstitutionWriter::ApplyPatternToSourceAsString(
    220     const Settings* settings,
    221     const SubstitutionPattern& pattern,
    222     const SourceFile& source) {
    223   std::string result_value;
    224   for (size_t i = 0; i < pattern.ranges().size(); i++) {
    225     const SubstitutionPattern::Subrange& subrange = pattern.ranges()[i];
    226     if (subrange.type == SUBSTITUTION_LITERAL) {
    227       result_value.append(subrange.literal);
    228     } else {
    229       result_value.append(
    230           GetSourceSubstitution(settings, source, subrange.type,
    231                                 OUTPUT_ABSOLUTE, SourceDir()));
    232     }
    233   }
    234   return result_value;
    235 }
    236 
    237 // static
    238 OutputFile SubstitutionWriter::ApplyPatternToSourceAsOutputFile(
    239     const Settings* settings,
    240     const SubstitutionPattern& pattern,
    241     const SourceFile& source) {
    242   SourceFile result_as_source = ApplyPatternToSource(settings, pattern, source);
    243   CHECK(result_as_source.is_source_absolute())
    244       << "The result of the pattern \""
    245       << pattern.AsString()
    246       << "\" was not an absolute path beginning in \"//\".";
    247   return OutputFile(settings->build_settings(), result_as_source);
    248 }
    249 
    250 // static
    251 void SubstitutionWriter::ApplyListToSource(
    252     const Settings* settings,
    253     const SubstitutionList& list,
    254     const SourceFile& source,
    255     std::vector<SourceFile>* output) {
    256   for (size_t i = 0; i < list.list().size(); i++) {
    257     output->push_back(ApplyPatternToSource(
    258         settings, list.list()[i], source));
    259   }
    260 }
    261 
    262 // static
    263 void SubstitutionWriter::ApplyListToSourceAsString(
    264     const Settings* settings,
    265     const SubstitutionList& list,
    266     const SourceFile& source,
    267     std::vector<std::string>* output) {
    268   for (size_t i = 0; i < list.list().size(); i++) {
    269     output->push_back(ApplyPatternToSourceAsString(
    270         settings, list.list()[i], source));
    271   }
    272 }
    273 
    274 // static
    275 void SubstitutionWriter::ApplyListToSourceAsOutputFile(
    276     const Settings* settings,
    277     const SubstitutionList& list,
    278     const SourceFile& source,
    279     std::vector<OutputFile>* output) {
    280   for (size_t i = 0; i < list.list().size(); i++) {
    281     output->push_back(ApplyPatternToSourceAsOutputFile(
    282         settings, list.list()[i], source));
    283   }
    284 }
    285 
    286 // static
    287 void SubstitutionWriter::ApplyListToSources(
    288     const Settings* settings,
    289     const SubstitutionList& list,
    290     const std::vector<SourceFile>& sources,
    291     std::vector<SourceFile>* output) {
    292   output->clear();
    293   for (size_t i = 0; i < sources.size(); i++)
    294     ApplyListToSource(settings, list, sources[i], output);
    295 }
    296 
    297 // static
    298 void SubstitutionWriter::ApplyListToSourcesAsString(
    299     const Settings* settings,
    300     const SubstitutionList& list,
    301     const std::vector<SourceFile>& sources,
    302     std::vector<std::string>* output) {
    303   output->clear();
    304   for (size_t i = 0; i < sources.size(); i++)
    305     ApplyListToSourceAsString(settings, list, sources[i], output);
    306 }
    307 
    308 // static
    309 void SubstitutionWriter::ApplyListToSourcesAsOutputFile(
    310     const Settings* settings,
    311     const SubstitutionList& list,
    312     const std::vector<SourceFile>& sources,
    313     std::vector<OutputFile>* output) {
    314   output->clear();
    315   for (size_t i = 0; i < sources.size(); i++)
    316     ApplyListToSourceAsOutputFile(settings, list, sources[i], output);
    317 }
    318 
    319 // static
    320 void SubstitutionWriter::WriteNinjaVariablesForSource(
    321     const Settings* settings,
    322     const SourceFile& source,
    323     const std::vector<SubstitutionType>& types,
    324     const EscapeOptions& escape_options,
    325     std::ostream& out) {
    326   for (size_t i = 0; i < types.size(); i++) {
    327     // Don't write SOURCE since that just maps to Ninja's $in variable, which
    328     // is implicit in the rule.
    329     if (types[i] != SUBSTITUTION_SOURCE) {
    330       out << "  " << kSubstitutionNinjaNames[types[i]] << " = ";
    331         EscapeStringToStream(
    332             out,
    333             GetSourceSubstitution(settings, source, types[i], OUTPUT_RELATIVE,
    334                                   settings->build_settings()->build_dir()),
    335             escape_options);
    336       out << std::endl;
    337     }
    338   }
    339 }
    340 
    341 // static
    342 std::string SubstitutionWriter::GetSourceSubstitution(
    343     const Settings* settings,
    344     const SourceFile& source,
    345     SubstitutionType type,
    346     OutputStyle output_style,
    347     const SourceDir& relative_to) {
    348   std::string to_rebase;
    349   switch (type) {
    350     case SUBSTITUTION_SOURCE:
    351       if (source.is_system_absolute())
    352         return source.value();
    353       to_rebase = source.value();
    354       break;
    355 
    356     case SUBSTITUTION_SOURCE_NAME_PART:
    357       return FindFilenameNoExtension(&source.value()).as_string();
    358 
    359     case SUBSTITUTION_SOURCE_FILE_PART:
    360       return source.GetName();
    361 
    362     case SUBSTITUTION_SOURCE_DIR:
    363       if (source.is_system_absolute())
    364         return DirectoryWithNoLastSlash(source.GetDir());
    365       to_rebase = DirectoryWithNoLastSlash(source.GetDir());
    366       break;
    367 
    368     case SUBSTITUTION_SOURCE_ROOT_RELATIVE_DIR:
    369       if (source.is_system_absolute())
    370         return DirectoryWithNoLastSlash(source.GetDir());
    371       return RebaseSourceAbsolutePath(
    372           DirectoryWithNoLastSlash(source.GetDir()), SourceDir("//"));
    373 
    374     case SUBSTITUTION_SOURCE_GEN_DIR:
    375       to_rebase = DirectoryWithNoLastSlash(
    376           GetGenDirForSourceDir(settings, source.GetDir()));
    377       break;
    378 
    379     case SUBSTITUTION_SOURCE_OUT_DIR:
    380       to_rebase = DirectoryWithNoLastSlash(
    381           GetOutputDirForSourceDir(settings, source.GetDir()));
    382       break;
    383 
    384     default:
    385       NOTREACHED()
    386           << "Unsupported substitution for this function: "
    387           << kSubstitutionNames[type];
    388       return std::string();
    389   }
    390 
    391   // If we get here, the result is a path that should be made relative or
    392   // absolute according to the output_style. Other cases (just file name or
    393   // extension extraction) will have been handled via early return above.
    394   if (output_style == OUTPUT_ABSOLUTE)
    395     return to_rebase;
    396   return RebaseSourceAbsolutePath(to_rebase, relative_to);
    397 }
    398 
    399 // static
    400 OutputFile SubstitutionWriter::ApplyPatternToTargetAsOutputFile(
    401     const Target* target,
    402     const Tool* tool,
    403     const SubstitutionPattern& pattern) {
    404   std::string result_value;
    405   for (size_t i = 0; i < pattern.ranges().size(); i++) {
    406     const SubstitutionPattern::Subrange& subrange = pattern.ranges()[i];
    407     if (subrange.type == SUBSTITUTION_LITERAL) {
    408       result_value.append(subrange.literal);
    409     } else {
    410       std::string subst;
    411       CHECK(GetTargetSubstitution(target, subrange.type, &subst));
    412       result_value.append(subst);
    413     }
    414   }
    415   return OutputFile(result_value);
    416 }
    417 
    418 // static
    419 void SubstitutionWriter::ApplyListToTargetAsOutputFile(
    420     const Target* target,
    421     const Tool* tool,
    422     const SubstitutionList& list,
    423     std::vector<OutputFile>* output) {
    424   for (size_t i = 0; i < list.list().size(); i++) {
    425     output->push_back(ApplyPatternToTargetAsOutputFile(
    426         target, tool, list.list()[i]));
    427   }
    428 }
    429 
    430 // static
    431 bool SubstitutionWriter::GetTargetSubstitution(
    432     const Target* target,
    433     SubstitutionType type,
    434     std::string* result) {
    435   switch (type) {
    436     case SUBSTITUTION_LABEL:
    437       // Only include the toolchain for non-default toolchains.
    438       *result = target->label().GetUserVisibleName(
    439           !target->settings()->is_default());
    440       break;
    441     case SUBSTITUTION_ROOT_GEN_DIR:
    442       SetDirOrDotWithNoSlash(
    443           GetToolchainGenDirAsOutputFile(target->settings()).value(),
    444           result);
    445       break;
    446     case SUBSTITUTION_ROOT_OUT_DIR:
    447       SetDirOrDotWithNoSlash(
    448           target->settings()->toolchain_output_subdir().value(),
    449           result);
    450       break;
    451     case SUBSTITUTION_TARGET_GEN_DIR:
    452       SetDirOrDotWithNoSlash(
    453           GetTargetGenDirAsOutputFile(target).value(),
    454           result);
    455       break;
    456     case SUBSTITUTION_TARGET_OUT_DIR:
    457       SetDirOrDotWithNoSlash(
    458           GetTargetOutputDirAsOutputFile(target).value(),
    459           result);
    460       break;
    461     case SUBSTITUTION_TARGET_OUTPUT_NAME:
    462       *result = target->GetComputedOutputName(true);
    463       break;
    464     default:
    465       return false;
    466   }
    467   return true;
    468 }
    469 
    470 // static
    471 std::string SubstitutionWriter::GetTargetSubstitution(
    472     const Target* target,
    473     SubstitutionType type) {
    474   std::string result;
    475   GetTargetSubstitution(target, type, &result);
    476   return result;
    477 }
    478 
    479 // static
    480 OutputFile SubstitutionWriter::ApplyPatternToCompilerAsOutputFile(
    481     const Target* target,
    482     const SourceFile& source,
    483     const SubstitutionPattern& pattern) {
    484   OutputFile result;
    485   for (size_t i = 0; i < pattern.ranges().size(); i++) {
    486     const SubstitutionPattern::Subrange& subrange = pattern.ranges()[i];
    487     if (subrange.type == SUBSTITUTION_LITERAL) {
    488       result.value().append(subrange.literal);
    489     } else {
    490       result.value().append(
    491           GetCompilerSubstitution(target, source, subrange.type));
    492     }
    493   }
    494   return result;
    495 }
    496 
    497 // static
    498 void SubstitutionWriter::ApplyListToCompilerAsOutputFile(
    499     const Target* target,
    500     const SourceFile& source,
    501     const SubstitutionList& list,
    502     std::vector<OutputFile>* output) {
    503   for (size_t i = 0; i < list.list().size(); i++) {
    504     output->push_back(ApplyPatternToCompilerAsOutputFile(
    505         target, source, list.list()[i]));
    506   }
    507 }
    508 
    509 // static
    510 std::string SubstitutionWriter::GetCompilerSubstitution(
    511     const Target* target,
    512     const SourceFile& source,
    513     SubstitutionType type) {
    514   // First try the common tool ones.
    515   std::string result;
    516   if (GetTargetSubstitution(target, type, &result))
    517     return result;
    518 
    519   // Fall-through to the source ones.
    520   return GetSourceSubstitution(
    521       target->settings(), source, type, OUTPUT_RELATIVE,
    522       target->settings()->build_settings()->build_dir());
    523 }
    524 
    525 // static
    526 OutputFile SubstitutionWriter::ApplyPatternToLinkerAsOutputFile(
    527     const Target* target,
    528     const Tool* tool,
    529     const SubstitutionPattern& pattern) {
    530   OutputFile result;
    531   for (size_t i = 0; i < pattern.ranges().size(); i++) {
    532     const SubstitutionPattern::Subrange& subrange = pattern.ranges()[i];
    533     if (subrange.type == SUBSTITUTION_LITERAL) {
    534       result.value().append(subrange.literal);
    535     } else {
    536       result.value().append(GetLinkerSubstitution(target, tool, subrange.type));
    537     }
    538   }
    539   return result;
    540 }
    541 
    542 // static
    543 void SubstitutionWriter::ApplyListToLinkerAsOutputFile(
    544     const Target* target,
    545     const Tool* tool,
    546     const SubstitutionList& list,
    547     std::vector<OutputFile>* output) {
    548   for (size_t i = 0; i < list.list().size(); i++) {
    549     output->push_back(ApplyPatternToLinkerAsOutputFile(
    550         target, tool, list.list()[i]));
    551   }
    552 }
    553 
    554 // static
    555 std::string SubstitutionWriter::GetLinkerSubstitution(
    556     const Target* target,
    557     const Tool* tool,
    558     SubstitutionType type) {
    559   // First try the common tool ones.
    560   std::string result;
    561   if (GetTargetSubstitution(target, type, &result))
    562     return result;
    563 
    564   // Fall-through to the linker-specific ones.
    565   switch (type) {
    566     case SUBSTITUTION_OUTPUT_EXTENSION:
    567       // Use the extension provided on the target if nonempty, otherwise
    568       // fall back on the default. Note that the target's output extension
    569       // does not include the dot but the tool's does.
    570       if (target->output_extension().empty())
    571         return tool->default_output_extension();
    572       return std::string(".") + target->output_extension();
    573 
    574     default:
    575       NOTREACHED();
    576       return std::string();
    577   }
    578 }
    579