cocoapods 0.39.0 → 1.0.0.beta.1
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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +261 -12
- data/lib/cocoapods.rb +1 -0
- data/lib/cocoapods/command.rb +1 -0
- data/lib/cocoapods/command/env.rb +66 -0
- data/lib/cocoapods/command/init.rb +1 -1
- data/lib/cocoapods/command/lib.rb +1 -1
- data/lib/cocoapods/command/project.rb +0 -4
- data/lib/cocoapods/command/repo/lint.rb +7 -6
- data/lib/cocoapods/command/repo/push.rb +22 -1
- data/lib/cocoapods/command/setup.rb +0 -24
- data/lib/cocoapods/command/spec/create.rb +3 -1
- data/lib/cocoapods/command/spec/edit.rb +14 -21
- data/lib/cocoapods/command/spec/env_spec.rb +53 -0
- data/lib/cocoapods/command/spec/lint.rb +1 -1
- data/lib/cocoapods/config.rb +1 -34
- data/lib/cocoapods/downloader.rb +9 -4
- data/lib/cocoapods/external_sources.rb +0 -4
- data/lib/cocoapods/external_sources/abstract_external_source.rb +38 -11
- data/lib/cocoapods/external_sources/path_source.rb +2 -2
- data/lib/cocoapods/gem_version.rb +2 -2
- data/lib/cocoapods/generator/acknowledgements.rb +1 -1
- data/lib/cocoapods/generator/acknowledgements/plist.rb +1 -1
- data/lib/cocoapods/generator/copy_resources_script.rb +28 -21
- data/lib/cocoapods/generator/info_plist_file.rb +34 -8
- data/lib/cocoapods/generator/module_map.rb +3 -18
- data/lib/cocoapods/generator/xcconfig/aggregate_xcconfig.rb +22 -10
- data/lib/cocoapods/generator/xcconfig/pod_xcconfig.rb +2 -1
- data/lib/cocoapods/generator/xcconfig/xcconfig_helper.rb +2 -1
- data/lib/cocoapods/hooks_manager.rb +3 -11
- data/lib/cocoapods/installer.rb +45 -25
- data/lib/cocoapods/installer/analyzer.rb +53 -25
- data/lib/cocoapods/installer/analyzer/sandbox_analyzer.rb +2 -13
- data/lib/cocoapods/installer/analyzer/target_inspection_result.rb +4 -0
- data/lib/cocoapods/installer/analyzer/target_inspector.rb +22 -19
- data/lib/cocoapods/installer/file_references_installer.rb +53 -6
- data/lib/cocoapods/installer/installation_options.rb +156 -0
- data/lib/cocoapods/installer/migrator.rb +1 -56
- data/lib/cocoapods/installer/pod_source_installer.rb +10 -8
- data/lib/cocoapods/installer/podfile_validator.rb +42 -1
- data/lib/cocoapods/installer/post_install_hooks_context.rb +19 -2
- data/lib/cocoapods/installer/target_installer.rb +6 -2
- data/lib/cocoapods/installer/target_installer/aggregate_target_installer.rb +6 -5
- data/lib/cocoapods/installer/target_installer/pod_target_installer.rb +82 -14
- data/lib/cocoapods/installer/user_project_integrator.rb +37 -16
- data/lib/cocoapods/installer/user_project_integrator/target_integrator.rb +14 -136
- data/lib/cocoapods/installer/user_project_integrator/target_integrator/xcconfig_integrator.rb +15 -22
- data/lib/cocoapods/project.rb +109 -19
- data/lib/cocoapods/resolver.rb +17 -15
- data/lib/cocoapods/resolver/lazy_specification.rb +4 -0
- data/lib/cocoapods/sandbox.rb +0 -32
- data/lib/cocoapods/sandbox/headers_store.rb +2 -2
- data/lib/cocoapods/sandbox/podspec_finder.rb +1 -1
- data/lib/cocoapods/sources_manager.rb +181 -50
- data/lib/cocoapods/target/aggregate_target.rb +17 -11
- data/lib/cocoapods/target/pod_target.rb +31 -4
- data/lib/cocoapods/user_interface.rb +32 -3
- data/lib/cocoapods/user_interface/error_report.rb +46 -36
- data/lib/cocoapods/validator.rb +132 -43
- metadata +164 -79
@@ -5,6 +5,9 @@ module Pod
|
|
5
5
|
#
|
6
6
|
class Analyzer
|
7
7
|
include Config::Mixin
|
8
|
+
include InstallationOptions::Mixin
|
9
|
+
|
10
|
+
delegate_installation_options { podfile }
|
8
11
|
|
9
12
|
autoload :AnalysisResult, 'cocoapods/installer/analyzer/analysis_result'
|
10
13
|
autoload :SandboxAnalyzer, 'cocoapods/installer/analyzer/sandbox_analyzer'
|
@@ -57,7 +60,7 @@ module Pod
|
|
57
60
|
validate_podfile!
|
58
61
|
validate_lockfile_version!
|
59
62
|
@result = AnalysisResult.new
|
60
|
-
if
|
63
|
+
if installation_options.integrate_targets?
|
61
64
|
@result.target_inspections = inspect_targets_to_integrate
|
62
65
|
else
|
63
66
|
verify_platforms_specified!
|
@@ -157,6 +160,7 @@ module Pod
|
|
157
160
|
unless validator.valid?
|
158
161
|
raise Informative, validator.message
|
159
162
|
end
|
163
|
+
validator.warnings.uniq.each { |w| UI.warn(w) }
|
160
164
|
end
|
161
165
|
|
162
166
|
# @!group Analysis steps
|
@@ -225,9 +229,14 @@ module Pod
|
|
225
229
|
#
|
226
230
|
def generate_targets
|
227
231
|
pod_targets = generate_pod_targets(result.specs_by_target)
|
228
|
-
result.specs_by_target.map do |target_definition
|
232
|
+
aggregate_targets = result.specs_by_target.keys.reject(&:abstract?).map do |target_definition|
|
229
233
|
generate_target(target_definition, pod_targets)
|
230
234
|
end
|
235
|
+
aggregate_targets.each do |target|
|
236
|
+
target.search_paths_aggregate_targets = aggregate_targets.select do |aggregate_target|
|
237
|
+
target.target_definition.targets_to_inherit_search_paths.include?(aggregate_target.target_definition)
|
238
|
+
end
|
239
|
+
end
|
231
240
|
end
|
232
241
|
|
233
242
|
# Setup the aggregate target for a single user target
|
@@ -244,15 +253,16 @@ module Pod
|
|
244
253
|
target = AggregateTarget.new(target_definition, sandbox)
|
245
254
|
target.host_requires_frameworks |= target_definition.uses_frameworks?
|
246
255
|
|
247
|
-
if
|
256
|
+
if installation_options.integrate_targets?
|
248
257
|
target_inspection = result.target_inspections[target_definition]
|
249
|
-
|
250
|
-
target.
|
258
|
+
raise "missing inspection: #{target_definition.name}" unless target_inspection
|
259
|
+
target.user_project = target_inspection.project
|
260
|
+
target.client_root = target.user_project_path.dirname.realpath
|
251
261
|
target.user_target_uuids = target_inspection.project_target_uuids
|
252
262
|
target.user_build_configurations = target_inspection.build_configurations
|
253
263
|
target.archs = target_inspection.archs
|
254
264
|
else
|
255
|
-
target.client_root = config.installation_root
|
265
|
+
target.client_root = config.installation_root.realpath
|
256
266
|
target.user_target_uuids = []
|
257
267
|
target.user_build_configurations = target_definition.build_configurations || { 'Release' => :release, 'Debug' => :debug }
|
258
268
|
if target_definition.platform && target_definition.platform.name == :osx
|
@@ -276,7 +286,9 @@ module Pod
|
|
276
286
|
# @return [Array<PodTarget>]
|
277
287
|
#
|
278
288
|
def generate_pod_targets(specs_by_target)
|
279
|
-
if
|
289
|
+
if installation_options.deduplicate_targets?
|
290
|
+
dedupe_cache = {}
|
291
|
+
|
280
292
|
all_specs = specs_by_target.flat_map do |target_definition, dependent_specs|
|
281
293
|
dependent_specs.group_by(&:root).map do |root_spec, specs|
|
282
294
|
[root_spec, specs, target_definition]
|
@@ -294,7 +306,7 @@ module Pod
|
|
294
306
|
# There are different sets of subspecs or the spec is used across different platforms
|
295
307
|
targets_by_distinctors.flat_map do |distinctor, target_definitions|
|
296
308
|
specs, = *distinctor
|
297
|
-
generate_pod_target(target_definitions, specs).scoped
|
309
|
+
generate_pod_target(target_definitions, specs).scoped(dedupe_cache)
|
298
310
|
end
|
299
311
|
else
|
300
312
|
(specs, _), target_definitions = targets_by_distinctors.first
|
@@ -308,7 +320,7 @@ module Pod
|
|
308
320
|
dependent_targets = transitive_dependencies_for_pod_target(target, pod_targets)
|
309
321
|
target.dependent_targets = dependent_targets
|
310
322
|
if dependent_targets.any?(&:scoped?)
|
311
|
-
target.scoped
|
323
|
+
target.scoped(dedupe_cache)
|
312
324
|
else
|
313
325
|
target
|
314
326
|
end
|
@@ -317,7 +329,7 @@ module Pod
|
|
317
329
|
pod_targets = specs_by_target.flat_map do |target_definition, specs|
|
318
330
|
grouped_specs = specs.group_by.group_by(&:root).values.uniq
|
319
331
|
grouped_specs.flat_map do |pod_specs|
|
320
|
-
generate_pod_target([target_definition], pod_specs).scoped
|
332
|
+
generate_pod_target([target_definition], pod_specs).scoped(dedupe_cache)
|
321
333
|
end
|
322
334
|
end
|
323
335
|
pod_targets.each do |target|
|
@@ -369,7 +381,7 @@ module Pod
|
|
369
381
|
def generate_pod_target(target_definitions, pod_specs)
|
370
382
|
pod_target = PodTarget.new(pod_specs, target_definitions, sandbox)
|
371
383
|
|
372
|
-
if
|
384
|
+
if installation_options.integrate_targets?
|
373
385
|
target_inspections = result.target_inspections.select { |t, _| target_definitions.include?(t) }.values
|
374
386
|
pod_target.user_build_configurations = target_inspections.map(&:build_configurations).reduce({}, &:merge)
|
375
387
|
pod_target.archs = target_inspections.flat_map(&:archs).compact.uniq.sort
|
@@ -454,6 +466,7 @@ module Pod
|
|
454
466
|
else
|
455
467
|
source = ExternalSources.from_dependency(dependency, podfile.defined_in_file)
|
456
468
|
end
|
469
|
+
source.can_cache = installation_options.clean?
|
457
470
|
source.fetch(sandbox)
|
458
471
|
end
|
459
472
|
|
@@ -469,7 +482,6 @@ module Pod
|
|
469
482
|
deps_to_fetch_if_needed = deps_with_external_source.select { |dep| result.podfile_state.unchanged.include?(dep.name) }
|
470
483
|
deps_to_fetch += deps_to_fetch_if_needed.select do |dep|
|
471
484
|
sandbox.specification(dep.name).nil? ||
|
472
|
-
!dep.external_source[:local].nil? ||
|
473
485
|
!dep.external_source[:path].nil? ||
|
474
486
|
!sandbox.pod_dir(dep.root_name).directory? ||
|
475
487
|
checkout_requires_update?(dep)
|
@@ -612,13 +624,21 @@ module Pod
|
|
612
624
|
def sources
|
613
625
|
@sources ||= begin
|
614
626
|
sources = podfile.sources
|
615
|
-
|
616
|
-
|
617
|
-
|
627
|
+
|
628
|
+
# Add any sources specified using the :source flag on individual dependencies.
|
629
|
+
dependency_sources = podfile.dependencies.map(&:podspec_repo).compact
|
630
|
+
|
631
|
+
all_dependencies_have_sources = dependency_sources.count == podfile.dependencies.count
|
632
|
+
if all_dependencies_have_sources
|
633
|
+
sources = dependency_sources
|
634
|
+
elsif sources.empty?
|
635
|
+
sources = ['https://github.com/CocoaPods/Specs.git']
|
618
636
|
else
|
619
|
-
sources
|
620
|
-
|
621
|
-
|
637
|
+
sources += dependency_sources
|
638
|
+
end
|
639
|
+
|
640
|
+
sources.uniq.map do |source_url|
|
641
|
+
SourcesManager.find_or_create_source_with_url(source_url)
|
622
642
|
end
|
623
643
|
end
|
624
644
|
end
|
@@ -634,7 +654,7 @@ module Pod
|
|
634
654
|
# @return [void]
|
635
655
|
#
|
636
656
|
def verify_platforms_specified!
|
637
|
-
unless
|
657
|
+
unless installation_options.integrate_targets?
|
638
658
|
podfile.target_definition_list.each do |target_definition|
|
639
659
|
if !target_definition.empty? && target_definition.platform.nil?
|
640
660
|
raise Informative, 'It is necessary to specify the platform in the Podfile if not integrating.'
|
@@ -654,12 +674,20 @@ module Pod
|
|
654
674
|
def inspect_targets_to_integrate
|
655
675
|
inspection_result = {}
|
656
676
|
UI.section 'Inspecting targets to integrate' do
|
657
|
-
podfile.target_definition_list.
|
658
|
-
|
659
|
-
|
660
|
-
|
661
|
-
|
662
|
-
|
677
|
+
inspectors = podfile.target_definition_list.map do |target_definition|
|
678
|
+
next if target_definition.abstract?
|
679
|
+
TargetInspector.new(target_definition, config.installation_root)
|
680
|
+
end.compact
|
681
|
+
inspectors.group_by(&:compute_project_path).each do |project_path, target_inspectors|
|
682
|
+
project = Xcodeproj::Project.open(project_path)
|
683
|
+
target_inspectors.each do |inspector|
|
684
|
+
target_definition = inspector.target_definition
|
685
|
+
inspector.user_project = project
|
686
|
+
results = inspector.compute_results
|
687
|
+
inspection_result[target_definition] = results
|
688
|
+
UI.message('Using `ARCHS` setting to build architectures of ' \
|
689
|
+
"target `#{target_definition.label}`: (`#{results.archs.join('`, `')}`)")
|
690
|
+
end
|
663
691
|
end
|
664
692
|
end
|
665
693
|
inspection_result
|
@@ -14,7 +14,7 @@ module Pod
|
|
14
14
|
# - The version of the Pod changed.
|
15
15
|
# - The SHA of the specification file changed.
|
16
16
|
# - The specific installed (sub)specs of the same Pod changed.
|
17
|
-
# - The specification is
|
17
|
+
# - The specification is from an external source and the
|
18
18
|
# installation process is in update mode.
|
19
19
|
# - The directory of the Pod is empty.
|
20
20
|
# - The Pod has been pre-downloaded.
|
@@ -135,7 +135,7 @@ module Pod
|
|
135
135
|
# changed and thus should be reinstalled.
|
136
136
|
#
|
137
137
|
# @note In update mode, as there is no way to know if a remote source
|
138
|
-
# hash changed the Pods
|
138
|
+
# hash changed the Pods from external
|
139
139
|
# sources are always marked as changed.
|
140
140
|
#
|
141
141
|
# @note A Pod whose folder is empty is considered changed.
|
@@ -152,10 +152,6 @@ module Pod
|
|
152
152
|
return true if resolved_spec_names(pod) != sandbox_spec_names(pod)
|
153
153
|
return true if sandbox.predownloaded?(pod)
|
154
154
|
return true if folder_empty?(pod)
|
155
|
-
return true if sandbox.head_pod?(pod) != sandbox_head_version?(pod)
|
156
|
-
if update_mode
|
157
|
-
return true if sandbox.head_pod?(pod)
|
158
|
-
end
|
159
155
|
false
|
160
156
|
end
|
161
157
|
|
@@ -238,13 +234,6 @@ module Pod
|
|
238
234
|
sandbox_manifest.checksum(pod)
|
239
235
|
end
|
240
236
|
|
241
|
-
# @return [Bool] Wether the Pod is installed in the sandbox is in head
|
242
|
-
# mode.
|
243
|
-
#
|
244
|
-
def sandbox_head_version?(pod)
|
245
|
-
sandbox_version(pod).head? == true
|
246
|
-
end
|
247
|
-
|
248
237
|
#--------------------------------------#
|
249
238
|
|
250
239
|
def folder_exist?(pod)
|
@@ -1,3 +1,5 @@
|
|
1
|
+
require 'active_support/core_ext/array/conversions'
|
2
|
+
|
1
3
|
module Pod
|
2
4
|
class Installer
|
3
5
|
class Analyzer
|
@@ -25,27 +27,26 @@ module Pod
|
|
25
27
|
|
26
28
|
# Inspect the #target_definition
|
27
29
|
#
|
30
|
+
# @raise If no `user_project` is set
|
31
|
+
#
|
28
32
|
# @return [TargetInspectionResult]
|
29
33
|
#
|
30
34
|
def compute_results
|
31
|
-
|
32
|
-
|
35
|
+
raise ArgumentError, 'Cannot compute results without a user project set' unless user_project
|
36
|
+
|
33
37
|
targets = compute_targets(user_project)
|
34
38
|
|
35
39
|
result = TargetInspectionResult.new
|
36
40
|
result.target_definition = target_definition
|
37
|
-
result.project_path =
|
41
|
+
result.project_path = user_project.path
|
38
42
|
result.project_target_uuids = targets.map(&:uuid)
|
39
43
|
result.build_configurations = compute_build_configurations(targets)
|
40
44
|
result.platform = compute_platform(targets)
|
41
45
|
result.archs = compute_archs(targets)
|
46
|
+
result.project = user_project
|
42
47
|
result
|
43
48
|
end
|
44
49
|
|
45
|
-
#-----------------------------------------------------------------------#
|
46
|
-
|
47
|
-
private
|
48
|
-
|
49
50
|
# Returns the path of the user project that the #target_definition
|
50
51
|
# should integrate.
|
51
52
|
#
|
@@ -77,6 +78,15 @@ module Pod
|
|
77
78
|
path
|
78
79
|
end
|
79
80
|
|
81
|
+
# @return [Xcodeproj::Project] the user's Xcode project, used for target
|
82
|
+
# inspection
|
83
|
+
#
|
84
|
+
attr_accessor :user_project
|
85
|
+
|
86
|
+
#-----------------------------------------------------------------------#
|
87
|
+
|
88
|
+
private
|
89
|
+
|
80
90
|
# Returns a list of the targets from the project of #target_definition
|
81
91
|
# that needs to be integrated.
|
82
92
|
#
|
@@ -94,19 +104,12 @@ module Pod
|
|
94
104
|
#
|
95
105
|
def compute_targets(user_project)
|
96
106
|
native_targets = user_project.native_targets
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
elsif target_definition.link_with_first_target?
|
102
|
-
targets = [native_targets.first].compact
|
103
|
-
raise Informative, 'Unable to find a target' if targets.empty?
|
104
|
-
else
|
105
|
-
target = native_targets.find { |t| t.name == target_definition.name.to_s }
|
106
|
-
targets = [target].compact
|
107
|
-
raise Informative, "Unable to find a target named `#{target_definition.name}`" if targets.empty?
|
107
|
+
target = native_targets.find { |t| t.name == target_definition.name.to_s }
|
108
|
+
unless target
|
109
|
+
found = native_targets.map { |t| "`#{t.name}`" }.to_sentence
|
110
|
+
raise Informative, "Unable to find a target named `#{target_definition.name}`, did find #{found}."
|
108
111
|
end
|
109
|
-
|
112
|
+
[target]
|
110
113
|
end
|
111
114
|
|
112
115
|
# @param [Array<PBXNativeTarget] the user's targets of the project of
|
@@ -117,21 +117,19 @@ module Pod
|
|
117
117
|
pod_target.file_accessors.each do |file_accessor|
|
118
118
|
framework_exp = /\.framework\//
|
119
119
|
headers_sandbox = Pathname.new(file_accessor.spec.root.name)
|
120
|
-
pod_target.build_headers.add_search_path(headers_sandbox, pod_target.platform)
|
121
120
|
|
122
121
|
# When integrating Pod as frameworks, built Pods are built into
|
123
122
|
# frameworks, whose headers are included inside the built
|
124
123
|
# framework. Those headers do not need to be linked from the
|
125
124
|
# sandbox.
|
126
125
|
unless pod_target.requires_frameworks? && pod_target.should_build?
|
126
|
+
pod_target.build_headers.add_search_path(headers_sandbox, pod_target.platform)
|
127
127
|
sandbox.public_headers.add_search_path(headers_sandbox, pod_target.platform)
|
128
|
-
end
|
129
128
|
|
130
|
-
|
131
|
-
|
132
|
-
|
129
|
+
header_mappings(headers_sandbox, file_accessor, file_accessor.headers).each do |namespaced_path, files|
|
130
|
+
pod_target.build_headers.add_files(namespaced_path, files.reject { |f| f.to_path =~ framework_exp })
|
131
|
+
end
|
133
132
|
|
134
|
-
unless pod_target.requires_frameworks? && pod_target.should_build?
|
135
133
|
header_mappings(headers_sandbox, file_accessor, file_accessor.public_headers).each do |namespaced_path, files|
|
136
134
|
sandbox.public_headers.add_files(namespaced_path, files.reject { |f| f.to_path =~ framework_exp })
|
137
135
|
end
|
@@ -179,6 +177,7 @@ module Pod
|
|
179
177
|
pod_name = file_accessor.spec.name
|
180
178
|
local = sandbox.local?(pod_name)
|
181
179
|
paths = file_accessor.send(file_accessor_key)
|
180
|
+
paths = allowable_project_paths(paths)
|
182
181
|
paths.each do |path|
|
183
182
|
group = pods_project.group_for_spec(file_accessor.spec.name, group_key)
|
184
183
|
pods_project.add_file_reference(path, group, local && reflect_file_system_structure_for_development)
|
@@ -186,6 +185,54 @@ module Pod
|
|
186
185
|
end
|
187
186
|
end
|
188
187
|
|
188
|
+
# Filters a list of paths down to those paths which can be added to
|
189
|
+
# the Xcode project. Some paths are intermediates and only their children
|
190
|
+
# should be added, while some paths are treated as bundles and their
|
191
|
+
# children should not be added directly.
|
192
|
+
#
|
193
|
+
# @param [Array<Pathname>] paths
|
194
|
+
# The paths to files or directories on disk.
|
195
|
+
#
|
196
|
+
# @return [Array<Pathname>] The paths which can be added to the Xcode project
|
197
|
+
#
|
198
|
+
def allowable_project_paths(paths)
|
199
|
+
lproj_paths = Set.new
|
200
|
+
lproj_paths_with_files = Set.new
|
201
|
+
allowable_paths = paths.select do |path|
|
202
|
+
path_str = path.to_s.downcase
|
203
|
+
|
204
|
+
# We add the directory for a Core Data model, but not the items in it.
|
205
|
+
next if path_str =~ /.*\.xcdatamodeld\/.+/
|
206
|
+
|
207
|
+
# We add the directory for an asset catalog, but not the items in it.
|
208
|
+
next if path_str =~ /.*\.xcassets\/.+/
|
209
|
+
|
210
|
+
if path_str =~ /\.lproj(\/|$)/
|
211
|
+
# If the element is an .lproj directory then save it and potentially
|
212
|
+
# add it later if we don't find any contained items.
|
213
|
+
if path_str.end_with?('.lproj') && path.directory?
|
214
|
+
lproj_paths << path_str
|
215
|
+
next
|
216
|
+
end
|
217
|
+
|
218
|
+
# Collect the paths for the .lproj directories that contain files.
|
219
|
+
lproj_path = /(^.*\.lproj)\/.*/.match(path_str)[1]
|
220
|
+
lproj_paths_with_files << lproj_path
|
221
|
+
|
222
|
+
# Directories nested within an .lproj directory are added as file
|
223
|
+
# system references so their contained items are not added directly.
|
224
|
+
next if path.dirname.dirname.to_s.downcase == lproj_path
|
225
|
+
end
|
226
|
+
|
227
|
+
true
|
228
|
+
end
|
229
|
+
|
230
|
+
# Only add the path for the .lproj directories that do not have anything
|
231
|
+
# within them added as well. This generally happens if the glob within the
|
232
|
+
# resources directory was not a recursive glob.
|
233
|
+
allowable_paths + lproj_paths.subtract(lproj_paths_with_files).to_a
|
234
|
+
end
|
235
|
+
|
189
236
|
# Computes the destination sub-directory in the sandbox
|
190
237
|
#
|
191
238
|
# @param [Pathname] headers_sandbox
|
@@ -0,0 +1,156 @@
|
|
1
|
+
require 'active_support/hash_with_indifferent_access'
|
2
|
+
|
3
|
+
module Pod
|
4
|
+
class Installer
|
5
|
+
# Represents the installation options the user can customize via a
|
6
|
+
# `Podfile`.
|
7
|
+
#
|
8
|
+
class InstallationOptions
|
9
|
+
# Parses installation options from a podfile.
|
10
|
+
#
|
11
|
+
# @param [Podfile] podfile the podfile to parse installation options
|
12
|
+
# from.
|
13
|
+
#
|
14
|
+
# @raise [Informative] if `podfile` does not specify a `CocoaPods`
|
15
|
+
# install.
|
16
|
+
#
|
17
|
+
# @return [Self]
|
18
|
+
#
|
19
|
+
def self.from_podfile(podfile)
|
20
|
+
name, options = podfile.installation_method
|
21
|
+
unless name.downcase == 'cocoapods'
|
22
|
+
raise Informative, "Currently need to specify a `cocoapods` install, you chose `#{name}`."
|
23
|
+
end
|
24
|
+
new(options)
|
25
|
+
end
|
26
|
+
|
27
|
+
# Defines a new installation option.
|
28
|
+
#
|
29
|
+
# @param [#to_s] name the name of the option.
|
30
|
+
#
|
31
|
+
# @param default the default value for the option.
|
32
|
+
#
|
33
|
+
# @param [Boolean] boolean whether the option has a boolean value.
|
34
|
+
#
|
35
|
+
# @return [void]
|
36
|
+
#
|
37
|
+
# @!macro [attach] option
|
38
|
+
#
|
39
|
+
# @note this option defaults to $2.
|
40
|
+
#
|
41
|
+
# @return the $1 $0 for installation.
|
42
|
+
#
|
43
|
+
def self.option(name, default, boolean: true)
|
44
|
+
name = name.to_s
|
45
|
+
raise ArgumentError, "The `#{name}` option is already defined" if defaults.key?(name)
|
46
|
+
defaults[name] = default
|
47
|
+
attr_accessor name
|
48
|
+
alias_method "#{name}?", name if boolean
|
49
|
+
end
|
50
|
+
|
51
|
+
# @return [Hash<Symbol,Object>] all known installation options and their
|
52
|
+
# default values.
|
53
|
+
#
|
54
|
+
def self.defaults
|
55
|
+
@defaults ||= {}
|
56
|
+
end
|
57
|
+
|
58
|
+
# @return [Array<Symbol>] the names of all known installation options.
|
59
|
+
#
|
60
|
+
def self.all_options
|
61
|
+
defaults.keys
|
62
|
+
end
|
63
|
+
|
64
|
+
# Initializes the installation options with a hash of options from a
|
65
|
+
# Podfile.
|
66
|
+
#
|
67
|
+
# @param [Hash] options the options to parse.
|
68
|
+
#
|
69
|
+
# @raise [Informative] if `options` contains any unknown keys.
|
70
|
+
#
|
71
|
+
def initialize(options = {})
|
72
|
+
options = ActiveSupport::HashWithIndifferentAccess.new(options)
|
73
|
+
unknown_keys = options.keys - self.class.all_options.map(&:to_s)
|
74
|
+
raise Informative, "Unknown installation options: #{unknown_keys.to_sentence}." unless unknown_keys.empty?
|
75
|
+
self.class.defaults.each do |key, default|
|
76
|
+
value = options.fetch(key, default)
|
77
|
+
send("#{key}=", value)
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
# @param [Boolean] include_defaults whether values that match the default
|
82
|
+
# for their option should be included. Defaults to `true`.
|
83
|
+
#
|
84
|
+
# @return [Hash] the options, keyed by option name.
|
85
|
+
#
|
86
|
+
def to_h(include_defaults: true)
|
87
|
+
self.class.defaults.reduce(ActiveSupport::HashWithIndifferentAccess.new) do |hash, (option, default)|
|
88
|
+
value = send(option)
|
89
|
+
hash[option] = value if include_defaults || value != default
|
90
|
+
hash
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
def ==(other)
|
95
|
+
other.is_a?(self.class) && to_h == other.to_h
|
96
|
+
end
|
97
|
+
|
98
|
+
alias_method :eql, :==
|
99
|
+
|
100
|
+
def hash
|
101
|
+
to_h.hash
|
102
|
+
end
|
103
|
+
|
104
|
+
option :clean, true
|
105
|
+
option :deduplicate_targets, true
|
106
|
+
option :deterministic_uuids, true
|
107
|
+
option :integrate_targets, true
|
108
|
+
option :lock_pod_sources, true
|
109
|
+
|
110
|
+
module Mixin
|
111
|
+
module ClassMethods
|
112
|
+
# Delegates the creation of {#installation_options} to the `Podfile`
|
113
|
+
# returned by the given block.
|
114
|
+
#
|
115
|
+
# @param blk a block that returns the `Podfile` to create
|
116
|
+
# installation options from.
|
117
|
+
#
|
118
|
+
# @return [Void]
|
119
|
+
#
|
120
|
+
def delegate_installation_options(&blk)
|
121
|
+
define_method(:installation_options) do
|
122
|
+
@installation_options ||= InstallationOptions.from_podfile(instance_eval(&blk))
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
# Delegates the installation options attributes directly to
|
127
|
+
# {#installation_options}.
|
128
|
+
#
|
129
|
+
# @return [Void]
|
130
|
+
#
|
131
|
+
def delegate_installation_option_attributes!
|
132
|
+
define_method(:respond_to_missing?) do |name, *args|
|
133
|
+
installation_options.respond_to?(name, *args) || super
|
134
|
+
end
|
135
|
+
|
136
|
+
define_method(:method_missing) do |name, *args, &blk|
|
137
|
+
if installation_options.respond_to?(name)
|
138
|
+
installation_options.send(name, *args, &blk)
|
139
|
+
else
|
140
|
+
super
|
141
|
+
end
|
142
|
+
end
|
143
|
+
end
|
144
|
+
end
|
145
|
+
|
146
|
+
# @return [InstallationOptions] The installation options.
|
147
|
+
#
|
148
|
+
attr_accessor :installation_options
|
149
|
+
|
150
|
+
def self.included(mod)
|
151
|
+
mod.extend(ClassMethods)
|
152
|
+
end
|
153
|
+
end
|
154
|
+
end
|
155
|
+
end
|
156
|
+
end
|