1// Copyright (c) 2013 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/gyp_binary_target_writer.h" 6 7#include <set> 8 9#include "base/logging.h" 10#include "tools/gn/builder_record.h" 11#include "tools/gn/config_values_extractors.h" 12#include "tools/gn/err.h" 13#include "tools/gn/escape.h" 14#include "tools/gn/filesystem_utils.h" 15#include "tools/gn/settings.h" 16#include "tools/gn/target.h" 17 18namespace { 19 20// This functor is used to capture the output of RecursiveTargetConfigToStream 21// in an vector. 22template<typename T> 23struct Accumulator { 24 Accumulator(std::vector<T>* result_in) : result(result_in) {} 25 26 void operator()(const T& s, std::ostream&) const { 27 result->push_back(s); 28 } 29 30 std::vector<T>* result; 31}; 32 33// Writes the given array values. The array should already be declared with the 34// opening "[" written to the output. The function will not write the 35// terminating "]" either. 36void WriteArrayValues(std::ostream& out, 37 const std::vector<std::string>& values) { 38 EscapeOptions options; 39 options.mode = ESCAPE_JSON; 40 for (size_t i = 0; i < values.size(); i++) { 41 out << " '"; 42 EscapeStringToStream(out, values[i], options); 43 out << "',"; 44 } 45} 46 47// Returns the value from the already-filled in cflags_* for the optimization 48// level to set in the GYP file. Additionally, this removes the flag from the 49// given vector so we don't get duplicates. 50std::string GetVCOptimization(std::vector<std::string>* cflags) { 51 // Searches for the "/O?" option and returns the corresponding GYP value. 52 for (size_t i = 0; i < cflags->size(); i++) { 53 const std::string& cur = (*cflags)[i]; 54 if (cur.size() == 3 && cur[0] == '/' && cur[1] == 'O') { 55 char level = cur[2]; 56 cflags->erase(cflags->begin() + i); // Invalidates |cur|! 57 switch (level) { 58 case 'd': return "'0'"; 59 case '1': return "'1'"; 60 case '2': return "'2'"; 61 case 'x': return "'3'"; 62 default: return "'2'"; 63 } 64 } 65 } 66 return "'2'"; // Default value. 67} 68 69// Finds all values from the given getter from all configs in the given list, 70// and adds them to the given result vector. 71template<typename T> 72void FillConfigListValues( 73 const LabelConfigVector& configs, 74 const std::vector<T>& (ConfigValues::* getter)() const, 75 std::vector<T>* result) { 76 for (size_t config_i = 0; config_i < configs.size(); config_i++) { 77 const std::vector<T>& values = 78 (configs[config_i].ptr->config_values().*getter)(); 79 for (size_t val_i = 0; val_i < values.size(); val_i++) 80 result->push_back(values[val_i]); 81 } 82} 83 84} // namespace 85 86GypBinaryTargetWriter::Flags::Flags() {} 87GypBinaryTargetWriter::Flags::~Flags() {} 88 89GypBinaryTargetWriter::GypBinaryTargetWriter(const TargetGroup& group, 90 const Toolchain* debug_toolchain, 91 const SourceDir& gyp_dir, 92 std::ostream& out) 93 : GypTargetWriter(group.debug->item()->AsTarget(), debug_toolchain, 94 gyp_dir, out), 95 group_(group) { 96} 97 98GypBinaryTargetWriter::~GypBinaryTargetWriter() { 99} 100 101void GypBinaryTargetWriter::Run() { 102 int indent = 4; 103 104 Indent(indent) << "{\n"; 105 106 WriteName(indent + kExtraIndent); 107 WriteType(indent + kExtraIndent); 108 109 if (target_->settings()->IsLinux()) 110 WriteLinuxConfiguration(indent + kExtraIndent); 111 else if (target_->settings()->IsWin()) 112 WriteVCConfiguration(indent + kExtraIndent); 113 else if (target_->settings()->IsMac()) 114 WriteMacConfiguration(indent + kExtraIndent); 115 WriteDirectDependentSettings(indent + kExtraIndent); 116 WriteAllDependentSettings(indent + kExtraIndent); 117 118 Indent(indent) << "},\n"; 119} 120 121void GypBinaryTargetWriter::WriteName(int indent) { 122 std::string name = helper_.GetNameForTarget(target_); 123 Indent(indent) << "'target_name': '" << name << "',\n"; 124 125 std::string product_name; 126 if (target_->output_name().empty()) 127 product_name = target_->label().name(); 128 else 129 product_name = name; 130 131 // TODO(brettw) GN knows not to prefix targets starting with "lib" with 132 // another "lib" on Linux, but GYP doesn't. We need to rename applicable 133 // targets here. 134 135 Indent(indent) << "'product_name': '" << product_name << "',\n"; 136} 137 138void GypBinaryTargetWriter::WriteType(int indent) { 139 Indent(indent) << "'type': "; 140 switch (target_->output_type()) { 141 case Target::EXECUTABLE: 142 out_ << "'executable',\n"; 143 break; 144 case Target::STATIC_LIBRARY: 145 out_ << "'static_library',\n"; 146 break; 147 case Target::SHARED_LIBRARY: 148 out_ << "'shared_library',\n"; 149 break; 150 case Target::SOURCE_SET: 151 out_ << "'static_library',\n"; // TODO(brettw) fixme. 152 break; 153 default: 154 NOTREACHED(); 155 } 156 157 if (target_->hard_dep()) 158 Indent(indent) << "'hard_dependency': 1,\n"; 159} 160 161void GypBinaryTargetWriter::WriteVCConfiguration(int indent) { 162 Indent(indent) << "'configurations': {\n"; 163 164 Indent(indent + kExtraIndent) << "'Debug': {\n"; 165 Flags debug_flags(FlagsFromTarget(group_.debug->item()->AsTarget())); 166 WriteVCFlags(debug_flags, indent + kExtraIndent * 2); 167 Indent(indent + kExtraIndent) << "},\n"; 168 169 Indent(indent + kExtraIndent) << "'Release': {\n"; 170 Flags release_flags(FlagsFromTarget(group_.release->item()->AsTarget())); 171 WriteVCFlags(release_flags, indent + kExtraIndent * 2); 172 Indent(indent + kExtraIndent) << "},\n"; 173 174 // Note that we always need Debug_x64 and Release_x64 defined or GYP will get 175 // confused, but we ca leave them empty if there's no 64-bit target. 176 Indent(indent + kExtraIndent) << "'Debug_x64': {\n"; 177 if (group_.debug64) { 178 Flags flags(FlagsFromTarget(group_.debug64->item()->AsTarget())); 179 WriteVCFlags(flags, indent + kExtraIndent * 2); 180 } 181 Indent(indent + kExtraIndent) << "},\n"; 182 183 Indent(indent + kExtraIndent) << "'Release_x64': {\n"; 184 if (group_.release64) { 185 Flags flags(FlagsFromTarget(group_.release64->item()->AsTarget())); 186 WriteVCFlags(flags, indent + kExtraIndent * 2); 187 } 188 Indent(indent + kExtraIndent) << "},\n"; 189 190 Indent(indent) << "},\n"; 191 192 WriteSources(target_, indent); 193 WriteDeps(target_, indent); 194} 195 196void GypBinaryTargetWriter::WriteLinuxConfiguration(int indent) { 197 // The Linux stuff works differently. On Linux we support cross-compiles and 198 // all ninja generators know to look for target conditions. Other platforms' 199 // generators don't all do this, so we can't have the same GYP structure. 200 Indent(indent) << "'target_conditions': [\n"; 201 // The host toolset is configured for the current computer, we will only have 202 // this when doing cross-compiles. 203 if (group_.host_debug && group_.host_release) { 204 Indent(indent + kExtraIndent) << "['_toolset == \"host\"', {\n"; 205 Indent(indent + kExtraIndent * 2) << "'configurations': {\n"; 206 Indent(indent + kExtraIndent * 3) << "'Debug': {\n"; 207 WriteLinuxFlagsForTarget(group_.host_debug->item()->AsTarget(), 208 indent + kExtraIndent * 4); 209 Indent(indent + kExtraIndent * 3) << "},\n"; 210 Indent(indent + kExtraIndent * 3) << "'Release': {\n"; 211 WriteLinuxFlagsForTarget(group_.host_release->item()->AsTarget(), 212 indent + kExtraIndent * 4); 213 Indent(indent + kExtraIndent * 3) << "},\n"; 214 Indent(indent + kExtraIndent * 2) << "}\n"; 215 216 // The sources are per-toolset but shared between debug & release. 217 WriteSources(group_.host_debug->item()->AsTarget(), 218 indent + kExtraIndent * 2); 219 220 Indent(indent + kExtraIndent) << "],\n"; 221 } 222 223 // The target toolset is the "regular" one. 224 Indent(indent + kExtraIndent) << "['_toolset == \"target\"', {\n"; 225 Indent(indent + kExtraIndent * 2) << "'configurations': {\n"; 226 Indent(indent + kExtraIndent * 3) << "'Debug': {\n"; 227 WriteLinuxFlagsForTarget(group_.debug->item()->AsTarget(), 228 indent + kExtraIndent * 4); 229 Indent(indent + kExtraIndent * 3) << "},\n"; 230 Indent(indent + kExtraIndent * 3) << "'Release': {\n"; 231 WriteLinuxFlagsForTarget(group_.release->item()->AsTarget(), 232 indent + kExtraIndent * 4); 233 Indent(indent + kExtraIndent * 3) << "},\n"; 234 Indent(indent + kExtraIndent * 2) << "},\n"; 235 236 WriteSources(target_, indent + kExtraIndent * 2); 237 238 Indent(indent + kExtraIndent) << "},],\n"; 239 Indent(indent) << "],\n"; 240 241 // Deps in GYP can not vary based on the toolset. 242 WriteDeps(target_, indent); 243} 244 245void GypBinaryTargetWriter::WriteMacConfiguration(int indent) { 246 Indent(indent) << "'configurations': {\n"; 247 248 Indent(indent + kExtraIndent) << "'Debug': {\n"; 249 Flags debug_flags(FlagsFromTarget(group_.debug->item()->AsTarget())); 250 WriteMacFlags(debug_flags, indent + kExtraIndent * 2); 251 Indent(indent + kExtraIndent) << "},\n"; 252 253 Indent(indent + kExtraIndent) << "'Release': {\n"; 254 Flags release_flags(FlagsFromTarget(group_.release->item()->AsTarget())); 255 WriteMacFlags(release_flags, indent + kExtraIndent * 2); 256 Indent(indent + kExtraIndent) << "},\n"; 257 258 Indent(indent) << "},\n"; 259 260 WriteSources(target_, indent); 261 WriteDeps(target_, indent); 262} 263 264void GypBinaryTargetWriter::WriteVCFlags(Flags& flags, int indent) { 265 // Defines and includes go outside of the msvs settings. 266 WriteNamedArray("defines", flags.defines, indent); 267 WriteIncludeDirs(flags, indent); 268 269 // C flags. 270 Indent(indent) << "'msvs_settings': {\n"; 271 Indent(indent + kExtraIndent) << "'VCCLCompilerTool': {\n"; 272 273 // GYP always uses the VC optimization flag to add a /O? on Visual Studio. 274 // This can produce duplicate values. So look up the GYP value corresponding 275 // to the flags used, and set the same one. 276 std::string optimization = GetVCOptimization(&flags.cflags); 277 WriteNamedArray("AdditionalOptions", flags.cflags, indent + kExtraIndent * 2); 278 // TODO(brettw) cflags_c and cflags_cc! 279 Indent(indent + kExtraIndent * 2) << "'Optimization': " 280 << optimization << ",\n"; 281 Indent(indent + kExtraIndent) << "},\n"; 282 283 // Linker tool stuff. 284 Indent(indent + kExtraIndent) << "'VCLinkerTool': {\n"; 285 286 // ...Library dirs. 287 EscapeOptions escape_options; 288 escape_options.mode = ESCAPE_JSON; 289 if (!flags.lib_dirs.empty()) { 290 Indent(indent + kExtraIndent * 2) << "'AdditionalLibraryDirectories': ["; 291 for (size_t i = 0; i < flags.lib_dirs.size(); i++) { 292 out_ << " '"; 293 EscapeStringToStream(out_, 294 helper_.GetDirReference(flags.lib_dirs[i], false), 295 escape_options); 296 out_ << "',"; 297 } 298 out_ << " ],\n"; 299 } 300 301 // ...Libraries. 302 WriteNamedArray("AdditionalDependencies", flags.libs, 303 indent + kExtraIndent * 2); 304 305 // ...LD flags. 306 // TODO(brettw) EnableUAC defaults to on and needs to be set. Also 307 // UACExecutionLevel and UACUIAccess depends on that and defaults to 0/false. 308 WriteNamedArray("AdditionalOptions", flags.ldflags, 14); 309 Indent(indent + kExtraIndent) << "},\n"; 310 Indent(indent) << "},\n"; 311} 312 313void GypBinaryTargetWriter::WriteMacFlags(Flags& flags, int indent) { 314 WriteNamedArray("defines", flags.defines, indent); 315 WriteIncludeDirs(flags, indent); 316 317 // Libraries and library directories. 318 EscapeOptions escape_options; 319 escape_options.mode = ESCAPE_JSON; 320 if (!flags.lib_dirs.empty()) { 321 Indent(indent + kExtraIndent) << "'library_dirs': ["; 322 for (size_t i = 0; i < flags.lib_dirs.size(); i++) { 323 out_ << " '"; 324 EscapeStringToStream(out_, 325 helper_.GetDirReference(flags.lib_dirs[i], false), 326 escape_options); 327 out_ << "',"; 328 } 329 out_ << " ],\n"; 330 } 331 if (!flags.libs.empty()) { 332 Indent(indent) << "'link_settings': {\n"; 333 Indent(indent + kExtraIndent) << "'libraries': ["; 334 for (size_t i = 0; i < flags.libs.size(); i++) { 335 out_ << " '-l"; 336 EscapeStringToStream(out_, flags.libs[i], escape_options); 337 out_ << "',"; 338 } 339 out_ << " ],\n"; 340 Indent(indent) << "},\n"; 341 } 342 343 Indent(indent) << "'xcode_settings': {\n"; 344 345 // C/C++ flags. 346 if (!flags.cflags.empty() || !flags.cflags_c.empty() || 347 !flags.cflags_objc.empty()) { 348 Indent(indent + kExtraIndent) << "'OTHER_CFLAGS': ["; 349 WriteArrayValues(out_, flags.cflags); 350 WriteArrayValues(out_, flags.cflags_c); 351 WriteArrayValues(out_, flags.cflags_objc); 352 out_ << " ],\n"; 353 } 354 if (!flags.cflags.empty() || !flags.cflags_cc.empty() || 355 !flags.cflags_objcc.empty()) { 356 Indent(indent + kExtraIndent) << "'OTHER_CPLUSPLUSFLAGS': ["; 357 WriteArrayValues(out_, flags.cflags); 358 WriteArrayValues(out_, flags.cflags_cc); 359 WriteArrayValues(out_, flags.cflags_objcc); 360 out_ << " ],\n"; 361 } 362 363 // Ld flags. Don't write these for static libraries. Otherwise, they'll be 364 // passed to the library tool which doesn't expect it (the toolchain does 365 // not use ldflags so these are ignored in the normal build). 366 if (target_->output_type() != Target::STATIC_LIBRARY) 367 WriteNamedArray("OTHER_LDFLAGS", flags.ldflags, indent + kExtraIndent); 368 369 // Write the compiler that XCode should use. When we're using clang, we want 370 // the custom one, otherwise don't add this and the default compiler will be 371 // used. 372 // 373 // TODO(brettw) this is a hack. We could add a way for the GN build to set 374 // these values but as far as I can see this is the only use for them, so 375 // currently we manually check the build config's is_clang value. 376 const Value* is_clang = 377 target_->settings()->base_config()->GetValue("is_clang"); 378 if (is_clang && is_clang->type() == Value::BOOLEAN && 379 is_clang->boolean_value()) { 380 base::FilePath clang_path = 381 target_->settings()->build_settings()->GetFullPath(SourceFile( 382 "//third_party/llvm-build/Release+Asserts/bin/clang")); 383 base::FilePath clang_pp_path = 384 target_->settings()->build_settings()->GetFullPath(SourceFile( 385 "//third_party/llvm-build/Release+Asserts/bin/clang++")); 386 387 Indent(indent) << "'CC': '" << FilePathToUTF8(clang_path) << "',\n"; 388 Indent(indent) << "'LDPLUSPLUS': '" 389 << FilePathToUTF8(clang_pp_path) << "',\n"; 390 } 391 392 Indent(indent) << "},\n"; 393} 394 395void GypBinaryTargetWriter::WriteLinuxFlagsForTarget(const Target* target, 396 int indent) { 397 Flags flags(FlagsFromTarget(target)); 398 WriteLinuxFlags(flags, indent); 399} 400 401void GypBinaryTargetWriter::WriteLinuxFlags(const Flags& flags, int indent) { 402 WriteIncludeDirs(flags, indent); 403 WriteNamedArray("defines", flags.defines, indent); 404 WriteNamedArray("cflags", flags.cflags, indent); 405 WriteNamedArray("cflags_c", flags.cflags_c, indent); 406 WriteNamedArray("cflags_cc", flags.cflags_cc, indent); 407 WriteNamedArray("cflags_objc", flags.cflags_objc, indent); 408 WriteNamedArray("cflags_objcc", flags.cflags_objcc, indent); 409 410 // Put libraries and library directories in with ldflags. 411 Indent(indent) << "'ldflags': ["; \ 412 WriteArrayValues(out_, flags.ldflags); 413 414 EscapeOptions escape_options; 415 escape_options.mode = ESCAPE_JSON; 416 for (size_t i = 0; i < flags.lib_dirs.size(); i++) { 417 out_ << " '-L"; 418 EscapeStringToStream(out_, 419 helper_.GetDirReference(flags.lib_dirs[i], false), 420 escape_options); 421 out_ << "',"; 422 } 423 424 for (size_t i = 0; i < flags.libs.size(); i++) { 425 out_ << " '-l"; 426 EscapeStringToStream(out_, flags.libs[i], escape_options); 427 out_ << "',"; 428 } 429 out_ << " ],\n"; 430} 431 432void GypBinaryTargetWriter::WriteSources(const Target* target, int indent) { 433 Indent(indent) << "'sources': [\n"; 434 435 const Target::FileList& sources = target->sources(); 436 for (size_t i = 0; i < sources.size(); i++) { 437 const SourceFile& input_file = sources[i]; 438 Indent(indent + kExtraIndent) << "'" << helper_.GetFileReference(input_file) 439 << "',\n"; 440 } 441 442 Indent(indent) << "],\n"; 443} 444 445void GypBinaryTargetWriter::WriteDeps(const Target* target, int indent) { 446 const LabelTargetVector& deps = target->deps(); 447 if (deps.empty()) 448 return; 449 450 EscapeOptions escape_options; 451 escape_options.mode = ESCAPE_JSON; 452 453 Indent(indent) << "'dependencies': [\n"; 454 for (size_t i = 0; i < deps.size(); i++) { 455 Indent(indent + kExtraIndent) << "'"; 456 EscapeStringToStream(out_, helper_.GetFullRefForTarget(deps[i].ptr), 457 escape_options); 458 out_ << "',\n"; 459 } 460 Indent(indent) << "],\n"; 461} 462 463void GypBinaryTargetWriter::WriteIncludeDirs(const Flags& flags, int indent) { 464 if (flags.include_dirs.empty()) 465 return; 466 467 EscapeOptions options; 468 options.mode = ESCAPE_JSON; 469 470 Indent(indent) << "'include_dirs': ["; 471 for (size_t i = 0; i < flags.include_dirs.size(); i++) { 472 out_ << " '"; 473 EscapeStringToStream(out_, 474 helper_.GetDirReference(flags.include_dirs[i], false), 475 options); 476 out_ << "',"; 477 } 478 out_ << " ],\n"; 479} 480 481void GypBinaryTargetWriter::WriteDirectDependentSettings(int indent) { 482 if (target_->direct_dependent_configs().empty()) 483 return; 484 Indent(indent) << "'direct_dependent_settings': {\n"; 485 486 Flags flags(FlagsFromConfigList(target_->direct_dependent_configs())); 487 if (target_->settings()->IsLinux()) 488 WriteLinuxFlags(flags, indent + kExtraIndent); 489 else if (target_->settings()->IsWin()) 490 WriteVCFlags(flags, indent + kExtraIndent); 491 else if (target_->settings()->IsMac()) 492 WriteMacFlags(flags, indent + kExtraIndent); 493 Indent(indent) << "},\n"; 494} 495 496void GypBinaryTargetWriter::WriteAllDependentSettings(int indent) { 497 if (target_->all_dependent_configs().empty()) 498 return; 499 Indent(indent) << "'all_dependent_settings': {\n"; 500 501 Flags flags(FlagsFromConfigList(target_->all_dependent_configs())); 502 if (target_->settings()->IsLinux()) 503 WriteLinuxFlags(flags, indent + kExtraIndent); 504 else if (target_->settings()->IsWin()) 505 WriteVCFlags(flags, indent + kExtraIndent); 506 else if (target_->settings()->IsMac()) 507 WriteMacFlags(flags, indent + kExtraIndent); 508 Indent(indent) << "},\n"; 509} 510 511GypBinaryTargetWriter::Flags GypBinaryTargetWriter::FlagsFromTarget( 512 const Target* target) const { 513 Flags ret; 514 515 // Extracts a vector of the given type and name from the config values. 516 #define EXTRACT(type, name) \ 517 { \ 518 Accumulator<type> acc(&ret.name); \ 519 RecursiveTargetConfigToStream<type>(target, &ConfigValues::name, \ 520 acc, out_); \ 521 } 522 523 EXTRACT(std::string, defines); 524 EXTRACT(SourceDir, include_dirs); 525 EXTRACT(std::string, cflags); 526 EXTRACT(std::string, cflags_c); 527 EXTRACT(std::string, cflags_cc); 528 EXTRACT(std::string, cflags_objc); 529 EXTRACT(std::string, cflags_objcc); 530 EXTRACT(std::string, ldflags); 531 532 #undef EXTRACT 533 534 const OrderedSet<SourceDir>& all_lib_dirs = target->all_lib_dirs(); 535 for (size_t i = 0; i < all_lib_dirs.size(); i++) 536 ret.lib_dirs.push_back(all_lib_dirs[i]); 537 538 const OrderedSet<std::string> all_libs = target->all_libs(); 539 for (size_t i = 0; i < all_libs.size(); i++) 540 ret.libs.push_back(all_libs[i]); 541 542 return ret; 543} 544 545GypBinaryTargetWriter::Flags GypBinaryTargetWriter::FlagsFromConfigList( 546 const LabelConfigVector& configs) const { 547 Flags ret; 548 549 #define EXTRACT(type, name) \ 550 FillConfigListValues<type>(configs, &ConfigValues::name, &ret.name); 551 552 EXTRACT(std::string, defines); 553 EXTRACT(SourceDir, include_dirs); 554 EXTRACT(std::string, cflags); 555 EXTRACT(std::string, cflags_c); 556 EXTRACT(std::string, cflags_cc); 557 EXTRACT(std::string, cflags_objc); 558 EXTRACT(std::string, cflags_objcc); 559 EXTRACT(std::string, ldflags); 560 EXTRACT(SourceDir, lib_dirs); 561 EXTRACT(std::string, libs); 562 563 #undef EXTRACT 564 565 return ret; 566} 567 568void GypBinaryTargetWriter::WriteNamedArray( 569 const char* name, 570 const std::vector<std::string>& values, 571 int indent) { 572 if (values.empty()) 573 return; 574 575 EscapeOptions options; 576 options.mode = ESCAPE_JSON; 577 578 Indent(indent) << "'" << name << "': ["; 579 WriteArrayValues(out_, values); 580 out_ << " ],\n"; 581} 582 583