xcodeproj 0.3.5 → 0.4.0.rc1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (32) hide show
  1. data/bin/xcodeproj +13 -0
  2. data/lib/xcodeproj.rb +13 -1
  3. data/lib/xcodeproj/command.rb +131 -0
  4. data/lib/xcodeproj/command/project_diff.rb +53 -0
  5. data/lib/xcodeproj/command/show.rb +35 -0
  6. data/lib/xcodeproj/command/target_diff.rb +43 -0
  7. data/lib/xcodeproj/config.rb +66 -38
  8. data/lib/xcodeproj/constants.rb +146 -0
  9. data/lib/xcodeproj/helper.rb +28 -0
  10. data/lib/xcodeproj/project.rb +462 -156
  11. data/lib/xcodeproj/project/object.rb +251 -138
  12. data/lib/xcodeproj/project/object/build_configuration.rb +27 -0
  13. data/lib/xcodeproj/project/object/build_file.rb +26 -0
  14. data/lib/xcodeproj/project/object/build_phase.rb +132 -61
  15. data/lib/xcodeproj/project/object/build_rule.rb +46 -0
  16. data/lib/xcodeproj/project/object/configuration_list.rb +47 -0
  17. data/lib/xcodeproj/project/object/container_item_proxy.rb +49 -0
  18. data/lib/xcodeproj/project/object/file_reference.rb +80 -48
  19. data/lib/xcodeproj/project/object/group.rb +213 -89
  20. data/lib/xcodeproj/project/object/native_target.rb +171 -114
  21. data/lib/xcodeproj/project/object/root_object.rb +66 -0
  22. data/lib/xcodeproj/project/object/target_dependency.rb +23 -0
  23. data/lib/xcodeproj/project/object_attributes.rb +382 -0
  24. data/lib/xcodeproj/project/object_list.rb +64 -118
  25. data/lib/xcodeproj/project/recursive_diff.rb +116 -0
  26. data/lib/xcodeproj/workspace.rb +56 -2
  27. metadata +38 -10
  28. data/lib/xcodeproj/project/association.rb +0 -54
  29. data/lib/xcodeproj/project/association/has_many.rb +0 -51
  30. data/lib/xcodeproj/project/association/has_one.rb +0 -39
  31. data/lib/xcodeproj/project/association/reflection.rb +0 -86
  32. data/lib/xcodeproj/project/object/configuration.rb +0 -97
@@ -1,146 +1,92 @@
1
1
  module Xcodeproj
2
2
  class Project
3
3
 
4
- # In case `scoped` is an Array the list's order is maintained.
5
- class PBXObjectList
6
- include Enumerable
7
-
8
- def initialize(represented_class, project)
9
- @represented_class = represented_class
10
- @project = project
11
- @callbacks = {}
12
-
13
- yield self if block_given?
14
- end
15
-
16
- # Specify callbacks for:
17
- # * :uuid_scope Returns the list of UUIDs to scope this list to.
18
- # * :push When an object is added to the list.
19
- def let(callback_name, &block)
20
- raise ArgumentError, "Incorrect callback `#{callback_name}'." unless [:uuid_scope, :push].include?(callback_name)
21
- @callbacks[callback_name] = block
22
- end
23
-
24
- def uuid_scope
25
- @callbacks[:uuid_scope].call
26
- end
27
-
28
- def empty?
29
- uuid_scope.empty?
30
- end
31
-
32
- def [](uuid)
33
- if uuid_scope.include?(uuid) && hash = @project.objects_hash[uuid]
34
- Object.const_get(hash['isa']).new(@project, uuid, hash)
35
- end
36
- end
37
-
38
- def add(klass, hash = {})
39
- object = klass.new(@project, nil, hash)
40
- self << object
41
- object
4
+ # This class represents an ordered relationship to many objects.
5
+ #
6
+ # It works in conjunction with the {AbstractObject} class to ensure that
7
+ # the project is not serialized with unreachable objects by updating the
8
+ # with reference count on modifications.
9
+ #
10
+ # @note Concerning the mutations methods it is safe to call only those
11
+ # which are overridden to inform objects reference count. Ideally all
12
+ # the array methods should be covered, but this is not done yet.
13
+ # Moreover it is a moving target because the methods of array
14
+ # usually are implemented in C
15
+ #
16
+ # @todo Cover all the array methods.
17
+ #
18
+ class ObjectList < Array
19
+
20
+ # {Xcodeproj} clients are not expected to create instances of
21
+ # {ObjectList}, it is always initialized empty and automatically by the
22
+ # synthesized methods generated by {AbstractObject.has_many}.
23
+ #
24
+ def initialize(attribute, owner)
25
+ @attribute = attribute
26
+ @owner = owner
42
27
  end
43
28
 
44
- def new(hash = {})
45
- add(@represented_class, hash)
46
- end
29
+ # @return [Array<Class>] The attribute that generated the list.
30
+ #
31
+ attr_reader :attribute
47
32
 
48
- # Run Ruby in debug mode to receive warnings about calls to :push when a
49
- # list does not have a callback registered for :push.
50
- def push(object)
51
- if @callbacks[:push]
52
- @callbacks[:push].call(object)
53
- else
54
- if $DEBUG
55
- warn "Pushed object onto a PBXObjectList that does not have a :push callback from: #{caller.first}"
56
- end
57
- end
58
- self
59
- end
60
- alias_method :<<, :push
33
+ # @return [Array<Class>] The object that owns the list.
34
+ #
35
+ attr_reader :owner
61
36
 
62
- def each
63
- uuid_scope.each do |uuid|
64
- yield self[uuid]
65
- end
37
+ # @return [Array<String>] The UUIDs of all the objects referenced by this
38
+ # list.
39
+ #
40
+ def uuids
41
+ map { |obj| obj.uuid }
66
42
  end
67
43
 
68
- def ==(other)
69
- self.to_a == other.to_a
70
- end
44
+ # @!group Notification enabled methods
71
45
 
72
- def size
73
- uuid_scope.size
74
- end
46
+ # TODO: the overridden methods are incomplete.
75
47
 
76
- # Since order can't always be guaranteed, these might need to move to an order specific subclass.
77
- def first
78
- to_a.first
79
- end
80
- def last
81
- to_a.last
48
+ def +(objects)
49
+ super
50
+ perform_additions_operations(objects)
82
51
  end
83
52
 
84
- def inspect
85
- "<PBXObjectList: #{map(&:inspect).join(', ')}>"
53
+ def <<(object)
54
+ super
55
+ perform_additions_operations(object)
86
56
  end
87
57
 
88
- def where(attributes)
89
- find { |o| o.matches_attributes?(attributes) }
58
+ def delete(object)
59
+ super
60
+ perform_deletion_operations(object)
90
61
  end
91
62
 
92
- # @todo is it really necessary to have an extra method for this?
93
- def object_named(name)
94
- where :name => name
95
- end
63
+ private
96
64
 
97
- # Returns a PBXObjectList instance of objects in the list.
98
- #
99
- # By default this list will scope the list by objects matching the
100
- # specified class and add objects, pushed onto the list, to the parent
101
- # list
102
- #
103
- # If a block is given the list instance is yielded so that the default
104
- # callbacks can be overridden.
105
- #
106
- # @param [AbstractPBXObject] klass The AbstractPBXObject subclass to
107
- # which the list should be scoped.
65
+ # Informs an object that it was added to the list. In practice it adds
66
+ # the owner of the list as referrer to the objects. It also validates the
67
+ # value.
108
68
  #
109
- # @yield [PBXObjectList] The list instance, allowing you to
110
- # easily override the callbacks.
69
+ # @return [void]
111
70
  #
112
- # @return [PBXObjectList<klass>] The list of matching objects.
113
- def list_by_class(klass)
114
- parent = self
115
- PBXObjectList.new(klass, @project) do |list|
116
- list.let(:push) do |object|
117
- # Objects added to the subselection should still use the same
118
- # callback as this list.
119
- parent << object
120
- end
121
- list.let(:uuid_scope) do
122
- parent.uuid_scope.select do |uuid|
123
- @project.objects_hash[uuid]['isa'] == klass.isa
124
- end
125
- end
126
- yield list if block_given?
71
+ def perform_additions_operations(objects)
72
+ objects = [objects] unless objects.is_a?(Array)
73
+ objects.each do |obj|
74
+ obj.add_referrer(owner)
75
+ attribute.validate_value(obj)
127
76
  end
128
77
  end
129
78
 
130
- # This only makes sense on those with a specific represented class. Not the main objects list.
131
- def method_missing(name, *args, &block)
132
- if @represented_class.respond_to?(name)
133
- object = @represented_class.send(name, @project, *args)
134
- # The callbacks are only for AbstractPBXObject instances instantiated
135
- # from the class method that we forwarded the message to.
136
- self << object if object.is_a?(Object::AbstractPBXObject)
137
- object
138
- else
139
- super
79
+ # Informs an object that it was removed from to the list, so it can
80
+ # remove it from its referrers and take the appropriate actions.
81
+ #
82
+ # @return [void]
83
+ #
84
+ def perform_deletion_operations(objects)
85
+ objects = [objects] unless objects.is_a?(Array)
86
+ objects.each do |obj|
87
+ obj.remove_referrer(owner)
140
88
  end
141
89
  end
142
-
143
90
  end
144
-
145
91
  end
146
92
  end
@@ -0,0 +1,116 @@
1
+ class Hash
2
+
3
+ # Computes the recursive difference of two hashes.
4
+ #
5
+ # Useful to compare two projects.
6
+ #
7
+ # Inspired from 'active_support/core_ext/hash/diff'.
8
+ #
9
+ # @example
10
+ # h1 = { :common => 'value', :changed => 'v1' }
11
+ # h2 = { :common => 'value', :changed => 'v2', :addition => 'new_value' }
12
+
13
+ # h1.recursive_diff(h2) == {
14
+ # :changed => {
15
+ # :self => 'v1',
16
+ # :other => 'v2'
17
+ # },
18
+ # :addition => {
19
+ # :self => nil,
20
+ # :other => 'new_value'
21
+ # }
22
+ # } #=> true
23
+ #
24
+ # @return [Hash] Returns the recursive difference of a hash.
25
+ #
26
+ def recursive_diff(other, self_key = 'self', other_key = 'other')
27
+ if other.is_a?(Hash)
28
+ r = {}
29
+ all_keys = self.keys + other.keys
30
+ all_keys.each do |key|
31
+ v1 = self[key]
32
+ v2 = other[key]
33
+ diff = v1.recursive_diff(v2, self_key, other_key)
34
+ r[key] = diff if diff
35
+ end
36
+ r unless r == {}
37
+ else
38
+ super
39
+ end
40
+ end
41
+
42
+ # @return [void]
43
+ #
44
+ def recursive_delete(key_to_delete)
45
+ delete(key_to_delete)
46
+ self.each do |key, value|
47
+ case value
48
+ when Hash
49
+ value.recursive_delete(key_to_delete)
50
+ when Array
51
+ value.each { |v| v.recursive_delete(key_to_delete) if v.is_a?(Hash)}
52
+ end
53
+ end
54
+ end
55
+ end
56
+
57
+
58
+ class Array
59
+
60
+ # @return [Array]
61
+ #
62
+ def recursive_diff(other, self_key = 'self', other_key = 'other')
63
+ if other.is_a?(Array)
64
+ new_objects_self = (self - other)
65
+ new_objects_other = (other - self)
66
+ unmatched_objects_self = []
67
+ array_result = []
68
+
69
+ # Try to match objects to reduce noise
70
+ new_objects_self.each do |value|
71
+ if value.is_a?(Hash)
72
+ other_value = new_objects_other.find do |other|
73
+ other.is_a?(Hash) && (value['displayName'] == other['displayName'])
74
+ end
75
+
76
+ if other_value
77
+ new_objects_other.delete(other_value)
78
+ match_diff = value.recursive_diff(other_value, self_key, other_key)
79
+ array_result << { value['displayName'] => match_diff} unless match_diff == {}
80
+ else
81
+ unmatched_objects_self << value
82
+ end
83
+ end
84
+ end
85
+
86
+ unless unmatched_objects_self.empty?
87
+ array_result << {
88
+ self_key => unmatched_objects_self.map do |v|
89
+ { v['displayName'] => v }
90
+ end
91
+ }
92
+ end
93
+
94
+ unless new_objects_other.empty?
95
+ array_result << {
96
+ other_key => new_objects_other.map do |v|
97
+ { v['displayName'] => v }
98
+ end
99
+ }
100
+ end
101
+
102
+ array_result unless array_result == []
103
+ else
104
+ super
105
+ end
106
+ end
107
+ end
108
+
109
+ class Object
110
+
111
+ # @return [Hash]
112
+ #
113
+ def recursive_diff(other, self_key = 'self', other_key = 'other')
114
+ { self_key => self, other_key => other } unless self == other
115
+ end
116
+ end
@@ -2,11 +2,33 @@ require 'fileutils'
2
2
  require 'rexml/document'
3
3
 
4
4
  module Xcodeproj
5
+
6
+ # The {Workspace} allows to generate, read and serialize Xcode Workspace
7
+ # documents.
8
+ #
5
9
  class Workspace
10
+
11
+ # @return [Array<String>] the paths of the projects contained in the
12
+ # workspace.
13
+ #
14
+ attr_reader :projpaths
15
+
16
+ # Returns a new workspace initialized with the given `xcodeproj` paths.
17
+ #
18
+ # @param [String] projpaths
19
+ # one or more `xcodeproj` paths.
20
+ #
6
21
  def initialize(*projpaths)
7
22
  @projpaths = projpaths
8
23
  end
9
24
 
25
+ # Returns a workspace generated by reading the contents of the given path.
26
+ #
27
+ # @param [String] path
28
+ # the path of the `xcworkspace` file.
29
+ #
30
+ # @return [Workspace] the generated workspace.
31
+ #
10
32
  def self.new_from_xcworkspace(path)
11
33
  begin
12
34
  from_s(File.read(File.join(path, 'contents.xcworkspacedata')))
@@ -15,6 +37,14 @@ module Xcodeproj
15
37
  end
16
38
  end
17
39
 
40
+ # Returns a workspace generated by reading the contents of the given
41
+ # XML representation.
42
+ #
43
+ # @param [String] xml
44
+ # the XML representation of the workspace.
45
+ #
46
+ # @return [Workspace] the generated workspace.
47
+ #
18
48
  def self.from_s(xml)
19
49
  document = REXML::Document.new(xml)
20
50
  projpaths = document.get_elements("/Workspace/FileRef").map do |node|
@@ -23,18 +53,35 @@ module Xcodeproj
23
53
  new(*projpaths)
24
54
  end
25
55
 
26
- attr_reader :projpaths
27
-
56
+ # Adds a new path to the list of the of projects contained in the
57
+ # workspace.
58
+ #
59
+ # @param [String] projpath
60
+ # The path of the project to add.
61
+ #
62
+ # @return [void]
63
+ #
28
64
  def <<(projpath)
29
65
  @projpaths << projpath
30
66
  end
31
67
 
68
+ # Checks if the workspace contains the project with the given path.
69
+ #
70
+ # @param [String] projpath
71
+ # The path of the project to add.
72
+ #
73
+ # @return [Boolean] whether the project is contained in the workspace.
74
+ #
32
75
  def include?(projpath)
33
76
  @projpaths.include?(projpath)
34
77
  end
35
78
 
79
+ # The template to generate a workspace XML representation.
80
+ #
36
81
  TEMPLATE = %q[<?xml version="1.0" encoding="UTF-8"?><Workspace version="1.0"></Workspace>]
37
82
 
83
+ # @return [String] the XML representation of the workspace.
84
+ #
38
85
  def to_s
39
86
  REXML::Document.new(TEMPLATE).tap do |document|
40
87
  @projpaths.each do |projpath|
@@ -45,6 +92,13 @@ module Xcodeproj
45
92
  end.to_s
46
93
  end
47
94
 
95
+ # Saves the workspace at the given `xcworkspace` path.
96
+ #
97
+ # @param [String] path
98
+ # the path where to save the project.
99
+ #
100
+ # @return [void]
101
+ #
48
102
  def save_as(path)
49
103
  FileUtils.mkdir_p(path)
50
104
  File.open(File.join(path, 'contents.xcworkspacedata'), 'w') do |out|
metadata CHANGED
@@ -1,15 +1,15 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: xcodeproj
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.5
5
- prerelease:
4
+ version: 0.4.0.rc1
5
+ prerelease: 6
6
6
  platform: ruby
7
7
  authors:
8
8
  - Eloy Duran
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-10-19 00:00:00.000000000 Z
12
+ date: 2012-10-21 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: activesupport
@@ -27,27 +27,54 @@ dependencies:
27
27
  - - ~>
28
28
  - !ruby/object:Gem::Version
29
29
  version: 3.2.6
30
+ - !ruby/object:Gem::Dependency
31
+ name: colored
32
+ requirement: !ruby/object:Gem::Requirement
33
+ none: false
34
+ requirements:
35
+ - - ~>
36
+ - !ruby/object:Gem::Version
37
+ version: '1.2'
38
+ type: :runtime
39
+ prerelease: false
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ~>
44
+ - !ruby/object:Gem::Version
45
+ version: '1.2'
30
46
  description: Xcodeproj lets you create and modify Xcode projects from Ruby. Script
31
47
  boring management tasks or build Xcode-friendly libraries. Also includes support
32
48
  for Xcode workspaces (.xcworkspace) and configuration files (.xcconfig).
33
49
  email: eloy.de.enige@gmail.com
34
- executables: []
50
+ executables:
51
+ - xcodeproj
35
52
  extensions:
36
53
  - ext/xcodeproj/extconf.rb
37
54
  extra_rdoc_files: []
38
55
  files:
56
+ - lib/xcodeproj/command/project_diff.rb
57
+ - lib/xcodeproj/command/show.rb
58
+ - lib/xcodeproj/command/target_diff.rb
59
+ - lib/xcodeproj/command.rb
39
60
  - lib/xcodeproj/config.rb
40
- - lib/xcodeproj/project/association/has_many.rb
41
- - lib/xcodeproj/project/association/has_one.rb
42
- - lib/xcodeproj/project/association/reflection.rb
43
- - lib/xcodeproj/project/association.rb
61
+ - lib/xcodeproj/constants.rb
62
+ - lib/xcodeproj/helper.rb
63
+ - lib/xcodeproj/project/object/build_configuration.rb
64
+ - lib/xcodeproj/project/object/build_file.rb
44
65
  - lib/xcodeproj/project/object/build_phase.rb
45
- - lib/xcodeproj/project/object/configuration.rb
66
+ - lib/xcodeproj/project/object/build_rule.rb
67
+ - lib/xcodeproj/project/object/configuration_list.rb
68
+ - lib/xcodeproj/project/object/container_item_proxy.rb
46
69
  - lib/xcodeproj/project/object/file_reference.rb
47
70
  - lib/xcodeproj/project/object/group.rb
48
71
  - lib/xcodeproj/project/object/native_target.rb
72
+ - lib/xcodeproj/project/object/root_object.rb
73
+ - lib/xcodeproj/project/object/target_dependency.rb
49
74
  - lib/xcodeproj/project/object.rb
75
+ - lib/xcodeproj/project/object_attributes.rb
50
76
  - lib/xcodeproj/project/object_list.rb
77
+ - lib/xcodeproj/project/recursive_diff.rb
51
78
  - lib/xcodeproj/project.rb
52
79
  - lib/xcodeproj/workspace.rb
53
80
  - lib/xcodeproj.rb
@@ -55,6 +82,7 @@ files:
55
82
  - ext/xcodeproj/xcodeproj_ext.c
56
83
  - README.md
57
84
  - LICENSE
85
+ - bin/xcodeproj
58
86
  homepage: https://github.com/cocoapods/xcodeproj
59
87
  licenses:
60
88
  - MIT
@@ -71,7 +99,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
71
99
  version: '0'
72
100
  segments:
73
101
  - 0
74
- hash: 1845437969595669642
102
+ hash: 1378880103836164332
75
103
  required_rubygems_version: !ruby/object:Gem::Requirement
76
104
  none: false
77
105
  requirements: