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