Chromium Code Reviews| 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> |
| (...skipping 23 matching lines...) Expand all Loading... | |
| 34 using TargetToFileList = std::unordered_map<const Target*, Target::FileList>; | 34 using TargetToFileList = std::unordered_map<const Target*, Target::FileList>; |
| 35 using TargetToNativeTarget = | 35 using TargetToNativeTarget = |
| 36 std::unordered_map<const Target*, PBXNativeTarget*>; | 36 std::unordered_map<const Target*, PBXNativeTarget*>; |
| 37 using FileToTargets = std::map<SourceFile, | 37 using FileToTargets = std::map<SourceFile, |
| 38 std::vector<const Target*>, | 38 std::vector<const Target*>, |
| 39 bool (*)(const SourceFile&, const SourceFile&)>; | 39 bool (*)(const SourceFile&, const SourceFile&)>; |
| 40 | 40 |
| 41 const char kEarlGreyFileNameIdentifier[] = "egtest.mm"; | 41 const char kEarlGreyFileNameIdentifier[] = "egtest.mm"; |
| 42 const char kXCTestFileNameIdentifier[] = "xctest.mm"; | 42 const char kXCTestFileNameIdentifier[] = "xctest.mm"; |
| 43 const char kXCTestModuleTargetNamePostfix[] = "_module"; | 43 const char kXCTestModuleTargetNamePostfix[] = "_module"; |
| 44 const char kXCTestFileReferenceFolder[] = "xctests/"; | |
| 45 | 44 |
| 46 struct SafeEnvironmentVariableInfo { | 45 struct SafeEnvironmentVariableInfo { |
| 47 const char* name; | 46 const char* name; |
| 48 bool capture_at_generation; | 47 bool capture_at_generation; |
| 49 }; | 48 }; |
| 50 | 49 |
| 51 SafeEnvironmentVariableInfo kSafeEnvironmentVariables[] = { | 50 SafeEnvironmentVariableInfo kSafeEnvironmentVariables[] = { |
| 52 {"HOME", true}, {"LANG", true}, {"PATH", true}, | 51 {"HOME", true}, {"LANG", true}, {"PATH", true}, |
| 53 {"USER", true}, {"TMPDIR", false}, | 52 {"USER", true}, {"TMPDIR", false}, |
| 54 }; | 53 }; |
| 55 | 54 |
| 56 bool CompareSourceFiles(const SourceFile& lhs, const SourceFile& rhs) { | |
| 57 if (lhs.GetName() < rhs.GetName()) | |
| 58 return true; | |
| 59 else if (lhs.GetName() > rhs.GetName()) | |
| 60 return false; | |
| 61 else | |
| 62 return lhs.value() < rhs.value(); | |
| 63 } | |
| 64 | |
| 65 XcodeWriter::TargetOsType GetTargetOs(const Args& args) { | 55 XcodeWriter::TargetOsType GetTargetOs(const Args& args) { |
| 66 const Value* target_os_value = args.GetArgOverride(variables::kTargetOs); | 56 const Value* target_os_value = args.GetArgOverride(variables::kTargetOs); |
| 67 if (target_os_value) { | 57 if (target_os_value) { |
| 68 if (target_os_value->type() == Value::STRING) { | 58 if (target_os_value->type() == Value::STRING) { |
| 69 if (target_os_value->string_value() == "ios") | 59 if (target_os_value->string_value() == "ios") |
| 70 return XcodeWriter::WRITER_TARGET_OS_IOS; | 60 return XcodeWriter::WRITER_TARGET_OS_IOS; |
| 71 } | 61 } |
| 72 } | 62 } |
| 73 return XcodeWriter::WRITER_TARGET_OS_MACOS; | 63 return XcodeWriter::WRITER_TARGET_OS_MACOS; |
| 74 } | 64 } |
| (...skipping 39 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 114 } | 104 } |
| 115 | 105 |
| 116 bool IsXCTestModuleTarget(const Target* target) { | 106 bool IsXCTestModuleTarget(const Target* target) { |
| 117 return target->output_type() == Target::CREATE_BUNDLE && | 107 return target->output_type() == Target::CREATE_BUNDLE && |
| 118 target->bundle_data().product_type() == | 108 target->bundle_data().product_type() == |
| 119 "com.apple.product-type.bundle.unit-test" && | 109 "com.apple.product-type.bundle.unit-test" && |
| 120 base::EndsWith(target->label().name(), kXCTestModuleTargetNamePostfix, | 110 base::EndsWith(target->label().name(), kXCTestModuleTargetNamePostfix, |
| 121 base::CompareCase::SENSITIVE); | 111 base::CompareCase::SENSITIVE); |
| 122 } | 112 } |
| 123 | 113 |
| 114 bool IsXCTestFile(const SourceFile& file) { | |
| 115 return base::EndsWith(file.GetName(), kEarlGreyFileNameIdentifier, | |
| 116 base::CompareCase::SENSITIVE) || | |
| 117 base::EndsWith(file.GetName(), kXCTestFileNameIdentifier, | |
| 118 base::CompareCase::SENSITIVE); | |
| 119 } | |
| 120 | |
| 124 const Target* FindXCTestApplicationTarget( | 121 const Target* FindXCTestApplicationTarget( |
| 125 const Target* xctest_module_target, | 122 const Target* xctest_module_target, |
| 126 const std::vector<const Target*>& targets) { | 123 const std::vector<const Target*>& targets) { |
| 127 DCHECK(IsXCTestModuleTarget(xctest_module_target)); | 124 DCHECK(IsXCTestModuleTarget(xctest_module_target)); |
| 128 DCHECK(base::EndsWith(xctest_module_target->label().name(), | 125 DCHECK(base::EndsWith(xctest_module_target->label().name(), |
| 129 kXCTestModuleTargetNamePostfix, | 126 kXCTestModuleTargetNamePostfix, |
| 130 base::CompareCase::SENSITIVE)); | 127 base::CompareCase::SENSITIVE)); |
| 131 std::string application_target_name = | 128 std::string application_target_name = |
| 132 xctest_module_target->label().name().substr( | 129 xctest_module_target->label().name().substr( |
| 133 0, xctest_module_target->label().name().size() - | 130 0, xctest_module_target->label().name().size() - |
| (...skipping 20 matching lines...) Expand all Loading... | |
| 154 | 151 |
| 155 // Searches the list of xctest files recursively under |target|. | 152 // Searches the list of xctest files recursively under |target|. |
| 156 void SearchXCTestFiles(const Target* target, | 153 void SearchXCTestFiles(const Target* target, |
| 157 TargetToFileList* xctest_files_per_target) { | 154 TargetToFileList* xctest_files_per_target) { |
| 158 // Early return if already visited and processed. | 155 // Early return if already visited and processed. |
| 159 if (xctest_files_per_target->find(target) != xctest_files_per_target->end()) | 156 if (xctest_files_per_target->find(target) != xctest_files_per_target->end()) |
| 160 return; | 157 return; |
| 161 | 158 |
| 162 Target::FileList xctest_files; | 159 Target::FileList xctest_files; |
| 163 for (const SourceFile& file : target->sources()) { | 160 for (const SourceFile& file : target->sources()) { |
| 164 if (base::EndsWith(file.GetName(), kEarlGreyFileNameIdentifier, | 161 if (IsXCTestFile(file)) { |
| 165 base::CompareCase::SENSITIVE) || | |
| 166 base::EndsWith(file.GetName(), kXCTestFileNameIdentifier, | |
| 167 base::CompareCase::SENSITIVE)) { | |
| 168 xctest_files.push_back(file); | 162 xctest_files.push_back(file); |
| 169 } | 163 } |
| 170 } | 164 } |
| 171 | 165 |
| 172 // Call recursively on public and private deps. | 166 // Call recursively on public and private deps. |
| 173 for (const auto& target : target->public_deps()) { | 167 for (const auto& target : target->public_deps()) { |
| 174 SearchXCTestFiles(target.ptr, xctest_files_per_target); | 168 SearchXCTestFiles(target.ptr, xctest_files_per_target); |
| 175 const Target::FileList& deps_xctest_files = | 169 const Target::FileList& deps_xctest_files = |
| 176 (*xctest_files_per_target)[target.ptr]; | 170 (*xctest_files_per_target)[target.ptr]; |
| 177 xctest_files.insert(xctest_files.end(), deps_xctest_files.begin(), | 171 xctest_files.insert(xctest_files.end(), deps_xctest_files.begin(), |
| (...skipping 23 matching lines...) Expand all Loading... | |
| 201 std::vector<Target::FileList>* file_lists) { | 195 std::vector<Target::FileList>* file_lists) { |
| 202 TargetToFileList xctest_files_per_target; | 196 TargetToFileList xctest_files_per_target; |
| 203 | 197 |
| 204 for (const Target* target : application_targets) { | 198 for (const Target* target : application_targets) { |
| 205 DCHECK(IsApplicationTarget(target)); | 199 DCHECK(IsApplicationTarget(target)); |
| 206 SearchXCTestFiles(target, &xctest_files_per_target); | 200 SearchXCTestFiles(target, &xctest_files_per_target); |
| 207 file_lists->push_back(xctest_files_per_target[target]); | 201 file_lists->push_back(xctest_files_per_target[target]); |
| 208 } | 202 } |
| 209 } | 203 } |
| 210 | 204 |
| 211 // Maps each xctest file to a list of xctest application targets that contains | |
| 212 // the file. | |
| 213 void MapXCTestFileToApplicationTargets( | |
| 214 const std::vector<const Target*>& xctest_application_targets, | |
| 215 const std::vector<Target::FileList>& xctest_file_lists, | |
| 216 FileToTargets* xctest_file_to_application_targets) { | |
| 217 DCHECK_EQ(xctest_application_targets.size(), xctest_file_lists.size()); | |
| 218 | |
| 219 for (size_t i = 0; i < xctest_application_targets.size(); ++i) { | |
| 220 const Target* xctest_application_target = xctest_application_targets[i]; | |
| 221 DCHECK(IsApplicationTarget(xctest_application_target)); | |
| 222 | |
| 223 for (const SourceFile& source : xctest_file_lists[i]) { | |
| 224 auto iter = xctest_file_to_application_targets->find(source); | |
| 225 if (iter == xctest_file_to_application_targets->end()) { | |
| 226 iter = | |
| 227 xctest_file_to_application_targets | |
| 228 ->insert(std::make_pair(source, std::vector<const Target*>())) | |
| 229 .first; | |
| 230 } | |
| 231 iter->second.push_back(xctest_application_target); | |
| 232 } | |
| 233 } | |
| 234 } | |
| 235 | |
| 236 class CollectPBXObjectsPerClassHelper : public PBXObjectVisitor { | 205 class CollectPBXObjectsPerClassHelper : public PBXObjectVisitor { |
| 237 public: | 206 public: |
| 238 CollectPBXObjectsPerClassHelper() {} | 207 CollectPBXObjectsPerClassHelper() {} |
| 239 | 208 |
| 240 void Visit(PBXObject* object) override { | 209 void Visit(PBXObject* object) override { |
| 241 DCHECK(object); | 210 DCHECK(object); |
| 242 objects_per_class_[object->Class()].push_back(object); | 211 objects_per_class_[object->Class()].push_back(object); |
| 243 } | 212 } |
| 244 | 213 |
| 245 const std::map<PBXObjectClass, std::vector<const PBXObject*>>& | 214 const std::map<PBXObjectClass, std::vector<const PBXObject*>>& |
| (...skipping 88 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 334 config_name = config_name.substr(0, separator); | 303 config_name = config_name.substr(0, separator); |
| 335 | 304 |
| 336 std::vector<const Target*> targets; | 305 std::vector<const Target*> targets; |
| 337 std::vector<const Target*> all_targets = builder.GetAllResolvedTargets(); | 306 std::vector<const Target*> all_targets = builder.GetAllResolvedTargets(); |
| 338 if (!XcodeWriter::FilterTargets(build_settings, all_targets, | 307 if (!XcodeWriter::FilterTargets(build_settings, all_targets, |
| 339 dir_filters_string, &targets, err)) { | 308 dir_filters_string, &targets, err)) { |
| 340 return false; | 309 return false; |
| 341 } | 310 } |
| 342 | 311 |
| 343 XcodeWriter workspace(workspace_name); | 312 XcodeWriter workspace(workspace_name); |
| 344 workspace.CreateProductsProject(targets, attributes, source_path, config_name, | 313 workspace.CreateProductsProject(targets, all_targets, attributes, source_path, |
| 345 root_target_name, ninja_extra_args, | 314 config_name, root_target_name, |
| 346 build_settings, target_os); | 315 ninja_extra_args, build_settings, target_os); |
| 347 | |
| 348 workspace.CreateSourcesProject( | |
| 349 all_targets, build_settings->build_dir(), attributes, source_path, | |
| 350 build_settings->root_path_utf8(), config_name, target_os); | |
| 351 | 316 |
| 352 return workspace.WriteFiles(build_settings, err); | 317 return workspace.WriteFiles(build_settings, err); |
| 353 } | 318 } |
| 354 | 319 |
| 355 XcodeWriter::XcodeWriter(const std::string& name) : name_(name) { | 320 XcodeWriter::XcodeWriter(const std::string& name) : name_(name) { |
| 356 if (name_.empty()) | 321 if (name_.empty()) |
| 357 name_.assign("all"); | 322 name_.assign("all"); |
| 358 } | 323 } |
| 359 | 324 |
| 360 XcodeWriter::~XcodeWriter() {} | 325 XcodeWriter::~XcodeWriter() {} |
| (...skipping 59 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 420 for (const Target* target : targets) { | 385 for (const Target* target : targets) { |
| 421 if (!IsXCTestModuleTarget(target)) | 386 if (!IsXCTestModuleTarget(target)) |
| 422 continue; | 387 continue; |
| 423 | 388 |
| 424 xctest_module_targets->push_back(target); | 389 xctest_module_targets->push_back(target); |
| 425 } | 390 } |
| 426 } | 391 } |
| 427 | 392 |
| 428 void XcodeWriter::CreateProductsProject( | 393 void XcodeWriter::CreateProductsProject( |
| 429 const std::vector<const Target*>& targets, | 394 const std::vector<const Target*>& targets, |
| 395 const std::vector<const Target*>& all_targets, | |
| 430 const PBXAttributes& attributes, | 396 const PBXAttributes& attributes, |
| 431 const std::string& source_path, | 397 const std::string& source_path, |
| 432 const std::string& config_name, | 398 const std::string& config_name, |
| 433 const std::string& root_target, | 399 const std::string& root_target, |
| 434 const std::string& ninja_extra_args, | 400 const std::string& ninja_extra_args, |
| 435 const BuildSettings* build_settings, | 401 const BuildSettings* build_settings, |
| 436 TargetOsType target_os) { | 402 TargetOsType target_os) { |
| 437 std::unique_ptr<PBXProject> main_project( | 403 std::unique_ptr<PBXProject> main_project( |
| 438 new PBXProject("products", config_name, source_path, attributes)); | 404 new PBXProject("products", config_name, source_path, attributes)); |
| 405 SourceDir source_dir("//"); | |
| 406 | |
| 407 // Add all source files for indexing. | |
| 408 std::vector<SourceFile> sources; | |
| 409 for (const Target* target : all_targets) { | |
| 410 for (const SourceFile& source : target->sources()) { | |
| 411 if (IsStringInOutputDir(build_settings->build_dir(), source.value())) | |
| 412 continue; | |
| 413 | |
| 414 sources.push_back(source); | |
| 415 } | |
| 416 } | |
| 417 | |
| 418 // Sort sources to ensure determinisn of the project file generation and | |
| 419 // remove duplicate reference to the source files (can happen due to the | |
| 420 // bundle_data targets). | |
| 421 std::sort(sources.begin(), sources.end()); | |
| 422 sources.erase(std::unique(sources.begin(), sources.end()), sources.end()); | |
| 423 | |
| 424 for (const SourceFile& source : sources) { | |
| 425 std::string source_file = RebasePath(source.value(), source_dir, | |
| 426 build_settings->root_path_utf8()); | |
| 427 main_project->AddSourceFileToIndexingTarget(source_file, source_file, | |
| 428 CompilerFlags::NONE); | |
| 429 } | |
| 439 | 430 |
| 440 // Filter xctest module and application targets and find list of xctest files | 431 // Filter xctest module and application targets and find list of xctest files |
| 441 // recursively under them. | 432 // recursively under them. |
| 442 std::vector<const Target*> xctest_module_targets; | 433 std::vector<const Target*> xctest_module_targets; |
| 443 FilterXCTestModuleTargets(targets, &xctest_module_targets); | 434 FilterXCTestModuleTargets(targets, &xctest_module_targets); |
| 444 | 435 |
| 445 std::vector<const Target*> xctest_application_targets; | 436 std::vector<const Target*> xctest_application_targets; |
| 446 FindXCTestApplicationTargets(xctest_module_targets, targets, | 437 FindXCTestApplicationTargets(xctest_module_targets, targets, |
| 447 &xctest_application_targets); | 438 &xctest_application_targets); |
| 448 DCHECK_EQ(xctest_module_targets.size(), xctest_application_targets.size()); | 439 DCHECK_EQ(xctest_module_targets.size(), xctest_application_targets.size()); |
| (...skipping 46 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 495 .GetBundleRootDirOutput(target->settings()) | 486 .GetBundleRootDirOutput(target->settings()) |
| 496 .value(), | 487 .value(), |
| 497 build_settings->build_dir()), | 488 build_settings->build_dir()), |
| 498 target->bundle_data().product_type(), | 489 target->bundle_data().product_type(), |
| 499 GetBuildScript(target->label().name(), ninja_extra_args, env.get()), | 490 GetBuildScript(target->label().name(), ninja_extra_args, env.get()), |
| 500 extra_attributes); | 491 extra_attributes); |
| 501 | 492 |
| 502 if (!IsXCTestModuleTarget(target)) | 493 if (!IsXCTestModuleTarget(target)) |
| 503 continue; | 494 continue; |
| 504 | 495 |
| 505 // Populate |xctest_application_to_module_native_target| for XCTest | 496 // Add xctest files to the "Compiler Sources" of corresponding xctest |
| 506 // module targets. | 497 // native targets. |
| 507 const Target* application_target = | 498 const Target::FileList& xctest_file_list = |
| 508 FindXCTestApplicationTarget(target, xctest_application_targets); | 499 xctest_file_lists[std::find(xctest_module_targets.begin(), |
|
sdefresne
2017/01/17 10:08:40
nit: I find this line really confusing (I had to r
liaoyuke
2017/01/17 18:35:19
Done.
| |
| 509 xctest_application_to_module_native_target.insert( | 500 xctest_module_targets.end(), target) - |
| 510 std::make_pair(application_target, native_target)); | 501 xctest_module_targets.begin()]; |
| 502 for (const SourceFile& source : xctest_file_list) { | |
| 503 std::string source_path = RebasePath( | |
| 504 source.value(), source_dir, build_settings->root_path_utf8()); | |
| 505 | |
| 506 // Test files need to be known to Xcode for proper indexing and for | |
| 507 // discovery of tests function for XCTest, but the compilation is done | |
| 508 // via ninja and thus must prevent Xcode from compiling the files by | |
| 509 // adding '-help' as per file compiler flag. | |
| 510 main_project->AddSourceFile(source_path, source_path, | |
| 511 CompilerFlags::HELP, native_target); | |
| 512 } | |
| 511 | 513 |
| 512 break; | 514 break; |
| 513 } | 515 } |
| 514 | 516 |
| 515 default: | 517 default: |
| 516 break; | 518 break; |
| 517 } | 519 } |
| 518 } | 520 } |
| 519 | 521 |
| 520 FileToTargets xctest_file_to_application_targets(CompareSourceFiles); | |
| 521 MapXCTestFileToApplicationTargets(xctest_application_targets, | |
| 522 xctest_file_lists, | |
| 523 &xctest_file_to_application_targets); | |
| 524 | |
| 525 // Add xctest files to the "Compiler Sources" of corresponding xctest native | |
| 526 // targets. | |
| 527 SourceDir source_dir("//"); | |
| 528 for (const auto& item : xctest_file_to_application_targets) { | |
| 529 const SourceFile& source = item.first; | |
| 530 for (const Target* xctest_application_target : item.second) { | |
| 531 std::string navigator_path = | |
| 532 kXCTestFileReferenceFolder + source.GetName(); | |
| 533 std::string source_path = RebasePath(source.value(), source_dir, | |
| 534 build_settings->root_path_utf8()); | |
| 535 PBXNativeTarget* xctest_module_native_target = | |
| 536 xctest_application_to_module_native_target[xctest_application_target]; | |
| 537 | |
| 538 // Test files need to be known to Xcode for proper indexing and for | |
| 539 // discovery of tests function for XCTest, but the compilation is done | |
| 540 // via ninja and thus must prevent Xcode from compiling the files by | |
| 541 // adding '-help' as per file compiler flag. | |
| 542 main_project->AddSourceFile(navigator_path, source_path, | |
| 543 CompilerFlags::HELP, | |
| 544 xctest_module_native_target); | |
| 545 } | |
| 546 } | |
| 547 | |
| 548 projects_.push_back(std::move(main_project)); | 522 projects_.push_back(std::move(main_project)); |
| 549 } | 523 } |
| 550 | 524 |
| 551 void XcodeWriter::CreateSourcesProject( | |
| 552 const std::vector<const Target*>& targets, | |
| 553 const SourceDir& root_build_dir, | |
| 554 const PBXAttributes& attributes, | |
| 555 const std::string& source_path, | |
| 556 const std::string& absolute_source_path, | |
| 557 const std::string& config_name, | |
| 558 TargetOsType target_os) { | |
| 559 std::vector<SourceFile> sources; | |
| 560 for (const Target* target : targets) { | |
| 561 for (const SourceFile& source : target->sources()) { | |
| 562 if (IsStringInOutputDir(root_build_dir, source.value())) | |
| 563 continue; | |
| 564 | |
| 565 sources.push_back(source); | |
| 566 } | |
| 567 } | |
| 568 | |
| 569 std::unique_ptr<PBXProject> sources_for_indexing( | |
| 570 new PBXProject("sources", config_name, source_path, attributes)); | |
| 571 | |
| 572 // Sort sources to ensure determinisn of the project file generation and | |
| 573 // remove duplicate reference to the source files (can happen due to the | |
| 574 // bundle_data targets). | |
| 575 std::sort(sources.begin(), sources.end()); | |
| 576 sources.erase(std::unique(sources.begin(), sources.end()), sources.end()); | |
| 577 | |
| 578 SourceDir source_dir("//"); | |
| 579 for (const SourceFile& source : sources) { | |
| 580 std::string source_file = | |
| 581 RebasePath(source.value(), source_dir, absolute_source_path); | |
| 582 sources_for_indexing->AddSourceFileToIndexingTarget( | |
| 583 source_file, source_file, CompilerFlags::NONE); | |
| 584 } | |
| 585 | |
| 586 projects_.push_back(std::move(sources_for_indexing)); | |
| 587 } | |
| 588 | |
| 589 bool XcodeWriter::WriteFiles(const BuildSettings* build_settings, Err* err) { | 525 bool XcodeWriter::WriteFiles(const BuildSettings* build_settings, Err* err) { |
| 590 for (const auto& project : projects_) { | 526 for (const auto& project : projects_) { |
| 591 if (!WriteProjectFile(build_settings, project.get(), err)) | 527 if (!WriteProjectFile(build_settings, project.get(), err)) |
| 592 return false; | 528 return false; |
| 593 } | 529 } |
| 594 | 530 |
| 595 SourceFile xcworkspacedata_file = | 531 SourceFile xcworkspacedata_file = |
| 596 build_settings->build_dir().ResolveRelativeFile( | 532 build_settings->build_dir().ResolveRelativeFile( |
| 597 Value(nullptr, name_ + ".xcworkspace/contents.xcworkspacedata"), err); | 533 Value(nullptr, name_ + ".xcworkspace/contents.xcworkspacedata"), err); |
| 598 if (xcworkspacedata_file.is_null()) | 534 if (xcworkspacedata_file.is_null()) |
| (...skipping 55 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 654 for (auto* object : pair.second) { | 590 for (auto* object : pair.second) { |
| 655 object->Print(out, 2); | 591 object->Print(out, 2); |
| 656 } | 592 } |
| 657 out << "/* End " << ToString(pair.first) << " section */\n"; | 593 out << "/* End " << ToString(pair.first) << " section */\n"; |
| 658 } | 594 } |
| 659 | 595 |
| 660 out << "\t};\n" | 596 out << "\t};\n" |
| 661 << "\trootObject = " << project->Reference() << ";\n" | 597 << "\trootObject = " << project->Reference() << ";\n" |
| 662 << "}\n"; | 598 << "}\n"; |
| 663 } | 599 } |
| OLD | NEW |