/
pod_target.rb
311 lines (278 loc) · 10.5 KB
/
pod_target.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
module Pod
# Stores the information relative to the target used to compile a single Pod.
# A pod can have one or more activated spec/subspecs.
#
class PodTarget < Target
# @return [Array<Specification>] the spec and subspecs for the target.
#
attr_reader :specs
# @return [Array<PBXNativeTarget>] the target definitions of the Podfile
# that generated this target.
#
attr_reader :target_definitions
# @return [HeadersStore] the header directory for the target.
#
attr_reader :build_headers
# @return [String] used as suffix in the label
#
# @note This affects the value returned by #configuration_build_dir
# and accessors relying on this as #build_product_path.
#
attr_reader :scope_suffix
# @return [Array<PodTarget>] the targets that this target has a dependency
# upon.
#
attr_accessor :dependent_targets
# @param [Array<Specification>] specs @see #specs
# @param [Array<TargetDefinition>] target_definitions @see #target_definitions
# @param [Sandbox] sandbox @see #sandbox
# @param [String] scope_suffix @see #scope_suffix
#
def initialize(specs, target_definitions, sandbox, scope_suffix = nil)
raise "Can't initialize a PodTarget without specs!" if specs.nil? || specs.empty?
raise "Can't initialize a PodTarget without TargetDefinition!" if target_definitions.nil? || target_definitions.empty?
raise "Can't initialize a PodTarget with only abstract TargetDefinitions" if target_definitions.all?(&:abstract?)
raise "Can't initialize a PodTarget with an empty string scope suffix!" if scope_suffix == ''
super()
@specs = specs
@target_definitions = target_definitions
@sandbox = sandbox
@scope_suffix = scope_suffix
@build_headers = Sandbox::HeadersStore.new(sandbox, 'Private')
@file_accessors = []
@resource_bundle_targets = []
@dependent_targets = []
@build_config_cache = {}
end
# @param [Hash{Array => PodTarget}] cache
# the cached PodTarget for a previously scoped (specs, target_definition)
# @return [Array<PodTarget>] a scoped copy for each target definition.
#
def scoped(cache = {})
target_definitions.map do |target_definition|
cache_key = [specs, target_definition]
if cache[cache_key]
cache[cache_key]
else
target = PodTarget.new(specs, [target_definition], sandbox, target_definition.label)
target.file_accessors = file_accessors
target.user_build_configurations = user_build_configurations
target.native_target = native_target
target.archs = archs
target.dependent_targets = dependent_targets.flat_map { |pt| pt.scoped(cache) }.select { |pt| pt.target_definitions == [target_definition] }
target.host_requires_frameworks = host_requires_frameworks
cache[cache_key] = target
end
end
end
# @return [String] the label for the target.
#
def label
if scope_suffix.nil? || scope_suffix[0] == '.'
"#{root_spec.name}#{scope_suffix}"
else
"#{root_spec.name}-#{scope_suffix}"
end
end
# @return [String] the Swift version for the target.
#
def swift_version
target_definitions.map(&:swift_version).compact.uniq.first
end
# @note The deployment target for the pod target is the maximum of all
# the deployment targets for the current platform of the target
# (or the minimum required to support the current installation
# strategy, if higher).
#
# @return [Platform] the platform for this target.
#
def platform
@platform ||= begin
platform_name = target_definitions.first.platform.name
default = Podfile::TargetDefinition::PLATFORM_DEFAULTS[platform_name]
deployment_target = specs.map do |spec|
Version.new(spec.deployment_target(platform_name) || default)
end.max
if platform_name == :ios && requires_frameworks?
minimum = Version.new('8.0')
deployment_target = [deployment_target, minimum].max
end
Platform.new(platform_name, deployment_target)
end
end
# @visibility private
#
attr_writer :platform
# @return [Podfile] The podfile which declares the dependency.
#
def podfile
target_definitions.first.podfile
end
# @return [String] The name to use for the source code module constructed
# for this target, and which will be used to import the module in
# implementation source files.
#
def product_module_name
root_spec.module_name
end
# @return [Array<Sandbox::FileAccessor>] the file accessors for the
# specifications of this target.
#
attr_accessor :file_accessors
# @return [Array<PBXTarget>] the resource bundle targets belonging
# to this target.
attr_reader :resource_bundle_targets
# @return [Bool] Whether or not this target should be build.
#
# A target should not be build if it has no source files.
#
def should_build?
return @should_build if defined? @should_build
@should_build = begin
source_files = file_accessors.flat_map(&:source_files)
source_files -= file_accessors.flat_map(&:headers)
!source_files.empty?
end
end
# @return [Array<Specification::Consumer>] the specification consumers for
# the target.
#
def spec_consumers
specs.map { |spec| spec.consumer(platform) }
end
# @return [Boolean] Whether the target uses Swift code
#
def uses_swift?
return @uses_swift if defined? @uses_swift
@uses_swift = begin
file_accessors.any? do |file_accessor|
file_accessor.source_files.any? { |sf| sf.extname == '.swift' }
end
end
end
# @return [Specification] The root specification for the target.
#
def root_spec
specs.first.root
end
# @return [String] The name of the Pod that this target refers to.
#
def pod_name
root_spec.name
end
# @param [String] bundle_name
# The name of the bundle product, which is given by the +spec+.
#
# @return [String] The derived name of the resource bundle target.
#
def resources_bundle_target_label(bundle_name)
"#{label}-#{bundle_name}"
end
# @return [Array<String>] The names of the Pods on which this target
# depends.
#
def dependencies
spec_consumers.flat_map do |consumer|
consumer.dependencies.map { |dep| Specification.root_name(dep.name) }
end.uniq
end
# @return [Array<PodTarget>] the recursive targets that this target has a
# dependency upon.
#
def recursive_dependent_targets
targets = dependent_targets.clone
targets.each do |target|
target.dependent_targets.each do |t|
targets.push(t) unless t == self || targets.include?(t)
end
end
targets
end
# Checks if the target should be included in the build configuration with
# the given name of a given target definition.
#
# @param [TargetDefinition] target_definition
# The target definition to check.
#
# @param [String] configuration_name
# The name of the build configuration.
#
def include_in_build_config?(target_definition, configuration_name)
key = [target_definition.label, configuration_name]
if @build_config_cache.has_key?(key)
return @build_config_cache[key]
end
whitelists = target_definition_dependencies(target_definition).map do |dependency|
target_definition.pod_whitelisted_for_configuration?(dependency.name, configuration_name)
end.uniq
if whitelists.empty?
@build_config_cache[key] = true
return true
elsif whitelists.count == 1
@build_config_cache[key] = whitelists.first
whitelists.first
else
raise Informative, "The subspecs of `#{pod_name}` are linked to " \
"different build configurations for the `#{target_definition}` " \
'target. CocoaPods does not currently support subspecs across ' \
'different build configurations.'
end
end
# Checks if warnings should be inhibited for this pod.
#
# @return [Bool]
#
def inhibit_warnings?
return @inhibit_warnings if defined? @inhibit_warnings
whitelists = target_definitions.map do |target_definition|
target_definition.inhibits_warnings_for_pod?(root_spec.name)
end.uniq
if whitelists.empty?
@inhibit_warnings = false
return false
elsif whitelists.count == 1
@inhibit_warnings = whitelists.first
whitelists.first
else
UI.warn "The pod `#{pod_name}` is linked to different targets " \
"(#{target_definitions.map(&:label)}), which contain different " \
'settings to inhibit warnings. CocoaPods does not currently ' \
'support different settings and will fall back to your preference ' \
'set in the root target definition.'
return podfile.root_target_definitions.first.inhibits_warnings_for_pod?(root_spec.name)
end
end
# @param [String] dir
# The directory (which might be a variable) relative to which
# the returned path should be. This must be used if the
# $CONFIGURATION_BUILD_DIR is modified.
#
# @return [String] The absolute path to the configuration build dir
#
def configuration_build_dir(dir = Generator::XCConfig::XCConfigHelper::CONFIGURATION_BUILD_DIR_VARIABLE)
"#{dir}/#{label}"
end
# @param [String] dir
# @see #configuration_build_dir
#
# @return [String] The absolute path to the build product
#
def build_product_path(dir = Generator::XCConfig::XCConfigHelper::CONFIGURATION_BUILD_DIR_VARIABLE)
"#{configuration_build_dir(dir)}/#{product_name}"
end
private
# @param [TargetDefinition] target_definition
# The target definition to check.
#
# @return [Array<Dependency>] The dependency of the target definition for
# this Pod. Return an empty array if the Pod is not a direct
# dependency of the target definition but the dependency of one or
# more Pods.
#
def target_definition_dependencies(target_definition)
target_definition.dependencies.select do |dependency|
Specification.root_name(dependency.name) == pod_name
end
end
end
end