| OLD | NEW |
| 1 // Copyright 2016 The Chromium Authors. All rights reserved. | 1 // Copyright 2016 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "tools/gn/xcode_writer.h" | 5 #include "tools/gn/xcode_writer.h" |
| 6 | 6 |
| 7 #include <iomanip> | 7 #include <iomanip> |
| 8 #include <map> | 8 #include <map> |
| 9 #include <memory> | 9 #include <memory> |
| 10 #include <sstream> | 10 #include <sstream> |
| 11 #include <string> | 11 #include <string> |
| 12 #include <utility> | 12 #include <utility> |
| 13 | 13 |
| 14 #include "base/environment.h" | 14 #include "base/environment.h" |
| 15 #include "base/logging.h" | 15 #include "base/logging.h" |
| 16 #include "base/sha1.h" | 16 #include "base/sha1.h" |
| 17 #include "base/strings/string_number_conversions.h" | 17 #include "base/strings/string_number_conversions.h" |
| 18 #include "base/strings/string_util.h" | 18 #include "base/strings/string_util.h" |
| 19 #include "tools/gn/args.h" | 19 #include "tools/gn/args.h" |
| 20 #include "tools/gn/build_settings.h" | 20 #include "tools/gn/build_settings.h" |
| 21 #include "tools/gn/builder.h" | 21 #include "tools/gn/builder.h" |
| 22 #include "tools/gn/commands.h" | 22 #include "tools/gn/commands.h" |
| 23 #include "tools/gn/config_values_extractors.h" |
| 23 #include "tools/gn/deps_iterator.h" | 24 #include "tools/gn/deps_iterator.h" |
| 24 #include "tools/gn/filesystem_utils.h" | 25 #include "tools/gn/filesystem_utils.h" |
| 25 #include "tools/gn/settings.h" | 26 #include "tools/gn/settings.h" |
| 26 #include "tools/gn/source_file.h" | 27 #include "tools/gn/source_file.h" |
| 27 #include "tools/gn/target.h" | 28 #include "tools/gn/target.h" |
| 28 #include "tools/gn/value.h" | 29 #include "tools/gn/value.h" |
| 29 #include "tools/gn/variables.h" | 30 #include "tools/gn/variables.h" |
| 30 #include "tools/gn/xcode_object.h" | 31 #include "tools/gn/xcode_object.h" |
| 31 | 32 |
| 32 namespace { | 33 namespace { |
| (...skipping 113 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 146 const std::string& dir_filters_string, | 147 const std::string& dir_filters_string, |
| 147 const BuildSettings* build_settings, | 148 const BuildSettings* build_settings, |
| 148 Builder* builder, | 149 Builder* builder, |
| 149 Err* err) { | 150 Err* err) { |
| 150 const XcodeWriter::TargetOsType target_os = | 151 const XcodeWriter::TargetOsType target_os = |
| 151 GetTargetOs(build_settings->build_args()); | 152 GetTargetOs(build_settings->build_args()); |
| 152 | 153 |
| 153 PBXAttributes attributes; | 154 PBXAttributes attributes; |
| 154 switch (target_os) { | 155 switch (target_os) { |
| 155 case XcodeWriter::WRITER_TARGET_OS_IOS: | 156 case XcodeWriter::WRITER_TARGET_OS_IOS: |
| 156 attributes["SDKROOT"] = "iphoneos"; | 157 attributes.insert(std::make_pair("SDKROOT", "iphoneos")); |
| 157 attributes["TARGETED_DEVICE_FAMILY"] = "1,2"; | 158 attributes.insert(std::make_pair("TARGETED_DEVICE_FAMILY", "1,2")); |
| 158 break; | 159 break; |
| 159 case XcodeWriter::WRITER_TARGET_OS_MACOS: | 160 case XcodeWriter::WRITER_TARGET_OS_MACOS: |
| 160 attributes["ARCHS"] = GetArchs(build_settings->build_args()); | 161 attributes.insert(std::make_pair("SDKROOT", "macosx10.11")); |
| 161 attributes["SDKROOT"] = "macosx10.11"; | 162 attributes.insert( |
| 163 std::make_pair("ARCHS", GetArchs(build_settings->build_args()))); |
| 162 break; | 164 break; |
| 163 } | 165 } |
| 164 | 166 |
| 165 const std::string source_path = | 167 const std::string source_path = |
| 166 base::FilePath::FromUTF8Unsafe( | 168 base::FilePath::FromUTF8Unsafe( |
| 167 RebasePath("//", build_settings->build_dir())) | 169 RebasePath("//", build_settings->build_dir())) |
| 168 .StripTrailingSeparators() | 170 .StripTrailingSeparators() |
| 169 .AsUTF8Unsafe(); | 171 .AsUTF8Unsafe(); |
| 170 | 172 |
| 171 std::string config_name = build_settings->build_dir() | 173 std::string config_name = build_settings->build_dir() |
| (...skipping 137 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 309 break; | 311 break; |
| 310 | 312 |
| 311 default: | 313 default: |
| 312 break; | 314 break; |
| 313 } | 315 } |
| 314 } | 316 } |
| 315 | 317 |
| 316 projects_.push_back(std::move(main_project)); | 318 projects_.push_back(std::move(main_project)); |
| 317 } | 319 } |
| 318 | 320 |
| 321 namespace { |
| 322 |
| 323 template <typename T> |
| 324 void SortAndRemoveDuplicates(std::vector<T>& vec) { |
| 325 std::sort(vec.begin(), vec.end()); |
| 326 vec.erase(std::unique(vec.begin(), vec.end()), vec.end()); |
| 327 } |
| 328 |
| 329 template <typename T> |
| 330 std::vector<T> IntersectSortedVectors(const std::vector<T>& vec, |
| 331 const std::vector<T>& vec2) { |
| 332 std::vector<T> intersection; |
| 333 std::set_intersection(vec.begin(), vec.end(), vec2.begin(), vec2.end(), |
| 334 std::back_inserter(intersection)); |
| 335 return intersection; |
| 336 } |
| 337 |
| 338 template <typename T> |
| 339 std::vector<T> ExcludeSortedVectors(const std::vector<T>& from, |
| 340 const std::vector<T>& what) { |
| 341 std::vector<T> difference; |
| 342 std::set_difference(from.begin(), from.end(), what.begin(), what.end(), |
| 343 std::back_inserter(difference)); |
| 344 return difference; |
| 345 } |
| 346 |
| 347 // Extracts dialect from flags, will not replace altrady extracted value |
| 348 void ExtractDialect(std::string& res, |
| 349 const std::vector<std::string>& flags, |
| 350 bool cc) { |
| 351 for (const auto& flag : flags) { |
| 352 if (res.empty() && flag.compare(0, 5, "-std=") == 0) { |
| 353 res = flag; |
| 354 } |
| 355 if (!res.empty()) { |
| 356 break; |
| 357 } |
| 358 } |
| 359 |
| 360 // we're looking for c++ dialect, it must have + in name |
| 361 if (cc && res.find('+') == std::string::npos) |
| 362 res.clear(); |
| 363 |
| 364 // we're looking for c dialect, it must not have + in name |
| 365 if (!cc && res.find('+') != std::string::npos) |
| 366 res.clear(); |
| 367 } |
| 368 |
| 369 void PrepareBuildAttributes(const std::vector<const Target*>& indexed_targets, |
| 370 const SourceDir& root_build_dir, |
| 371 std::map<SourceFile, std::string>& cflags, |
| 372 PBXAttributes& attributes) { |
| 373 // these will be specified at xcode target level and apply to all files |
| 374 std::vector<SourceDir> common_includes; |
| 375 std::vector<std::string> common_defines; |
| 376 |
| 377 // gather common includes and defines |
| 378 for (const auto* target : indexed_targets) { |
| 379 std::vector<SourceDir> target_includes; |
| 380 std::vector<std::string> target_defines; |
| 381 |
| 382 for (ConfigValuesIterator it(target); !it.done(); it.Next()) { |
| 383 target_includes.insert(target_includes.end(), |
| 384 it.cur().include_dirs().begin(), |
| 385 it.cur().include_dirs().end()); |
| 386 target_defines.insert(target_defines.end(), it.cur().defines().begin(), |
| 387 it.cur().defines().end()); |
| 388 } |
| 389 SortAndRemoveDuplicates(target_includes); |
| 390 SortAndRemoveDuplicates(target_defines); |
| 391 |
| 392 if (target == indexed_targets.front()) { |
| 393 common_includes = std::move(target_includes); |
| 394 common_defines = std::move(target_defines); |
| 395 } else { |
| 396 if (common_includes.empty() && common_defines.empty()) |
| 397 break; |
| 398 common_includes = |
| 399 IntersectSortedVectors(common_includes, target_includes); |
| 400 common_defines = IntersectSortedVectors(common_defines, target_defines); |
| 401 } |
| 402 } |
| 403 |
| 404 // gather specific cflags for individual files |
| 405 for (const auto* target : indexed_targets) { |
| 406 // only add taget includes to specific files; ignore target specific defines |
| 407 // because they prevent using precompiled headers during indexing |
| 408 std::vector<SourceDir> target_includes; |
| 409 |
| 410 std::string target_dialect_c; |
| 411 std::string target_dialect_cc; |
| 412 std::string target_dialect_objc; |
| 413 std::string target_dialect_objcc; |
| 414 |
| 415 for (ConfigValuesIterator it(target); !it.done(); it.Next()) { |
| 416 target_includes.insert(target_includes.end(), |
| 417 it.cur().include_dirs().begin(), |
| 418 it.cur().include_dirs().end()); |
| 419 |
| 420 ExtractDialect(target_dialect_c, it.cur().cflags_c(), false); |
| 421 ExtractDialect(target_dialect_c, it.cur().cflags(), false); |
| 422 ExtractDialect(target_dialect_cc, it.cur().cflags_cc(), true); |
| 423 ExtractDialect(target_dialect_cc, it.cur().cflags(), true); |
| 424 ExtractDialect(target_dialect_objc, it.cur().cflags_objc(), false); |
| 425 ExtractDialect(target_dialect_objc, it.cur().cflags(), false); |
| 426 ExtractDialect(target_dialect_objcc, it.cur().cflags_objcc(), true); |
| 427 ExtractDialect(target_dialect_objcc, it.cur().cflags(), true); |
| 428 } |
| 429 SortAndRemoveDuplicates(target_includes); |
| 430 |
| 431 std::string target_cflags; |
| 432 |
| 433 // includes specific for this target |
| 434 target_includes = ExcludeSortedVectors(target_includes, common_includes); |
| 435 for (const auto& include : target_includes) { |
| 436 std::string rebased = include.is_system_absolute() |
| 437 ? include.value() |
| 438 : RebasePath(include.value(), root_build_dir); |
| 439 target_cflags += " -I"; |
| 440 target_cflags += rebased; |
| 441 } |
| 442 |
| 443 if (!target_cflags.empty()) // remove leading space |
| 444 target_cflags = target_cflags.substr(1); |
| 445 |
| 446 // for each file set target specific cflags and language dialect |
| 447 // according to file type |
| 448 for (const SourceFile& source : target->sources()) { |
| 449 std::string file_cflags = target_cflags; |
| 450 std::string file_dialect; |
| 451 const auto& ext = FindExtension(&source.value()); |
| 452 if (ext == "c") |
| 453 file_dialect = target_dialect_c; |
| 454 else if (ext == "cc" || ext == "cpp" || ext == "cxx") |
| 455 file_dialect = target_dialect_cc; |
| 456 else if (ext == "m") |
| 457 file_dialect = target_dialect_objc; |
| 458 else if (ext == "mm") |
| 459 file_dialect = target_dialect_objcc; |
| 460 |
| 461 if (!file_cflags.empty() && !file_dialect.empty()) |
| 462 file_cflags += " "; |
| 463 file_cflags += file_dialect; |
| 464 |
| 465 cflags[source] = file_cflags; |
| 466 } |
| 467 } |
| 468 |
| 469 // HEADERMAP breaks indexing if there are multiple files with same name |
| 470 // in different folders; It also means that header lookup during indexing |
| 471 // works differently than during build, which is not desired |
| 472 attributes.erase("USE_HEADERMAP"); |
| 473 attributes.insert(std::make_pair("USE_HEADERMAP", "NO")); |
| 474 |
| 475 if (!common_includes.empty()) { |
| 476 for (const auto& include : common_includes) { |
| 477 std::string rebased = include.is_system_absolute() |
| 478 ? include.value() |
| 479 : RebasePath(include.value(), root_build_dir); |
| 480 attributes.insert(std::make_pair("HEADER_SEARCH_PATHS", rebased)); |
| 481 } |
| 482 } |
| 483 |
| 484 if (!common_defines.empty()) { |
| 485 for (const auto& define : common_defines) { |
| 486 attributes.insert(std::make_pair("GCC_PREPROCESSOR_DEFINITIONS", define)); |
| 487 } |
| 488 } |
| 489 } |
| 490 |
| 491 } // namespace |
| 492 |
| 319 void XcodeWriter::CreateSourcesProject( | 493 void XcodeWriter::CreateSourcesProject( |
| 320 const std::vector<const Target*>& targets, | 494 const std::vector<const Target*>& targets, |
| 321 const SourceDir& root_build_dir, | 495 const SourceDir& root_build_dir, |
| 322 const PBXAttributes& attributes, | 496 const PBXAttributes& attributes, |
| 323 const std::string& source_path, | 497 const std::string& source_path, |
| 324 const std::string& config_name, | 498 const std::string& config_name, |
| 325 TargetOsType target_os) { | 499 TargetOsType target_os) { |
| 326 std::vector<SourceFile> sources; | 500 std::vector<SourceFile> sources; |
| 501 std::vector<const Target*> indexed_targets; |
| 502 |
| 327 for (const Target* target : targets) { | 503 for (const Target* target : targets) { |
| 328 if (!target->settings()->is_default()) | 504 if (!target->settings()->is_default()) |
| 329 continue; | 505 continue; |
| 330 | 506 |
| 331 for (const SourceFile& source : target->sources()) { | 507 for (const SourceFile& source : target->sources()) { |
| 332 if (source.is_system_absolute()) | 508 if (source.is_system_absolute()) |
| 333 continue; | 509 continue; |
| 334 | 510 |
| 335 if (IsStringInOutputDir(root_build_dir, source.value())) | 511 if (IsStringInOutputDir(root_build_dir, source.value())) |
| 336 continue; | 512 continue; |
| 337 | 513 |
| 338 sources.push_back(source); | 514 sources.push_back(source); |
| 515 |
| 516 if (PBXProject::SourceFileShouldBeIndexed(source.value()) && |
| 517 (indexed_targets.empty() || indexed_targets.back() != target)) { |
| 518 indexed_targets.push_back(target); |
| 519 } |
| 339 } | 520 } |
| 340 } | 521 } |
| 341 | 522 |
| 523 std::map<SourceFile, std::string> cflags; |
| 524 PBXAttributes updated_attributes(attributes); |
| 525 PrepareBuildAttributes(indexed_targets, root_build_dir, cflags, |
| 526 updated_attributes); |
| 527 |
| 342 std::unique_ptr<PBXProject> sources_for_indexing( | 528 std::unique_ptr<PBXProject> sources_for_indexing( |
| 343 new PBXProject("sources", config_name, source_path, attributes)); | 529 new PBXProject("sources", config_name, source_path, updated_attributes)); |
| 344 | 530 |
| 345 // Sort sources to ensure determinisn of the project file generation and | 531 // Sort sources to ensure determinisn of the project file generation and |
| 346 // remove duplicate reference to the source files (can happen due to the | 532 // remove duplicate reference to the source files (can happen due to the |
| 347 // bundle_data targets). | 533 // bundle_data targets). |
| 348 std::sort(sources.begin(), sources.end()); | 534 SortAndRemoveDuplicates(sources); |
| 349 sources.erase(std::unique(sources.begin(), sources.end()), sources.end()); | |
| 350 | 535 |
| 351 SourceDir source_dir("//"); | 536 SourceDir source_dir("//"); |
| 352 for (const SourceFile& source : sources) { | 537 for (const SourceFile& source : sources) { |
| 353 std::string source_file = RebasePath(source.value(), source_dir); | 538 std::string source_file = RebasePath(source.value(), source_dir); |
| 354 sources_for_indexing->AddSourceFile(source_file); | 539 sources_for_indexing->AddSourceFile(source_file, cflags[source]); |
| 355 } | 540 } |
| 356 | 541 |
| 357 projects_.push_back(std::move(sources_for_indexing)); | 542 projects_.push_back(std::move(sources_for_indexing)); |
| 358 } | 543 } |
| 359 | 544 |
| 360 bool XcodeWriter::WriteFiles(const BuildSettings* build_settings, Err* err) { | 545 bool XcodeWriter::WriteFiles(const BuildSettings* build_settings, Err* err) { |
| 361 for (const auto& project : projects_) { | 546 for (const auto& project : projects_) { |
| 362 if (!WriteProjectFile(build_settings, project.get(), err)) | 547 if (!WriteProjectFile(build_settings, project.get(), err)) |
| 363 return false; | 548 return false; |
| 364 } | 549 } |
| (...skipping 60 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 425 for (const auto& object : pair.second) { | 610 for (const auto& object : pair.second) { |
| 426 object->Print(out, 2); | 611 object->Print(out, 2); |
| 427 } | 612 } |
| 428 out << "/* End " << ToString(pair.first) << " section */\n"; | 613 out << "/* End " << ToString(pair.first) << " section */\n"; |
| 429 } | 614 } |
| 430 | 615 |
| 431 out << "\t};\n" | 616 out << "\t};\n" |
| 432 << "\trootObject = " << project->Reference() << ";\n" | 617 << "\trootObject = " << project->Reference() << ";\n" |
| 433 << "}\n"; | 618 << "}\n"; |
| 434 } | 619 } |
| OLD | NEW |