| OLD | NEW |
| (Empty) |
| 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 "base/strings/string_util.h" | |
| 11 #include "tools/gn/builder_record.h" | |
| 12 #include "tools/gn/config_values_extractors.h" | |
| 13 #include "tools/gn/err.h" | |
| 14 #include "tools/gn/escape.h" | |
| 15 #include "tools/gn/filesystem_utils.h" | |
| 16 #include "tools/gn/settings.h" | |
| 17 #include "tools/gn/target.h" | |
| 18 | |
| 19 namespace { | |
| 20 | |
| 21 // This functor is used to capture the output of RecursiveTargetConfigToStream | |
| 22 // in an vector. | |
| 23 template<typename T> | |
| 24 struct Accumulator { | |
| 25 Accumulator(std::vector<T>* result_in) : result(result_in) {} | |
| 26 | |
| 27 void operator()(const T& s, std::ostream&) const { | |
| 28 result->push_back(s); | |
| 29 } | |
| 30 | |
| 31 std::vector<T>* result; | |
| 32 }; | |
| 33 | |
| 34 // Writes the given array values. The array should already be declared with the | |
| 35 // opening "[" written to the output. The function will not write the | |
| 36 // terminating "]" either. | |
| 37 void WriteArrayValues(std::ostream& out, | |
| 38 const std::vector<std::string>& values) { | |
| 39 EscapeOptions options; | |
| 40 options.mode = ESCAPE_JSON; | |
| 41 for (size_t i = 0; i < values.size(); i++) { | |
| 42 out << " '"; | |
| 43 EscapeStringToStream(out, values[i], options); | |
| 44 out << "',"; | |
| 45 } | |
| 46 } | |
| 47 | |
| 48 // Returns the value from the already-filled in cflags_* for the optimization | |
| 49 // level to set in the GYP file. Additionally, this removes the flag from the | |
| 50 // given vector so we don't get duplicates. | |
| 51 std::string GetVCOptimization(std::vector<std::string>* cflags) { | |
| 52 // Searches for the "/O?" option and returns the corresponding GYP value. | |
| 53 for (size_t i = 0; i < cflags->size(); i++) { | |
| 54 const std::string& cur = (*cflags)[i]; | |
| 55 if (cur.size() == 3 && cur[0] == '/' && cur[1] == 'O') { | |
| 56 char level = cur[2]; | |
| 57 cflags->erase(cflags->begin() + i); // Invalidates |cur|! | |
| 58 switch (level) { | |
| 59 case 'd': return "'0'"; | |
| 60 case '1': return "'1'"; | |
| 61 case '2': return "'2'"; | |
| 62 case 'x': return "'3'"; | |
| 63 default: return "'2'"; | |
| 64 } | |
| 65 } | |
| 66 } | |
| 67 return "'2'"; // Default value. | |
| 68 } | |
| 69 | |
| 70 // Returns the value from the already-filled in cflags for the processor | |
| 71 // architecture to set in the GYP file. Additionally, this removes the flag | |
| 72 // from the given vector so we don't get duplicates. | |
| 73 std::string GetMacArch(std::vector<std::string>* cflags) { | |
| 74 // Searches for the "-arch" option and returns the corresponding GYP value. | |
| 75 for (size_t i = 0; i < cflags->size(); i++) { | |
| 76 const std::string& cur = (*cflags)[i]; | |
| 77 if (cur == "-arch") { | |
| 78 // This is the first part of a list with ["-arch", "i386"], return the | |
| 79 // following item, and delete both of them. | |
| 80 if (i < cflags->size() - 1) { | |
| 81 std::string ret = (*cflags)[i + 1]; | |
| 82 cflags->erase(cflags->begin() + i, cflags->begin() + i + 2); | |
| 83 return ret; | |
| 84 } | |
| 85 } else if (StartsWithASCII(cur, "-arch ", true)) { | |
| 86 // The arch was passed as one GN string value, e.g. "-arch i386". Return | |
| 87 // the stuff following the space and delete the item. | |
| 88 std::string ret = cur.substr(6); | |
| 89 cflags->erase(cflags->begin() + i); | |
| 90 return ret; | |
| 91 } | |
| 92 } | |
| 93 return std::string(); | |
| 94 } | |
| 95 | |
| 96 // Searches for -miphoneos-version-min, returns the resulting value, and | |
| 97 // removes it from the list. Returns the empty string if it's not found. | |
| 98 std::string GetIPhoneVersionMin(std::vector<std::string>* cflags) { | |
| 99 // Searches for the "-arch" option and returns the corresponding GYP value. | |
| 100 const char prefix[] = "-miphoneos-version-min="; | |
| 101 for (size_t i = 0; i < cflags->size(); i++) { | |
| 102 const std::string& cur = (*cflags)[i]; | |
| 103 if (StartsWithASCII(cur, prefix, true)) { | |
| 104 std::string result = cur.substr(arraysize(prefix) - 1); | |
| 105 cflags->erase(cflags->begin() + i); | |
| 106 return result; | |
| 107 } | |
| 108 } | |
| 109 return std::string(); | |
| 110 } | |
| 111 | |
| 112 // Finds all values from the given getter from all configs in the given list, | |
| 113 // and adds them to the given result vector. | |
| 114 template<typename T> | |
| 115 void FillConfigListValues( | |
| 116 const LabelConfigVector& configs, | |
| 117 const std::vector<T>& (ConfigValues::* getter)() const, | |
| 118 std::vector<T>* result) { | |
| 119 for (size_t config_i = 0; config_i < configs.size(); config_i++) { | |
| 120 const std::vector<T>& values = | |
| 121 (configs[config_i].ptr->config_values().*getter)(); | |
| 122 for (size_t val_i = 0; val_i < values.size(); val_i++) | |
| 123 result->push_back(values[val_i]); | |
| 124 } | |
| 125 } | |
| 126 | |
| 127 bool IsClang(const Target* target) { | |
| 128 const Value* is_clang = | |
| 129 target->settings()->base_config()->GetValue("is_clang"); | |
| 130 return is_clang && is_clang->type() == Value::BOOLEAN && | |
| 131 is_clang->boolean_value(); | |
| 132 } | |
| 133 | |
| 134 bool IsIOS(const Target* target) { | |
| 135 const Value* is_ios = target->settings()->base_config()->GetValue("is_ios"); | |
| 136 return is_ios && is_ios->type() == Value::BOOLEAN && is_ios->boolean_value(); | |
| 137 } | |
| 138 | |
| 139 // Returns true if the current target is an iOS simulator build. | |
| 140 bool IsIOSSimulator(const std::vector<std::string>& cflags) { | |
| 141 // Search for the sysroot flag. We expect one flag to be the | |
| 142 // switch, and the following one to be the value. | |
| 143 const char sysroot[] = "-isysroot"; | |
| 144 for (size_t i = 0; i < cflags.size(); i++) { | |
| 145 const std::string& cur = cflags[i]; | |
| 146 if (cur == sysroot) { | |
| 147 if (i == cflags.size() - 1) | |
| 148 return false; // No following value. | |
| 149 | |
| 150 // The argument is a file path, we check the prefix of the file name. | |
| 151 base::FilePath path(UTF8ToFilePath(cflags[i + 1])); | |
| 152 std::string path_file_part = FilePathToUTF8(path.BaseName()); | |
| 153 return StartsWithASCII(path_file_part, "iphonesimulator", false); | |
| 154 } | |
| 155 } | |
| 156 return false; | |
| 157 } | |
| 158 | |
| 159 } // namespace | |
| 160 | |
| 161 GypBinaryTargetWriter::Flags::Flags() {} | |
| 162 GypBinaryTargetWriter::Flags::~Flags() {} | |
| 163 | |
| 164 GypBinaryTargetWriter::GypBinaryTargetWriter(const TargetGroup& group, | |
| 165 const Toolchain* debug_toolchain, | |
| 166 const SourceDir& gyp_dir, | |
| 167 std::ostream& out) | |
| 168 : GypTargetWriter(group.get()->item()->AsTarget(), debug_toolchain, | |
| 169 gyp_dir, out), | |
| 170 group_(group) { | |
| 171 } | |
| 172 | |
| 173 GypBinaryTargetWriter::~GypBinaryTargetWriter() { | |
| 174 } | |
| 175 | |
| 176 void GypBinaryTargetWriter::Run() { | |
| 177 int indent = 4; | |
| 178 | |
| 179 Indent(indent) << "{\n"; | |
| 180 | |
| 181 WriteName(indent + kExtraIndent); | |
| 182 WriteType(indent + kExtraIndent); | |
| 183 | |
| 184 if (target_->settings()->IsLinux()) | |
| 185 WriteLinuxConfiguration(indent + kExtraIndent); | |
| 186 else if (target_->settings()->IsWin()) | |
| 187 WriteVCConfiguration(indent + kExtraIndent); | |
| 188 else if (target_->settings()->IsMac()) | |
| 189 WriteMacConfiguration(indent + kExtraIndent); | |
| 190 WriteDirectDependentSettings(indent + kExtraIndent); | |
| 191 WriteAllDependentSettings(indent + kExtraIndent); | |
| 192 | |
| 193 Indent(indent) << "},\n"; | |
| 194 } | |
| 195 | |
| 196 void GypBinaryTargetWriter::WriteName(int indent) { | |
| 197 std::string name = helper_.GetNameForTarget(target_); | |
| 198 Indent(indent) << "'target_name': '" << name << "',\n"; | |
| 199 | |
| 200 std::string product_name; | |
| 201 if (target_->output_name().empty()) | |
| 202 product_name = target_->label().name(); | |
| 203 else | |
| 204 product_name = name; | |
| 205 | |
| 206 // TODO(brettw) GN knows not to prefix targets starting with "lib" with | |
| 207 // another "lib" on Linux, but GYP doesn't. We need to rename applicable | |
| 208 // targets here. | |
| 209 | |
| 210 Indent(indent) << "'product_name': '" << product_name << "',\n"; | |
| 211 | |
| 212 std::string product_extension = target_->output_extension(); | |
| 213 if (!product_extension.empty()) | |
| 214 Indent(indent) << "'product_extension': '" << product_extension << "',\n"; | |
| 215 } | |
| 216 | |
| 217 void GypBinaryTargetWriter::WriteType(int indent) { | |
| 218 Indent(indent) << "'type': "; | |
| 219 switch (target_->output_type()) { | |
| 220 case Target::EXECUTABLE: | |
| 221 out_ << "'executable',\n"; | |
| 222 break; | |
| 223 case Target::STATIC_LIBRARY: | |
| 224 out_ << "'static_library',\n"; | |
| 225 break; | |
| 226 case Target::SHARED_LIBRARY: | |
| 227 out_ << "'shared_library',\n"; | |
| 228 break; | |
| 229 case Target::SOURCE_SET: | |
| 230 out_ << "'static_library',\n"; // TODO(brettw) fixme. | |
| 231 break; | |
| 232 default: | |
| 233 NOTREACHED(); | |
| 234 } | |
| 235 | |
| 236 if (target_->hard_dep()) | |
| 237 Indent(indent) << "'hard_dependency': 1,\n"; | |
| 238 | |
| 239 // Write out the toolsets depending on whether there is a host build. If no | |
| 240 // toolset is specified, GYP assumes a target build. | |
| 241 if (group_.debug && group_.host_debug) | |
| 242 Indent(indent) << "'toolsets': ['target', 'host'],\n"; | |
| 243 else if (group_.host_debug) | |
| 244 Indent(indent) << "'toolsets': ['host'],\n"; | |
| 245 } | |
| 246 | |
| 247 void GypBinaryTargetWriter::WriteVCConfiguration(int indent) { | |
| 248 Indent(indent) << "'configurations': {\n"; | |
| 249 | |
| 250 Indent(indent + kExtraIndent) << "'Debug': {\n"; | |
| 251 Indent(indent + kExtraIndent * 2) << | |
| 252 "'msvs_configuration_platform': 'Win32',\n"; | |
| 253 Flags debug_flags(FlagsFromTarget(group_.debug->item()->AsTarget())); | |
| 254 WriteVCFlags(debug_flags, indent + kExtraIndent * 2); | |
| 255 Indent(indent + kExtraIndent) << "},\n"; | |
| 256 | |
| 257 Indent(indent + kExtraIndent) << "'Release': {\n"; | |
| 258 Indent(indent + kExtraIndent * 2) << | |
| 259 "'msvs_configuration_platform': 'Win32',\n"; | |
| 260 Flags release_flags(FlagsFromTarget(group_.release->item()->AsTarget())); | |
| 261 WriteVCFlags(release_flags, indent + kExtraIndent * 2); | |
| 262 Indent(indent + kExtraIndent) << "},\n"; | |
| 263 | |
| 264 // Note that we always need Debug_x64 and Release_x64 defined or GYP will get | |
| 265 // confused, but we ca leave them empty if there's no 64-bit target. | |
| 266 Indent(indent + kExtraIndent) << "'Debug_x64': {\n"; | |
| 267 if (group_.debug64) { | |
| 268 Indent(indent + kExtraIndent * 2) << | |
| 269 "'msvs_configuration_platform': 'x64',\n"; | |
| 270 Flags flags(FlagsFromTarget(group_.debug64->item()->AsTarget())); | |
| 271 WriteVCFlags(flags, indent + kExtraIndent * 2); | |
| 272 } | |
| 273 Indent(indent + kExtraIndent) << "},\n"; | |
| 274 | |
| 275 Indent(indent + kExtraIndent) << "'Release_x64': {\n"; | |
| 276 if (group_.release64) { | |
| 277 Indent(indent + kExtraIndent * 2) << | |
| 278 "'msvs_configuration_platform': 'x64',\n"; | |
| 279 Flags flags(FlagsFromTarget(group_.release64->item()->AsTarget())); | |
| 280 WriteVCFlags(flags, indent + kExtraIndent * 2); | |
| 281 } | |
| 282 Indent(indent + kExtraIndent) << "},\n"; | |
| 283 | |
| 284 Indent(indent) << "},\n"; | |
| 285 | |
| 286 WriteSources(target_, indent); | |
| 287 WriteDeps(target_, indent); | |
| 288 } | |
| 289 | |
| 290 void GypBinaryTargetWriter::WriteLinuxConfiguration(int indent) { | |
| 291 // The Linux stuff works differently. On Linux we support cross-compiles and | |
| 292 // all ninja generators know to look for target conditions. Other platforms' | |
| 293 // generators don't all do this, so we can't have the same GYP structure. | |
| 294 Indent(indent) << "'target_conditions': [\n"; | |
| 295 // The host toolset is configured for the current computer, we will only have | |
| 296 // this when doing cross-compiles. | |
| 297 if (group_.host_debug && group_.host_release) { | |
| 298 Indent(indent + kExtraIndent) << "['_toolset == \"host\"', {\n"; | |
| 299 Indent(indent + kExtraIndent * 2) << "'configurations': {\n"; | |
| 300 Indent(indent + kExtraIndent * 3) << "'Debug': {\n"; | |
| 301 WriteLinuxFlagsForTarget(group_.host_debug->item()->AsTarget(), | |
| 302 indent + kExtraIndent * 4); | |
| 303 Indent(indent + kExtraIndent * 3) << "},\n"; | |
| 304 Indent(indent + kExtraIndent * 3) << "'Release': {\n"; | |
| 305 WriteLinuxFlagsForTarget(group_.host_release->item()->AsTarget(), | |
| 306 indent + kExtraIndent * 4); | |
| 307 Indent(indent + kExtraIndent * 3) << "},\n"; | |
| 308 Indent(indent + kExtraIndent * 2) << "}\n"; | |
| 309 | |
| 310 // The sources are per-toolset but shared between debug & release. | |
| 311 WriteSources(group_.host_debug->item()->AsTarget(), | |
| 312 indent + kExtraIndent * 2); | |
| 313 | |
| 314 Indent(indent + kExtraIndent) << "],\n"; | |
| 315 } | |
| 316 | |
| 317 // The target toolset is the "regular" one. | |
| 318 Indent(indent + kExtraIndent) << "['_toolset == \"target\"', {\n"; | |
| 319 Indent(indent + kExtraIndent * 2) << "'configurations': {\n"; | |
| 320 Indent(indent + kExtraIndent * 3) << "'Debug': {\n"; | |
| 321 WriteLinuxFlagsForTarget(group_.debug->item()->AsTarget(), | |
| 322 indent + kExtraIndent * 4); | |
| 323 Indent(indent + kExtraIndent * 3) << "},\n"; | |
| 324 Indent(indent + kExtraIndent * 3) << "'Release': {\n"; | |
| 325 WriteLinuxFlagsForTarget(group_.release->item()->AsTarget(), | |
| 326 indent + kExtraIndent * 4); | |
| 327 Indent(indent + kExtraIndent * 3) << "},\n"; | |
| 328 Indent(indent + kExtraIndent * 2) << "},\n"; | |
| 329 | |
| 330 WriteSources(target_, indent + kExtraIndent * 2); | |
| 331 | |
| 332 Indent(indent + kExtraIndent) << "},],\n"; | |
| 333 Indent(indent) << "],\n"; | |
| 334 | |
| 335 // Deps in GYP can not vary based on the toolset. | |
| 336 WriteDeps(target_, indent); | |
| 337 } | |
| 338 | |
| 339 void GypBinaryTargetWriter::WriteMacConfiguration(int indent) { | |
| 340 // The Mac flags are parameterized by the GYP generator (Ninja vs. XCode). | |
| 341 const char kNinjaGeneratorCondition[] = | |
| 342 "'conditions': [['\"<(GENERATOR)\"==\"ninja\"', {\n"; | |
| 343 const char kNinjaGeneratorElse[] = "}, {\n"; | |
| 344 const char kNinjaGeneratorEnd[] = "}]],\n"; | |
| 345 | |
| 346 Indent(indent) << "'configurations': {\n"; | |
| 347 | |
| 348 // Debug. | |
| 349 Indent(indent + kExtraIndent) << "'Debug': {\n"; | |
| 350 Indent(indent + kExtraIndent * 2) << kNinjaGeneratorCondition; | |
| 351 { | |
| 352 // Ninja generator. | |
| 353 WriteMacTargetAndHostFlags(group_.debug, group_.host_debug, | |
| 354 indent + kExtraIndent * 3); | |
| 355 } | |
| 356 Indent(indent + kExtraIndent * 2) << kNinjaGeneratorElse; | |
| 357 if (group_.xcode_debug) { | |
| 358 // XCode generator. | |
| 359 WriteMacTargetAndHostFlags(group_.xcode_debug, group_.xcode_host_debug, | |
| 360 indent + kExtraIndent * 3); | |
| 361 } | |
| 362 Indent(indent + kExtraIndent * 2) << kNinjaGeneratorEnd; | |
| 363 Indent(indent + kExtraIndent) << "},\n"; | |
| 364 | |
| 365 // Release. | |
| 366 Indent(indent + kExtraIndent) << "'Release': {\n"; | |
| 367 Indent(indent + kExtraIndent * 2) << kNinjaGeneratorCondition; | |
| 368 { | |
| 369 // Ninja generator. | |
| 370 WriteMacTargetAndHostFlags(group_.release, group_.host_release, | |
| 371 indent + kExtraIndent * 3); | |
| 372 } | |
| 373 Indent(indent + kExtraIndent * 2) << kNinjaGeneratorElse; | |
| 374 if (group_.xcode_release) { | |
| 375 // XCode generator. | |
| 376 WriteMacTargetAndHostFlags(group_.xcode_release, group_.xcode_host_release, | |
| 377 indent + kExtraIndent * 3); | |
| 378 } | |
| 379 Indent(indent + kExtraIndent * 2) << kNinjaGeneratorEnd; | |
| 380 Indent(indent + kExtraIndent) << "},\n"; | |
| 381 | |
| 382 Indent(indent) << "},\n"; | |
| 383 | |
| 384 WriteSources(target_, indent); | |
| 385 WriteDeps(target_, indent); | |
| 386 } | |
| 387 | |
| 388 void GypBinaryTargetWriter::WriteVCFlags(Flags& flags, int indent) { | |
| 389 // Defines and includes go outside of the msvs settings. | |
| 390 WriteNamedArray("defines", flags.defines, indent); | |
| 391 WriteIncludeDirs(flags, indent); | |
| 392 | |
| 393 // C flags. | |
| 394 Indent(indent) << "'msvs_settings': {\n"; | |
| 395 Indent(indent + kExtraIndent) << "'VCCLCompilerTool': {\n"; | |
| 396 | |
| 397 // GYP always uses the VC optimization flag to add a /O? on Visual Studio. | |
| 398 // This can produce duplicate values. So look up the GYP value corresponding | |
| 399 // to the flags used, and set the same one. | |
| 400 std::string optimization = GetVCOptimization(&flags.cflags); | |
| 401 WriteNamedArray("AdditionalOptions", flags.cflags, indent + kExtraIndent * 2); | |
| 402 // TODO(brettw) cflags_c and cflags_cc! | |
| 403 Indent(indent + kExtraIndent * 2) << "'Optimization': " | |
| 404 << optimization << ",\n"; | |
| 405 Indent(indent + kExtraIndent) << "},\n"; | |
| 406 | |
| 407 // Linker tool stuff. | |
| 408 Indent(indent + kExtraIndent) << "'VCLinkerTool': {\n"; | |
| 409 | |
| 410 // ...Library dirs. | |
| 411 EscapeOptions escape_options; | |
| 412 escape_options.mode = ESCAPE_JSON; | |
| 413 if (!flags.lib_dirs.empty()) { | |
| 414 Indent(indent + kExtraIndent * 2) << "'AdditionalLibraryDirectories': ["; | |
| 415 for (size_t i = 0; i < flags.lib_dirs.size(); i++) { | |
| 416 out_ << " '"; | |
| 417 EscapeStringToStream(out_, | |
| 418 helper_.GetDirReference(flags.lib_dirs[i], false), | |
| 419 escape_options); | |
| 420 out_ << "',"; | |
| 421 } | |
| 422 out_ << " ],\n"; | |
| 423 } | |
| 424 | |
| 425 // ...Libraries. | |
| 426 WriteNamedArray("AdditionalDependencies", flags.libs, | |
| 427 indent + kExtraIndent * 2); | |
| 428 | |
| 429 // ...LD flags. | |
| 430 // TODO(brettw) EnableUAC defaults to on and needs to be set. Also | |
| 431 // UACExecutionLevel and UACUIAccess depends on that and defaults to 0/false. | |
| 432 WriteNamedArray("AdditionalOptions", flags.ldflags, 14); | |
| 433 Indent(indent + kExtraIndent) << "},\n"; | |
| 434 Indent(indent) << "},\n"; | |
| 435 } | |
| 436 | |
| 437 void GypBinaryTargetWriter::WriteMacFlags(const Target* target, | |
| 438 Flags& flags, | |
| 439 int indent) { | |
| 440 WriteNamedArray("defines", flags.defines, indent); | |
| 441 WriteIncludeDirs(flags, indent); | |
| 442 | |
| 443 // Libraries and library directories. | |
| 444 EscapeOptions escape_options; | |
| 445 escape_options.mode = ESCAPE_JSON; | |
| 446 if (!flags.lib_dirs.empty()) { | |
| 447 Indent(indent + kExtraIndent) << "'library_dirs': ["; | |
| 448 for (size_t i = 0; i < flags.lib_dirs.size(); i++) { | |
| 449 out_ << " '"; | |
| 450 EscapeStringToStream(out_, | |
| 451 helper_.GetDirReference(flags.lib_dirs[i], false), | |
| 452 escape_options); | |
| 453 out_ << "',"; | |
| 454 } | |
| 455 out_ << " ],\n"; | |
| 456 } | |
| 457 if (!flags.libs.empty()) { | |
| 458 Indent(indent) << "'link_settings': {\n"; | |
| 459 Indent(indent + kExtraIndent) << "'libraries': ["; | |
| 460 for (size_t i = 0; i < flags.libs.size(); i++) { | |
| 461 out_ << " '-l"; | |
| 462 EscapeStringToStream(out_, flags.libs[i], escape_options); | |
| 463 out_ << "',"; | |
| 464 } | |
| 465 out_ << " ],\n"; | |
| 466 Indent(indent) << "},\n"; | |
| 467 } | |
| 468 | |
| 469 Indent(indent) << "'xcode_settings': {\n"; | |
| 470 | |
| 471 // Architecture. GYP reads this value and uses it to generate the -arch | |
| 472 // flag, so we always want to remove it from the cflags (which is a side | |
| 473 // effect of what GetMacArch does), even in cases where we don't use the | |
| 474 // value (in the iOS arm below). | |
| 475 std::string arch = GetMacArch(&flags.cflags); | |
| 476 if (IsIOS(target)) { | |
| 477 // When writing an iOS "target" (not host) target, we set VALID_ARCHS | |
| 478 // instead of ARCHS and always use this hardcoded value. This matches the | |
| 479 // GYP build. | |
| 480 Indent(indent + kExtraIndent) << "'VALID_ARCHS': ['armv7', 'i386'],\n"; | |
| 481 | |
| 482 // Tell XCode to target both iPhone and iPad. GN has no such concept. | |
| 483 Indent(indent + kExtraIndent) << "'TARGETED_DEVICE_FAMILY': '1,2',\n"; | |
| 484 | |
| 485 if (IsIOSSimulator(flags.cflags)) { | |
| 486 Indent(indent + kExtraIndent) << "'SDKROOT': 'iphonesimulator',\n"; | |
| 487 } else { | |
| 488 Indent(indent + kExtraIndent) << "'SDKROOT': 'iphoneos',\n"; | |
| 489 std::string min_ver = GetIPhoneVersionMin(&flags.cflags); | |
| 490 if (!min_ver.empty()) { | |
| 491 Indent(indent + kExtraIndent) << "'IPHONEOS_DEPLOYMENT_TARGET': '" | |
| 492 << min_ver << "',\n"; | |
| 493 } | |
| 494 } | |
| 495 } else { | |
| 496 // When doing regular Mac and "host" iOS (which look like regular Mac) | |
| 497 // builds, we can set the ARCHS value to what's specified in the build. | |
| 498 if (arch == "i386") | |
| 499 Indent(indent + kExtraIndent) << "'ARCHS': [ 'i386' ],\n"; | |
| 500 else if (arch == "x86_64") | |
| 501 Indent(indent + kExtraIndent) << "'ARCHS': [ 'x86_64' ],\n"; | |
| 502 } | |
| 503 | |
| 504 // C/C++ flags. | |
| 505 if (!flags.cflags.empty() || !flags.cflags_c.empty() || | |
| 506 !flags.cflags_objc.empty()) { | |
| 507 Indent(indent + kExtraIndent) << "'OTHER_CFLAGS': ["; | |
| 508 WriteArrayValues(out_, flags.cflags); | |
| 509 WriteArrayValues(out_, flags.cflags_c); | |
| 510 WriteArrayValues(out_, flags.cflags_objc); | |
| 511 out_ << " ],\n"; | |
| 512 } | |
| 513 if (!flags.cflags.empty() || !flags.cflags_cc.empty() || | |
| 514 !flags.cflags_objcc.empty()) { | |
| 515 Indent(indent + kExtraIndent) << "'OTHER_CPLUSPLUSFLAGS': ["; | |
| 516 WriteArrayValues(out_, flags.cflags); | |
| 517 WriteArrayValues(out_, flags.cflags_cc); | |
| 518 WriteArrayValues(out_, flags.cflags_objcc); | |
| 519 out_ << " ],\n"; | |
| 520 } | |
| 521 | |
| 522 // Ld flags. Don't write these for static libraries. Otherwise, they'll be | |
| 523 // passed to the library tool which doesn't expect it (the toolchain does | |
| 524 // not use ldflags so these are ignored in the normal build). | |
| 525 if (target->output_type() != Target::STATIC_LIBRARY) | |
| 526 WriteNamedArray("OTHER_LDFLAGS", flags.ldflags, indent + kExtraIndent); | |
| 527 | |
| 528 // Write the compiler that XCode should use. When we're using clang, we want | |
| 529 // the custom one, otherwise don't add this and the default compiler will be | |
| 530 // used. | |
| 531 // | |
| 532 // TODO(brettw) this is a hack. We could add a way for the GN build to set | |
| 533 // these values but as far as I can see this is the only use for them, so | |
| 534 // currently we manually check the build config's is_clang value. | |
| 535 if (IsClang(target)) { | |
| 536 base::FilePath clang_path = | |
| 537 target_->settings()->build_settings()->GetFullPath(SourceFile( | |
| 538 "//third_party/llvm-build/Release+Asserts/bin/clang")); | |
| 539 base::FilePath clang_pp_path = | |
| 540 target_->settings()->build_settings()->GetFullPath(SourceFile( | |
| 541 "//third_party/llvm-build/Release+Asserts/bin/clang++")); | |
| 542 | |
| 543 Indent(indent + kExtraIndent) | |
| 544 << "'CC': '" << FilePathToUTF8(clang_path) << "',\n"; | |
| 545 Indent(indent + kExtraIndent) | |
| 546 << "'LDPLUSPLUS': '" << FilePathToUTF8(clang_pp_path) << "',\n"; | |
| 547 } | |
| 548 | |
| 549 Indent(indent) << "},\n"; | |
| 550 } | |
| 551 | |
| 552 void GypBinaryTargetWriter::WriteLinuxFlagsForTarget(const Target* target, | |
| 553 int indent) { | |
| 554 Flags flags(FlagsFromTarget(target)); | |
| 555 WriteLinuxFlags(flags, indent); | |
| 556 } | |
| 557 | |
| 558 void GypBinaryTargetWriter::WriteLinuxFlags(const Flags& flags, int indent) { | |
| 559 WriteIncludeDirs(flags, indent); | |
| 560 WriteNamedArray("defines", flags.defines, indent); | |
| 561 WriteNamedArray("cflags", flags.cflags, indent); | |
| 562 WriteNamedArray("cflags_c", flags.cflags_c, indent); | |
| 563 WriteNamedArray("cflags_cc", flags.cflags_cc, indent); | |
| 564 WriteNamedArray("cflags_objc", flags.cflags_objc, indent); | |
| 565 WriteNamedArray("cflags_objcc", flags.cflags_objcc, indent); | |
| 566 | |
| 567 // Put libraries and library directories in with ldflags. | |
| 568 Indent(indent) << "'ldflags': ["; \ | |
| 569 WriteArrayValues(out_, flags.ldflags); | |
| 570 | |
| 571 EscapeOptions escape_options; | |
| 572 escape_options.mode = ESCAPE_JSON; | |
| 573 for (size_t i = 0; i < flags.lib_dirs.size(); i++) { | |
| 574 out_ << " '-L"; | |
| 575 EscapeStringToStream(out_, | |
| 576 helper_.GetDirReference(flags.lib_dirs[i], false), | |
| 577 escape_options); | |
| 578 out_ << "',"; | |
| 579 } | |
| 580 | |
| 581 for (size_t i = 0; i < flags.libs.size(); i++) { | |
| 582 out_ << " '-l"; | |
| 583 EscapeStringToStream(out_, flags.libs[i], escape_options); | |
| 584 out_ << "',"; | |
| 585 } | |
| 586 out_ << " ],\n"; | |
| 587 } | |
| 588 | |
| 589 void GypBinaryTargetWriter::WriteMacTargetAndHostFlags( | |
| 590 const BuilderRecord* target, | |
| 591 const BuilderRecord* host, | |
| 592 int indent) { | |
| 593 // The Mac flags are sometimes (when cross-compiling) also parameterized on | |
| 594 // the toolset. | |
| 595 const char kToolsetTargetCondition[] = | |
| 596 "'target_conditions': [['_toolset==\"target\"', {\n"; | |
| 597 const char kToolsetTargetElse[] = "}, {\n"; | |
| 598 const char kToolsetTargetEnd[] = "}]],\n"; | |
| 599 | |
| 600 int extra_indent = 0; | |
| 601 if (host) { | |
| 602 // Write out the first part of the conditional. | |
| 603 Indent(indent) << kToolsetTargetCondition; | |
| 604 extra_indent = kExtraIndent; | |
| 605 } | |
| 606 | |
| 607 // Always write the target flags (may or may not be inside a target | |
| 608 // conditional). | |
| 609 { | |
| 610 Flags flags(FlagsFromTarget(target->item()->AsTarget())); | |
| 611 WriteMacFlags(target->item()->AsTarget(), flags, indent + extra_indent); | |
| 612 } | |
| 613 | |
| 614 // Now optionally write the host conditional arm. | |
| 615 if (host) { | |
| 616 Indent(indent) << kToolsetTargetElse; | |
| 617 Flags flags(FlagsFromTarget(host->item()->AsTarget())); | |
| 618 WriteMacFlags(host->item()->AsTarget(), flags, indent + kExtraIndent); | |
| 619 Indent(indent) << kToolsetTargetEnd; | |
| 620 } | |
| 621 } | |
| 622 | |
| 623 void GypBinaryTargetWriter::WriteSources(const Target* target, int indent) { | |
| 624 Indent(indent) << "'sources': [\n"; | |
| 625 | |
| 626 const Target::FileList& sources = target->sources(); | |
| 627 for (size_t i = 0; i < sources.size(); i++) { | |
| 628 const SourceFile& input_file = sources[i]; | |
| 629 Indent(indent + kExtraIndent) << "'" << helper_.GetFileReference(input_file) | |
| 630 << "',\n"; | |
| 631 } | |
| 632 | |
| 633 Indent(indent) << "],\n"; | |
| 634 } | |
| 635 | |
| 636 void GypBinaryTargetWriter::WriteDeps(const Target* target, int indent) { | |
| 637 const LabelTargetVector& deps = target->deps(); | |
| 638 if (deps.empty()) | |
| 639 return; | |
| 640 | |
| 641 EscapeOptions escape_options; | |
| 642 escape_options.mode = ESCAPE_JSON; | |
| 643 | |
| 644 Indent(indent) << "'dependencies': [\n"; | |
| 645 for (size_t i = 0; i < deps.size(); i++) { | |
| 646 Indent(indent + kExtraIndent) << "'"; | |
| 647 EscapeStringToStream(out_, helper_.GetFullRefForTarget(deps[i].ptr), | |
| 648 escape_options); | |
| 649 out_ << "',\n"; | |
| 650 } | |
| 651 Indent(indent) << "],\n"; | |
| 652 } | |
| 653 | |
| 654 void GypBinaryTargetWriter::WriteIncludeDirs(const Flags& flags, int indent) { | |
| 655 if (flags.include_dirs.empty()) | |
| 656 return; | |
| 657 | |
| 658 EscapeOptions options; | |
| 659 options.mode = ESCAPE_JSON; | |
| 660 | |
| 661 Indent(indent) << "'include_dirs': ["; | |
| 662 for (size_t i = 0; i < flags.include_dirs.size(); i++) { | |
| 663 out_ << " '"; | |
| 664 EscapeStringToStream(out_, | |
| 665 helper_.GetDirReference(flags.include_dirs[i], false), | |
| 666 options); | |
| 667 out_ << "',"; | |
| 668 } | |
| 669 out_ << " ],\n"; | |
| 670 } | |
| 671 | |
| 672 void GypBinaryTargetWriter::WriteDirectDependentSettings(int indent) { | |
| 673 if (target_->direct_dependent_configs().empty()) | |
| 674 return; | |
| 675 Indent(indent) << "'direct_dependent_settings': {\n"; | |
| 676 | |
| 677 Flags flags(FlagsFromConfigList(target_->direct_dependent_configs())); | |
| 678 if (target_->settings()->IsLinux()) | |
| 679 WriteLinuxFlags(flags, indent + kExtraIndent); | |
| 680 else if (target_->settings()->IsWin()) | |
| 681 WriteVCFlags(flags, indent + kExtraIndent); | |
| 682 else if (target_->settings()->IsMac()) | |
| 683 WriteMacFlags(target_, flags, indent + kExtraIndent); | |
| 684 Indent(indent) << "},\n"; | |
| 685 } | |
| 686 | |
| 687 void GypBinaryTargetWriter::WriteAllDependentSettings(int indent) { | |
| 688 if (target_->all_dependent_configs().empty()) | |
| 689 return; | |
| 690 Indent(indent) << "'all_dependent_settings': {\n"; | |
| 691 | |
| 692 Flags flags(FlagsFromConfigList(target_->all_dependent_configs())); | |
| 693 if (target_->settings()->IsLinux()) | |
| 694 WriteLinuxFlags(flags, indent + kExtraIndent); | |
| 695 else if (target_->settings()->IsWin()) | |
| 696 WriteVCFlags(flags, indent + kExtraIndent); | |
| 697 else if (target_->settings()->IsMac()) | |
| 698 WriteMacFlags(target_, flags, indent + kExtraIndent); | |
| 699 Indent(indent) << "},\n"; | |
| 700 } | |
| 701 | |
| 702 GypBinaryTargetWriter::Flags GypBinaryTargetWriter::FlagsFromTarget( | |
| 703 const Target* target) const { | |
| 704 Flags ret; | |
| 705 | |
| 706 // Extracts a vector of the given type and name from the config values. | |
| 707 #define EXTRACT(type, name) \ | |
| 708 { \ | |
| 709 Accumulator<type> acc(&ret.name); \ | |
| 710 RecursiveTargetConfigToStream<type>(target, &ConfigValues::name, \ | |
| 711 acc, out_); \ | |
| 712 } | |
| 713 | |
| 714 EXTRACT(std::string, defines); | |
| 715 EXTRACT(SourceDir, include_dirs); | |
| 716 EXTRACT(std::string, cflags); | |
| 717 EXTRACT(std::string, cflags_c); | |
| 718 EXTRACT(std::string, cflags_cc); | |
| 719 EXTRACT(std::string, cflags_objc); | |
| 720 EXTRACT(std::string, cflags_objcc); | |
| 721 EXTRACT(std::string, ldflags); | |
| 722 | |
| 723 #undef EXTRACT | |
| 724 | |
| 725 const OrderedSet<SourceDir>& all_lib_dirs = target->all_lib_dirs(); | |
| 726 for (size_t i = 0; i < all_lib_dirs.size(); i++) | |
| 727 ret.lib_dirs.push_back(all_lib_dirs[i]); | |
| 728 | |
| 729 const OrderedSet<std::string> all_libs = target->all_libs(); | |
| 730 for (size_t i = 0; i < all_libs.size(); i++) | |
| 731 ret.libs.push_back(all_libs[i]); | |
| 732 | |
| 733 return ret; | |
| 734 } | |
| 735 | |
| 736 GypBinaryTargetWriter::Flags GypBinaryTargetWriter::FlagsFromConfigList( | |
| 737 const LabelConfigVector& configs) const { | |
| 738 Flags ret; | |
| 739 | |
| 740 #define EXTRACT(type, name) \ | |
| 741 FillConfigListValues<type>(configs, &ConfigValues::name, &ret.name); | |
| 742 | |
| 743 EXTRACT(std::string, defines); | |
| 744 EXTRACT(SourceDir, include_dirs); | |
| 745 EXTRACT(std::string, cflags); | |
| 746 EXTRACT(std::string, cflags_c); | |
| 747 EXTRACT(std::string, cflags_cc); | |
| 748 EXTRACT(std::string, cflags_objc); | |
| 749 EXTRACT(std::string, cflags_objcc); | |
| 750 EXTRACT(std::string, ldflags); | |
| 751 EXTRACT(SourceDir, lib_dirs); | |
| 752 EXTRACT(std::string, libs); | |
| 753 | |
| 754 #undef EXTRACT | |
| 755 | |
| 756 return ret; | |
| 757 } | |
| 758 | |
| 759 void GypBinaryTargetWriter::WriteNamedArray( | |
| 760 const char* name, | |
| 761 const std::vector<std::string>& values, | |
| 762 int indent) { | |
| 763 if (values.empty()) | |
| 764 return; | |
| 765 | |
| 766 EscapeOptions options; | |
| 767 options.mode = ESCAPE_JSON; | |
| 768 | |
| 769 Indent(indent) << "'" << name << "': ["; | |
| 770 WriteArrayValues(out_, values); | |
| 771 out_ << " ],\n"; | |
| 772 } | |
| 773 | |
| OLD | NEW |