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 |