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
3 // found in the LICENSE file.
4
5 #include "tools/gn/xcode_object.h"
6
7 #include <iomanip>
8 #include <memory>
9 #include <sstream>
10 #include <utility>
11
12 #include "base/logging.h"
13 #include "base/macros.h"
14 #include "base/strings/string_util.h"
15 #include "tools/gn/filesystem_utils.h"
16
17 // Helper methods -------------------------------------------------------------
18
19 namespace {
20 struct IndentRules {
21 bool one_line;
22 unsigned level;
23 };
24
EmptyPBXObjectVector()25 std::vector<std::unique_ptr<PBXObject>> EmptyPBXObjectVector() {
26 return std::vector<std::unique_ptr<PBXObject>>();
27 }
28
CharNeedEscaping(char c)29 bool CharNeedEscaping(char c) {
30 if (base::IsAsciiAlpha(c) || base::IsAsciiDigit(c))
31 return false;
32 if (c == '$' || c == '.' || c == '/' || c == '_')
33 return false;
34 return true;
35 }
36
StringNeedEscaping(const std::string & string)37 bool StringNeedEscaping(const std::string& string) {
38 if (string.empty())
39 return true;
40 if (string.find("___") != std::string::npos)
41 return true;
42
43 for (char c : string) {
44 if (CharNeedEscaping(c))
45 return true;
46 }
47 return false;
48 }
49
EncodeString(const std::string & string)50 std::string EncodeString(const std::string& string) {
51 if (!StringNeedEscaping(string))
52 return string;
53
54 std::stringstream buffer;
55 buffer << '"';
56 for (char c : string) {
57 if (c <= 31) {
58 switch (c) {
59 case '\a':
60 buffer << "\\a";
61 break;
62 case '\b':
63 buffer << "\\b";
64 break;
65 case '\t':
66 buffer << "\\t";
67 break;
68 case '\n':
69 case '\r':
70 buffer << "\\n";
71 break;
72 case '\v':
73 buffer << "\\v";
74 break;
75 case '\f':
76 buffer << "\\f";
77 break;
78 default:
79 buffer << std::hex << std::setw(4) << std::left << "\\U"
80 << static_cast<unsigned>(c);
81 break;
82 }
83 } else {
84 if (c == '"' || c == '\\')
85 buffer << '\\';
86 buffer << c;
87 }
88 }
89 buffer << '"';
90 return buffer.str();
91 }
92
93 struct SourceTypeForExt {
94 const char* ext;
95 const char* source_type;
96 };
97
98 const SourceTypeForExt kSourceTypeForExt[] = {
99 {"a", "archive.ar"},
100 {"app", "wrapper.application"},
101 {"appex", "wrapper.app-extension"},
102 {"bdic", "file"},
103 {"bundle", "wrapper.cfbundle"},
104 {"c", "sourcecode.c.c"},
105 {"cc", "sourcecode.cpp.cpp"},
106 {"cpp", "sourcecode.cpp.cpp"},
107 {"css", "text.css"},
108 {"cxx", "sourcecode.cpp.cpp"},
109 {"dart", "sourcecode"},
110 {"dylib", "compiled.mach-o.dylib"},
111 {"framework", "wrapper.framework"},
112 {"h", "sourcecode.c.h"},
113 {"hxx", "sourcecode.cpp.h"},
114 {"icns", "image.icns"},
115 {"java", "sourcecode.java"},
116 {"js", "sourcecode.javascript"},
117 {"kext", "wrapper.kext"},
118 {"m", "sourcecode.c.objc"},
119 {"mm", "sourcecode.cpp.objcpp"},
120 {"nib", "wrapper.nib"},
121 {"o", "compiled.mach-o.objfile"},
122 {"pdf", "image.pdf"},
123 {"pl", "text.script.perl"},
124 {"plist", "text.plist.xml"},
125 {"pm", "text.script.perl"},
126 {"png", "image.png"},
127 {"py", "text.script.python"},
128 {"r", "sourcecode.rez"},
129 {"rez", "sourcecode.rez"},
130 {"s", "sourcecode.asm"},
131 {"storyboard", "file.storyboard"},
132 {"strings", "text.plist.strings"},
133 {"swift", "sourcecode.swift"},
134 {"ttf", "file"},
135 {"xcassets", "folder.assetcatalog"},
136 {"xcconfig", "text.xcconfig"},
137 {"xcdatamodel", "wrapper.xcdatamodel"},
138 {"xcdatamodeld", "wrapper.xcdatamodeld"},
139 {"xib", "file.xib"},
140 {"y", "sourcecode.yacc"},
141 };
142
GetSourceType(const base::StringPiece & ext)143 const char* GetSourceType(const base::StringPiece& ext) {
144 for (size_t i = 0; i < arraysize(kSourceTypeForExt); ++i) {
145 if (kSourceTypeForExt[i].ext == ext)
146 return kSourceTypeForExt[i].source_type;
147 }
148
149 return "text";
150 }
151
HasExplicitFileType(const base::StringPiece & ext)152 bool HasExplicitFileType(const base::StringPiece& ext) {
153 return ext == "dart";
154 }
155
IsSourceFileForIndexing(const base::StringPiece & ext)156 bool IsSourceFileForIndexing(const base::StringPiece& ext) {
157 return ext == "c" || ext == "cc" || ext == "cpp" || ext == "cxx" ||
158 ext == "m" || ext == "mm";
159 }
160
PrintValue(std::ostream & out,IndentRules rules,unsigned value)161 void PrintValue(std::ostream& out, IndentRules rules, unsigned value) {
162 out << value;
163 }
164
PrintValue(std::ostream & out,IndentRules rules,const char * value)165 void PrintValue(std::ostream& out, IndentRules rules, const char* value) {
166 out << EncodeString(value);
167 }
168
PrintValue(std::ostream & out,IndentRules rules,const std::string & value)169 void PrintValue(std::ostream& out,
170 IndentRules rules,
171 const std::string& value) {
172 out << EncodeString(value);
173 }
174
PrintValue(std::ostream & out,IndentRules rules,const PBXObject * value)175 void PrintValue(std::ostream& out, IndentRules rules, const PBXObject* value) {
176 out << value->Reference();
177 }
178
179 template <typename ObjectClass>
PrintValue(std::ostream & out,IndentRules rules,const std::unique_ptr<ObjectClass> & value)180 void PrintValue(std::ostream& out,
181 IndentRules rules,
182 const std::unique_ptr<ObjectClass>& value) {
183 PrintValue(out, rules, value.get());
184 }
185
186 template <typename ValueType>
PrintValue(std::ostream & out,IndentRules rules,const std::vector<ValueType> & values)187 void PrintValue(std::ostream& out,
188 IndentRules rules,
189 const std::vector<ValueType>& values) {
190 IndentRules sub_rule{rules.one_line, rules.level + 1};
191 out << "(" << (rules.one_line ? " " : "\n");
192 for (const auto& value : values) {
193 if (!sub_rule.one_line)
194 out << std::string(sub_rule.level, '\t');
195
196 PrintValue(out, sub_rule, value);
197 out << "," << (rules.one_line ? " " : "\n");
198 }
199
200 if (!rules.one_line && rules.level)
201 out << std::string(rules.level, '\t');
202 out << ")";
203 }
204
205 template <typename ValueType>
PrintValue(std::ostream & out,IndentRules rules,const std::map<std::string,ValueType> & values)206 void PrintValue(std::ostream& out,
207 IndentRules rules,
208 const std::map<std::string, ValueType>& values) {
209 IndentRules sub_rule{rules.one_line, rules.level + 1};
210 out << "{" << (rules.one_line ? " " : "\n");
211 for (const auto& pair : values) {
212 if (!sub_rule.one_line)
213 out << std::string(sub_rule.level, '\t');
214
215 out << pair.first << " = ";
216 PrintValue(out, sub_rule, pair.second);
217 out << ";" << (rules.one_line ? " " : "\n");
218 }
219
220 if (!rules.one_line && rules.level)
221 out << std::string(rules.level, '\t');
222 out << "}";
223 }
224
225 template <typename ValueType>
PrintProperty(std::ostream & out,IndentRules rules,const char * name,ValueType && value)226 void PrintProperty(std::ostream& out,
227 IndentRules rules,
228 const char* name,
229 ValueType&& value) {
230 if (!rules.one_line && rules.level)
231 out << std::string(rules.level, '\t');
232
233 out << name << " = ";
234 PrintValue(out, rules, std::forward<ValueType>(value));
235 out << ";" << (rules.one_line ? " " : "\n");
236 }
237 } // namespace
238
239 // PBXObjectClass -------------------------------------------------------------
240
ToString(PBXObjectClass cls)241 const char* ToString(PBXObjectClass cls) {
242 switch (cls) {
243 case PBXAggregateTargetClass:
244 return "PBXAggregateTarget";
245 case PBXBuildFileClass:
246 return "PBXBuildFile";
247 case PBXContainerItemProxyClass:
248 return "PBXContainerItemProxy";
249 case PBXFileReferenceClass:
250 return "PBXFileReference";
251 case PBXFrameworksBuildPhaseClass:
252 return "PBXFrameworksBuildPhase";
253 case PBXGroupClass:
254 return "PBXGroup";
255 case PBXNativeTargetClass:
256 return "PBXNativeTarget";
257 case PBXProjectClass:
258 return "PBXProject";
259 case PBXShellScriptBuildPhaseClass:
260 return "PBXShellScriptBuildPhase";
261 case PBXSourcesBuildPhaseClass:
262 return "PBXSourcesBuildPhase";
263 case PBXTargetDependencyClass:
264 return "PBXTargetDependency";
265 case XCBuildConfigurationClass:
266 return "XCBuildConfiguration";
267 case XCConfigurationListClass:
268 return "XCConfigurationList";
269 }
270 NOTREACHED();
271 return nullptr;
272 }
273
274 // PBXObjectVisitor -----------------------------------------------------------
275
276 PBXObjectVisitor::PBXObjectVisitor() = default;
277
278 PBXObjectVisitor::~PBXObjectVisitor() = default;
279
280 // PBXObject ------------------------------------------------------------------
281
282 PBXObject::PBXObject() = default;
283
284 PBXObject::~PBXObject() = default;
285
SetId(const std::string & id)286 void PBXObject::SetId(const std::string& id) {
287 DCHECK(id_.empty());
288 DCHECK(!id.empty());
289 id_.assign(id);
290 }
291
Reference() const292 std::string PBXObject::Reference() const {
293 std::string comment = Comment();
294 if (comment.empty())
295 return id_;
296
297 return id_ + " /* " + comment + " */";
298 }
299
Comment() const300 std::string PBXObject::Comment() const {
301 return Name();
302 }
303
Visit(PBXObjectVisitor & visitor)304 void PBXObject::Visit(PBXObjectVisitor& visitor) {
305 visitor.Visit(this);
306 }
307
308 // PBXBuildPhase --------------------------------------------------------------
309
310 PBXBuildPhase::PBXBuildPhase() = default;
311
312 PBXBuildPhase::~PBXBuildPhase() = default;
313
314 // PBXTarget ------------------------------------------------------------------
315
PBXTarget(const std::string & name,const std::string & shell_script,const std::string & config_name,const PBXAttributes & attributes)316 PBXTarget::PBXTarget(const std::string& name,
317 const std::string& shell_script,
318 const std::string& config_name,
319 const PBXAttributes& attributes)
320 : configurations_(new XCConfigurationList(config_name, attributes, this)),
321 name_(name) {
322 if (!shell_script.empty()) {
323 build_phases_.push_back(
324 std::make_unique<PBXShellScriptBuildPhase>(name, shell_script));
325 }
326 }
327
328 PBXTarget::~PBXTarget() = default;
329
AddDependency(std::unique_ptr<PBXTargetDependency> dependency)330 void PBXTarget::AddDependency(std::unique_ptr<PBXTargetDependency> dependency) {
331 DCHECK(dependency);
332 dependencies_.push_back(std::move(dependency));
333 }
334
Name() const335 std::string PBXTarget::Name() const {
336 return name_;
337 }
338
Visit(PBXObjectVisitor & visitor)339 void PBXTarget::Visit(PBXObjectVisitor& visitor) {
340 PBXObject::Visit(visitor);
341 configurations_->Visit(visitor);
342 for (const auto& dependency : dependencies_)
343 dependency->Visit(visitor);
344 for (const auto& build_phase : build_phases_)
345 build_phase->Visit(visitor);
346 }
347
348 // PBXAggregateTarget ---------------------------------------------------------
349
PBXAggregateTarget(const std::string & name,const std::string & shell_script,const std::string & config_name,const PBXAttributes & attributes)350 PBXAggregateTarget::PBXAggregateTarget(const std::string& name,
351 const std::string& shell_script,
352 const std::string& config_name,
353 const PBXAttributes& attributes)
354 : PBXTarget(name, shell_script, config_name, attributes) {}
355
356 PBXAggregateTarget::~PBXAggregateTarget() = default;
357
Class() const358 PBXObjectClass PBXAggregateTarget::Class() const {
359 return PBXAggregateTargetClass;
360 }
361
Print(std::ostream & out,unsigned indent) const362 void PBXAggregateTarget::Print(std::ostream& out, unsigned indent) const {
363 const std::string indent_str(indent, '\t');
364 const IndentRules rules = {false, indent + 1};
365 out << indent_str << Reference() << " = {\n";
366 PrintProperty(out, rules, "isa", ToString(Class()));
367 PrintProperty(out, rules, "buildConfigurationList", configurations_);
368 PrintProperty(out, rules, "buildPhases", build_phases_);
369 PrintProperty(out, rules, "dependencies", EmptyPBXObjectVector());
370 PrintProperty(out, rules, "name", name_);
371 PrintProperty(out, rules, "productName", name_);
372 out << indent_str << "};\n";
373 }
374
375 // PBXBuildFile ---------------------------------------------------------------
376
PBXBuildFile(const PBXFileReference * file_reference,const PBXSourcesBuildPhase * build_phase,const CompilerFlags compiler_flag)377 PBXBuildFile::PBXBuildFile(const PBXFileReference* file_reference,
378 const PBXSourcesBuildPhase* build_phase,
379 const CompilerFlags compiler_flag)
380 : file_reference_(file_reference),
381 build_phase_(build_phase),
382 compiler_flag_(compiler_flag) {
383 DCHECK(file_reference_);
384 DCHECK(build_phase_);
385 }
386
387 PBXBuildFile::~PBXBuildFile() = default;
388
Class() const389 PBXObjectClass PBXBuildFile::Class() const {
390 return PBXBuildFileClass;
391 }
392
Name() const393 std::string PBXBuildFile::Name() const {
394 return file_reference_->Name() + " in " + build_phase_->Name();
395 }
396
Print(std::ostream & out,unsigned indent) const397 void PBXBuildFile::Print(std::ostream& out, unsigned indent) const {
398 const std::string indent_str(indent, '\t');
399 const IndentRules rules = {true, 0};
400 out << indent_str << Reference() << " = {";
401 PrintProperty(out, rules, "isa", ToString(Class()));
402 PrintProperty(out, rules, "fileRef", file_reference_);
403 if (compiler_flag_ == CompilerFlags::HELP) {
404 std::map<std::string, std::string> settings = {
405 {"COMPILER_FLAGS", "--help"},
406 };
407 PrintProperty(out, rules, "settings", settings);
408 }
409 out << "};\n";
410 }
411
412 // PBXContainerItemProxy ------------------------------------------------------
PBXContainerItemProxy(const PBXProject * project,const PBXTarget * target)413 PBXContainerItemProxy::PBXContainerItemProxy(const PBXProject* project,
414 const PBXTarget* target)
415 : project_(project), target_(target) {}
416
417 PBXContainerItemProxy::~PBXContainerItemProxy() = default;
418
Class() const419 PBXObjectClass PBXContainerItemProxy::Class() const {
420 return PBXContainerItemProxyClass;
421 }
422
Visit(PBXObjectVisitor & visitor)423 void PBXContainerItemProxy::Visit(PBXObjectVisitor& visitor) {
424 PBXObject::Visit(visitor);
425 }
426
Name() const427 std::string PBXContainerItemProxy::Name() const {
428 return "PBXContainerItemProxy";
429 }
430
Print(std::ostream & out,unsigned indent) const431 void PBXContainerItemProxy::Print(std::ostream& out, unsigned indent) const {
432 const std::string indent_str(indent, '\t');
433 const IndentRules rules = {true, 0};
434 out << indent_str << Reference() << " = {";
435 PrintProperty(out, rules, "isa", ToString(Class()));
436 PrintProperty(out, rules, "containerPortal", project_);
437 PrintProperty(out, rules, "proxyType", 1u);
438 PrintProperty(out, rules, "remoteGlobalIDString", target_);
439 PrintProperty(out, rules, "remoteInfo", target_->Name());
440 out << indent_str << "};\n";
441 }
442
443 // PBXFileReference -----------------------------------------------------------
444
PBXFileReference(const std::string & name,const std::string & path,const std::string & type)445 PBXFileReference::PBXFileReference(const std::string& name,
446 const std::string& path,
447 const std::string& type)
448 : name_(name), path_(path), type_(type) {}
449
450 PBXFileReference::~PBXFileReference() = default;
451
Class() const452 PBXObjectClass PBXFileReference::Class() const {
453 return PBXFileReferenceClass;
454 }
455
Name() const456 std::string PBXFileReference::Name() const {
457 return name_;
458 }
459
Print(std::ostream & out,unsigned indent) const460 void PBXFileReference::Print(std::ostream& out, unsigned indent) const {
461 const std::string indent_str(indent, '\t');
462 const IndentRules rules = {true, 0};
463 out << indent_str << Reference() << " = {";
464 PrintProperty(out, rules, "isa", ToString(Class()));
465
466 if (!type_.empty()) {
467 PrintProperty(out, rules, "explicitFileType", type_);
468 PrintProperty(out, rules, "includeInIndex", 0u);
469 } else {
470 base::StringPiece ext = FindExtension(&name_);
471 if (HasExplicitFileType(ext))
472 PrintProperty(out, rules, "explicitFileType", GetSourceType(ext));
473 else
474 PrintProperty(out, rules, "lastKnownFileType", GetSourceType(ext));
475 }
476
477 if (!name_.empty())
478 PrintProperty(out, rules, "name", name_);
479
480 DCHECK(!path_.empty());
481 PrintProperty(out, rules, "path", path_);
482 PrintProperty(out, rules, "sourceTree",
483 type_.empty() ? "<group>" : "BUILT_PRODUCTS_DIR");
484 out << "};\n";
485 }
486
487 // PBXFrameworksBuildPhase ----------------------------------------------------
488
489 PBXFrameworksBuildPhase::PBXFrameworksBuildPhase() = default;
490
491 PBXFrameworksBuildPhase::~PBXFrameworksBuildPhase() = default;
492
Class() const493 PBXObjectClass PBXFrameworksBuildPhase::Class() const {
494 return PBXFrameworksBuildPhaseClass;
495 }
496
Name() const497 std::string PBXFrameworksBuildPhase::Name() const {
498 return "Frameworks";
499 }
500
Print(std::ostream & out,unsigned indent) const501 void PBXFrameworksBuildPhase::Print(std::ostream& out, unsigned indent) const {
502 const std::string indent_str(indent, '\t');
503 const IndentRules rules = {false, indent + 1};
504 out << indent_str << Reference() << " = {\n";
505 PrintProperty(out, rules, "isa", ToString(Class()));
506 PrintProperty(out, rules, "buildActionMask", 0x7fffffffu);
507 PrintProperty(out, rules, "files", EmptyPBXObjectVector());
508 PrintProperty(out, rules, "runOnlyForDeploymentPostprocessing", 0u);
509 out << indent_str << "};\n";
510 }
511
512 // PBXGroup -------------------------------------------------------------------
513
PBXGroup(const std::string & path,const std::string & name)514 PBXGroup::PBXGroup(const std::string& path, const std::string& name)
515 : name_(name), path_(path) {}
516
517 PBXGroup::~PBXGroup() = default;
518
AddChild(std::unique_ptr<PBXObject> child)519 PBXObject* PBXGroup::AddChild(std::unique_ptr<PBXObject> child) {
520 DCHECK(child);
521 children_.push_back(std::move(child));
522 return children_.back().get();
523 }
524
AddSourceFile(const std::string & navigator_path,const std::string & source_path)525 PBXFileReference* PBXGroup::AddSourceFile(const std::string& navigator_path,
526 const std::string& source_path) {
527 DCHECK(!navigator_path.empty());
528 DCHECK(!source_path.empty());
529 std::string::size_type sep = navigator_path.find("/");
530 if (sep == std::string::npos) {
531 // Prevent same file reference being created and added multiple times.
532 for (const auto& child : children_) {
533 if (child->Class() != PBXFileReferenceClass)
534 continue;
535
536 PBXFileReference* child_as_file_reference =
537 static_cast<PBXFileReference*>(child.get());
538 if (child_as_file_reference->Name() == navigator_path &&
539 child_as_file_reference->path() == source_path) {
540 return child_as_file_reference;
541 }
542 }
543
544 children_.push_back(std::make_unique<PBXFileReference>(
545 navigator_path, source_path, std::string()));
546 return static_cast<PBXFileReference*>(children_.back().get());
547 }
548
549 PBXGroup* group = nullptr;
550 base::StringPiece component(navigator_path.data(), sep);
551 for (const auto& child : children_) {
552 if (child->Class() != PBXGroupClass)
553 continue;
554
555 PBXGroup* child_as_group = static_cast<PBXGroup*>(child.get());
556 if (child_as_group->name_ == component) {
557 group = child_as_group;
558 break;
559 }
560 }
561
562 if (!group) {
563 children_.push_back(std::make_unique<PBXGroup>(component.as_string(),
564 component.as_string()));
565 group = static_cast<PBXGroup*>(children_.back().get());
566 }
567
568 DCHECK(group);
569 DCHECK(group->name_ == component);
570 return group->AddSourceFile(navigator_path.substr(sep + 1), source_path);
571 }
572
Class() const573 PBXObjectClass PBXGroup::Class() const {
574 return PBXGroupClass;
575 }
576
Name() const577 std::string PBXGroup::Name() const {
578 if (!name_.empty())
579 return name_;
580 if (!path_.empty())
581 return path_;
582 return std::string();
583 }
584
Visit(PBXObjectVisitor & visitor)585 void PBXGroup::Visit(PBXObjectVisitor& visitor) {
586 PBXObject::Visit(visitor);
587 for (const auto& child : children_) {
588 child->Visit(visitor);
589 }
590 }
591
Print(std::ostream & out,unsigned indent) const592 void PBXGroup::Print(std::ostream& out, unsigned indent) const {
593 const std::string indent_str(indent, '\t');
594 const IndentRules rules = {false, indent + 1};
595 out << indent_str << Reference() << " = {\n";
596 PrintProperty(out, rules, "isa", ToString(Class()));
597 PrintProperty(out, rules, "children", children_);
598 if (!name_.empty())
599 PrintProperty(out, rules, "name", name_);
600 if (is_source_ && !path_.empty())
601 PrintProperty(out, rules, "path", path_);
602 PrintProperty(out, rules, "sourceTree", "<group>");
603 out << indent_str << "};\n";
604 }
605
606 // PBXNativeTarget ------------------------------------------------------------
607
PBXNativeTarget(const std::string & name,const std::string & shell_script,const std::string & config_name,const PBXAttributes & attributes,const std::string & product_type,const std::string & product_name,const PBXFileReference * product_reference)608 PBXNativeTarget::PBXNativeTarget(const std::string& name,
609 const std::string& shell_script,
610 const std::string& config_name,
611 const PBXAttributes& attributes,
612 const std::string& product_type,
613 const std::string& product_name,
614 const PBXFileReference* product_reference)
615 : PBXTarget(name, shell_script, config_name, attributes),
616 product_reference_(product_reference),
617 product_type_(product_type),
618 product_name_(product_name) {
619 DCHECK(product_reference_);
620 build_phases_.push_back(std::make_unique<PBXSourcesBuildPhase>());
621 source_build_phase_ =
622 static_cast<PBXSourcesBuildPhase*>(build_phases_.back().get());
623
624 build_phases_.push_back(std::make_unique<PBXFrameworksBuildPhase>());
625 }
626
627 PBXNativeTarget::~PBXNativeTarget() = default;
628
AddFileForIndexing(const PBXFileReference * file_reference,const CompilerFlags compiler_flag)629 void PBXNativeTarget::AddFileForIndexing(const PBXFileReference* file_reference,
630 const CompilerFlags compiler_flag) {
631 DCHECK(file_reference);
632 source_build_phase_->AddBuildFile(std::make_unique<PBXBuildFile>(
633 file_reference, source_build_phase_, compiler_flag));
634 }
635
Class() const636 PBXObjectClass PBXNativeTarget::Class() const {
637 return PBXNativeTargetClass;
638 }
639
Print(std::ostream & out,unsigned indent) const640 void PBXNativeTarget::Print(std::ostream& out, unsigned indent) const {
641 const std::string indent_str(indent, '\t');
642 const IndentRules rules = {false, indent + 1};
643 out << indent_str << Reference() << " = {\n";
644 PrintProperty(out, rules, "isa", ToString(Class()));
645 PrintProperty(out, rules, "buildConfigurationList", configurations_);
646 PrintProperty(out, rules, "buildPhases", build_phases_);
647 PrintProperty(out, rules, "buildRules", EmptyPBXObjectVector());
648 PrintProperty(out, rules, "dependencies", dependencies_);
649 PrintProperty(out, rules, "name", name_);
650 PrintProperty(out, rules, "productName", product_name_);
651 PrintProperty(out, rules, "productReference", product_reference_);
652 PrintProperty(out, rules, "productType", product_type_);
653 out << indent_str << "};\n";
654 }
655
656 // PBXProject -----------------------------------------------------------------
657
PBXProject(const std::string & name,const std::string & config_name,const std::string & source_path,const PBXAttributes & attributes)658 PBXProject::PBXProject(const std::string& name,
659 const std::string& config_name,
660 const std::string& source_path,
661 const PBXAttributes& attributes)
662 : name_(name), config_name_(config_name), target_for_indexing_(nullptr) {
663 attributes_["BuildIndependentTargetsInParallel"] = "YES";
664
665 main_group_.reset(new PBXGroup);
666 sources_ = static_cast<PBXGroup*>(
667 main_group_->AddChild(std::make_unique<PBXGroup>(source_path, "Source")));
668 sources_->set_is_source(true);
669 products_ = static_cast<PBXGroup*>(main_group_->AddChild(
670 std::make_unique<PBXGroup>(std::string(), "Product")));
671 main_group_->AddChild(std::make_unique<PBXGroup>(std::string(), "Build"));
672
673 configurations_.reset(new XCConfigurationList(config_name, attributes, this));
674 }
675
676 PBXProject::~PBXProject() = default;
677
AddSourceFileToIndexingTarget(const std::string & navigator_path,const std::string & source_path,const CompilerFlags compiler_flag)678 void PBXProject::AddSourceFileToIndexingTarget(
679 const std::string& navigator_path,
680 const std::string& source_path,
681 const CompilerFlags compiler_flag) {
682 if (!target_for_indexing_) {
683 AddIndexingTarget();
684 }
685 AddSourceFile(navigator_path, source_path, compiler_flag,
686 target_for_indexing_);
687 }
688
AddSourceFile(const std::string & navigator_path,const std::string & source_path,const CompilerFlags compiler_flag,PBXNativeTarget * target)689 void PBXProject::AddSourceFile(const std::string& navigator_path,
690 const std::string& source_path,
691 const CompilerFlags compiler_flag,
692 PBXNativeTarget* target) {
693 PBXFileReference* file_reference =
694 sources_->AddSourceFile(navigator_path, source_path);
695 base::StringPiece ext = FindExtension(&source_path);
696 if (!IsSourceFileForIndexing(ext))
697 return;
698
699 DCHECK(target);
700 target->AddFileForIndexing(file_reference, compiler_flag);
701 }
702
AddAggregateTarget(const std::string & name,const std::string & shell_script)703 void PBXProject::AddAggregateTarget(const std::string& name,
704 const std::string& shell_script) {
705 PBXAttributes attributes;
706 attributes["CODE_SIGNING_REQUIRED"] = "NO";
707 attributes["CONFIGURATION_BUILD_DIR"] = ".";
708 attributes["PRODUCT_NAME"] = name;
709
710 targets_.push_back(std::make_unique<PBXAggregateTarget>(
711 name, shell_script, config_name_, attributes));
712 }
713
AddIndexingTarget()714 void PBXProject::AddIndexingTarget() {
715 DCHECK(!target_for_indexing_);
716 PBXAttributes attributes;
717 attributes["EXECUTABLE_PREFIX"] = "";
718 attributes["HEADER_SEARCH_PATHS"] = sources_->path();
719 attributes["PRODUCT_NAME"] = "sources";
720
721 PBXFileReference* product_reference = static_cast<PBXFileReference*>(
722 products_->AddChild(std::make_unique<PBXFileReference>(
723 std::string(), "sources", "compiled.mach-o.executable")));
724
725 const char product_type[] = "com.apple.product-type.tool";
726 targets_.push_back(std::make_unique<PBXNativeTarget>(
727 "sources", std::string(), config_name_, attributes, product_type,
728 "sources", product_reference));
729 target_for_indexing_ = static_cast<PBXNativeTarget*>(targets_.back().get());
730 }
731
AddNativeTarget(const std::string & name,const std::string & type,const std::string & output_name,const std::string & output_type,const std::string & shell_script,const PBXAttributes & extra_attributes)732 PBXNativeTarget* PBXProject::AddNativeTarget(
733 const std::string& name,
734 const std::string& type,
735 const std::string& output_name,
736 const std::string& output_type,
737 const std::string& shell_script,
738 const PBXAttributes& extra_attributes) {
739 base::StringPiece ext = FindExtension(&output_name);
740 PBXFileReference* product = static_cast<PBXFileReference*>(
741 products_->AddChild(std::make_unique<PBXFileReference>(
742 std::string(), output_name,
743 type.empty() ? GetSourceType(ext) : type)));
744
745 // Per Xcode build settings documentation: Product Name (PRODUCT_NAME) should
746 // the basename of the product generated by the target.
747 // Therefore, take the basename of output name without file extension as the
748 // "PRODUCT_NAME".
749 size_t basename_offset = FindFilenameOffset(output_name);
750 std::string output_basename = basename_offset != std::string::npos
751 ? output_name.substr(basename_offset)
752 : output_name;
753 size_t ext_offset = FindExtensionOffset(output_basename);
754 std::string product_name = ext_offset != std::string::npos
755 ? output_basename.substr(0, ext_offset - 1)
756 : output_basename;
757
758 PBXAttributes attributes = extra_attributes;
759 attributes["CODE_SIGNING_REQUIRED"] = "NO";
760 attributes["CONFIGURATION_BUILD_DIR"] = ".";
761 attributes["PRODUCT_NAME"] = product_name;
762
763 targets_.push_back(std::make_unique<PBXNativeTarget>(
764 name, shell_script, config_name_, attributes, output_type, product_name,
765 product));
766 return static_cast<PBXNativeTarget*>(targets_.back().get());
767 }
768
SetProjectDirPath(const std::string & project_dir_path)769 void PBXProject::SetProjectDirPath(const std::string& project_dir_path) {
770 DCHECK(!project_dir_path.empty());
771 project_dir_path_.assign(project_dir_path);
772 }
773
SetProjectRoot(const std::string & project_root)774 void PBXProject::SetProjectRoot(const std::string& project_root) {
775 DCHECK(!project_root.empty());
776 project_root_.assign(project_root);
777 }
778
AddTarget(std::unique_ptr<PBXTarget> target)779 void PBXProject::AddTarget(std::unique_ptr<PBXTarget> target) {
780 DCHECK(target);
781 targets_.push_back(std::move(target));
782 }
783
Class() const784 PBXObjectClass PBXProject::Class() const {
785 return PBXProjectClass;
786 }
787
Name() const788 std::string PBXProject::Name() const {
789 return name_;
790 }
791
Comment() const792 std::string PBXProject::Comment() const {
793 return "Project object";
794 }
795
Visit(PBXObjectVisitor & visitor)796 void PBXProject::Visit(PBXObjectVisitor& visitor) {
797 PBXObject::Visit(visitor);
798 configurations_->Visit(visitor);
799 main_group_->Visit(visitor);
800 for (const auto& target : targets_) {
801 target->Visit(visitor);
802 }
803 }
804
Print(std::ostream & out,unsigned indent) const805 void PBXProject::Print(std::ostream& out, unsigned indent) const {
806 const std::string indent_str(indent, '\t');
807 const IndentRules rules = {false, indent + 1};
808 out << indent_str << Reference() << " = {\n";
809 PrintProperty(out, rules, "isa", ToString(Class()));
810 PrintProperty(out, rules, "attributes", attributes_);
811 PrintProperty(out, rules, "buildConfigurationList", configurations_);
812 PrintProperty(out, rules, "compatibilityVersion", "Xcode 3.2");
813 PrintProperty(out, rules, "developmentRegion", "English");
814 PrintProperty(out, rules, "hasScannedForEncodings", 1u);
815 PrintProperty(out, rules, "knownRegions", std::vector<std::string>({"en"}));
816 PrintProperty(out, rules, "mainGroup", main_group_);
817 PrintProperty(out, rules, "projectDirPath", project_dir_path_);
818 PrintProperty(out, rules, "projectRoot", project_root_);
819 PrintProperty(out, rules, "targets", targets_);
820 out << indent_str << "};\n";
821 }
822
823 // PBXShellScriptBuildPhase ---------------------------------------------------
824
PBXShellScriptBuildPhase(const std::string & name,const std::string & shell_script)825 PBXShellScriptBuildPhase::PBXShellScriptBuildPhase(
826 const std::string& name,
827 const std::string& shell_script)
828 : name_("Action \"Compile and copy " + name + " via ninja\""),
829 shell_script_(shell_script) {}
830
831 PBXShellScriptBuildPhase::~PBXShellScriptBuildPhase() = default;
832
Class() const833 PBXObjectClass PBXShellScriptBuildPhase::Class() const {
834 return PBXShellScriptBuildPhaseClass;
835 }
836
Name() const837 std::string PBXShellScriptBuildPhase::Name() const {
838 return name_;
839 }
840
Print(std::ostream & out,unsigned indent) const841 void PBXShellScriptBuildPhase::Print(std::ostream& out, unsigned indent) const {
842 const std::string indent_str(indent, '\t');
843 const IndentRules rules = {false, indent + 1};
844 out << indent_str << Reference() << " = {\n";
845 PrintProperty(out, rules, "isa", ToString(Class()));
846 PrintProperty(out, rules, "buildActionMask", 0x7fffffffu);
847 PrintProperty(out, rules, "files", EmptyPBXObjectVector());
848 PrintProperty(out, rules, "inputPaths", EmptyPBXObjectVector());
849 PrintProperty(out, rules, "name", name_);
850 PrintProperty(out, rules, "outputPaths", EmptyPBXObjectVector());
851 PrintProperty(out, rules, "runOnlyForDeploymentPostprocessing", 0u);
852 PrintProperty(out, rules, "shellPath", "/bin/sh");
853 PrintProperty(out, rules, "shellScript", shell_script_);
854 PrintProperty(out, rules, "showEnvVarsInLog", 0u);
855 out << indent_str << "};\n";
856 }
857
858 // PBXSourcesBuildPhase -------------------------------------------------------
859
860 PBXSourcesBuildPhase::PBXSourcesBuildPhase() = default;
861
862 PBXSourcesBuildPhase::~PBXSourcesBuildPhase() = default;
863
AddBuildFile(std::unique_ptr<PBXBuildFile> build_file)864 void PBXSourcesBuildPhase::AddBuildFile(
865 std::unique_ptr<PBXBuildFile> build_file) {
866 files_.push_back(std::move(build_file));
867 }
868
Class() const869 PBXObjectClass PBXSourcesBuildPhase::Class() const {
870 return PBXSourcesBuildPhaseClass;
871 }
872
Name() const873 std::string PBXSourcesBuildPhase::Name() const {
874 return "Sources";
875 }
876
Visit(PBXObjectVisitor & visitor)877 void PBXSourcesBuildPhase::Visit(PBXObjectVisitor& visitor) {
878 PBXBuildPhase::Visit(visitor);
879 for (const auto& file : files_) {
880 file->Visit(visitor);
881 }
882 }
883
Print(std::ostream & out,unsigned indent) const884 void PBXSourcesBuildPhase::Print(std::ostream& out, unsigned indent) const {
885 const std::string indent_str(indent, '\t');
886 const IndentRules rules = {false, indent + 1};
887 out << indent_str << Reference() << " = {\n";
888 PrintProperty(out, rules, "isa", ToString(Class()));
889 PrintProperty(out, rules, "buildActionMask", 0x7fffffffu);
890 PrintProperty(out, rules, "files", files_);
891 PrintProperty(out, rules, "runOnlyForDeploymentPostprocessing", 0u);
892 out << indent_str << "};\n";
893 }
894
PBXTargetDependency(const PBXTarget * target,std::unique_ptr<PBXContainerItemProxy> container_item_proxy)895 PBXTargetDependency::PBXTargetDependency(
896 const PBXTarget* target,
897 std::unique_ptr<PBXContainerItemProxy> container_item_proxy)
898 : target_(target), container_item_proxy_(std::move(container_item_proxy)) {}
899
900 PBXTargetDependency::~PBXTargetDependency() = default;
901
Class() const902 PBXObjectClass PBXTargetDependency::Class() const {
903 return PBXTargetDependencyClass;
904 }
Name() const905 std::string PBXTargetDependency::Name() const {
906 return "PBXTargetDependency";
907 }
Visit(PBXObjectVisitor & visitor)908 void PBXTargetDependency::Visit(PBXObjectVisitor& visitor) {
909 PBXObject::Visit(visitor);
910 container_item_proxy_->Visit(visitor);
911 }
Print(std::ostream & out,unsigned indent) const912 void PBXTargetDependency::Print(std::ostream& out, unsigned indent) const {
913 const std::string indent_str(indent, '\t');
914 const IndentRules rules = {false, indent + 1};
915 out << indent_str << Reference() << " = {\n";
916 PrintProperty(out, rules, "isa", ToString(Class()));
917 PrintProperty(out, rules, "target", target_);
918 PrintProperty(out, rules, "targetProxy", container_item_proxy_);
919 out << indent_str << "};\n";
920 }
921
922 // XCBuildConfiguration -------------------------------------------------------
923
XCBuildConfiguration(const std::string & name,const PBXAttributes & attributes)924 XCBuildConfiguration::XCBuildConfiguration(const std::string& name,
925 const PBXAttributes& attributes)
926 : attributes_(attributes), name_(name) {}
927
928 XCBuildConfiguration::~XCBuildConfiguration() = default;
929
Class() const930 PBXObjectClass XCBuildConfiguration::Class() const {
931 return XCBuildConfigurationClass;
932 }
933
Name() const934 std::string XCBuildConfiguration::Name() const {
935 return name_;
936 }
937
Print(std::ostream & out,unsigned indent) const938 void XCBuildConfiguration::Print(std::ostream& out, unsigned indent) const {
939 const std::string indent_str(indent, '\t');
940 const IndentRules rules = {false, indent + 1};
941 out << indent_str << Reference() << " = {\n";
942 PrintProperty(out, rules, "isa", ToString(Class()));
943 PrintProperty(out, rules, "buildSettings", attributes_);
944 PrintProperty(out, rules, "name", name_);
945 out << indent_str << "};\n";
946 }
947
948 // XCConfigurationList --------------------------------------------------------
949
XCConfigurationList(const std::string & name,const PBXAttributes & attributes,const PBXObject * owner_reference)950 XCConfigurationList::XCConfigurationList(const std::string& name,
951 const PBXAttributes& attributes,
952 const PBXObject* owner_reference)
953 : owner_reference_(owner_reference) {
954 DCHECK(owner_reference_);
955 configurations_.push_back(
956 std::make_unique<XCBuildConfiguration>(name, attributes));
957 }
958
959 XCConfigurationList::~XCConfigurationList() = default;
960
Class() const961 PBXObjectClass XCConfigurationList::Class() const {
962 return XCConfigurationListClass;
963 }
964
Name() const965 std::string XCConfigurationList::Name() const {
966 std::stringstream buffer;
967 buffer << "Build configuration list for "
968 << ToString(owner_reference_->Class()) << " \""
969 << owner_reference_->Name() << "\"";
970 return buffer.str();
971 }
972
Visit(PBXObjectVisitor & visitor)973 void XCConfigurationList::Visit(PBXObjectVisitor& visitor) {
974 PBXObject::Visit(visitor);
975 for (const auto& configuration : configurations_) {
976 configuration->Visit(visitor);
977 }
978 }
979
Print(std::ostream & out,unsigned indent) const980 void XCConfigurationList::Print(std::ostream& out, unsigned indent) const {
981 const std::string indent_str(indent, '\t');
982 const IndentRules rules = {false, indent + 1};
983 out << indent_str << Reference() << " = {\n";
984 PrintProperty(out, rules, "isa", ToString(Class()));
985 PrintProperty(out, rules, "buildConfigurations", configurations_);
986 PrintProperty(out, rules, "defaultConfigurationIsVisible", 1u);
987 PrintProperty(out, rules, "defaultConfigurationName",
988 configurations_[0]->Name());
989 out << indent_str << "};\n";
990 }
991