blob: 8d81246750e140a87e012bf1b915ac84cacbb654 [file] [log] [blame]
// 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 <iterator>
#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 std::string_view& ext) {
for (size_t i = 0; i < std::size(kSourceTypeForExt); ++i) {
if (kSourceTypeForExt[i].ext == ext)
return kSourceTypeForExt[i].source_type;
}
return "text";
}
bool HasExplicitFileType(const std::string_view& ext) {
return ext == "dart";
}
bool IsSourceFileForIndexing(const std::string_view& 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 {
std::string_view 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;
std::string_view 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>(std::string(component),
std::string(component)));
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);
std::string_view 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) {
std::string_view 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";
}