| // Copyright 2016 The Chromium Authors. All rights reserved. | |
| // Use of this source code is governed by a BSD-style license that can be | |
| // found in the LICENSE file. | |
| #include "tools/gn/xcode_object.h" | |
| #include <iomanip> | |
| #include <memory> | |
| #include <sstream> | |
| #include <utility> | |
| #include "base/logging.h" | |
| #include "base/macros.h" | |
| #include "base/strings/string_util.h" | |
| #include "tools/gn/filesystem_utils.h" | |
| // Helper methods ------------------------------------------------------------- | |
| namespace { | |
| struct IndentRules { | |
| bool one_line; | |
| unsigned level; | |
| }; | |
| std::vector<std::unique_ptr<PBXObject>> EmptyPBXObjectVector() { | |
| return std::vector<std::unique_ptr<PBXObject>>(); | |
| } | |
| bool CharNeedEscaping(char c) { | |
| if (base::IsAsciiAlpha(c) || base::IsAsciiDigit(c)) | |
| return false; | |
| if (c == '$' || c == '.' || c == '/' || c == '_') | |
| return false; | |
| return true; | |
| } | |
| bool StringNeedEscaping(const std::string& string) { | |
| if (string.empty()) | |
| return true; | |
| if (string.find("___") != std::string::npos) | |
| return true; | |
| for (char c : string) { | |
| if (CharNeedEscaping(c)) | |
| return true; | |
| } | |
| return false; | |
| } | |
| std::string EncodeString(const std::string& string) { | |
| if (!StringNeedEscaping(string)) | |
| return string; | |
| std::stringstream buffer; | |
| buffer << '"'; | |
| for (char c : string) { | |
| if (c <= 31) { | |
| switch (c) { | |
| case '\a': | |
| buffer << "\\a"; | |
| break; | |
| case '\b': | |
| buffer << "\\b"; | |
| break; | |
| case '\t': | |
| buffer << "\\t"; | |
| break; | |
| case '\n': | |
| case '\r': | |
| buffer << "\\n"; | |
| break; | |
| case '\v': | |
| buffer << "\\v"; | |
| break; | |
| case '\f': | |
| buffer << "\\f"; | |
| break; | |
| default: | |
| buffer << std::hex << std::setw(4) << std::left << "\\U" | |
| << static_cast<unsigned>(c); | |
| break; | |
| } | |
| } else { | |
| if (c == '"' || c == '\\') | |
| buffer << '\\'; | |
| buffer << c; | |
| } | |
| } | |
| buffer << '"'; | |
| return buffer.str(); | |
| } | |
| struct SourceTypeForExt { | |
| const char* ext; | |
| const char* source_type; | |
| }; | |
| const SourceTypeForExt kSourceTypeForExt[] = { | |
| {"a", "archive.ar"}, | |
| {"app", "wrapper.application"}, | |
| {"appex", "wrapper.app-extension"}, | |
| {"bdic", "file"}, | |
| {"bundle", "wrapper.cfbundle"}, | |
| {"c", "sourcecode.c.c"}, | |
| {"cc", "sourcecode.cpp.cpp"}, | |
| {"cpp", "sourcecode.cpp.cpp"}, | |
| {"css", "text.css"}, | |
| {"cxx", "sourcecode.cpp.cpp"}, | |
| {"dart", "sourcecode"}, | |
| {"dylib", "compiled.mach-o.dylib"}, | |
| {"framework", "wrapper.framework"}, | |
| {"h", "sourcecode.c.h"}, | |
| {"hxx", "sourcecode.cpp.h"}, | |
| {"icns", "image.icns"}, | |
| {"java", "sourcecode.java"}, | |
| {"js", "sourcecode.javascript"}, | |
| {"kext", "wrapper.kext"}, | |
| {"m", "sourcecode.c.objc"}, | |
| {"mm", "sourcecode.cpp.objcpp"}, | |
| {"nib", "wrapper.nib"}, | |
| {"o", "compiled.mach-o.objfile"}, | |
| {"pdf", "image.pdf"}, | |
| {"pl", "text.script.perl"}, | |
| {"plist", "text.plist.xml"}, | |
| {"pm", "text.script.perl"}, | |
| {"png", "image.png"}, | |
| {"py", "text.script.python"}, | |
| {"r", "sourcecode.rez"}, | |
| {"rez", "sourcecode.rez"}, | |
| {"s", "sourcecode.asm"}, | |
| {"storyboard", "file.storyboard"}, | |
| {"strings", "text.plist.strings"}, | |
| {"swift", "sourcecode.swift"}, | |
| {"ttf", "file"}, | |
| {"xcassets", "folder.assetcatalog"}, | |
| {"xcconfig", "text.xcconfig"}, | |
| {"xcdatamodel", "wrapper.xcdatamodel"}, | |
| {"xcdatamodeld", "wrapper.xcdatamodeld"}, | |
| {"xib", "file.xib"}, | |
| {"y", "sourcecode.yacc"}, | |
| }; | |
| const char* GetSourceType(const base::StringPiece& ext) { | |
| for (size_t i = 0; i < arraysize(kSourceTypeForExt); ++i) { | |
| if (kSourceTypeForExt[i].ext == ext) | |
| return kSourceTypeForExt[i].source_type; | |
| } | |
| return "text"; | |
| } | |
| bool HasExplicitFileType(const base::StringPiece& ext) { | |
| return ext == "dart"; | |
| } | |
| bool IsSourceFileForIndexing(const base::StringPiece& ext) { | |
| return ext == "c" || ext == "cc" || ext == "cpp" || ext == "cxx" || | |
| ext == "m" || ext == "mm"; | |
| } | |
| void PrintValue(std::ostream& out, IndentRules rules, unsigned value) { | |
| out << value; | |
| } | |
| void PrintValue(std::ostream& out, IndentRules rules, const char* value) { | |
| out << EncodeString(value); | |
| } | |
| void PrintValue(std::ostream& out, | |
| IndentRules rules, | |
| const std::string& value) { | |
| out << EncodeString(value); | |
| } | |
| void PrintValue(std::ostream& out, IndentRules rules, const PBXObject* value) { | |
| out << value->Reference(); | |
| } | |
| template <typename ObjectClass> | |
| void PrintValue(std::ostream& out, | |
| IndentRules rules, | |
| const std::unique_ptr<ObjectClass>& value) { | |
| PrintValue(out, rules, value.get()); | |
| } | |
| template <typename ValueType> | |
| void PrintValue(std::ostream& out, | |
| IndentRules rules, | |
| const std::vector<ValueType>& values) { | |
| IndentRules sub_rule{rules.one_line, rules.level + 1}; | |
| out << "(" << (rules.one_line ? " " : "\n"); | |
| for (const auto& value : values) { | |
| if (!sub_rule.one_line) | |
| out << std::string(sub_rule.level, '\t'); | |
| PrintValue(out, sub_rule, value); | |
| out << "," << (rules.one_line ? " " : "\n"); | |
| } | |
| if (!rules.one_line && rules.level) | |
| out << std::string(rules.level, '\t'); | |
| out << ")"; | |
| } | |
| template <typename ValueType> | |
| void PrintValue(std::ostream& out, | |
| IndentRules rules, | |
| const std::map<std::string, ValueType>& values) { | |
| IndentRules sub_rule{rules.one_line, rules.level + 1}; | |
| out << "{" << (rules.one_line ? " " : "\n"); | |
| for (const auto& pair : values) { | |
| if (!sub_rule.one_line) | |
| out << std::string(sub_rule.level, '\t'); | |
| out << pair.first << " = "; | |
| PrintValue(out, sub_rule, pair.second); | |
| out << ";" << (rules.one_line ? " " : "\n"); | |
| } | |
| if (!rules.one_line && rules.level) | |
| out << std::string(rules.level, '\t'); | |
| out << "}"; | |
| } | |
| template <typename ValueType> | |
| void PrintProperty(std::ostream& out, | |
| IndentRules rules, | |
| const char* name, | |
| ValueType&& value) { | |
| if (!rules.one_line && rules.level) | |
| out << std::string(rules.level, '\t'); | |
| out << name << " = "; | |
| PrintValue(out, rules, std::forward<ValueType>(value)); | |
| out << ";" << (rules.one_line ? " " : "\n"); | |
| } | |
| } // namespace | |
| // PBXObjectClass ------------------------------------------------------------- | |
| const char* ToString(PBXObjectClass cls) { | |
| switch (cls) { | |
| case PBXAggregateTargetClass: | |
| return "PBXAggregateTarget"; | |
| case PBXBuildFileClass: | |
| return "PBXBuildFile"; | |
| case PBXContainerItemProxyClass: | |
| return "PBXContainerItemProxy"; | |
| case PBXFileReferenceClass: | |
| return "PBXFileReference"; | |
| case PBXFrameworksBuildPhaseClass: | |
| return "PBXFrameworksBuildPhase"; | |
| case PBXGroupClass: | |
| return "PBXGroup"; | |
| case PBXNativeTargetClass: | |
| return "PBXNativeTarget"; | |
| case PBXProjectClass: | |
| return "PBXProject"; | |
| case PBXShellScriptBuildPhaseClass: | |
| return "PBXShellScriptBuildPhase"; | |
| case PBXSourcesBuildPhaseClass: | |
| return "PBXSourcesBuildPhase"; | |
| case PBXTargetDependencyClass: | |
| return "PBXTargetDependency"; | |
| case XCBuildConfigurationClass: | |
| return "XCBuildConfiguration"; | |
| case XCConfigurationListClass: | |
| return "XCConfigurationList"; | |
| } | |
| NOTREACHED(); | |
| return nullptr; | |
| } | |
| // PBXObjectVisitor ----------------------------------------------------------- | |
| PBXObjectVisitor::PBXObjectVisitor() = default; | |
| PBXObjectVisitor::~PBXObjectVisitor() = default; | |
| // PBXObject ------------------------------------------------------------------ | |
| PBXObject::PBXObject() = default; | |
| PBXObject::~PBXObject() = default; | |
| void PBXObject::SetId(const std::string& id) { | |
| DCHECK(id_.empty()); | |
| DCHECK(!id.empty()); | |
| id_.assign(id); | |
| } | |
| std::string PBXObject::Reference() const { | |
| std::string comment = Comment(); | |
| if (comment.empty()) | |
| return id_; | |
| return id_ + " /* " + comment + " */"; | |
| } | |
| std::string PBXObject::Comment() const { | |
| return Name(); | |
| } | |
| void PBXObject::Visit(PBXObjectVisitor& visitor) { | |
| visitor.Visit(this); | |
| } | |
| // PBXBuildPhase -------------------------------------------------------------- | |
| PBXBuildPhase::PBXBuildPhase() = default; | |
| PBXBuildPhase::~PBXBuildPhase() = default; | |
| // PBXTarget ------------------------------------------------------------------ | |
| PBXTarget::PBXTarget(const std::string& name, | |
| const std::string& shell_script, | |
| const std::string& config_name, | |
| const PBXAttributes& attributes) | |
| : configurations_(new XCConfigurationList(config_name, attributes, this)), | |
| name_(name) { | |
| if (!shell_script.empty()) { | |
| build_phases_.push_back( | |
| std::make_unique<PBXShellScriptBuildPhase>(name, shell_script)); | |
| } | |
| } | |
| PBXTarget::~PBXTarget() = default; | |
| void PBXTarget::AddDependency(std::unique_ptr<PBXTargetDependency> dependency) { | |
| DCHECK(dependency); | |
| dependencies_.push_back(std::move(dependency)); | |
| } | |
| std::string PBXTarget::Name() const { | |
| return name_; | |
| } | |
| void PBXTarget::Visit(PBXObjectVisitor& visitor) { | |
| PBXObject::Visit(visitor); | |
| configurations_->Visit(visitor); | |
| for (const auto& dependency : dependencies_) | |
| dependency->Visit(visitor); | |
| for (const auto& build_phase : build_phases_) | |
| build_phase->Visit(visitor); | |
| } | |
| // PBXAggregateTarget --------------------------------------------------------- | |
| PBXAggregateTarget::PBXAggregateTarget(const std::string& name, | |
| const std::string& shell_script, | |
| const std::string& config_name, | |
| const PBXAttributes& attributes) | |
| : PBXTarget(name, shell_script, config_name, attributes) {} | |
| PBXAggregateTarget::~PBXAggregateTarget() = default; | |
| PBXObjectClass PBXAggregateTarget::Class() const { | |
| return PBXAggregateTargetClass; | |
| } | |
| void PBXAggregateTarget::Print(std::ostream& out, unsigned indent) const { | |
| const std::string indent_str(indent, '\t'); | |
| const IndentRules rules = {false, indent + 1}; | |
| out << indent_str << Reference() << " = {\n"; | |
| PrintProperty(out, rules, "isa", ToString(Class())); | |
| PrintProperty(out, rules, "buildConfigurationList", configurations_); | |
| PrintProperty(out, rules, "buildPhases", build_phases_); | |
| PrintProperty(out, rules, "dependencies", EmptyPBXObjectVector()); | |
| PrintProperty(out, rules, "name", name_); | |
| PrintProperty(out, rules, "productName", name_); | |
| out << indent_str << "};\n"; | |
| } | |
| // PBXBuildFile --------------------------------------------------------------- | |
| PBXBuildFile::PBXBuildFile(const PBXFileReference* file_reference, | |
| const PBXSourcesBuildPhase* build_phase, | |
| const CompilerFlags compiler_flag) | |
| : file_reference_(file_reference), | |
| build_phase_(build_phase), | |
| compiler_flag_(compiler_flag) { | |
| DCHECK(file_reference_); | |
| DCHECK(build_phase_); | |
| } | |
| PBXBuildFile::~PBXBuildFile() = default; | |
| PBXObjectClass PBXBuildFile::Class() const { | |
| return PBXBuildFileClass; | |
| } | |
| std::string PBXBuildFile::Name() const { | |
| return file_reference_->Name() + " in " + build_phase_->Name(); | |
| } | |
| void PBXBuildFile::Print(std::ostream& out, unsigned indent) const { | |
| const std::string indent_str(indent, '\t'); | |
| const IndentRules rules = {true, 0}; | |
| out << indent_str << Reference() << " = {"; | |
| PrintProperty(out, rules, "isa", ToString(Class())); | |
| PrintProperty(out, rules, "fileRef", file_reference_); | |
| if (compiler_flag_ == CompilerFlags::HELP) { | |
| std::map<std::string, std::string> settings = { | |
| {"COMPILER_FLAGS", "--help"}, | |
| }; | |
| PrintProperty(out, rules, "settings", settings); | |
| } | |
| out << "};\n"; | |
| } | |
| // PBXContainerItemProxy ------------------------------------------------------ | |
| PBXContainerItemProxy::PBXContainerItemProxy(const PBXProject* project, | |
| const PBXTarget* target) | |
| : project_(project), target_(target) {} | |
| PBXContainerItemProxy::~PBXContainerItemProxy() = default; | |
| PBXObjectClass PBXContainerItemProxy::Class() const { | |
| return PBXContainerItemProxyClass; | |
| } | |
| void PBXContainerItemProxy::Visit(PBXObjectVisitor& visitor) { | |
| PBXObject::Visit(visitor); | |
| } | |
| std::string PBXContainerItemProxy::Name() const { | |
| return "PBXContainerItemProxy"; | |
| } | |
| void PBXContainerItemProxy::Print(std::ostream& out, unsigned indent) const { | |
| const std::string indent_str(indent, '\t'); | |
| const IndentRules rules = {true, 0}; | |
| out << indent_str << Reference() << " = {"; | |
| PrintProperty(out, rules, "isa", ToString(Class())); | |
| PrintProperty(out, rules, "containerPortal", project_); | |
| PrintProperty(out, rules, "proxyType", 1u); | |
| PrintProperty(out, rules, "remoteGlobalIDString", target_); | |
| PrintProperty(out, rules, "remoteInfo", target_->Name()); | |
| out << indent_str << "};\n"; | |
| } | |
| // PBXFileReference ----------------------------------------------------------- | |
| PBXFileReference::PBXFileReference(const std::string& name, | |
| const std::string& path, | |
| const std::string& type) | |
| : name_(name), path_(path), type_(type) {} | |
| PBXFileReference::~PBXFileReference() = default; | |
| PBXObjectClass PBXFileReference::Class() const { | |
| return PBXFileReferenceClass; | |
| } | |
| std::string PBXFileReference::Name() const { | |
| return name_; | |
| } | |
| void PBXFileReference::Print(std::ostream& out, unsigned indent) const { | |
| const std::string indent_str(indent, '\t'); | |
| const IndentRules rules = {true, 0}; | |
| out << indent_str << Reference() << " = {"; | |
| PrintProperty(out, rules, "isa", ToString(Class())); | |
| if (!type_.empty()) { | |
| PrintProperty(out, rules, "explicitFileType", type_); | |
| PrintProperty(out, rules, "includeInIndex", 0u); | |
| } else { | |
| base::StringPiece ext = FindExtension(&name_); | |
| if (HasExplicitFileType(ext)) | |
| PrintProperty(out, rules, "explicitFileType", GetSourceType(ext)); | |
| else | |
| PrintProperty(out, rules, "lastKnownFileType", GetSourceType(ext)); | |
| } | |
| if (!name_.empty()) | |
| PrintProperty(out, rules, "name", name_); | |
| DCHECK(!path_.empty()); | |
| PrintProperty(out, rules, "path", path_); | |
| PrintProperty(out, rules, "sourceTree", | |
| type_.empty() ? "<group>" : "BUILT_PRODUCTS_DIR"); | |
| out << "};\n"; | |
| } | |
| // PBXFrameworksBuildPhase ---------------------------------------------------- | |
| PBXFrameworksBuildPhase::PBXFrameworksBuildPhase() = default; | |
| PBXFrameworksBuildPhase::~PBXFrameworksBuildPhase() = default; | |
| PBXObjectClass PBXFrameworksBuildPhase::Class() const { | |
| return PBXFrameworksBuildPhaseClass; | |
| } | |
| std::string PBXFrameworksBuildPhase::Name() const { | |
| return "Frameworks"; | |
| } | |
| void PBXFrameworksBuildPhase::Print(std::ostream& out, unsigned indent) const { | |
| const std::string indent_str(indent, '\t'); | |
| const IndentRules rules = {false, indent + 1}; | |
| out << indent_str << Reference() << " = {\n"; | |
| PrintProperty(out, rules, "isa", ToString(Class())); | |
| PrintProperty(out, rules, "buildActionMask", 0x7fffffffu); | |
| PrintProperty(out, rules, "files", EmptyPBXObjectVector()); | |
| PrintProperty(out, rules, "runOnlyForDeploymentPostprocessing", 0u); | |
| out << indent_str << "};\n"; | |
| } | |
| // PBXGroup ------------------------------------------------------------------- | |
| PBXGroup::PBXGroup(const std::string& path, const std::string& name) | |
| : name_(name), path_(path) {} | |
| PBXGroup::~PBXGroup() = default; | |
| PBXObject* PBXGroup::AddChild(std::unique_ptr<PBXObject> child) { | |
| DCHECK(child); | |
| children_.push_back(std::move(child)); | |
| return children_.back().get(); | |
| } | |
| PBXFileReference* PBXGroup::AddSourceFile(const std::string& navigator_path, | |
| const std::string& source_path) { | |
| DCHECK(!navigator_path.empty()); | |
| DCHECK(!source_path.empty()); | |
| std::string::size_type sep = navigator_path.find("/"); | |
| if (sep == std::string::npos) { | |
| // Prevent same file reference being created and added multiple times. | |
| for (const auto& child : children_) { | |
| if (child->Class() != PBXFileReferenceClass) | |
| continue; | |
| PBXFileReference* child_as_file_reference = | |
| static_cast<PBXFileReference*>(child.get()); | |
| if (child_as_file_reference->Name() == navigator_path && | |
| child_as_file_reference->path() == source_path) { | |
| return child_as_file_reference; | |
| } | |
| } | |
| children_.push_back(std::make_unique<PBXFileReference>( | |
| navigator_path, source_path, std::string())); | |
| return static_cast<PBXFileReference*>(children_.back().get()); | |
| } | |
| PBXGroup* group = nullptr; | |
| base::StringPiece component(navigator_path.data(), sep); | |
| for (const auto& child : children_) { | |
| if (child->Class() != PBXGroupClass) | |
| continue; | |
| PBXGroup* child_as_group = static_cast<PBXGroup*>(child.get()); | |
| if (child_as_group->name_ == component) { | |
| group = child_as_group; | |
| break; | |
| } | |
| } | |
| if (!group) { | |
| children_.push_back(std::make_unique<PBXGroup>(component.as_string(), | |
| component.as_string())); | |
| group = static_cast<PBXGroup*>(children_.back().get()); | |
| } | |
| DCHECK(group); | |
| DCHECK(group->name_ == component); | |
| return group->AddSourceFile(navigator_path.substr(sep + 1), source_path); | |
| } | |
| PBXObjectClass PBXGroup::Class() const { | |
| return PBXGroupClass; | |
| } | |
| std::string PBXGroup::Name() const { | |
| if (!name_.empty()) | |
| return name_; | |
| if (!path_.empty()) | |
| return path_; | |
| return std::string(); | |
| } | |
| void PBXGroup::Visit(PBXObjectVisitor& visitor) { | |
| PBXObject::Visit(visitor); | |
| for (const auto& child : children_) { | |
| child->Visit(visitor); | |
| } | |
| } | |
| void PBXGroup::Print(std::ostream& out, unsigned indent) const { | |
| const std::string indent_str(indent, '\t'); | |
| const IndentRules rules = {false, indent + 1}; | |
| out << indent_str << Reference() << " = {\n"; | |
| PrintProperty(out, rules, "isa", ToString(Class())); | |
| PrintProperty(out, rules, "children", children_); | |
| if (!name_.empty()) | |
| PrintProperty(out, rules, "name", name_); | |
| if (is_source_ && !path_.empty()) | |
| PrintProperty(out, rules, "path", path_); | |
| PrintProperty(out, rules, "sourceTree", "<group>"); | |
| out << indent_str << "};\n"; | |
| } | |
| // PBXNativeTarget ------------------------------------------------------------ | |
| PBXNativeTarget::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) | |
| : PBXTarget(name, shell_script, config_name, attributes), | |
| product_reference_(product_reference), | |
| product_type_(product_type), | |
| product_name_(product_name) { | |
| DCHECK(product_reference_); | |
| build_phases_.push_back(std::make_unique<PBXSourcesBuildPhase>()); | |
| source_build_phase_ = | |
| static_cast<PBXSourcesBuildPhase*>(build_phases_.back().get()); | |
| build_phases_.push_back(std::make_unique<PBXFrameworksBuildPhase>()); | |
| } | |
| PBXNativeTarget::~PBXNativeTarget() = default; | |
| void PBXNativeTarget::AddFileForIndexing(const PBXFileReference* file_reference, | |
| const CompilerFlags compiler_flag) { | |
| DCHECK(file_reference); | |
| source_build_phase_->AddBuildFile(std::make_unique<PBXBuildFile>( | |
| file_reference, source_build_phase_, compiler_flag)); | |
| } | |
| PBXObjectClass PBXNativeTarget::Class() const { | |
| return PBXNativeTargetClass; | |
| } | |
| void PBXNativeTarget::Print(std::ostream& out, unsigned indent) const { | |
| const std::string indent_str(indent, '\t'); | |
| const IndentRules rules = {false, indent + 1}; | |
| out << indent_str << Reference() << " = {\n"; | |
| PrintProperty(out, rules, "isa", ToString(Class())); | |
| PrintProperty(out, rules, "buildConfigurationList", configurations_); | |
| PrintProperty(out, rules, "buildPhases", build_phases_); | |
| PrintProperty(out, rules, "buildRules", EmptyPBXObjectVector()); | |
| PrintProperty(out, rules, "dependencies", dependencies_); | |
| PrintProperty(out, rules, "name", name_); | |
| PrintProperty(out, rules, "productName", product_name_); | |
| PrintProperty(out, rules, "productReference", product_reference_); | |
| PrintProperty(out, rules, "productType", product_type_); | |
| out << indent_str << "};\n"; | |
| } | |
| // PBXProject ----------------------------------------------------------------- | |
| PBXProject::PBXProject(const std::string& name, | |
| const std::string& config_name, | |
| const std::string& source_path, | |
| const PBXAttributes& attributes) | |
| : name_(name), config_name_(config_name), target_for_indexing_(nullptr) { | |
| attributes_["BuildIndependentTargetsInParallel"] = "YES"; | |
| main_group_.reset(new PBXGroup); | |
| sources_ = static_cast<PBXGroup*>( | |
| main_group_->AddChild(std::make_unique<PBXGroup>(source_path, "Source"))); | |
| sources_->set_is_source(true); | |
| products_ = static_cast<PBXGroup*>(main_group_->AddChild( | |
| std::make_unique<PBXGroup>(std::string(), "Product"))); | |
| main_group_->AddChild(std::make_unique<PBXGroup>(std::string(), "Build")); | |
| configurations_.reset(new XCConfigurationList(config_name, attributes, this)); | |
| } | |
| PBXProject::~PBXProject() = default; | |
| void PBXProject::AddSourceFileToIndexingTarget( | |
| const std::string& navigator_path, | |
| const std::string& source_path, | |
| const CompilerFlags compiler_flag) { | |
| if (!target_for_indexing_) { | |
| AddIndexingTarget(); | |
| } | |
| AddSourceFile(navigator_path, source_path, compiler_flag, | |
| target_for_indexing_); | |
| } | |
| void PBXProject::AddSourceFile(const std::string& navigator_path, | |
| const std::string& source_path, | |
| const CompilerFlags compiler_flag, | |
| PBXNativeTarget* target) { | |
| PBXFileReference* file_reference = | |
| sources_->AddSourceFile(navigator_path, source_path); | |
| base::StringPiece ext = FindExtension(&source_path); | |
| if (!IsSourceFileForIndexing(ext)) | |
| return; | |
| DCHECK(target); | |
| target->AddFileForIndexing(file_reference, compiler_flag); | |
| } | |
| void PBXProject::AddAggregateTarget(const std::string& name, | |
| const std::string& shell_script) { | |
| PBXAttributes attributes; | |
| attributes["CODE_SIGNING_REQUIRED"] = "NO"; | |
| attributes["CONFIGURATION_BUILD_DIR"] = "."; | |
| attributes["PRODUCT_NAME"] = name; | |
| targets_.push_back(std::make_unique<PBXAggregateTarget>( | |
| name, shell_script, config_name_, attributes)); | |
| } | |
| void PBXProject::AddIndexingTarget() { | |
| DCHECK(!target_for_indexing_); | |
| PBXAttributes attributes; | |
| attributes["EXECUTABLE_PREFIX"] = ""; | |
| attributes["HEADER_SEARCH_PATHS"] = sources_->path(); | |
| attributes["PRODUCT_NAME"] = "sources"; | |
| PBXFileReference* product_reference = static_cast<PBXFileReference*>( | |
| products_->AddChild(std::make_unique<PBXFileReference>( | |
| std::string(), "sources", "compiled.mach-o.executable"))); | |
| const char product_type[] = "com.apple.product-type.tool"; | |
| targets_.push_back(std::make_unique<PBXNativeTarget>( | |
| "sources", std::string(), config_name_, attributes, product_type, | |
| "sources", product_reference)); | |
| target_for_indexing_ = static_cast<PBXNativeTarget*>(targets_.back().get()); | |
| } | |
| PBXNativeTarget* PBXProject::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) { | |
| base::StringPiece ext = FindExtension(&output_name); | |
| PBXFileReference* product = static_cast<PBXFileReference*>( | |
| products_->AddChild(std::make_unique<PBXFileReference>( | |
| std::string(), output_name, | |
| type.empty() ? GetSourceType(ext) : type))); | |
| // Per Xcode build settings documentation: Product Name (PRODUCT_NAME) should | |
| // the basename of the product generated by the target. | |
| // Therefore, take the basename of output name without file extension as the | |
| // "PRODUCT_NAME". | |
| size_t basename_offset = FindFilenameOffset(output_name); | |
| std::string output_basename = basename_offset != std::string::npos | |
| ? output_name.substr(basename_offset) | |
| : output_name; | |
| size_t ext_offset = FindExtensionOffset(output_basename); | |
| std::string product_name = ext_offset != std::string::npos | |
| ? output_basename.substr(0, ext_offset - 1) | |
| : output_basename; | |
| PBXAttributes attributes = extra_attributes; | |
| attributes["CODE_SIGNING_REQUIRED"] = "NO"; | |
| attributes["CONFIGURATION_BUILD_DIR"] = "."; | |
| attributes["PRODUCT_NAME"] = product_name; | |
| targets_.push_back(std::make_unique<PBXNativeTarget>( | |
| name, shell_script, config_name_, attributes, output_type, product_name, | |
| product)); | |
| return static_cast<PBXNativeTarget*>(targets_.back().get()); | |
| } | |
| void PBXProject::SetProjectDirPath(const std::string& project_dir_path) { | |
| DCHECK(!project_dir_path.empty()); | |
| project_dir_path_.assign(project_dir_path); | |
| } | |
| void PBXProject::SetProjectRoot(const std::string& project_root) { | |
| DCHECK(!project_root.empty()); | |
| project_root_.assign(project_root); | |
| } | |
| void PBXProject::AddTarget(std::unique_ptr<PBXTarget> target) { | |
| DCHECK(target); | |
| targets_.push_back(std::move(target)); | |
| } | |
| PBXObjectClass PBXProject::Class() const { | |
| return PBXProjectClass; | |
| } | |
| std::string PBXProject::Name() const { | |
| return name_; | |
| } | |
| std::string PBXProject::Comment() const { | |
| return "Project object"; | |
| } | |
| void PBXProject::Visit(PBXObjectVisitor& visitor) { | |
| PBXObject::Visit(visitor); | |
| configurations_->Visit(visitor); | |
| main_group_->Visit(visitor); | |
| for (const auto& target : targets_) { | |
| target->Visit(visitor); | |
| } | |
| } | |
| void PBXProject::Print(std::ostream& out, unsigned indent) const { | |
| const std::string indent_str(indent, '\t'); | |
| const IndentRules rules = {false, indent + 1}; | |
| out << indent_str << Reference() << " = {\n"; | |
| PrintProperty(out, rules, "isa", ToString(Class())); | |
| PrintProperty(out, rules, "attributes", attributes_); | |
| PrintProperty(out, rules, "buildConfigurationList", configurations_); | |
| PrintProperty(out, rules, "compatibilityVersion", "Xcode 3.2"); | |
| PrintProperty(out, rules, "developmentRegion", "English"); | |
| PrintProperty(out, rules, "hasScannedForEncodings", 1u); | |
| PrintProperty(out, rules, "knownRegions", std::vector<std::string>({"en"})); | |
| PrintProperty(out, rules, "mainGroup", main_group_); | |
| PrintProperty(out, rules, "projectDirPath", project_dir_path_); | |
| PrintProperty(out, rules, "projectRoot", project_root_); | |
| PrintProperty(out, rules, "targets", targets_); | |
| out << indent_str << "};\n"; | |
| } | |
| // PBXShellScriptBuildPhase --------------------------------------------------- | |
| PBXShellScriptBuildPhase::PBXShellScriptBuildPhase( | |
| const std::string& name, | |
| const std::string& shell_script) | |
| : name_("Action \"Compile and copy " + name + " via ninja\""), | |
| shell_script_(shell_script) {} | |
| PBXShellScriptBuildPhase::~PBXShellScriptBuildPhase() = default; | |
| PBXObjectClass PBXShellScriptBuildPhase::Class() const { | |
| return PBXShellScriptBuildPhaseClass; | |
| } | |
| std::string PBXShellScriptBuildPhase::Name() const { | |
| return name_; | |
| } | |
| void PBXShellScriptBuildPhase::Print(std::ostream& out, unsigned indent) const { | |
| const std::string indent_str(indent, '\t'); | |
| const IndentRules rules = {false, indent + 1}; | |
| out << indent_str << Reference() << " = {\n"; | |
| PrintProperty(out, rules, "isa", ToString(Class())); | |
| PrintProperty(out, rules, "buildActionMask", 0x7fffffffu); | |
| PrintProperty(out, rules, "files", EmptyPBXObjectVector()); | |
| PrintProperty(out, rules, "inputPaths", EmptyPBXObjectVector()); | |
| PrintProperty(out, rules, "name", name_); | |
| PrintProperty(out, rules, "outputPaths", EmptyPBXObjectVector()); | |
| PrintProperty(out, rules, "runOnlyForDeploymentPostprocessing", 0u); | |
| PrintProperty(out, rules, "shellPath", "/bin/sh"); | |
| PrintProperty(out, rules, "shellScript", shell_script_); | |
| PrintProperty(out, rules, "showEnvVarsInLog", 0u); | |
| out << indent_str << "};\n"; | |
| } | |
| // PBXSourcesBuildPhase ------------------------------------------------------- | |
| PBXSourcesBuildPhase::PBXSourcesBuildPhase() = default; | |
| PBXSourcesBuildPhase::~PBXSourcesBuildPhase() = default; | |
| void PBXSourcesBuildPhase::AddBuildFile( | |
| std::unique_ptr<PBXBuildFile> build_file) { | |
| files_.push_back(std::move(build_file)); | |
| } | |
| PBXObjectClass PBXSourcesBuildPhase::Class() const { | |
| return PBXSourcesBuildPhaseClass; | |
| } | |
| std::string PBXSourcesBuildPhase::Name() const { | |
| return "Sources"; | |
| } | |
| void PBXSourcesBuildPhase::Visit(PBXObjectVisitor& visitor) { | |
| PBXBuildPhase::Visit(visitor); | |
| for (const auto& file : files_) { | |
| file->Visit(visitor); | |
| } | |
| } | |
| void PBXSourcesBuildPhase::Print(std::ostream& out, unsigned indent) const { | |
| const std::string indent_str(indent, '\t'); | |
| const IndentRules rules = {false, indent + 1}; | |
| out << indent_str << Reference() << " = {\n"; | |
| PrintProperty(out, rules, "isa", ToString(Class())); | |
| PrintProperty(out, rules, "buildActionMask", 0x7fffffffu); | |
| PrintProperty(out, rules, "files", files_); | |
| PrintProperty(out, rules, "runOnlyForDeploymentPostprocessing", 0u); | |
| out << indent_str << "};\n"; | |
| } | |
| PBXTargetDependency::PBXTargetDependency( | |
| const PBXTarget* target, | |
| std::unique_ptr<PBXContainerItemProxy> container_item_proxy) | |
| : target_(target), container_item_proxy_(std::move(container_item_proxy)) {} | |
| PBXTargetDependency::~PBXTargetDependency() = default; | |
| PBXObjectClass PBXTargetDependency::Class() const { | |
| return PBXTargetDependencyClass; | |
| } | |
| std::string PBXTargetDependency::Name() const { | |
| return "PBXTargetDependency"; | |
| } | |
| void PBXTargetDependency::Visit(PBXObjectVisitor& visitor) { | |
| PBXObject::Visit(visitor); | |
| container_item_proxy_->Visit(visitor); | |
| } | |
| void PBXTargetDependency::Print(std::ostream& out, unsigned indent) const { | |
| const std::string indent_str(indent, '\t'); | |
| const IndentRules rules = {false, indent + 1}; | |
| out << indent_str << Reference() << " = {\n"; | |
| PrintProperty(out, rules, "isa", ToString(Class())); | |
| PrintProperty(out, rules, "target", target_); | |
| PrintProperty(out, rules, "targetProxy", container_item_proxy_); | |
| out << indent_str << "};\n"; | |
| } | |
| // XCBuildConfiguration ------------------------------------------------------- | |
| XCBuildConfiguration::XCBuildConfiguration(const std::string& name, | |
| const PBXAttributes& attributes) | |
| : attributes_(attributes), name_(name) {} | |
| XCBuildConfiguration::~XCBuildConfiguration() = default; | |
| PBXObjectClass XCBuildConfiguration::Class() const { | |
| return XCBuildConfigurationClass; | |
| } | |
| std::string XCBuildConfiguration::Name() const { | |
| return name_; | |
| } | |
| void XCBuildConfiguration::Print(std::ostream& out, unsigned indent) const { | |
| const std::string indent_str(indent, '\t'); | |
| const IndentRules rules = {false, indent + 1}; | |
| out << indent_str << Reference() << " = {\n"; | |
| PrintProperty(out, rules, "isa", ToString(Class())); | |
| PrintProperty(out, rules, "buildSettings", attributes_); | |
| PrintProperty(out, rules, "name", name_); | |
| out << indent_str << "};\n"; | |
| } | |
| // XCConfigurationList -------------------------------------------------------- | |
| XCConfigurationList::XCConfigurationList(const std::string& name, | |
| const PBXAttributes& attributes, | |
| const PBXObject* owner_reference) | |
| : owner_reference_(owner_reference) { | |
| DCHECK(owner_reference_); | |
| configurations_.push_back( | |
| std::make_unique<XCBuildConfiguration>(name, attributes)); | |
| } | |
| XCConfigurationList::~XCConfigurationList() = default; | |
| PBXObjectClass XCConfigurationList::Class() const { | |
| return XCConfigurationListClass; | |
| } | |
| std::string XCConfigurationList::Name() const { | |
| std::stringstream buffer; | |
| buffer << "Build configuration list for " | |
| << ToString(owner_reference_->Class()) << " \"" | |
| << owner_reference_->Name() << "\""; | |
| return buffer.str(); | |
| } | |
| void XCConfigurationList::Visit(PBXObjectVisitor& visitor) { | |
| PBXObject::Visit(visitor); | |
| for (const auto& configuration : configurations_) { | |
| configuration->Visit(visitor); | |
| } | |
| } | |
| void XCConfigurationList::Print(std::ostream& out, unsigned indent) const { | |
| const std::string indent_str(indent, '\t'); | |
| const IndentRules rules = {false, indent + 1}; | |
| out << indent_str << Reference() << " = {\n"; | |
| PrintProperty(out, rules, "isa", ToString(Class())); | |
| PrintProperty(out, rules, "buildConfigurations", configurations_); | |
| PrintProperty(out, rules, "defaultConfigurationIsVisible", 1u); | |
| PrintProperty(out, rules, "defaultConfigurationName", | |
| configurations_[0]->Name()); | |
| out << indent_str << "};\n"; | |
| } |