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