OLD | NEW |
(Empty) | |
| 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/substitution_list.h" |
| 14 #include "tools/gn/substitution_pattern.h" |
| 15 |
| 16 const char kSourceExpansion_Help[] = |
| 17 "How Source Expansion Works\n" |
| 18 "\n" |
| 19 " Source expansion is used for the action_foreach and copy target types\n" |
| 20 " to map source file names to output file names or arguments.\n" |
| 21 "\n" |
| 22 " To perform source expansion in the outputs, GN maps every entry in the\n" |
| 23 " sources to every entry in the outputs list, producing the cross\n" |
| 24 " product of all combinations, expanding placeholders (see below).\n" |
| 25 "\n" |
| 26 " Source expansion in the args works similarly, but performing the\n" |
| 27 " placeholder substitution produces a different set of arguments for\n" |
| 28 " each invocation of the script.\n" |
| 29 "\n" |
| 30 " If no placeholders are found, the outputs or args list will be treated\n" |
| 31 " as a static list of literal file names that do not depend on the\n" |
| 32 " sources.\n" |
| 33 "\n" |
| 34 " See \"gn help copy\" and \"gn help action_foreach\" for more on how\n" |
| 35 " this is applied.\n" |
| 36 "\n" |
| 37 "Placeholders\n" |
| 38 "\n" |
| 39 " {{source}}\n" |
| 40 " The name of the source file including directory (*). This will\n" |
| 41 " generally be used for specifying inputs to a script in the\n" |
| 42 " \"args\" variable.\n" |
| 43 " \"//foo/bar/baz.txt\" => \"../../foo/bar/baz.txt\"\n" |
| 44 "\n" |
| 45 " {{source_file_part}}\n" |
| 46 " The file part of the source including the extension.\n" |
| 47 " \"//foo/bar/baz.txt\" => \"baz.txt\"\n" |
| 48 "\n" |
| 49 " {{source_name_part}}\n" |
| 50 " The filename part of the source file with no directory or\n" |
| 51 " extension. This will generally be used for specifying a\n" |
| 52 " transformation from a soruce file to a destination file with the\n" |
| 53 " same name but different extension.\n" |
| 54 " \"//foo/bar/baz.txt\" => \"baz\"\n" |
| 55 "\n" |
| 56 " {{source_dir}}\n" |
| 57 " The directory (*) containing the source file with no\n" |
| 58 " trailing slash.\n" |
| 59 " \"//foo/bar/baz.txt\" => \"../../foo/bar\"\n" |
| 60 "\n" |
| 61 " {{source_root_relative_dir}}\n" |
| 62 " The path to the source file's directory relative to the source\n" |
| 63 " root, with no leading \"//\" or trailing slashes. If the path is\n" |
| 64 " system-absolute, (beginning in a single slash) this will just\n" |
| 65 " return the path with no trailing slash. This value will always\n" |
| 66 " be the same, regardless of whether it appears in the \"outputs\"\n" |
| 67 " or \"args\" section.\n" |
| 68 " \"//foo/bar/baz.txt\" => \"foo/bar\"\n" |
| 69 "\n" |
| 70 " {{source_gen_dir}}\n" |
| 71 " The generated file directory (*) corresponding to the source\n" |
| 72 " file's path. This will be different than the target's generated\n" |
| 73 " file directory if the source file is in a different directory\n" |
| 74 " than the BUILD.gn file.\n" |
| 75 " \"//foo/bar/baz.txt\" => \"gen/foo/bar\"\n" |
| 76 "\n" |
| 77 " {{source_out_dir}}\n" |
| 78 " The object file directory (*) corresponding to the source file's\n" |
| 79 " path, relative to the build directory. this us be different than\n" |
| 80 " the target's out directory if the source file is in a different\n" |
| 81 " directory than the build.gn file.\n" |
| 82 " \"//foo/bar/baz.txt\" => \"obj/foo/bar\"\n" |
| 83 "\n" |
| 84 "(*) Note on directories\n" |
| 85 "\n" |
| 86 " Paths containing directories (except the source_root_relative_dir)\n" |
| 87 " will be different depending on what context the expansion is evaluated\n" |
| 88 " in. Generally it should \"just work\" but it means you can't\n" |
| 89 " concatenate strings containing these values with reasonable results.\n" |
| 90 "\n" |
| 91 " Details: source expansions can be used in the \"outputs\" variable,\n" |
| 92 " the \"args\" variable, and in calls to \"process_file_template\". The\n" |
| 93 " \"args\" are passed to a script which is run from the build directory,\n" |
| 94 " so these directories will relative to the build directory for the\n" |
| 95 " script to find. In the other cases, the directories will be source-\n" |
| 96 " absolute (begin with a \"//\") because the results of those expansions\n" |
| 97 " will be handled by GN internally.\n" |
| 98 "\n" |
| 99 "Examples\n" |
| 100 "\n" |
| 101 " Non-varying outputs:\n" |
| 102 " action(\"hardcoded_outputs\") {\n" |
| 103 " sources = [ \"input1.idl\", \"input2.idl\" ]\n" |
| 104 " outputs = [ \"$target_out_dir/output1.dat\",\n" |
| 105 " \"$target_out_dir/output2.dat\" ]\n" |
| 106 " }\n" |
| 107 " The outputs in this case will be the two literal files given.\n" |
| 108 "\n" |
| 109 " Varying outputs:\n" |
| 110 " action_foreach(\"varying_outputs\") {\n" |
| 111 " sources = [ \"input1.idl\", \"input2.idl\" ]\n" |
| 112 " outputs = [ \"{{source_gen_dir}}/{{source_name_part}}.h\",\n" |
| 113 " \"{{source_gen_dir}}/{{source_name_part}}.cc\" ]\n" |
| 114 " }\n" |
| 115 " Performing source expansion will result in the following output names:\n" |
| 116 " //out/Debug/obj/mydirectory/input1.h\n" |
| 117 " //out/Debug/obj/mydirectory/input1.cc\n" |
| 118 " //out/Debug/obj/mydirectory/input2.h\n" |
| 119 " //out/Debug/obj/mydirectory/input2.cc\n"; |
| 120 |
| 121 SubstitutionWriter::SubstitutionWriter() { |
| 122 } |
| 123 |
| 124 SubstitutionWriter::~SubstitutionWriter() { |
| 125 } |
| 126 |
| 127 // static |
| 128 SourceFile SubstitutionWriter::ApplyPatternToSource( |
| 129 const Settings* settings, |
| 130 const SubstitutionPattern& pattern, |
| 131 const SourceFile& source) { |
| 132 std::string result_value; |
| 133 for (size_t i = 0; i < pattern.ranges().size(); i++) { |
| 134 const SubstitutionPattern::Subrange& subrange = pattern.ranges()[i]; |
| 135 if (subrange.type == SUBSTITUTION_LITERAL) { |
| 136 result_value.append(subrange.literal); |
| 137 } else { |
| 138 result_value.append( |
| 139 GetSourceSubstitution(settings, source, subrange.type, |
| 140 OUTPUT_ABSOLUTE, SourceDir())); |
| 141 } |
| 142 } |
| 143 CHECK(!result_value.empty() && result_value[0] == '/') |
| 144 << "The result of the pattern \"" |
| 145 << pattern.AsString() |
| 146 << "\" was not a path beginning in \"/\" or \"//\"."; |
| 147 return SourceFile(SourceFile::SWAP_IN, &result_value); |
| 148 } |
| 149 |
| 150 // static |
| 151 OutputFile SubstitutionWriter::ApplyPatternToSourceAsOutputFile( |
| 152 const Settings* settings, |
| 153 const SubstitutionPattern& pattern, |
| 154 const SourceFile& source) { |
| 155 SourceFile result_as_source = ApplyPatternToSource(settings, pattern, source); |
| 156 CHECK(result_as_source.is_source_absolute()) |
| 157 << "The result of the pattern \"" |
| 158 << pattern.AsString() |
| 159 << "\" was not an absolute path beginning in \"//\"."; |
| 160 return OutputFile( |
| 161 RebaseSourceAbsolutePath(result_as_source.value(), |
| 162 settings->build_settings()->build_dir())); |
| 163 } |
| 164 |
| 165 // static |
| 166 void SubstitutionWriter::ApplyListToSource( |
| 167 const Settings* settings, |
| 168 const SubstitutionList& list, |
| 169 const SourceFile& source, |
| 170 std::vector<SourceFile>* output) { |
| 171 for (size_t i = 0; i < list.list().size(); i++) { |
| 172 output->push_back(ApplyPatternToSource( |
| 173 settings, list.list()[i], source)); |
| 174 } |
| 175 } |
| 176 |
| 177 // static |
| 178 void SubstitutionWriter::ApplyListToSourceAsOutputFile( |
| 179 const Settings* settings, |
| 180 const SubstitutionList& list, |
| 181 const SourceFile& source, |
| 182 std::vector<OutputFile>* output) { |
| 183 for (size_t i = 0; i < list.list().size(); i++) { |
| 184 output->push_back(ApplyPatternToSourceAsOutputFile( |
| 185 settings, list.list()[i], source)); |
| 186 } |
| 187 } |
| 188 |
| 189 // static |
| 190 void SubstitutionWriter::ApplyListToSources( |
| 191 const Settings* settings, |
| 192 const SubstitutionList& list, |
| 193 const std::vector<SourceFile>& sources, |
| 194 std::vector<SourceFile>* output) { |
| 195 output->clear(); |
| 196 for (size_t i = 0; i < sources.size(); i++) |
| 197 ApplyListToSource(settings, list, sources[i], output); |
| 198 } |
| 199 |
| 200 // static |
| 201 void SubstitutionWriter::ApplyListToSourcesAsOutputFile( |
| 202 const Settings* settings, |
| 203 const SubstitutionList& list, |
| 204 const std::vector<SourceFile>& sources, |
| 205 std::vector<OutputFile>* output) { |
| 206 output->clear(); |
| 207 for (size_t i = 0; i < sources.size(); i++) |
| 208 ApplyListToSourceAsOutputFile(settings, list, sources[i], output); |
| 209 } |
| 210 |
| 211 // static |
| 212 void SubstitutionWriter::WriteNinjaVariablesForSource( |
| 213 const Settings* settings, |
| 214 const SourceFile& source, |
| 215 const std::vector<SubstitutionType>& types, |
| 216 const EscapeOptions& escape_options, |
| 217 std::ostream& out) { |
| 218 for (size_t i = 0; i < types.size(); i++) { |
| 219 // Don't write SOURCE since that just maps to Ninja's $in variable, which |
| 220 // is implicit in the rule. |
| 221 if (types[i] != SUBSTITUTION_SOURCE) { |
| 222 out << " " << kSubstitutionNinjaNames[types[i]] << " = "; |
| 223 EscapeStringToStream( |
| 224 out, |
| 225 GetSourceSubstitution(settings, source, types[i], OUTPUT_RELATIVE, |
| 226 settings->build_settings()->build_dir()), |
| 227 escape_options); |
| 228 out << std::endl; |
| 229 } |
| 230 } |
| 231 } |
| 232 |
| 233 // static |
| 234 void SubstitutionWriter::WriteWithNinjaVariables( |
| 235 const SubstitutionPattern& pattern, |
| 236 const EscapeOptions& escape_options, |
| 237 std::ostream& out) { |
| 238 // The result needs to be quoted as if it was one string, but the $ for |
| 239 // the inserted Ninja variables can't be escaped. So write to a buffer with |
| 240 // no quoting, and then quote the whole thing if necessary. |
| 241 EscapeOptions no_quoting(escape_options); |
| 242 no_quoting.inhibit_quoting = true; |
| 243 |
| 244 bool needs_quotes = false; |
| 245 std::string result; |
| 246 for (size_t i = 0; i < pattern.ranges().size(); i++) { |
| 247 const SubstitutionPattern::Subrange range = pattern.ranges()[i]; |
| 248 if (range.type == SUBSTITUTION_LITERAL) { |
| 249 result.append(EscapeString(range.literal, no_quoting, &needs_quotes)); |
| 250 } else { |
| 251 result.append("${"); |
| 252 result.append(kSubstitutionNinjaNames[range.type]); |
| 253 result.append("}"); |
| 254 } |
| 255 } |
| 256 |
| 257 if (needs_quotes && !escape_options.inhibit_quoting) |
| 258 out << "\"" << result << "\""; |
| 259 else |
| 260 out << result; |
| 261 } |
| 262 |
| 263 // static |
| 264 std::string SubstitutionWriter::GetSourceSubstitution( |
| 265 const Settings* settings, |
| 266 const SourceFile& source, |
| 267 SubstitutionType type, |
| 268 OutputStyle output_style, |
| 269 const SourceDir& relative_to) { |
| 270 std::string to_rebase; |
| 271 switch (type) { |
| 272 case SUBSTITUTION_SOURCE: |
| 273 if (source.is_system_absolute()) |
| 274 return source.value(); |
| 275 to_rebase = source.value(); |
| 276 break; |
| 277 |
| 278 case SUBSTITUTION_SOURCE_NAME_PART: |
| 279 return FindFilenameNoExtension(&source.value()).as_string(); |
| 280 |
| 281 case SUBSTITUTION_SOURCE_FILE_PART: |
| 282 return source.GetName(); |
| 283 |
| 284 case SUBSTITUTION_SOURCE_DIR: |
| 285 if (source.is_system_absolute()) |
| 286 return DirectoryWithNoLastSlash(source.GetDir()); |
| 287 to_rebase = DirectoryWithNoLastSlash(source.GetDir()); |
| 288 break; |
| 289 |
| 290 case SUBSTITUTION_SOURCE_ROOT_RELATIVE_DIR: |
| 291 if (source.is_system_absolute()) |
| 292 return DirectoryWithNoLastSlash(source.GetDir()); |
| 293 return RebaseSourceAbsolutePath( |
| 294 DirectoryWithNoLastSlash(source.GetDir()), SourceDir("//")); |
| 295 |
| 296 case SUBSTITUTION_SOURCE_GEN_DIR: |
| 297 to_rebase = DirectoryWithNoLastSlash( |
| 298 GetGenDirForSourceDir(settings, source.GetDir())); |
| 299 break; |
| 300 |
| 301 case SUBSTITUTION_SOURCE_OUT_DIR: |
| 302 to_rebase = DirectoryWithNoLastSlash( |
| 303 GetOutputDirForSourceDir(settings, source.GetDir())); |
| 304 break; |
| 305 |
| 306 default: |
| 307 NOTREACHED(); |
| 308 return std::string(); |
| 309 } |
| 310 |
| 311 // If we get here, the result is a path that should be made relative or |
| 312 // absolute according to the output_style. Other cases (just file name or |
| 313 // extension extraction) will have been handled via early return above. |
| 314 if (output_style == OUTPUT_ABSOLUTE) |
| 315 return to_rebase; |
| 316 return RebaseSourceAbsolutePath(to_rebase, relative_to); |
| 317 } |
OLD | NEW |