xcodeproj 0.9.0 → 0.10.0

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.
@@ -0,0 +1,182 @@
1
+ require 'xcodeproj/project/object/helpers/groupable_helper'
2
+
3
+ module Xcodeproj
4
+ class Project
5
+ module Object
6
+ class FileReferencesFactory
7
+ class << self
8
+
9
+ # Creates a new reference with the given path and adds it to the
10
+ # given group. The reference is configured according to the extension
11
+ # of the path.
12
+ #
13
+ # @param [PBXGroup] group
14
+ # The group to which to add the reference.
15
+ #
16
+ # @param [#to_s] path
17
+ # The, preferably absolute, path of the reference.
18
+ #
19
+ # @param [Symbol] source_tree
20
+ # The source tree key to use to configure the path (@see
21
+ # GroupableHelper::SOURCE_TREES_BY_KEY).
22
+ #
23
+ # @return [PBXFileReference, XCVersionGroup] The new reference.
24
+ #
25
+ def new_reference(group, path, source_tree)
26
+ if File.extname(path).downcase == '.xcdatamodeld'
27
+ ref = new_xcdatamodeld(group, path, source_tree)
28
+ else
29
+ ref = new_file_reference(group, path, source_tree)
30
+ end
31
+
32
+ configure_defaults_for_file_reference(ref)
33
+ ref
34
+ end
35
+
36
+ # Creates a file reference to a static library and adds it to the
37
+ # given group.
38
+ #
39
+ # @param [PBXGroup] group
40
+ # The group to which to add the reference.
41
+ #
42
+ # @param [#to_s] product_name
43
+ # The name of the static library.
44
+ #
45
+ # @return [PBXFileReference] The new file reference.
46
+ #
47
+ def new_static_library(group, product_name)
48
+ ref = new_reference(group, "lib#{product_name}.a", :built_products)
49
+ ref.include_in_index = '0'
50
+ ref.set_explicit_file_type
51
+ ref
52
+ end
53
+
54
+ # Creates a file reference to a new bundle and adds it to the given
55
+ # group.
56
+ #
57
+ # @param [PBXGroup] group
58
+ # The group to which to add the reference.
59
+ #
60
+ # @param [#to_s] product_name
61
+ # The name of the bundle.
62
+ #
63
+ # @return [PBXFileReference] The new file reference.
64
+ #
65
+ def new_bundle(group, product_name)
66
+ ref = new_reference(group, "#{product_name}.bundle", :built_products)
67
+ ref.include_in_index = '0'
68
+ ref.set_explicit_file_type("wrapper.cfbundle")
69
+ ref
70
+ end
71
+
72
+
73
+ private
74
+
75
+ # @group Private Helpers
76
+ #-------------------------------------------------------------------#
77
+
78
+ # Creates a new file reference with the given path and adds it to the
79
+ # given group.
80
+ #
81
+ # @param [PBXGroup] group
82
+ # The group to which to add the reference.
83
+ #
84
+ # @param [#to_s] path
85
+ # The, preferably absolute, path of the reference.
86
+ #
87
+ # @param [Symbol] source_tree
88
+ # The source tree key to use to configure the path (@see
89
+ # GroupableHelper::SOURCE_TREES_BY_KEY).
90
+ #
91
+ # @return [PBXFileReference] The new file reference.
92
+ #
93
+ def new_file_reference(group, path, source_tree)
94
+ path = Pathname.new(path)
95
+ ref = group.project.new(PBXFileReference)
96
+ group.children << ref
97
+ GroupableHelper.set_path_with_source_tree(ref, path, source_tree)
98
+ ref.set_last_known_file_type
99
+ ref
100
+ end
101
+
102
+ # Creates a new version group reference to an xcdatamodeled adding
103
+ # the xcdatamodel files included in the wrapper as children file
104
+ # references.
105
+ #
106
+ # @param [PBXGroup] group
107
+ # The group to which to add the reference.
108
+ #
109
+ # @param [#to_s] path
110
+ # The, preferably absolute, path of the reference.
111
+ #
112
+ # @param [Symbol] source_tree
113
+ # The source tree key to use to configure the path (@see
114
+ # GroupableHelper::SOURCE_TREES_BY_KEY).
115
+ #
116
+ # @note To match Xcode behaviour the current version is read from
117
+ # the .xccurrentversion file, if it doesn't exist the last
118
+ # xcdatamodel according to its path is set as the current
119
+ # version.
120
+ #
121
+ # @return [XCVersionGroup] The new reference.
122
+ #
123
+ def new_xcdatamodeld(group, path, source_tree)
124
+ path = Pathname.new(path)
125
+ ref = group.project.new(XCVersionGroup)
126
+ group.children << ref
127
+ GroupableHelper.set_path_with_source_tree(ref, path, source_tree)
128
+ ref.version_group_type = 'wrapper.xcdatamodel'
129
+
130
+ current_version_name = nil
131
+ if path.exist?
132
+ path.children.each do |child_path|
133
+ if File.extname(child_path) == '.xcdatamodel'
134
+ child_ref = new_file_reference(ref, child_path, :group)
135
+ last_child_ref = child_ref
136
+ elsif File.basename(child_path) == '.xccurrentversion'
137
+ full_path = path + child_path
138
+ xccurrentversion = Xcodeproj.read_plist(full_path)
139
+ current_version_name = xccurrentversion['_XCCurrentVersionName']
140
+ end
141
+ end
142
+
143
+ if current_version_name
144
+ ref.current_version = ref.children.find do |obj|
145
+ obj.path.split('/').last == current_version_name
146
+ end
147
+ end
148
+ end
149
+
150
+ ref
151
+ end
152
+
153
+ # Configures a file reference according to the extension to math
154
+ # Xcode behaviour.
155
+ #
156
+ # @param [PBXFileReference] ref
157
+ # The file reference to configure.
158
+ #
159
+ # @note To closely match the Xcode behaviour the name attribute of
160
+ # the file reference is set only if the path of the file is
161
+ # not equal to the path of the group.
162
+ #
163
+ # @return [void]
164
+ #
165
+ def configure_defaults_for_file_reference(ref)
166
+ if ref.path.include?('/')
167
+ ref.name = ref.path.split('/').last
168
+ end
169
+
170
+ if File.extname(ref.path).downcase == '.framework'
171
+ ref.include_in_index = nil
172
+ end
173
+ end
174
+
175
+ #-------------------------------------------------------------------#
176
+
177
+ end
178
+ end
179
+ end
180
+ end
181
+ end
182
+
@@ -0,0 +1,210 @@
1
+ module Xcodeproj
2
+ class Project
3
+ module Object
4
+ class GroupableHelper
5
+ class << self
6
+
7
+ # @param [PBXGroup, PBXFileReference] object
8
+ # The object to analyze.
9
+ #
10
+ # @return [PBXGroup, PBXProject] The parent of the object.
11
+ #
12
+ def parent(object)
13
+ check_parents_integrity(object)
14
+ object.referrers.first
15
+ end
16
+
17
+ # @param [PBXGroup, PBXFileReference] object
18
+ # The object to analyze.
19
+ #
20
+ # @return [Array<PBXGroup, PBXProject>] The parents of the object.
21
+ #
22
+ def parents(object)
23
+ if main_group?(object)
24
+ []
25
+ else
26
+ parent = parent(object)
27
+ parents(parent).push(parent)
28
+ end
29
+ end
30
+
31
+ # @param [PBXGroup, PBXFileReference] object
32
+ # The object to analyze.
33
+ #
34
+ # @return [String] A representation of the group hierarchy.
35
+ #
36
+ def hierarchy_path(object)
37
+ unless main_group?(object)
38
+ "#{parent(object).hierarchy_path}/#{object.display_name}"
39
+ end
40
+ end
41
+
42
+ # @param [PBXGroup, PBXFileReference] object
43
+ # The object to analyze.
44
+ #
45
+ # @return [Bool] Wether the object is the main group of the project.
46
+ #
47
+ def main_group?(object)
48
+ object.equal?(object.project.main_group)
49
+ end
50
+
51
+ # Moves the object to a new parent.
52
+ #
53
+ # @param [PBXGroup, PBXFileReference] object
54
+ # The object to move.
55
+ #
56
+ # @param [PBXGroup] new_parent
57
+ # The new parent.
58
+ #
59
+ # @return [void]
60
+ #
61
+ def move(object, new_parent)
62
+ unless object
63
+ raise "[Xcodeproj] Attempt to move nil object to `#{new_parent}`."
64
+ end
65
+ unless new_parent
66
+ raise "[Xcodeproj] Attempt to move object `#{object}` to nil parent."
67
+ end
68
+ if new_parent.equal?(object)
69
+ raise "[Xcodeproj] Attempt to move object `#{object}` to itself."
70
+ end
71
+ if parents(new_parent).include?(object)
72
+ raise "[Xcodeproj] Attempt to move object `#{object}` to a child object `#{new_parent}`."
73
+ end
74
+
75
+ object.parent.children.delete(object)
76
+ new_parent << object
77
+ end
78
+
79
+ # @param [PBXGroup, PBXFileReference] object
80
+ # The object to analyze.
81
+ #
82
+ # @return [Pathname] The absolute path of the object resolving the
83
+ # source tree.
84
+ #
85
+ def real_path(object)
86
+ source_tree = source_tree_real_path(object)
87
+ path = object.path || ''
88
+ if source_tree
89
+ source_tree + path
90
+ else
91
+ Pathname(path)
92
+ end
93
+ end
94
+
95
+ # @param [PBXGroup, PBXFileReference] object
96
+ # The object to analyze.
97
+ #
98
+ # @return [Pathname] The absolute path of the source tree of the
99
+ # object.
100
+ #
101
+ def source_tree_real_path(object)
102
+ case object.source_tree
103
+ when '<group>'
104
+ if parent(object).isa == 'PBXProject'
105
+ object.project.path.dirname
106
+ else
107
+ real_path(parent(object))
108
+ end
109
+ when 'SOURCE_ROOT'
110
+ object.project.path.dirname
111
+ when '<absolute>', 'BUILT_PRODUCTS_DIR', 'DEVELOPER_DIR'
112
+ nil
113
+ else
114
+ raise "[Xcodeproj] Unable to compute the source tree for " \
115
+ " `#{object.display_name}`: `#{object.source_tree}`"
116
+ end
117
+ end
118
+
119
+ # @return [Hash{Symbol => String}] The source tree values by they
120
+ # symbol representation.
121
+ #
122
+ SOURCE_TREES_BY_KEY = {
123
+ :absolute => '<absolute>',
124
+ :group => '<group>',
125
+ :project => 'SOURCE_ROOT',
126
+ :built_products => 'BUILT_PRODUCTS_DIR',
127
+ :developer_dir => 'DEVELOPER_DIR',
128
+ :sdk_root => 'SDKROOT',
129
+ }.freeze
130
+
131
+ # Sets the path of the given object according to the provided source
132
+ # tree key. The path is converted to relative according to the real
133
+ # path of the source tree for group and project source trees, if both
134
+ # paths are relative or absolute. Otherwise the path is set as
135
+ # provided.
136
+ #
137
+ # @param [PBXGroup, PBXFileReference] object
138
+ # The object whose path needs to be set.
139
+ #
140
+ # @param [#to_s] path
141
+ # The path.
142
+ #
143
+ # @param [Symbol] source_tree_key
144
+ # The source tree type, see {SOURCE_TREES_BY_KEY} for
145
+ # acceptable values.
146
+ #
147
+ # @return [void]
148
+ #
149
+ def set_path_with_source_tree(object, path, source_tree_key)
150
+ path = Pathname.new(path)
151
+ source_tree = SOURCE_TREES_BY_KEY[source_tree_key]
152
+ object.source_tree = source_tree
153
+
154
+ unless source_tree
155
+ raise "[Xcodeproj] Unrecognized source tree option `#{source_tree_key}` for path `#{path}`"
156
+ end
157
+
158
+ if source_tree_key == :absolute
159
+ unless path.absolute?
160
+ raise "[Xcodeproj] Attempt to set a relative path with an " \
161
+ "absolute source tree: `#{path}`"
162
+ end
163
+ object.path = path.to_s
164
+ elsif source_tree_key == :group || source_tree_key == :project
165
+ source_tree_real_path = GroupableHelper.source_tree_real_path(object)
166
+ if source_tree_real_path && source_tree_real_path.absolute? == path.absolute?
167
+ relative_path = path.relative_path_from(source_tree_real_path)
168
+ object.path = relative_path.to_s
169
+ else
170
+ object.path = path.to_s
171
+ end
172
+ else
173
+ object.path = path.to_s
174
+ end
175
+ end
176
+
177
+ private
178
+
179
+ # @group Helpers
180
+ #-------------------------------------------------------------------#
181
+
182
+ # Checks whether there is a single identifiable parent and raises
183
+ # otherwise.
184
+ #
185
+ # @return [void]
186
+ #
187
+ def check_parents_integrity(object)
188
+ referrers_count = object.referrers.count
189
+ if referrers_count > 1
190
+ referrers_count = object.referrers.select{ |obj| obj.isa == 'PBXGroup' }.count
191
+ end
192
+
193
+ if referrers_count == 0
194
+ raise "[Xcodeproj] Consistency issue: no parent " \
195
+ "for object `#{object.display_name}`: "\
196
+ "#{object.referrers}"
197
+ elsif referrers_count > 1
198
+ raise "[Xcodeproj] Consistency issue: unexpected multiple parents " \
199
+ "for object `#{object.display_name}`: "\
200
+ "#{object.referrers}"
201
+ end
202
+ end
203
+
204
+ #-------------------------------------------------------------------#
205
+
206
+ end
207
+ end
208
+ end
209
+ end
210
+ end
@@ -71,6 +71,29 @@ module Xcodeproj
71
71
  build_configuration_list.build_configurations
72
72
  end
73
73
 
74
+ # Adds a new build configuration to the target and populates its with
75
+ # default settings according to the provided type.
76
+ #
77
+ # @note If a build configuration with the given name is already
78
+ # present no new build configuration is added.
79
+ #
80
+ # @param [String] name
81
+ # The name of the build configuration.
82
+ #
83
+ # @param [Symbol] type
84
+ # The type of the build configuration used to populate the build
85
+ # settings, must be :debug or :release.
86
+ #
87
+ def add_build_configuration(name, type, skip_existing_names = true)
88
+ unless build_configuration_list[name]
89
+ build_configuration = project.new(XCBuildConfiguration)
90
+ build_configuration.name = name
91
+ build_configuration.build_settings = ProjectHelper.common_build_settings(type, platform_name, deployment_target, product_type)
92
+ build_configuration_list.build_configurations << build_configuration
93
+ build_configuration
94
+ end
95
+ end
96
+
74
97
  # @param [String] build_configuration_name
75
98
  # the name of a build configuration.
76
99
  #
@@ -49,16 +49,14 @@ module Xcodeproj
49
49
  product = product_group.new_static_library(name)
50
50
  target.product_reference = product
51
51
 
52
+ # Build phases
53
+ target.build_phases << project.new(PBXSourcesBuildPhase)
54
+ target.build_phases << project.new(PBXFrameworksBuildPhase)
55
+
52
56
  # Frameworks
53
57
  framework_name = (platform == :ios) ? 'Foundation' : 'Cocoa'
54
58
  framework_ref = project.add_system_framework(framework_name, target)
55
59
 
56
- # Build phases
57
- target.build_phases << project.new(PBXSourcesBuildPhase)
58
- frameworks_phase = project.new(PBXFrameworksBuildPhase)
59
- frameworks_phase.add_file_reference(framework_ref)
60
- target.build_phases << frameworks_phase
61
-
62
60
  target
63
61
  end
64
62
 
@@ -88,20 +86,9 @@ module Xcodeproj
88
86
  target.product_name = name
89
87
  target.product_type = Constants::PRODUCT_TYPE_UTI[:bundle]
90
88
 
91
- # Configuration List
92
- build_settings = {
93
- 'PRODUCT_NAME' => '$(TARGET_NAME)',
94
- 'WRAPPER_EXTENSION' => 'bundle',
95
- 'SKIP_INSTALL' => 'YES'
96
- }
97
-
98
- if platform == :osx
99
- build_settings['COMBINE_HIDPI_IMAGES'] = 'YES'
100
- build_settings['SDKROOT'] = 'macosx'
101
- else
102
- build_settings['SDKROOT'] = 'iphoneos'
103
- end
89
+ build_settings = common_build_settings(nil, platform, nil, target.product_type)
104
90
 
91
+ # Configuration List
105
92
  cl = project.new(XCConfigurationList)
106
93
  cl.default_configuration_is_visible = '0'
107
94
  cl.default_configuration_name = 'Release'
@@ -132,10 +119,8 @@ module Xcodeproj
132
119
 
133
120
  #-----------------------------------------------------------------------#
134
121
 
135
- # Adds a file reference for a system framework to the project.
136
- #
137
- # The file reference can then be added to the build files of a
138
- # {PBXFrameworksBuildPhase}.
122
+ # Adds a file reference for a system framework to the project if needed
123
+ # and adds it to the {PBXFrameworksBuildPhase} of the given target.
139
124
  #
140
125
  # @param [Project] project
141
126
  # the project to which the configuration list should be added.
@@ -144,12 +129,12 @@ module Xcodeproj
144
129
  # The name of a framework.
145
130
  #
146
131
  # @param [PBXNativeTarget] target
147
- # The target for which to add the framework.
132
+ # The target to which to add the framework.
148
133
  #
149
134
  # @note This method adds a reference to the highest know SDK for the
150
135
  # given platform.
151
136
  #
152
- # @return [PBXFileReference] The generated file reference.
137
+ # @return [PBXFileReference] The file reference of the framework.
153
138
  #
154
139
  def self.add_system_framework(project, name, target)
155
140
  sdk = target.sdk
@@ -171,13 +156,13 @@ module Xcodeproj
171
156
  end
172
157
 
173
158
  path = base_dir + "System/Library/Frameworks/#{name}.framework"
174
- if ref = project.frameworks_group.files.find { |f| f.path == path }
175
- ref
176
- else
177
- ref = project.frameworks_group.new_file(path)
178
- ref.source_tree = 'DEVELOPER_DIR'
179
- ref
159
+ ref = project.frameworks_group.files.find { |f| f.path == path }
160
+ unless ref
161
+ ref = project.frameworks_group.new_file(path, :developer_dir)
180
162
  end
163
+
164
+ target.frameworks_build_phase.add_file_reference(ref)
165
+ ref
181
166
  end
182
167
 
183
168
  # @!group Private Helpers
@@ -231,20 +216,36 @@ module Xcodeproj
231
216
  #
232
217
  # @return [Hash] The common build settings
233
218
  #
234
- def self.common_build_settings(type, platform, deployment_target = nil)
235
- common_settings = Constants::COMMON_BUILD_SETTINGS
236
- settings = common_settings[:all].dup
237
- settings.merge!(common_settings[type])
238
- settings.merge!(common_settings[platform])
239
- settings.merge!(common_settings[[platform, type]])
240
- if deployment_target
241
- if platform == :ios
242
- settings['IPHONEOS_DEPLOYMENT_TARGET'] = deployment_target
243
- elsif platform == :osx
244
- settings['MACOSX_DEPLOYMENT_TARGET'] = deployment_target
219
+ def self.common_build_settings(type, platform, deployment_target = nil, target_product_type = nil)
220
+ if target_product_type == Constants::PRODUCT_TYPE_UTI[:bundle]
221
+ build_settings = {
222
+ 'PRODUCT_NAME' => '$(TARGET_NAME)',
223
+ 'WRAPPER_EXTENSION' => 'bundle',
224
+ 'SKIP_INSTALL' => 'YES'
225
+ }
226
+
227
+ if platform == :osx
228
+ build_settings['COMBINE_HIDPI_IMAGES'] = 'YES'
229
+ build_settings['SDKROOT'] = 'macosx'
230
+ else
231
+ build_settings['SDKROOT'] = 'iphoneos'
232
+ end
233
+ build_settings
234
+ else
235
+ common_settings = Constants::COMMON_BUILD_SETTINGS
236
+ settings = common_settings[:all].dup
237
+ settings.merge!(common_settings[type])
238
+ settings.merge!(common_settings[platform])
239
+ settings.merge!(common_settings[[platform, type]])
240
+ if deployment_target
241
+ if platform == :ios
242
+ settings['IPHONEOS_DEPLOYMENT_TARGET'] = deployment_target
243
+ elsif platform == :osx
244
+ settings['MACOSX_DEPLOYMENT_TARGET'] = deployment_target
245
+ end
245
246
  end
247
+ settings
246
248
  end
247
- settings
248
249
  end
249
250
 
250
251
  #-----------------------------------------------------------------------#