xcodeproj 0.9.0 → 0.10.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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
  #-----------------------------------------------------------------------#