// 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 #include #include #include #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> EmptyPBXObjectVector() { return std::vector>(); } 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(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 void PrintValue(std::ostream& out, IndentRules rules, const std::unique_ptr& value) { PrintValue(out, rules, value.get()); } template void PrintValue(std::ostream& out, IndentRules rules, const std::vector& 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 void PrintValue(std::ostream& out, IndentRules rules, const std::map& 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 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(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(name, shell_script)); } } PBXTarget::~PBXTarget() = default; void PBXTarget::AddDependency(std::unique_ptr 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 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() ? "" : "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 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(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( navigator_path, source_path, std::string())); return static_cast(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(child.get()); if (child_as_group->name_ == component) { group = child_as_group; break; } } if (!group) { children_.push_back(std::make_unique(component.as_string(), component.as_string())); group = static_cast(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", ""); 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()); source_build_phase_ = static_cast(build_phases_.back().get()); build_phases_.push_back(std::make_unique()); } PBXNativeTarget::~PBXNativeTarget() = default; void PBXNativeTarget::AddFileForIndexing(const PBXFileReference* file_reference, const CompilerFlags compiler_flag) { DCHECK(file_reference); source_build_phase_->AddBuildFile(std::make_unique( 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( main_group_->AddChild(std::make_unique(source_path, "Source"))); sources_->set_is_source(true); products_ = static_cast(main_group_->AddChild( std::make_unique(std::string(), "Product"))); main_group_->AddChild(std::make_unique(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( 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( products_->AddChild(std::make_unique( std::string(), "sources", "compiled.mach-o.executable"))); const char product_type[] = "com.apple.product-type.tool"; targets_.push_back(std::make_unique( "sources", std::string(), config_name_, attributes, product_type, "sources", product_reference)); target_for_indexing_ = static_cast(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( products_->AddChild(std::make_unique( 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( name, shell_script, config_name_, attributes, output_type, product_name, product)); return static_cast(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 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({"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 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 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(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"; }