Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for adding subprojects #172

Merged
merged 10 commits into from
Jul 27, 2014
8 changes: 7 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,16 @@

## Master

* [PlistHelper] Now the `plutil` tool is used to save the files if available to
* `PlistHelper`: Now the `plutil` tool is used to save the files if available to
produce output consistent with Xcode.
[Fabio Pelosin](https://github.com/irrationalfab)

* `PBXGroup`: Now file references to Xcode projects are properly handled and
setup. Also the `ObjectDictionary` class has been improved and now can be
used to edit the attributes using it.
[Fabio Pelosin](https://github.com/irrationalfab)
[Xcodeproj#172](https://github.com/CocoaPods/Xcodeproj/pull/172)

## 0.18.0

###### Enhancements
Expand Down
17 changes: 10 additions & 7 deletions lib/xcodeproj/constants.rb
Original file line number Diff line number Diff line change
Expand Up @@ -71,21 +71,24 @@ module Constants
FILE_TYPES_BY_EXTENSION = {
'a' => 'archive.ar',
'app' => 'wrapper.application',
'bundle' => 'wrapper.plug-in',
'dylib' => 'compiled.mach-o.dylib',
'framework' => 'wrapper.framework',
'bundle' => 'wrapper.plug-in',
'h' => 'sourcecode.c.h',
'm' => 'sourcecode.c.objc',
'markdown' => 'text',
'mdimporter' => 'wrapper.cfbundle',
'octest' => 'wrapper.cfbundle',
'pch' => 'sourcecode.c.h',
'xcconfig' => 'text.xcconfig',
'xcdatamodel' => 'wrapper.xcdatamodel',
'xib' => 'file.xib',
'plist' => 'text.plist.xml',
'sh' => 'text.script.sh',
'swift' => 'sourcecode.swift',
'plist' => 'text.plist.xml',
'markdown' => 'text',
'xcassets' => 'folder.assetcatalog',
'xctest' => 'wrapper.cfbundle'
'xcconfig' => 'text.xcconfig',
'xcdatamodel' => 'wrapper.xcdatamodel',
'xcodeproj' => 'wrapper.pb-project',
'xctest' => 'wrapper.cfbundle',
'xib' => 'file.xib',
}.freeze

# @return [Hash] The uniform type identifier of various product types.
Expand Down
59 changes: 59 additions & 0 deletions lib/xcodeproj/project/name_helper.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
module Xcodeproj
class Project
module Object
# Converts between camel case names used in the xcodeproj plist files
# and the ruby symbols used to represent them.
#
module NameHelper
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The proliferation of ‘helpers’ is ok, but they should be named more semantically. If this helper is responsible for changing case of a string, then I don’t think NameHelper really clarifies that. Also, ‘name’ doesn’t seem to be appropriate, isn’t it more like ‘keys’ ?

So how about something like PlistKeyCaseConverter?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@irrationalfab ^^

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is not about keys only but also the attributes of the objects. PlistCaseConverter works for me

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Gotcha, maybe just CaseConverter then? The Plist part in my suggestion was about the ‘keys being those in the plist’.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

# @return [String] The plist equivalent of the given Ruby name.
#
# @param [Symbol, String] name
# The name to convert
#
# @param [Symbol, Nil] type
# The type of conversion. Pass `nil` for normal camel case and
# `:lower` for camel case starting with a lower case letter.
#
# @example
# NameHelper.convert_to_plist(:project_ref) #=> ProjectRef
#
def self.convert_to_plist(name, type = nil)
case name
when :remote_global_id_string
'remoteGlobalIDString'
else
if type == :lower
cache = self.plist_cache[:lower] ||= {}
cache[name] ||= name.to_s.camelize(:lower)
else
cache = self.plist_cache[:normal] ||= {}
cache[name] ||= name.to_s.camelize()
end
end
end

# @return [Symbol] The Ruby equivalent of the given plist name.
#
# @param [String] name
# The name to convert
#
# @example
# NameHelper.convert_to_ruby('ProjectRef') #=> :project_ref
#
def self.convert_to_ruby(name)
name.to_s.underscore.to_sym
end

# @return [Hash] A cache for the conversion to the Plist format.
#
# @note A cache is used because this operation is performed for each
# attribute of the project when it is saved and caching it has
# an important performance benefit.
#
def self.plist_cache
@plist_cache ||= {}
end
end
end
end
end
29 changes: 18 additions & 11 deletions lib/xcodeproj/project/object.rb
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,9 @@ def initialize(project, uuid)
@project, @uuid = project, uuid
@isa = self.class.isa
@referrers = []
raise "[Xcodeproj] Attempt to initialize an abstract class." unless @isa.match(/^(PBX|XC)/)
unless @isa.match(/^(PBX|XC)/)
raise "[Xcodeproj] Attempt to initialize an abstract class."
end
end

# Initializes the object with the default values of simple attributes.
Expand Down Expand Up @@ -261,7 +263,10 @@ def remove_reference(object)
def configure_with_plist(objects_by_uuid_plist)
object_plist = objects_by_uuid_plist[uuid].dup

raise "[Xcodeproj] Attempt to initialize `#{isa}` from plist with different isa `#{object_plist}`" unless object_plist['isa'] == isa
unless object_plist['isa'] == isa
raise "[Xcodeproj] Attempt to initialize `#{isa}` from plist with " \
"different isa `#{object_plist}`"
end
object_plist.delete('isa')

simple_attributes.each do |attrb|
Expand Down Expand Up @@ -303,9 +308,10 @@ def configure_with_plist(objects_by_uuid_plist)
end

unless object_plist.empty?
raise "[!] Xcodeproj doesn't know about the following attributes " \
"#{object_plist.inspect} for the '#{isa}' isa.\n" \
"Please file an issue: https://github.com/CocoaPods/Xcodeproj/issues/new"
raise "[!] Xcodeproj doesn't know about the following " \
"attributes #{object_plist.inspect} for the '#{isa}' isa." \
"\nPlease file an issue: " \
"https://github.com/CocoaPods/Xcodeproj/issues/new"
end
end

Expand Down Expand Up @@ -333,15 +339,15 @@ def configure_with_plist(objects_by_uuid_plist)
def object_with_uuid(uuid, objects_by_uuid_plist, attribute)
unless object = project.objects_by_uuid[uuid] || project.new_from_plist(uuid, objects_by_uuid_plist)
UI.warn "`#{inspect}` attempted to initialize an object with " \
"an unknown UUID. `#{uuid}` for attribute: `#{attribute.name}`."\
" This can be the result of a merge and the unknown UUID is " \
"being discarded."
"an unknown UUID. `#{uuid}` for attribute: " \
"`#{attribute.name}`. This can be the result of a merge and " \
"the unknown UUID is being discarded."
end
object
rescue NameError
attributes = objects_by_uuid_plist[uuid]
raise "`#{isa}` attempted to initialize an object with unknown ISA "\
"`#{attributes['isa']}` from attributes: `#{attributes}`\n" \
"`#{attributes['isa']}` from attributes: `#{attributes}`\n" \
"Please file an issue: https://github.com/CocoaPods/Xcodeproj/issues/new"
end

Expand Down Expand Up @@ -460,9 +466,10 @@ def inspect
end
end

require 'xcodeproj/project/object_list'
require 'xcodeproj/project/object_dictionary'
require 'xcodeproj/project/name_helper'
require 'xcodeproj/project/object_attributes'
require 'xcodeproj/project/object_dictionary'
require 'xcodeproj/project/object_list'

# Required because some classes have cyclical references to each other.
#
Expand Down
4 changes: 2 additions & 2 deletions lib/xcodeproj/project/object/file_reference.rb
Original file line number Diff line number Diff line change
Expand Up @@ -245,7 +245,7 @@ def proxy?
#
def project_reference_metadata
project.root_object.project_references.find do |project_reference|
project_reference['ProjectRef'] == self
project_reference[:project_ref] == self
end
end

Expand Down Expand Up @@ -313,7 +313,7 @@ def remove_from_project
if project_reference = project_reference_metadata
file_reference_proxies.each(&:remove_from_project)
target_dependency_proxies.each(&:remove_from_project)
project_reference['ProductGroup'].remove_from_project
project_reference[:product_group].remove_from_project
project.root_object.project_references.delete(project_reference)
end
super
Expand Down
59 changes: 58 additions & 1 deletion lib/xcodeproj/project/object/helpers/file_references_factory.rb
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,11 @@ class << self
# @return [PBXFileReference, XCVersionGroup] The new reference.
#
def new_reference(group, path, source_tree)
if File.extname(path).downcase == '.xcdatamodeld'
ref = case File.extname(path).downcase
when '.xcdatamodeld'
ref = new_xcdatamodeld(group, path, source_tree)
when '.xcodeproj'
ref = new_subproject(group, path, source_tree)
else
ref = new_file_reference(group, path, source_tree)
end
Expand Down Expand Up @@ -154,6 +157,60 @@ def new_xcdatamodeld(group, path, source_tree)
ref
end

# Creates a file reference to another Xcode subproject and setups the
# proxies to the targets.
#
# @param [PBXGroup] group
# The group to which to add the reference.
#
# @param [#to_s] path
# The, preferably absolute, path of the reference.
#
# @param [Symbol] source_tree
# The source tree key to use to configure the path (@see
# GroupableHelper::SOURCE_TREES_BY_KEY).
#
# @note To analyze the targets the given project is read and thus
# it should already exist in the disk.
#
# @return [PBXFileReference] The new file reference.
#
def new_subproject(group, path, source_tree)
ref = new_file_reference(group, path, source_tree)
ref.include_in_index = nil

product_group_ref = group.project.new(PBXGroup)
product_group_ref.name = 'Products'
product_group_ref.source_tree = '<group>'

subproj = Project.open(path)
subproj.products_group.files.each do |product_reference|
container_proxy = group.project.new(PBXContainerItemProxy)
container_proxy.container_portal = ref.uuid
container_proxy.proxy_type = '2'
container_proxy.remote_global_id_string = product_reference.uuid
container_proxy.remote_info = 'Subproject'


reference_proxy = group.project.new(PBXReferenceProxy)
extension = File.extname(product_reference.path)[1..-1]
reference_proxy.file_type = Constants::FILE_TYPES_BY_EXTENSION[extension]
reference_proxy.path = product_reference.path
reference_proxy.remote_ref = container_proxy
reference_proxy.source_tree = 'BUILT_PRODUCTS_DIR'

product_group_ref << reference_proxy
end

attribute = PBXProject.references_by_keys_attributes.find {|attribute| attribute.name == :project_references }
project_reference = ObjectDictionary.new(attribute, group.project.root_object)
project_reference[:project_ref] = ref
project_reference[:product_group] = product_group_ref
group.project.root_object.project_references << project_reference

ref
end

# Configures a file reference according to the extension to math
# Xcode behaviour.
#
Expand Down
Loading