xcocoapods 1.5.3
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 +7 -0
- data/CHANGELOG.md +6303 -0
- data/LICENSE +28 -0
- data/README.md +80 -0
- data/bin/pod +56 -0
- data/bin/sandbox-pod +168 -0
- data/lib/cocoapods.rb +73 -0
- data/lib/cocoapods/command.rb +175 -0
- data/lib/cocoapods/command/cache.rb +28 -0
- data/lib/cocoapods/command/cache/clean.rb +90 -0
- data/lib/cocoapods/command/cache/list.rb +69 -0
- data/lib/cocoapods/command/env.rb +66 -0
- data/lib/cocoapods/command/init.rb +128 -0
- data/lib/cocoapods/command/install.rb +45 -0
- data/lib/cocoapods/command/ipc.rb +19 -0
- data/lib/cocoapods/command/ipc/list.rb +40 -0
- data/lib/cocoapods/command/ipc/podfile.rb +31 -0
- data/lib/cocoapods/command/ipc/podfile_json.rb +30 -0
- data/lib/cocoapods/command/ipc/repl.rb +51 -0
- data/lib/cocoapods/command/ipc/spec.rb +29 -0
- data/lib/cocoapods/command/ipc/update_search_index.rb +24 -0
- data/lib/cocoapods/command/lib.rb +11 -0
- data/lib/cocoapods/command/lib/create.rb +105 -0
- data/lib/cocoapods/command/lib/lint.rb +121 -0
- data/lib/cocoapods/command/list.rb +39 -0
- data/lib/cocoapods/command/options/project_directory.rb +36 -0
- data/lib/cocoapods/command/options/repo_update.rb +34 -0
- data/lib/cocoapods/command/outdated.rb +140 -0
- data/lib/cocoapods/command/repo.rb +29 -0
- data/lib/cocoapods/command/repo/add.rb +103 -0
- data/lib/cocoapods/command/repo/lint.rb +82 -0
- data/lib/cocoapods/command/repo/list.rb +93 -0
- data/lib/cocoapods/command/repo/push.rb +281 -0
- data/lib/cocoapods/command/repo/remove.rb +36 -0
- data/lib/cocoapods/command/repo/update.rb +28 -0
- data/lib/cocoapods/command/setup.rb +103 -0
- data/lib/cocoapods/command/spec.rb +112 -0
- data/lib/cocoapods/command/spec/cat.rb +51 -0
- data/lib/cocoapods/command/spec/create.rb +283 -0
- data/lib/cocoapods/command/spec/edit.rb +87 -0
- data/lib/cocoapods/command/spec/env_spec.rb +53 -0
- data/lib/cocoapods/command/spec/lint.rb +137 -0
- data/lib/cocoapods/command/spec/which.rb +43 -0
- data/lib/cocoapods/command/update.rb +101 -0
- data/lib/cocoapods/config.rb +347 -0
- data/lib/cocoapods/core_overrides.rb +1 -0
- data/lib/cocoapods/downloader.rb +190 -0
- data/lib/cocoapods/downloader/cache.rb +233 -0
- data/lib/cocoapods/downloader/request.rb +86 -0
- data/lib/cocoapods/downloader/response.rb +16 -0
- data/lib/cocoapods/executable.rb +222 -0
- data/lib/cocoapods/external_sources.rb +57 -0
- data/lib/cocoapods/external_sources/abstract_external_source.rb +205 -0
- data/lib/cocoapods/external_sources/downloader_source.rb +30 -0
- data/lib/cocoapods/external_sources/path_source.rb +55 -0
- data/lib/cocoapods/external_sources/podspec_source.rb +54 -0
- data/lib/cocoapods/gem_version.rb +5 -0
- data/lib/cocoapods/generator/acknowledgements.rb +107 -0
- data/lib/cocoapods/generator/acknowledgements/markdown.rb +44 -0
- data/lib/cocoapods/generator/acknowledgements/plist.rb +94 -0
- data/lib/cocoapods/generator/app_target_helper.rb +244 -0
- data/lib/cocoapods/generator/bridge_support.rb +22 -0
- data/lib/cocoapods/generator/constant.rb +19 -0
- data/lib/cocoapods/generator/copy_resources_script.rb +230 -0
- data/lib/cocoapods/generator/dummy_source.rb +31 -0
- data/lib/cocoapods/generator/embed_frameworks_script.rb +215 -0
- data/lib/cocoapods/generator/header.rb +103 -0
- data/lib/cocoapods/generator/info_plist_file.rb +116 -0
- data/lib/cocoapods/generator/module_map.rb +99 -0
- data/lib/cocoapods/generator/prefix_header.rb +60 -0
- data/lib/cocoapods/generator/umbrella_header.rb +46 -0
- data/lib/cocoapods/hooks_manager.rb +132 -0
- data/lib/cocoapods/installer.rb +703 -0
- data/lib/cocoapods/installer/analyzer.rb +972 -0
- data/lib/cocoapods/installer/analyzer/analysis_result.rb +87 -0
- data/lib/cocoapods/installer/analyzer/locking_dependency_analyzer.rb +98 -0
- data/lib/cocoapods/installer/analyzer/pod_variant.rb +67 -0
- data/lib/cocoapods/installer/analyzer/pod_variant_set.rb +157 -0
- data/lib/cocoapods/installer/analyzer/podfile_dependency_cache.rb +54 -0
- data/lib/cocoapods/installer/analyzer/sandbox_analyzer.rb +240 -0
- data/lib/cocoapods/installer/analyzer/specs_state.rb +84 -0
- data/lib/cocoapods/installer/analyzer/target_inspection_result.rb +53 -0
- data/lib/cocoapods/installer/analyzer/target_inspector.rb +260 -0
- data/lib/cocoapods/installer/installation_options.rb +158 -0
- data/lib/cocoapods/installer/pod_source_installer.rb +202 -0
- data/lib/cocoapods/installer/pod_source_preparer.rb +77 -0
- data/lib/cocoapods/installer/podfile_validator.rb +139 -0
- data/lib/cocoapods/installer/post_install_hooks_context.rb +132 -0
- data/lib/cocoapods/installer/pre_install_hooks_context.rb +51 -0
- data/lib/cocoapods/installer/source_provider_hooks_context.rb +34 -0
- data/lib/cocoapods/installer/user_project_integrator.rb +250 -0
- data/lib/cocoapods/installer/user_project_integrator/target_integrator.rb +463 -0
- data/lib/cocoapods/installer/user_project_integrator/target_integrator/xcconfig_integrator.rb +146 -0
- data/lib/cocoapods/installer/xcode.rb +8 -0
- data/lib/cocoapods/installer/xcode/pods_project_generator.rb +416 -0
- data/lib/cocoapods/installer/xcode/pods_project_generator/aggregate_target_installer.rb +181 -0
- data/lib/cocoapods/installer/xcode/pods_project_generator/app_host_installer.rb +84 -0
- data/lib/cocoapods/installer/xcode/pods_project_generator/file_references_installer.rb +334 -0
- data/lib/cocoapods/installer/xcode/pods_project_generator/pod_target_installer.rb +777 -0
- data/lib/cocoapods/installer/xcode/pods_project_generator/pod_target_integrator.rb +116 -0
- data/lib/cocoapods/installer/xcode/pods_project_generator/target_installation_result.rb +86 -0
- data/lib/cocoapods/installer/xcode/pods_project_generator/target_installer.rb +256 -0
- data/lib/cocoapods/installer/xcode/pods_project_generator/target_installer_helper.rb +68 -0
- data/lib/cocoapods/installer/xcode/target_validator.rb +147 -0
- data/lib/cocoapods/open-uri.rb +33 -0
- data/lib/cocoapods/project.rb +414 -0
- data/lib/cocoapods/resolver.rb +585 -0
- data/lib/cocoapods/resolver/lazy_specification.rb +79 -0
- data/lib/cocoapods/sandbox.rb +404 -0
- data/lib/cocoapods/sandbox/file_accessor.rb +444 -0
- data/lib/cocoapods/sandbox/headers_store.rb +146 -0
- data/lib/cocoapods/sandbox/path_list.rb +220 -0
- data/lib/cocoapods/sandbox/pod_dir_cleaner.rb +85 -0
- data/lib/cocoapods/sandbox/podspec_finder.rb +23 -0
- data/lib/cocoapods/sources_manager.rb +157 -0
- data/lib/cocoapods/target.rb +261 -0
- data/lib/cocoapods/target/aggregate_target.rb +338 -0
- data/lib/cocoapods/target/build_settings.rb +1075 -0
- data/lib/cocoapods/target/pod_target.rb +559 -0
- data/lib/cocoapods/user_interface.rb +459 -0
- data/lib/cocoapods/user_interface/error_report.rb +187 -0
- data/lib/cocoapods/user_interface/inspector_reporter.rb +109 -0
- data/lib/cocoapods/validator.rb +981 -0
- metadata +533 -0
|
@@ -0,0 +1,240 @@
|
|
|
1
|
+
module Pod
|
|
2
|
+
class Installer
|
|
3
|
+
class Analyzer
|
|
4
|
+
# Analyze the sandbox to detect which Pods should be removed, and which
|
|
5
|
+
# ones should be reinstalled.
|
|
6
|
+
#
|
|
7
|
+
# The logic is the following:
|
|
8
|
+
#
|
|
9
|
+
# Added
|
|
10
|
+
# - If not present in the sandbox lockfile.
|
|
11
|
+
# - The directory of the Pod doesn't exits.
|
|
12
|
+
#
|
|
13
|
+
# Changed
|
|
14
|
+
# - The version of the Pod changed.
|
|
15
|
+
# - The SHA of the specification file changed.
|
|
16
|
+
# - The specific installed (sub)specs of the same Pod changed.
|
|
17
|
+
# - The specification is from an external source and the
|
|
18
|
+
# installation process is in update mode.
|
|
19
|
+
# - The directory of the Pod is empty.
|
|
20
|
+
# - The Pod has been pre-downloaded.
|
|
21
|
+
#
|
|
22
|
+
# Removed
|
|
23
|
+
# - If a specification is present in the lockfile but not in the resolved
|
|
24
|
+
# specs.
|
|
25
|
+
#
|
|
26
|
+
# Unchanged
|
|
27
|
+
# - If none of the above conditions match.
|
|
28
|
+
#
|
|
29
|
+
class SandboxAnalyzer
|
|
30
|
+
# @return [Sandbox] The sandbox to analyze.
|
|
31
|
+
#
|
|
32
|
+
attr_reader :sandbox
|
|
33
|
+
|
|
34
|
+
# @return [Array<Specifications>] The specifications returned by the
|
|
35
|
+
# resolver.
|
|
36
|
+
#
|
|
37
|
+
attr_reader :specs
|
|
38
|
+
|
|
39
|
+
# @return [Bool] Whether the installation is performed in update mode.
|
|
40
|
+
#
|
|
41
|
+
attr_reader :update_mode
|
|
42
|
+
|
|
43
|
+
alias_method :update_mode?, :update_mode
|
|
44
|
+
|
|
45
|
+
# Init a new SandboxAnalyzer
|
|
46
|
+
#
|
|
47
|
+
# @param [Sandbox] sandbox @see sandbox
|
|
48
|
+
# @param [Array<Specifications>] specs @see specs
|
|
49
|
+
# @param [Bool] update_mode @see update_mode
|
|
50
|
+
#
|
|
51
|
+
def initialize(sandbox, specs, update_mode)
|
|
52
|
+
@sandbox = sandbox
|
|
53
|
+
@specs = specs
|
|
54
|
+
@update_mode = update_mode
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
# Performs the analysis to the detect the state of the sandbox respect
|
|
58
|
+
# to the resolved specifications.
|
|
59
|
+
#
|
|
60
|
+
# @return [void]
|
|
61
|
+
#
|
|
62
|
+
def analyze
|
|
63
|
+
state = SpecsState.new
|
|
64
|
+
if sandbox_manifest
|
|
65
|
+
all_names = (resolved_pods + sandbox_pods).uniq.sort
|
|
66
|
+
all_names.sort.each do |name|
|
|
67
|
+
state.add_name(name, pod_state(name))
|
|
68
|
+
end
|
|
69
|
+
else
|
|
70
|
+
state.added.merge(resolved_pods)
|
|
71
|
+
end
|
|
72
|
+
state
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
#---------------------------------------------------------------------#
|
|
76
|
+
|
|
77
|
+
private
|
|
78
|
+
|
|
79
|
+
# @!group Pod state
|
|
80
|
+
|
|
81
|
+
# Returns the state of the Pod with the given name.
|
|
82
|
+
#
|
|
83
|
+
# @param [String] pod
|
|
84
|
+
# the name of the Pod.
|
|
85
|
+
#
|
|
86
|
+
# @return [Symbol] The state
|
|
87
|
+
#
|
|
88
|
+
def pod_state(pod)
|
|
89
|
+
return :added if pod_added?(pod)
|
|
90
|
+
return :deleted if pod_deleted?(pod)
|
|
91
|
+
return :changed if pod_changed?(pod)
|
|
92
|
+
:unchanged
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
# Returns whether the Pod with the given name should be installed.
|
|
96
|
+
#
|
|
97
|
+
# @note A Pod whose folder doesn't exists is considered added.
|
|
98
|
+
#
|
|
99
|
+
# @param [String] pod
|
|
100
|
+
# the name of the Pod.
|
|
101
|
+
#
|
|
102
|
+
# @return [Bool] Whether the Pod is added.
|
|
103
|
+
#
|
|
104
|
+
def pod_added?(pod)
|
|
105
|
+
return true if resolved_pods.include?(pod) && !sandbox_pods.include?(pod)
|
|
106
|
+
return true if !folder_exist?(pod) && !sandbox.local?(pod)
|
|
107
|
+
false
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
# Returns whether the Pod with the given name should be removed from
|
|
111
|
+
# the installation.
|
|
112
|
+
#
|
|
113
|
+
# @param [String] pod
|
|
114
|
+
# the name of the Pod.
|
|
115
|
+
#
|
|
116
|
+
# @return [Bool] Whether the Pod is deleted.
|
|
117
|
+
#
|
|
118
|
+
def pod_deleted?(pod)
|
|
119
|
+
return true if !resolved_pods.include?(pod) && sandbox_pods.include?(pod)
|
|
120
|
+
false
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
# Returns whether the Pod with the given name should be considered
|
|
124
|
+
# changed and thus should be reinstalled.
|
|
125
|
+
#
|
|
126
|
+
# @note In update mode, as there is no way to know if a remote source
|
|
127
|
+
# hash changed the Pods from external
|
|
128
|
+
# sources are always marked as changed.
|
|
129
|
+
#
|
|
130
|
+
# @note A Pod whose folder is empty is considered changed.
|
|
131
|
+
#
|
|
132
|
+
# @param [String] pod
|
|
133
|
+
# the name of the Pod.
|
|
134
|
+
#
|
|
135
|
+
# @return [Bool] Whether the Pod is changed.
|
|
136
|
+
#
|
|
137
|
+
def pod_changed?(pod)
|
|
138
|
+
spec = root_spec(pod)
|
|
139
|
+
return true if spec.version != sandbox_version(pod)
|
|
140
|
+
return true if spec.checksum != sandbox_checksum(pod)
|
|
141
|
+
return true if resolved_spec_names(pod) != sandbox_spec_names(pod)
|
|
142
|
+
return true if sandbox.predownloaded?(pod)
|
|
143
|
+
return true if folder_empty?(pod)
|
|
144
|
+
false
|
|
145
|
+
end
|
|
146
|
+
|
|
147
|
+
#---------------------------------------------------------------------#
|
|
148
|
+
|
|
149
|
+
private
|
|
150
|
+
|
|
151
|
+
# @!group Private helpers
|
|
152
|
+
|
|
153
|
+
# @return [Lockfile] The manifest to use for the sandbox.
|
|
154
|
+
#
|
|
155
|
+
def sandbox_manifest
|
|
156
|
+
sandbox.manifest
|
|
157
|
+
end
|
|
158
|
+
|
|
159
|
+
#--------------------------------------#
|
|
160
|
+
|
|
161
|
+
# @return [Array<String>] The name of the resolved Pods.
|
|
162
|
+
#
|
|
163
|
+
def resolved_pods
|
|
164
|
+
specs.map { |spec| spec.root.name }.uniq
|
|
165
|
+
end
|
|
166
|
+
|
|
167
|
+
# @return [Array<String>] The name of the Pods stored in the sandbox
|
|
168
|
+
# manifest.
|
|
169
|
+
#
|
|
170
|
+
def sandbox_pods
|
|
171
|
+
sandbox_manifest.pod_names.map { |name| Specification.root_name(name) }.uniq
|
|
172
|
+
end
|
|
173
|
+
|
|
174
|
+
# @return [Array<String>] The name of the resolved specifications
|
|
175
|
+
# (includes subspecs).
|
|
176
|
+
#
|
|
177
|
+
# @param [String] pod
|
|
178
|
+
# the name of the Pod.
|
|
179
|
+
#
|
|
180
|
+
def resolved_spec_names(pod)
|
|
181
|
+
specs.select { |s| s.root.name == pod }.map(&:name).uniq.sort
|
|
182
|
+
end
|
|
183
|
+
|
|
184
|
+
# @return [Array<String>] The name of the specifications stored in the
|
|
185
|
+
# sandbox manifest (includes subspecs).
|
|
186
|
+
#
|
|
187
|
+
# @param [String] pod
|
|
188
|
+
# the name of the Pod.
|
|
189
|
+
#
|
|
190
|
+
def sandbox_spec_names(pod)
|
|
191
|
+
sandbox_manifest.pod_names.select { |name| Specification.root_name(name) == pod }.uniq.sort
|
|
192
|
+
end
|
|
193
|
+
|
|
194
|
+
# @return [Specification] The root specification for the Pod with the
|
|
195
|
+
# given name.
|
|
196
|
+
#
|
|
197
|
+
# @param [String] pod
|
|
198
|
+
# the name of the Pod.
|
|
199
|
+
#
|
|
200
|
+
def root_spec(pod)
|
|
201
|
+
specs.find { |s| s.root.name == pod }.root
|
|
202
|
+
end
|
|
203
|
+
|
|
204
|
+
#--------------------------------------#
|
|
205
|
+
|
|
206
|
+
# @return [Version] The version of Pod with the given name stored in
|
|
207
|
+
# the sandbox.
|
|
208
|
+
#
|
|
209
|
+
# @param [String] pod
|
|
210
|
+
# the name of the Pod.
|
|
211
|
+
#
|
|
212
|
+
def sandbox_version(pod)
|
|
213
|
+
sandbox_manifest.version(pod)
|
|
214
|
+
end
|
|
215
|
+
|
|
216
|
+
# @return [String] The checksum of the specification of the Pod with
|
|
217
|
+
# the given name stored in the sandbox.
|
|
218
|
+
#
|
|
219
|
+
# @param [String] pod
|
|
220
|
+
# the name of the Pod.
|
|
221
|
+
#
|
|
222
|
+
def sandbox_checksum(pod)
|
|
223
|
+
sandbox_manifest.checksum(pod)
|
|
224
|
+
end
|
|
225
|
+
|
|
226
|
+
#--------------------------------------#
|
|
227
|
+
|
|
228
|
+
def folder_exist?(pod)
|
|
229
|
+
sandbox.pod_dir(pod).exist?
|
|
230
|
+
end
|
|
231
|
+
|
|
232
|
+
def folder_empty?(pod)
|
|
233
|
+
Dir.glob(sandbox.pod_dir(pod) + '*').empty?
|
|
234
|
+
end
|
|
235
|
+
|
|
236
|
+
#---------------------------------------------------------------------#
|
|
237
|
+
end
|
|
238
|
+
end
|
|
239
|
+
end
|
|
240
|
+
end
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
require 'set'
|
|
2
|
+
|
|
3
|
+
module Pod
|
|
4
|
+
class Installer
|
|
5
|
+
class Analyzer
|
|
6
|
+
# This class represents the state of a collection of Pods.
|
|
7
|
+
#
|
|
8
|
+
# @note The names of the pods stored by this class are always the **root**
|
|
9
|
+
# name of the specification.
|
|
10
|
+
#
|
|
11
|
+
# @note The motivation for this class is to ensure that the names of the
|
|
12
|
+
# subspecs are added instead of the name of the Pods.
|
|
13
|
+
#
|
|
14
|
+
class SpecsState
|
|
15
|
+
# @return [Set<String>] the names of the pods that were added.
|
|
16
|
+
#
|
|
17
|
+
attr_reader :added
|
|
18
|
+
|
|
19
|
+
# @return [Set<String>] the names of the pods that were changed.
|
|
20
|
+
#
|
|
21
|
+
attr_reader :changed
|
|
22
|
+
|
|
23
|
+
# @return [Set<String>] the names of the pods that were deleted.
|
|
24
|
+
#
|
|
25
|
+
attr_reader :deleted
|
|
26
|
+
|
|
27
|
+
# @return [Set<String>] the names of the pods that were unchanged.
|
|
28
|
+
#
|
|
29
|
+
attr_reader :unchanged
|
|
30
|
+
|
|
31
|
+
# Initialize a new instance
|
|
32
|
+
#
|
|
33
|
+
# @param [Hash{Symbol=>String}] pods_by_state
|
|
34
|
+
# The name of the pods grouped by their state
|
|
35
|
+
# (`:added`, `:removed`, `:changed` or `:unchanged`).
|
|
36
|
+
#
|
|
37
|
+
def initialize(pods_by_state = nil)
|
|
38
|
+
@added = Set.new
|
|
39
|
+
@deleted = Set.new
|
|
40
|
+
@changed = Set.new
|
|
41
|
+
@unchanged = Set.new
|
|
42
|
+
|
|
43
|
+
if pods_by_state
|
|
44
|
+
{
|
|
45
|
+
:added => :added,
|
|
46
|
+
:changed => :changed,
|
|
47
|
+
:removed => :deleted,
|
|
48
|
+
:unchanged => :unchanged,
|
|
49
|
+
}.each do |state, spec_state|
|
|
50
|
+
Array(pods_by_state[state]).each do |name|
|
|
51
|
+
add_name(name, spec_state)
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
# Displays the state of each pod.
|
|
58
|
+
#
|
|
59
|
+
# @return [void]
|
|
60
|
+
#
|
|
61
|
+
def print
|
|
62
|
+
added .sort.each { |pod| UI.message('A'.green + " #{pod}", '', 2) }
|
|
63
|
+
deleted .sort.each { |pod| UI.message('R'.red + " #{pod}", '', 2) }
|
|
64
|
+
changed .sort.each { |pod| UI.message('M'.yellow + " #{pod}", '', 2) }
|
|
65
|
+
unchanged.sort.each { |pod| UI.message('-' + " #{pod}", '', 2) }
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
# Adds the name of a Pod to the give state.
|
|
69
|
+
#
|
|
70
|
+
# @param [String] name
|
|
71
|
+
# the name of the Pod.
|
|
72
|
+
#
|
|
73
|
+
# @param [Symbol] state
|
|
74
|
+
# the state of the Pod.
|
|
75
|
+
#
|
|
76
|
+
# @return [void]
|
|
77
|
+
#
|
|
78
|
+
def add_name(name, state)
|
|
79
|
+
send(state) << Specification.root_name(name)
|
|
80
|
+
end
|
|
81
|
+
end
|
|
82
|
+
end
|
|
83
|
+
end
|
|
84
|
+
end
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
module Pod
|
|
2
|
+
class Installer
|
|
3
|
+
class Analyzer
|
|
4
|
+
class TargetInspectionResult
|
|
5
|
+
# @return [TargetDefinition] the target definition, whose project was
|
|
6
|
+
# inspected
|
|
7
|
+
#
|
|
8
|
+
attr_reader :target_definition
|
|
9
|
+
|
|
10
|
+
# @return [Xcodeproj::Project] the user's Xcode project
|
|
11
|
+
#
|
|
12
|
+
attr_reader :project
|
|
13
|
+
|
|
14
|
+
# @return [Array<String>] the uuid of the user's targets
|
|
15
|
+
#
|
|
16
|
+
attr_reader :project_target_uuids
|
|
17
|
+
|
|
18
|
+
# @return [Hash{String=>Symbol}] A hash representing the user build
|
|
19
|
+
# configurations where each key corresponds to the name of a
|
|
20
|
+
# configuration and its value to its type (`:debug` or
|
|
21
|
+
# `:release`).
|
|
22
|
+
#
|
|
23
|
+
attr_reader :build_configurations
|
|
24
|
+
|
|
25
|
+
# @return [Platform] the platform of the user targets
|
|
26
|
+
#
|
|
27
|
+
attr_reader :platform
|
|
28
|
+
|
|
29
|
+
# @return [Array<String>] the architectures used by user's targets
|
|
30
|
+
#
|
|
31
|
+
attr_reader :archs
|
|
32
|
+
|
|
33
|
+
# Initialize a new instance
|
|
34
|
+
#
|
|
35
|
+
# @param [TargetDefinition] target_definition @see #target_definition
|
|
36
|
+
# @param [Xcodeproj::Project] project @see #project
|
|
37
|
+
# @param [Array<String>] project_target_uuids @see #project_target_uuids
|
|
38
|
+
# @param [Hash{String=>Symbol}] build_configurations @see #build_configurations
|
|
39
|
+
# @param [Platform] platform @see #platform
|
|
40
|
+
# @param [Array<String>] archs @see #archs
|
|
41
|
+
#
|
|
42
|
+
def initialize(target_definition, project, project_target_uuids, build_configurations, platform, archs)
|
|
43
|
+
@target_definition = target_definition
|
|
44
|
+
@project = project
|
|
45
|
+
@project_target_uuids = project_target_uuids
|
|
46
|
+
@build_configurations = build_configurations
|
|
47
|
+
@platform = platform
|
|
48
|
+
@archs = archs
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
end
|
|
@@ -0,0 +1,260 @@
|
|
|
1
|
+
require 'active_support/core_ext/array/conversions'
|
|
2
|
+
|
|
3
|
+
module Pod
|
|
4
|
+
class Installer
|
|
5
|
+
class Analyzer
|
|
6
|
+
class TargetInspector
|
|
7
|
+
PLATFORM_INFO_URL = 'https://guides.cocoapods.org/syntax/podfile.html#platform'.freeze
|
|
8
|
+
|
|
9
|
+
# @return [TargetDefinition] the target definition to inspect
|
|
10
|
+
#
|
|
11
|
+
attr_reader :target_definition
|
|
12
|
+
|
|
13
|
+
# @return [Pathname] the root of the CocoaPods installation where the
|
|
14
|
+
# Podfile is located
|
|
15
|
+
#
|
|
16
|
+
attr_reader :installation_root
|
|
17
|
+
|
|
18
|
+
# Initialize a new instance
|
|
19
|
+
#
|
|
20
|
+
# @param [TargetDefinition] target_definition
|
|
21
|
+
# @see #target_definition
|
|
22
|
+
#
|
|
23
|
+
# @param [Pathname] installation_root
|
|
24
|
+
# @see #installation_root
|
|
25
|
+
#
|
|
26
|
+
def initialize(target_definition, installation_root)
|
|
27
|
+
@target_definition = target_definition
|
|
28
|
+
@installation_root = installation_root
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
# Inspect the #target_definition
|
|
32
|
+
#
|
|
33
|
+
# @raise If no `user_project` is set
|
|
34
|
+
#
|
|
35
|
+
# @return [TargetInspectionResult] the result of the inspection of the target definition within the user project
|
|
36
|
+
#
|
|
37
|
+
def compute_results(user_project)
|
|
38
|
+
raise ArgumentError, 'Cannot compute results without a user project set' unless user_project
|
|
39
|
+
|
|
40
|
+
targets = compute_targets(user_project)
|
|
41
|
+
project_target_uuids = targets.map(&:uuid)
|
|
42
|
+
build_configurations = compute_build_configurations(targets)
|
|
43
|
+
platform = compute_platform(targets)
|
|
44
|
+
archs = compute_archs(targets)
|
|
45
|
+
swift_version = compute_swift_version_from_targets(targets)
|
|
46
|
+
|
|
47
|
+
result = TargetInspectionResult.new(target_definition, user_project, project_target_uuids,
|
|
48
|
+
build_configurations, platform, archs)
|
|
49
|
+
result.target_definition.swift_version = swift_version
|
|
50
|
+
result
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
# Returns the path of the user project that the #target_definition
|
|
54
|
+
# should integrate.
|
|
55
|
+
#
|
|
56
|
+
# @raise If the project is implicit and there are multiple projects.
|
|
57
|
+
#
|
|
58
|
+
# @raise If the path doesn't exits.
|
|
59
|
+
#
|
|
60
|
+
# @return [Pathname] the path of the user project.
|
|
61
|
+
#
|
|
62
|
+
def compute_project_path
|
|
63
|
+
if target_definition.user_project_path
|
|
64
|
+
path = installation_root + target_definition.user_project_path
|
|
65
|
+
path = "#{path}.xcodeproj" unless File.extname(path) == '.xcodeproj'
|
|
66
|
+
path = Pathname.new(path)
|
|
67
|
+
unless path.exist?
|
|
68
|
+
raise Informative, 'Unable to find the Xcode project ' \
|
|
69
|
+
"`#{path}` for the target `#{target_definition.label}`."
|
|
70
|
+
end
|
|
71
|
+
else
|
|
72
|
+
xcodeprojs = installation_root.children.select { |e| e.fnmatch('*.xcodeproj') }
|
|
73
|
+
if xcodeprojs.size == 1
|
|
74
|
+
path = xcodeprojs.first
|
|
75
|
+
else
|
|
76
|
+
raise Informative, 'Could not automatically select an Xcode project. ' \
|
|
77
|
+
"Specify one in your Podfile like so:\n\n" \
|
|
78
|
+
" project 'path/to/Project.xcodeproj'\n"
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
path
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
#-----------------------------------------------------------------------#
|
|
85
|
+
|
|
86
|
+
private
|
|
87
|
+
|
|
88
|
+
# Returns a list of the targets from the project of #target_definition
|
|
89
|
+
# that needs to be integrated.
|
|
90
|
+
#
|
|
91
|
+
# @note The method first looks if there is a target specified with
|
|
92
|
+
# the `link_with` option of the {TargetDefinition}. Otherwise
|
|
93
|
+
# it looks for the target that has the same name of the target
|
|
94
|
+
# definition. Finally if no target was found the first
|
|
95
|
+
# encountered target is returned (it is assumed to be the one
|
|
96
|
+
# to integrate in simple projects).
|
|
97
|
+
#
|
|
98
|
+
# @param [Xcodeproj::Project] user_project
|
|
99
|
+
# the user project
|
|
100
|
+
#
|
|
101
|
+
# @return [Array<PBXNativeTarget>]
|
|
102
|
+
#
|
|
103
|
+
def compute_targets(user_project)
|
|
104
|
+
native_targets = user_project.native_targets
|
|
105
|
+
target = native_targets.find { |t| t.name == target_definition.name.to_s }
|
|
106
|
+
unless target
|
|
107
|
+
found = native_targets.map { |t| "`#{t.name}`" }.to_sentence
|
|
108
|
+
raise Informative, "Unable to find a target named `#{target_definition.name}`, did find #{found}."
|
|
109
|
+
end
|
|
110
|
+
[target]
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
# @param [Array<PBXNativeTarget] user_targets the user's targets of the project of
|
|
114
|
+
# #target_definition which needs to be integrated
|
|
115
|
+
#
|
|
116
|
+
# @return [Hash{String=>Symbol}] A hash representing the user build
|
|
117
|
+
# configurations where each key corresponds to the name of a
|
|
118
|
+
# configuration and its value to its type (`:debug` or `:release`).
|
|
119
|
+
#
|
|
120
|
+
def compute_build_configurations(user_targets)
|
|
121
|
+
if user_targets
|
|
122
|
+
user_targets.flat_map { |t| t.build_configurations.map(&:name) }.each_with_object({}) do |name, hash|
|
|
123
|
+
hash[name] = name == 'Debug' ? :debug : :release
|
|
124
|
+
end.merge(target_definition.build_configurations || {})
|
|
125
|
+
else
|
|
126
|
+
target_definition.build_configurations || {}
|
|
127
|
+
end
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
# @param [Array<PBXNativeTarget] user_targets the user's targets of the project of
|
|
131
|
+
# #target_definition which needs to be integrated
|
|
132
|
+
#
|
|
133
|
+
# @return [Platform] The platform of the user's targets
|
|
134
|
+
#
|
|
135
|
+
# @note This resolves to the lowest deployment target across the user
|
|
136
|
+
# targets.
|
|
137
|
+
#
|
|
138
|
+
# @todo Is assigning the platform to the target definition the best way
|
|
139
|
+
# to go?
|
|
140
|
+
#
|
|
141
|
+
def compute_platform(user_targets)
|
|
142
|
+
return target_definition.platform if target_definition.platform
|
|
143
|
+
name = nil
|
|
144
|
+
deployment_target = nil
|
|
145
|
+
|
|
146
|
+
user_targets.each do |target|
|
|
147
|
+
name ||= target.platform_name
|
|
148
|
+
raise Informative, 'Targets with different platforms' unless name == target.platform_name
|
|
149
|
+
if !deployment_target || deployment_target > Version.new(target.deployment_target)
|
|
150
|
+
deployment_target = Version.new(target.deployment_target)
|
|
151
|
+
end
|
|
152
|
+
end
|
|
153
|
+
|
|
154
|
+
unless name
|
|
155
|
+
raise Informative,
|
|
156
|
+
"Unable to determine the platform for the `#{target_definition.name}` target."
|
|
157
|
+
end
|
|
158
|
+
|
|
159
|
+
UI.warn "Automatically assigning platform `#{name}` with version `#{deployment_target}` " \
|
|
160
|
+
"on target `#{target_definition.name}` because no platform was specified. " \
|
|
161
|
+
"Please specify a platform for this target in your Podfile. See `#{PLATFORM_INFO_URL}`."
|
|
162
|
+
|
|
163
|
+
target_definition.set_platform(name, deployment_target)
|
|
164
|
+
Platform.new(name, deployment_target)
|
|
165
|
+
end
|
|
166
|
+
|
|
167
|
+
# Computes the architectures relevant for the user's targets.
|
|
168
|
+
#
|
|
169
|
+
# @param [Array<PBXNativeTarget] user_targets the user's targets of the project of
|
|
170
|
+
# #target_definition which needs to be integrated
|
|
171
|
+
#
|
|
172
|
+
# @return [Array<String>]
|
|
173
|
+
#
|
|
174
|
+
def compute_archs(user_targets)
|
|
175
|
+
user_targets.flat_map do |target|
|
|
176
|
+
Array(target.common_resolved_build_setting('ARCHS'))
|
|
177
|
+
end.compact.uniq.sort
|
|
178
|
+
end
|
|
179
|
+
|
|
180
|
+
# Checks if any of the targets for the {TargetDefinition} computed before
|
|
181
|
+
# by #compute_user_project_targets is recommended to be build as a framework
|
|
182
|
+
# due the presence of Swift source code in any of the source build phases.
|
|
183
|
+
#
|
|
184
|
+
# @param [TargetDefinition] target_definition
|
|
185
|
+
# the target definition
|
|
186
|
+
#
|
|
187
|
+
# @param [Array<PBXNativeTarget>] native_targets
|
|
188
|
+
# the targets which are checked for presence of Swift source code
|
|
189
|
+
#
|
|
190
|
+
# @return [Boolean] Whether the user project targets to integrate into
|
|
191
|
+
# uses Swift
|
|
192
|
+
#
|
|
193
|
+
def compute_recommends_frameworks(target_definition, native_targets)
|
|
194
|
+
file_predicate = nil
|
|
195
|
+
file_predicate = proc do |file_ref|
|
|
196
|
+
if file_ref.respond_to?(:last_known_file_type)
|
|
197
|
+
file_ref.last_known_file_type == 'sourcecode.swift'
|
|
198
|
+
elsif file_ref.respond_to?(:files)
|
|
199
|
+
file_ref.files.any?(&file_predicate)
|
|
200
|
+
else
|
|
201
|
+
false
|
|
202
|
+
end
|
|
203
|
+
end
|
|
204
|
+
target_definition.platform.supports_dynamic_frameworks? || native_targets.any? do |target|
|
|
205
|
+
target.source_build_phase.files.any? do |build_file|
|
|
206
|
+
file_predicate.call(build_file.file_ref)
|
|
207
|
+
end
|
|
208
|
+
end
|
|
209
|
+
end
|
|
210
|
+
|
|
211
|
+
# Compute the Swift version for the target build configurations. If more
|
|
212
|
+
# than one Swift version is defined for a given target, then it will raise.
|
|
213
|
+
#
|
|
214
|
+
# @param [Array<PBXNativeTarget>] targets
|
|
215
|
+
# the targets that are checked for Swift versions.
|
|
216
|
+
#
|
|
217
|
+
# @return [String] the targets Swift version or nil
|
|
218
|
+
#
|
|
219
|
+
def compute_swift_version_from_targets(targets)
|
|
220
|
+
versions_to_targets = targets.inject({}) do |memo, target|
|
|
221
|
+
# User project may have an xcconfig that specifies the `SWIFT_VERSION`. We first check if that is true and
|
|
222
|
+
# that the xcconfig file actually exists. After the first integration the xcconfig set is most probably
|
|
223
|
+
# the one that was generated from CocoaPods. See https://github.com/CocoaPods/CocoaPods/issues/7731 for
|
|
224
|
+
# more details.
|
|
225
|
+
resolve_against_xcconfig = target.build_configuration_list.build_configurations.all? do |bc|
|
|
226
|
+
!bc.base_configuration_reference.nil? && File.exist?(bc.base_configuration_reference.real_path)
|
|
227
|
+
end
|
|
228
|
+
versions = target.resolved_build_setting('SWIFT_VERSION', resolve_against_xcconfig).values
|
|
229
|
+
versions.each do |version|
|
|
230
|
+
memo[version] = [] if memo[version].nil?
|
|
231
|
+
memo[version] << target.name unless memo[version].include? target.name
|
|
232
|
+
end
|
|
233
|
+
memo
|
|
234
|
+
end
|
|
235
|
+
|
|
236
|
+
case versions_to_targets.count
|
|
237
|
+
when 0
|
|
238
|
+
nil
|
|
239
|
+
when 1
|
|
240
|
+
versions_to_targets.keys.first
|
|
241
|
+
else
|
|
242
|
+
target_version_pairs = versions_to_targets.map do |version_names, target_names|
|
|
243
|
+
target_names.map { |target_name| [target_name, version_names] }
|
|
244
|
+
end
|
|
245
|
+
|
|
246
|
+
sorted_pairs = target_version_pairs.flat_map { |i| i }.sort_by do |target_name, version_name|
|
|
247
|
+
"#{target_name} #{version_name}"
|
|
248
|
+
end
|
|
249
|
+
|
|
250
|
+
formatted_output = sorted_pairs.map do |target, version_name|
|
|
251
|
+
"#{target}: Swift #{version_name}"
|
|
252
|
+
end.join("\n")
|
|
253
|
+
|
|
254
|
+
raise Informative, "There may only be up to 1 unique SWIFT_VERSION per target. Found target(s) with multiple Swift versions:\n#{formatted_output}"
|
|
255
|
+
end
|
|
256
|
+
end
|
|
257
|
+
end
|
|
258
|
+
end
|
|
259
|
+
end
|
|
260
|
+
end
|