cocoapods-dykit 0.5.2 → 0.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 +4 -4
- data/lib/pod/command.rb +2 -0
- data/lib/pod/command/dyinstall.rb +51 -0
- data/lib/pod/command/dyupdate.rb +106 -0
- data/lib/pod/command/fmwk.rb +4 -0
- data/lib/pod/command/lib/dylint.rb +1 -0
- data/lib/pod/gem_version.rb +1 -1
- data/lib/pod/installer.rb +715 -0
- data/lib/pod/installer/analyzer.rb +934 -0
- data/lib/pod/installer/analyzer/analysis_result.rb +57 -0
- data/lib/pod/installer/analyzer/locking_dependency_analyzer.rb +95 -0
- data/lib/pod/installer/analyzer/pod_variant.rb +68 -0
- data/lib/pod/installer/analyzer/pod_variant_set.rb +157 -0
- data/lib/pod/installer/analyzer/podfile_dependency_cache.rb +54 -0
- data/lib/pod/installer/analyzer/sandbox_analyzer.rb +251 -0
- data/lib/pod/installer/analyzer/specs_state.rb +84 -0
- data/lib/pod/installer/analyzer/target_inspection_result.rb +45 -0
- data/lib/pod/installer/analyzer/target_inspector.rb +254 -0
- data/lib/pod/installer/installation_options.rb +158 -0
- data/lib/pod/installer/pod_source_installer.rb +214 -0
- data/lib/pod/installer/pod_source_preparer.rb +77 -0
- data/lib/pod/installer/podfile_validator.rb +139 -0
- data/lib/pod/installer/post_install_hooks_context.rb +107 -0
- data/lib/pod/installer/pre_install_hooks_context.rb +42 -0
- data/lib/pod/installer/source_provider_hooks_context.rb +32 -0
- data/lib/pod/installer/user_project_integrator.rb +253 -0
- data/lib/pod/installer/user_project_integrator/target_integrator.rb +462 -0
- data/lib/pod/installer/user_project_integrator/target_integrator/xcconfig_integrator.rb +146 -0
- data/lib/pod/installer/xcode.rb +8 -0
- data/lib/pod/installer/xcode/pods_project_generator.rb +353 -0
- data/lib/pod/installer/xcode/pods_project_generator/aggregate_target_installer.rb +172 -0
- data/lib/pod/installer/xcode/pods_project_generator/file_references_installer.rb +367 -0
- data/lib/pod/installer/xcode/pods_project_generator/pod_target_installer.rb +718 -0
- data/lib/pod/installer/xcode/pods_project_generator/pod_target_integrator.rb +111 -0
- data/lib/pod/installer/xcode/pods_project_generator/target_installer.rb +265 -0
- data/lib/pod/installer/xcode/target_validator.rb +141 -0
- data/lib/pod/resolver.rb +632 -0
- metadata +34 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 9960e4f98da76b9263ffa9f64053a4252444f9d3
|
4
|
+
data.tar.gz: 95db61d9669d6c6f96531da70be6ec5740096ba9
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 7480c952b1b8eea11abd9528840c39dbe822359456ac11fe1406f288499a4382c778eb8e955a583b3721a2f63e99a6162d570d79f04b9356c3a72e7b4b9381e1
|
7
|
+
data.tar.gz: 646805ab6fbc89e624fb3d96c6485d4d329cb38acff9c4b26727f3e8ad0efd131651ff2e203d2a69335b7988b4b81830ebef6cec8b152a5d2de3a54c4effc63f
|
data/lib/pod/command.rb
CHANGED
@@ -0,0 +1,51 @@
|
|
1
|
+
# require 'cocoapods/installer'
|
2
|
+
require File.expand_path('../../installer.rb', __FILE__)
|
3
|
+
module Pod
|
4
|
+
class Command
|
5
|
+
class DyInstall < Command
|
6
|
+
include RepoUpdate
|
7
|
+
include ProjectDirectory
|
8
|
+
|
9
|
+
self.summary = 'Install project dependencies according to versions from a Podfile.lock'
|
10
|
+
|
11
|
+
self.description = <<-DESC
|
12
|
+
Downloads all dependencies defined in `Podfile` and creates an Xcode
|
13
|
+
Pods library project in `./Pods`.
|
14
|
+
|
15
|
+
The Xcode project file should be specified in your `Podfile` like this:
|
16
|
+
|
17
|
+
project 'path/to/XcodeProject.xcodeproj'
|
18
|
+
|
19
|
+
If no project is specified, then a search for an Xcode project will
|
20
|
+
be made. If more than one Xcode project is found, the command will
|
21
|
+
raise an error.
|
22
|
+
|
23
|
+
This will configure the project to reference the Pods static library,
|
24
|
+
add a build configuration file, and add a post build script to copy
|
25
|
+
Pod resources.
|
26
|
+
|
27
|
+
This may return one of several error codes if it encounters problems.
|
28
|
+
* `1` Generic error code
|
29
|
+
* `31` Spec not found (i.e out-of-date source repos, mistyped Pod name etc...)
|
30
|
+
DESC
|
31
|
+
|
32
|
+
def self.options
|
33
|
+
[
|
34
|
+
['--repo-update', 'Force running `pod repo update` before install'],
|
35
|
+
].concat(super).reject { |(name, _)| name == '--no-repo-update' }
|
36
|
+
end
|
37
|
+
|
38
|
+
def run
|
39
|
+
verify_podfile_exists!
|
40
|
+
installer = installer_for_config
|
41
|
+
installer.repo_update = repo_update?(:default => false)
|
42
|
+
installer.update = false
|
43
|
+
installer.install!
|
44
|
+
end
|
45
|
+
|
46
|
+
def installer_for_config
|
47
|
+
DyInstaller.new(config.sandbox, config.podfile, config.lockfile)
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
@@ -0,0 +1,106 @@
|
|
1
|
+
require File.expand_path('../../installer.rb', __FILE__)
|
2
|
+
module Pod
|
3
|
+
class Command
|
4
|
+
class DyUpdate < Command
|
5
|
+
include RepoUpdate
|
6
|
+
include ProjectDirectory
|
7
|
+
|
8
|
+
self.summary = 'Update outdated project dependencies and create new ' \
|
9
|
+
'Podfile.lock'
|
10
|
+
|
11
|
+
self.description = <<-DESC
|
12
|
+
Updates the Pods identified by the specified `POD_NAMES`, which is a
|
13
|
+
space-delimited list of pod names. If no `POD_NAMES` are specified, it
|
14
|
+
updates all the Pods, ignoring the contents of the Podfile.lock. This
|
15
|
+
command is reserved for the update of dependencies; pod install should
|
16
|
+
be used to install changes to the Podfile.
|
17
|
+
DESC
|
18
|
+
|
19
|
+
self.arguments = [
|
20
|
+
CLAide::Argument.new('POD_NAMES', false, true),
|
21
|
+
]
|
22
|
+
|
23
|
+
def self.options
|
24
|
+
[
|
25
|
+
['--sources=https://github.com/artsy/Specs,master', 'The sources from which to update dependent pods. ' \
|
26
|
+
'Multiple sources must be comma-delimited. The master repo will not be included by default with this option.'],
|
27
|
+
['--exclude-pods=podName', 'Pods to exclude during update. Multiple pods must be comma-delimited.'],
|
28
|
+
].concat(super)
|
29
|
+
end
|
30
|
+
|
31
|
+
def initialize(argv)
|
32
|
+
@pods = argv.arguments! unless argv.arguments.empty?
|
33
|
+
|
34
|
+
source_urls = argv.option('sources', '').split(',')
|
35
|
+
excluded_pods = argv.option('exclude-pods', '').split(',')
|
36
|
+
unless source_urls.empty?
|
37
|
+
source_pods = source_urls.flat_map { |url| config.sources_manager.source_with_name_or_url(url).pods }
|
38
|
+
unless source_pods.empty?
|
39
|
+
source_pods = source_pods.select { |pod| config.lockfile.pod_names.include?(pod) }
|
40
|
+
if @pods
|
41
|
+
@pods += source_pods
|
42
|
+
else
|
43
|
+
@pods = source_pods unless source_pods.empty?
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
unless excluded_pods.empty?
|
49
|
+
@pods ||= config.lockfile.pod_names.dup
|
50
|
+
|
51
|
+
non_installed_pods = (excluded_pods - @pods)
|
52
|
+
unless non_installed_pods.empty?
|
53
|
+
pluralized_words = non_installed_pods.length > 1 ? %w(Pods are) : %w(Pod is)
|
54
|
+
message = "Trying to skip `#{non_installed_pods.join('`, `')}` #{pluralized_words.first} " \
|
55
|
+
"which #{pluralized_words.last} not installed"
|
56
|
+
raise Informative, message
|
57
|
+
end
|
58
|
+
|
59
|
+
@pods.delete_if { |pod| excluded_pods.include?(pod) }
|
60
|
+
end
|
61
|
+
|
62
|
+
super
|
63
|
+
end
|
64
|
+
|
65
|
+
# Check if all given pods are installed
|
66
|
+
#
|
67
|
+
def verify_pods_are_installed!
|
68
|
+
lockfile_roots = config.lockfile.pod_names.map { |p| Specification.root_name(p) }
|
69
|
+
missing_pods = @pods.map { |p| Specification.root_name(p) }.select do |pod|
|
70
|
+
!lockfile_roots.include?(pod)
|
71
|
+
end
|
72
|
+
|
73
|
+
unless missing_pods.empty?
|
74
|
+
message = if missing_pods.length > 1
|
75
|
+
"Pods `#{missing_pods.join('`, `')}` are not " \
|
76
|
+
'installed and cannot be updated'
|
77
|
+
else
|
78
|
+
"The `#{missing_pods.first}` Pod is not installed " \
|
79
|
+
'and cannot be updated'
|
80
|
+
end
|
81
|
+
raise Informative, message
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
def run
|
86
|
+
verify_podfile_exists!
|
87
|
+
|
88
|
+
installer = installer_for_config
|
89
|
+
installer.repo_update = repo_update?(:default => true)
|
90
|
+
if @pods
|
91
|
+
verify_lockfile_exists!
|
92
|
+
verify_pods_are_installed!
|
93
|
+
installer.update = { :pods => @pods }
|
94
|
+
else
|
95
|
+
UI.puts 'Update all pods'.yellow
|
96
|
+
installer.update = true
|
97
|
+
end
|
98
|
+
installer.install!
|
99
|
+
end
|
100
|
+
|
101
|
+
def installer_for_config
|
102
|
+
DyInstaller.new(config.sandbox, config.podfile, config.lockfile)
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
data/lib/pod/command/fmwk.rb
CHANGED
data/lib/pod/gem_version.rb
CHANGED
@@ -0,0 +1,715 @@
|
|
1
|
+
require 'active_support/core_ext/string/inflections'
|
2
|
+
require 'fileutils'
|
3
|
+
|
4
|
+
module Pod
|
5
|
+
# The Installer is responsible of taking a Podfile and transform it in the
|
6
|
+
# Pods libraries. It also integrates the user project so the Pods
|
7
|
+
# libraries can be used out of the box.
|
8
|
+
#
|
9
|
+
# The Installer is capable of doing incremental updates to an existing Pod
|
10
|
+
# installation.
|
11
|
+
#
|
12
|
+
# The Installer gets the information that it needs mainly from 3 files:
|
13
|
+
#
|
14
|
+
# - Podfile: The specification written by the user that contains
|
15
|
+
# information about targets and Pods.
|
16
|
+
# - Podfile.lock: Contains information about the pods that were previously
|
17
|
+
# installed and in concert with the Podfile provides information about
|
18
|
+
# which specific version of a Pod should be installed. This file is
|
19
|
+
# ignored in update mode.
|
20
|
+
# - Manifest.lock: A file contained in the Pods folder that keeps track of
|
21
|
+
# the pods installed in the local machine. This files is used once the
|
22
|
+
# exact versions of the Pods has been computed to detect if that version
|
23
|
+
# is already installed. This file is not intended to be kept under source
|
24
|
+
# control and is a copy of the Podfile.lock.
|
25
|
+
#
|
26
|
+
# The Installer is designed to work in environments where the Podfile folder
|
27
|
+
# is under source control and environments where it is not. The rest of the
|
28
|
+
# files, like the user project and the workspace are assumed to be under
|
29
|
+
# source control.
|
30
|
+
#
|
31
|
+
class DyInstaller
|
32
|
+
# autoload :Analyzer, 'cocoapods/installer/analyzer'
|
33
|
+
# autoload :InstallationOptions, 'cocoapods/installer/installation_options'
|
34
|
+
# autoload :PostInstallHooksContext, 'cocoapods/installer/post_install_hooks_context'
|
35
|
+
# autoload :PreInstallHooksContext, 'cocoapods/installer/pre_install_hooks_context'
|
36
|
+
# autoload :SourceProviderHooksContext, 'cocoapods/installer/source_provider_hooks_context'
|
37
|
+
# autoload :PodfileValidator, 'cocoapods/installer/podfile_validator'
|
38
|
+
# autoload :PodSourceInstaller, 'cocoapods/installer/pod_source_installer'
|
39
|
+
# autoload :PodSourcePreparer, 'cocoapods/installer/pod_source_preparer'
|
40
|
+
# autoload :UserProjectIntegrator, 'cocoapods/installer/user_project_integrator'
|
41
|
+
# autoload :Xcode, 'cocoapods/installer/xcode'
|
42
|
+
autoload :Analyzer, File.expand_path('../installer/analyzer', __FILE__)
|
43
|
+
autoload :InstallationOptions, File.expand_path('../installer/installation_options', __FILE__)
|
44
|
+
autoload :PostInstallHooksContext, File.expand_path('../installer/post_install_hooks_context', __FILE__)
|
45
|
+
autoload :PreInstallHooksContext, File.expand_path('../installer/pre_install_hooks_context', __FILE__)
|
46
|
+
autoload :SourceProviderHooksContext, File.expand_path('../installer/source_provider_hooks_context', __FILE__)
|
47
|
+
autoload :PodfileValidator, File.expand_path('../installer/podfile_validator', __FILE__)
|
48
|
+
autoload :PodSourceInstaller, File.expand_path('../installer/pod_source_installer', __FILE__)
|
49
|
+
autoload :PodSourcePreparer, File.expand_path('../installer/pod_source_preparer', __FILE__)
|
50
|
+
autoload :UserProjectIntegrator, File.expand_path('../installer/user_project_integrator', __FILE__)
|
51
|
+
autoload :Xcode, File.expand_path('../installer/xcode', __FILE__)
|
52
|
+
|
53
|
+
include Config::Mixin
|
54
|
+
include InstallationOptions::Mixin
|
55
|
+
|
56
|
+
delegate_installation_options { podfile }
|
57
|
+
|
58
|
+
# @return [Sandbox] The sandbox where the Pods should be installed.
|
59
|
+
#
|
60
|
+
attr_reader :sandbox
|
61
|
+
|
62
|
+
# @return [Podfile] The Podfile specification that contains the information
|
63
|
+
# of the Pods that should be installed.
|
64
|
+
#
|
65
|
+
attr_reader :podfile
|
66
|
+
|
67
|
+
# @return [Lockfile] The Lockfile that stores the information about the
|
68
|
+
# Pods previously installed on any machine.
|
69
|
+
#
|
70
|
+
attr_reader :lockfile
|
71
|
+
|
72
|
+
# Initialize a new instance
|
73
|
+
#
|
74
|
+
# @param [Sandbox] sandbox @see sandbox
|
75
|
+
# @param [Podfile] podfile @see podfile
|
76
|
+
# @param [Lockfile] lockfile @see lockfile
|
77
|
+
#
|
78
|
+
def initialize(sandbox, podfile, lockfile = nil)
|
79
|
+
@sandbox = sandbox
|
80
|
+
@podfile = podfile
|
81
|
+
@lockfile = lockfile
|
82
|
+
|
83
|
+
@use_default_plugins = true
|
84
|
+
@has_dependencies = true
|
85
|
+
end
|
86
|
+
|
87
|
+
# @return [Hash, Boolean, nil] Pods that have been requested to be
|
88
|
+
# updated or true if all Pods should be updated.
|
89
|
+
# If all Pods should been updated the contents of the Lockfile are
|
90
|
+
# not taken into account for deciding what Pods to install.
|
91
|
+
#
|
92
|
+
attr_accessor :update
|
93
|
+
|
94
|
+
# @return [Boolean] Whether it has dependencies. Defaults to true.
|
95
|
+
#
|
96
|
+
attr_accessor :has_dependencies
|
97
|
+
alias_method :has_dependencies?, :has_dependencies
|
98
|
+
|
99
|
+
# @return [Boolean] Whether the spec repos should be updated.
|
100
|
+
#
|
101
|
+
attr_accessor :repo_update
|
102
|
+
alias_method :repo_update?, :repo_update
|
103
|
+
|
104
|
+
# @return [Boolean] Whether default plugins should be used during
|
105
|
+
# installation. Defaults to true.
|
106
|
+
#
|
107
|
+
attr_accessor :use_default_plugins
|
108
|
+
alias_method :use_default_plugins?, :use_default_plugins
|
109
|
+
|
110
|
+
# Installs the Pods.
|
111
|
+
#
|
112
|
+
# The installation process is mostly linear with a few minor complications
|
113
|
+
# to keep in mind:
|
114
|
+
#
|
115
|
+
# - The stored podspecs need to be cleaned before the resolution step
|
116
|
+
# otherwise the sandbox might return an old podspec and not download
|
117
|
+
# the new one from an external source.
|
118
|
+
# - The resolver might trigger the download of Pods from external sources
|
119
|
+
# necessary to retrieve their podspec (unless it is instructed not to
|
120
|
+
# do it).
|
121
|
+
#
|
122
|
+
# @return [void]
|
123
|
+
#
|
124
|
+
def install!
|
125
|
+
before_prepare = Time.now
|
126
|
+
prepare
|
127
|
+
before_resolve = Time.now
|
128
|
+
resolve_dependencies
|
129
|
+
before_download = Time.now
|
130
|
+
download_dependencies
|
131
|
+
before_validate_targets = Time.now
|
132
|
+
validate_targets
|
133
|
+
before_generate = Time.now
|
134
|
+
generate_pods_project
|
135
|
+
before_integreate = Time.now
|
136
|
+
if installation_options.integrate_targets?
|
137
|
+
integrate_user_project
|
138
|
+
else
|
139
|
+
UI.section 'Skipping User Project Integration'
|
140
|
+
end
|
141
|
+
before_post = Time.now
|
142
|
+
perform_post_install_actions
|
143
|
+
after_post = Time.now
|
144
|
+
puts('Time result:')
|
145
|
+
puts(" 【total: #{after_post - before_prepare}】")
|
146
|
+
puts(" 【prepare: #{before_resolve - before_prepare}】")
|
147
|
+
puts(" 【resolve_dependencies: #{before_download - before_resolve}】")
|
148
|
+
puts(" 【download_dependencies: #{before_validate_targets - before_download}】")
|
149
|
+
puts(" 【validate_targets: #{before_generate - before_validate_targets}】")
|
150
|
+
puts(" 【generate_pods_project: #{before_integreate - before_generate}】")
|
151
|
+
puts(" 【integrate_user_project: #{before_post - before_integreate}】")
|
152
|
+
puts(" 【perform_post_install_actions: #{after_post - before_post}】")
|
153
|
+
end
|
154
|
+
|
155
|
+
def prepare
|
156
|
+
# Raise if pwd is inside Pods
|
157
|
+
if Dir.pwd.start_with?(sandbox.root.to_path)
|
158
|
+
message = 'Command should be run from a directory outside Pods directory.'
|
159
|
+
message << "\n\n\tCurrent directory is #{UI.path(Pathname.pwd)}\n"
|
160
|
+
raise Informative, message
|
161
|
+
end
|
162
|
+
UI.message 'Preparing' do
|
163
|
+
deintegrate_if_different_major_version
|
164
|
+
sandbox.prepare
|
165
|
+
ensure_plugins_are_installed!
|
166
|
+
run_plugins_pre_install_hooks
|
167
|
+
end
|
168
|
+
end
|
169
|
+
|
170
|
+
# @return [Analyzer] The analyzer used to resolve dependencies
|
171
|
+
#
|
172
|
+
def resolve_dependencies
|
173
|
+
plugin_sources = run_source_provider_hooks
|
174
|
+
analyzer = create_analyzer(plugin_sources)
|
175
|
+
|
176
|
+
UI.section 'Updating local specs repositories' do
|
177
|
+
analyzer.update_repositories
|
178
|
+
end if repo_update?
|
179
|
+
|
180
|
+
UI.section 'Analyzing dependencies' do
|
181
|
+
analyze(analyzer)
|
182
|
+
validate_build_configurations
|
183
|
+
clean_sandbox
|
184
|
+
end
|
185
|
+
analyzer
|
186
|
+
end
|
187
|
+
|
188
|
+
def download_dependencies
|
189
|
+
UI.section 'Downloading dependencies' do
|
190
|
+
create_file_accessors
|
191
|
+
install_pod_sources
|
192
|
+
run_podfile_pre_install_hooks
|
193
|
+
clean_pod_sources
|
194
|
+
end
|
195
|
+
end
|
196
|
+
|
197
|
+
#-------------------------------------------------------------------------#
|
198
|
+
|
199
|
+
# @!group Pods Project Generation
|
200
|
+
|
201
|
+
private
|
202
|
+
|
203
|
+
def create_generator
|
204
|
+
Xcode::PodsProjectGenerator.new(aggregate_targets, sandbox, pod_targets, analysis_result, installation_options, config)
|
205
|
+
end
|
206
|
+
|
207
|
+
# Generate the 'Pods/Pods.xcodeproj' project.
|
208
|
+
#
|
209
|
+
def generate_pods_project(generator = create_generator)
|
210
|
+
UI.section 'Generating Pods project' do
|
211
|
+
generator.generate!
|
212
|
+
@pods_project = generator.project
|
213
|
+
run_podfile_post_install_hooks
|
214
|
+
generator.write
|
215
|
+
generator.share_development_pod_schemes
|
216
|
+
write_lockfiles
|
217
|
+
end
|
218
|
+
end
|
219
|
+
|
220
|
+
#-------------------------------------------------------------------------#
|
221
|
+
|
222
|
+
public
|
223
|
+
|
224
|
+
# @!group Installation results
|
225
|
+
|
226
|
+
# @return [Analyzer] the analyzer which provides the information about what
|
227
|
+
# needs to be installed.
|
228
|
+
#
|
229
|
+
attr_reader :analysis_result
|
230
|
+
|
231
|
+
# @return [Pod::Project] the `Pods/Pods.xcodeproj` project.
|
232
|
+
#
|
233
|
+
attr_reader :pods_project
|
234
|
+
|
235
|
+
# @return [Array<String>] The Pods that should be installed.
|
236
|
+
#
|
237
|
+
attr_reader :names_of_pods_to_install
|
238
|
+
|
239
|
+
# @return [Array<AggregateTarget>] The model representations of an
|
240
|
+
# aggregation of pod targets generated for a target definition
|
241
|
+
# in the Podfile as result of the analyzer.
|
242
|
+
#
|
243
|
+
attr_reader :aggregate_targets
|
244
|
+
|
245
|
+
# @return [Array<PodTarget>] The model representations of pod targets
|
246
|
+
# generated as result of the analyzer.
|
247
|
+
#
|
248
|
+
def pod_targets
|
249
|
+
aggregate_target_pod_targets = aggregate_targets.flat_map(&:pod_targets)
|
250
|
+
test_dependent_targets = aggregate_target_pod_targets.flat_map(&:test_dependent_targets)
|
251
|
+
(aggregate_target_pod_targets + test_dependent_targets).uniq
|
252
|
+
end
|
253
|
+
|
254
|
+
# @return [Array<Specification>] The specifications that where installed.
|
255
|
+
#
|
256
|
+
attr_accessor :installed_specs
|
257
|
+
|
258
|
+
#-------------------------------------------------------------------------#
|
259
|
+
|
260
|
+
private
|
261
|
+
|
262
|
+
# @!group Installation steps
|
263
|
+
|
264
|
+
# Performs the analysis.
|
265
|
+
#
|
266
|
+
# @return [void]
|
267
|
+
#
|
268
|
+
def analyze(analyzer = create_analyzer)
|
269
|
+
analyzer.update = update
|
270
|
+
@analysis_result = analyzer.analyze
|
271
|
+
@aggregate_targets = analyzer.result.targets
|
272
|
+
end
|
273
|
+
|
274
|
+
def create_analyzer(plugin_sources = nil)
|
275
|
+
Analyzer.new(sandbox, podfile, lockfile, plugin_sources).tap do |analyzer|
|
276
|
+
analyzer.installation_options = installation_options
|
277
|
+
analyzer.has_dependencies = has_dependencies?
|
278
|
+
end
|
279
|
+
end
|
280
|
+
|
281
|
+
# Ensures that the white-listed build configurations are known to prevent
|
282
|
+
# silent typos.
|
283
|
+
#
|
284
|
+
# @raise If an unknown user configuration is found.
|
285
|
+
#
|
286
|
+
def validate_build_configurations
|
287
|
+
whitelisted_configs = pod_targets.
|
288
|
+
flat_map(&:target_definitions).
|
289
|
+
flat_map(&:all_whitelisted_configurations).
|
290
|
+
map(&:downcase).
|
291
|
+
uniq
|
292
|
+
all_user_configurations = analysis_result.all_user_build_configurations.keys.map(&:downcase)
|
293
|
+
|
294
|
+
remainder = whitelisted_configs - all_user_configurations
|
295
|
+
unless remainder.empty?
|
296
|
+
raise Informative,
|
297
|
+
"Unknown #{'configuration'.pluralize(remainder.size)} whitelisted: #{remainder.sort.to_sentence}. " \
|
298
|
+
"CocoaPods found #{all_user_configurations.sort.to_sentence}, did you mean one of these?"
|
299
|
+
end
|
300
|
+
end
|
301
|
+
|
302
|
+
# @return [void] In this step we clean all the folders that will be
|
303
|
+
# regenerated from scratch and any file which might not be
|
304
|
+
# overwritten.
|
305
|
+
#
|
306
|
+
# @todo [#247] Clean the headers of only the pods to install.
|
307
|
+
#
|
308
|
+
def clean_sandbox
|
309
|
+
sandbox.public_headers.implode!
|
310
|
+
target_support_dirs = sandbox.target_support_files_root.children.select(&:directory?)
|
311
|
+
pod_targets.each do |pod_target|
|
312
|
+
pod_target.build_headers.implode!
|
313
|
+
target_support_dirs.delete(pod_target.support_files_dir)
|
314
|
+
end
|
315
|
+
|
316
|
+
aggregate_targets.each do |aggregate_target|
|
317
|
+
target_support_dirs.delete(aggregate_target.support_files_dir)
|
318
|
+
end
|
319
|
+
|
320
|
+
target_support_dirs.each { |dir| FileUtils.rm_rf(dir) }
|
321
|
+
|
322
|
+
unless sandbox_state.deleted.empty?
|
323
|
+
title_options = { :verbose_prefix => '-> '.red }
|
324
|
+
sandbox_state.deleted.each do |pod_name|
|
325
|
+
UI.titled_section("Removing #{pod_name}".red, title_options) do
|
326
|
+
sandbox.clean_pod(pod_name)
|
327
|
+
end
|
328
|
+
end
|
329
|
+
end
|
330
|
+
end
|
331
|
+
|
332
|
+
# @return [void] In this step we create the file accessors for the pod
|
333
|
+
# targets.
|
334
|
+
#
|
335
|
+
def create_file_accessors
|
336
|
+
sandbox.create_file_accessors(pod_targets)
|
337
|
+
end
|
338
|
+
|
339
|
+
# Downloads, installs the documentation and cleans the sources of the Pods
|
340
|
+
# which need to be installed.
|
341
|
+
#
|
342
|
+
# @return [void]
|
343
|
+
#
|
344
|
+
def install_pod_sources
|
345
|
+
@installed_specs = []
|
346
|
+
pods_to_install = sandbox_state.added | sandbox_state.changed
|
347
|
+
title_options = { :verbose_prefix => '-> '.green }
|
348
|
+
root_specs.sort_by(&:name).each do |spec|
|
349
|
+
if pods_to_install.include?(spec.name)
|
350
|
+
if sandbox_state.changed.include?(spec.name) && sandbox.manifest
|
351
|
+
current_version = spec.version
|
352
|
+
previous_version = sandbox.manifest.version(spec.name)
|
353
|
+
has_changed_version = current_version != previous_version
|
354
|
+
current_repo = analysis_result.specs_by_source.detect { |key, values| break key if values.map(&:name).include?(spec.name) }
|
355
|
+
current_repo &&= current_repo.url || current_repo.name
|
356
|
+
previous_spec_repo = sandbox.manifest.spec_repo(spec.name)
|
357
|
+
has_changed_repo = !previous_spec_repo.nil? && current_repo && (current_repo != previous_spec_repo)
|
358
|
+
title = "Installing #{spec.name} #{spec.version}"
|
359
|
+
title << " (was #{previous_version} and source changed to `#{current_repo}` from `#{previous_spec_repo}`)" if has_changed_version && has_changed_repo
|
360
|
+
title << " (was #{previous_version})" if has_changed_version && !has_changed_repo
|
361
|
+
title << " (source changed to `#{current_repo}` from `#{previous_spec_repo}`)" if !has_changed_version && has_changed_repo
|
362
|
+
else
|
363
|
+
title = "Installing #{spec}"
|
364
|
+
end
|
365
|
+
UI.titled_section(title.green, title_options) do
|
366
|
+
install_source_of_pod(spec.name)
|
367
|
+
end
|
368
|
+
else
|
369
|
+
UI.titled_section("Using #{spec}", title_options) do
|
370
|
+
create_pod_installer(spec.name)
|
371
|
+
end
|
372
|
+
end
|
373
|
+
end
|
374
|
+
end
|
375
|
+
|
376
|
+
def create_pod_installer(pod_name)
|
377
|
+
specs_by_platform = {}
|
378
|
+
pod_targets.each do |pod_target|
|
379
|
+
if pod_target.root_spec.name == pod_name
|
380
|
+
specs_by_platform[pod_target.platform] ||= []
|
381
|
+
specs_by_platform[pod_target.platform].concat(pod_target.specs)
|
382
|
+
end
|
383
|
+
end
|
384
|
+
|
385
|
+
raise Informative, "Could not install '#{pod_name}' pod. There is no target that supports it." if specs_by_platform.empty?
|
386
|
+
|
387
|
+
@pod_installers ||= []
|
388
|
+
pod_installer = PodSourceInstaller.new(sandbox, specs_by_platform, :can_cache => installation_options.clean?)
|
389
|
+
@pod_installers << pod_installer
|
390
|
+
pod_installer
|
391
|
+
end
|
392
|
+
|
393
|
+
# Install the Pods. If the resolver indicated that a Pod should be
|
394
|
+
# installed and it exits, it is removed and then reinstalled. In any case if
|
395
|
+
# the Pod doesn't exits it is installed.
|
396
|
+
#
|
397
|
+
# @return [void]
|
398
|
+
#
|
399
|
+
def install_source_of_pod(pod_name)
|
400
|
+
pod_installer = create_pod_installer(pod_name)
|
401
|
+
pod_installer.install!
|
402
|
+
@installed_specs.concat(pod_installer.specs_by_platform.values.flatten.uniq)
|
403
|
+
end
|
404
|
+
|
405
|
+
# Cleans the sources of the Pods if the config instructs to do so.
|
406
|
+
#
|
407
|
+
# @todo Why the @pod_installers might be empty?
|
408
|
+
#
|
409
|
+
def clean_pod_sources
|
410
|
+
return unless installation_options.clean?
|
411
|
+
return unless @pod_installers
|
412
|
+
@pod_installers.each(&:clean!)
|
413
|
+
end
|
414
|
+
|
415
|
+
# Unlocks the sources of the Pods.
|
416
|
+
#
|
417
|
+
# @todo Why the @pod_installers might be empty?
|
418
|
+
#
|
419
|
+
def unlock_pod_sources
|
420
|
+
return unless @pod_installers
|
421
|
+
@pod_installers.each do |installer|
|
422
|
+
pod_target = pod_targets.find { |target| target.pod_name == installer.name }
|
423
|
+
installer.unlock_files!(pod_target.file_accessors)
|
424
|
+
end
|
425
|
+
end
|
426
|
+
|
427
|
+
# Locks the sources of the Pods if the config instructs to do so.
|
428
|
+
#
|
429
|
+
# @todo Why the @pod_installers might be empty?
|
430
|
+
#
|
431
|
+
def lock_pod_sources
|
432
|
+
return unless installation_options.lock_pod_sources?
|
433
|
+
return unless @pod_installers
|
434
|
+
@pod_installers.each do |installer|
|
435
|
+
pod_target = pod_targets.find { |target| target.pod_name == installer.name }
|
436
|
+
installer.lock_files!(pod_target.file_accessors)
|
437
|
+
end
|
438
|
+
end
|
439
|
+
|
440
|
+
def validate_targets
|
441
|
+
validator = Xcode::TargetValidator.new(aggregate_targets, pod_targets)
|
442
|
+
validator.validate!
|
443
|
+
end
|
444
|
+
|
445
|
+
# Runs the registered callbacks for the plugins pre install hooks.
|
446
|
+
#
|
447
|
+
# @return [void]
|
448
|
+
#
|
449
|
+
def run_plugins_pre_install_hooks
|
450
|
+
context = PreInstallHooksContext.generate(sandbox, podfile, lockfile)
|
451
|
+
HooksManager.run(:pre_install, context, plugins)
|
452
|
+
end
|
453
|
+
|
454
|
+
# Performs any post-installation actions
|
455
|
+
#
|
456
|
+
# @return [void]
|
457
|
+
#
|
458
|
+
def perform_post_install_actions
|
459
|
+
unlock_pod_sources
|
460
|
+
run_plugins_post_install_hooks
|
461
|
+
warn_for_deprecations
|
462
|
+
warn_for_installed_script_phases
|
463
|
+
lock_pod_sources
|
464
|
+
print_post_install_message
|
465
|
+
end
|
466
|
+
|
467
|
+
def print_post_install_message
|
468
|
+
podfile_dependencies = analysis_result.podfile_dependency_cache.podfile_dependencies.size
|
469
|
+
pods_installed = root_specs.size
|
470
|
+
title_options = { :verbose_prefix => '-> '.green }
|
471
|
+
UI.titled_section('Pod installation complete! ' \
|
472
|
+
"There #{podfile_dependencies == 1 ? 'is' : 'are'} #{podfile_dependencies} " \
|
473
|
+
"#{'dependency'.pluralize(podfile_dependencies)} from the Podfile " \
|
474
|
+
"and #{pods_installed} total #{'pod'.pluralize(pods_installed)} installed.".green,
|
475
|
+
title_options)
|
476
|
+
end
|
477
|
+
|
478
|
+
# Runs the registered callbacks for the plugins post install hooks.
|
479
|
+
#
|
480
|
+
def run_plugins_post_install_hooks
|
481
|
+
context = PostInstallHooksContext.generate(sandbox, aggregate_targets)
|
482
|
+
HooksManager.run(:post_install, context, plugins)
|
483
|
+
end
|
484
|
+
|
485
|
+
# Runs the registered callbacks for the source provider plugin hooks.
|
486
|
+
#
|
487
|
+
# @return [void]
|
488
|
+
#
|
489
|
+
def run_source_provider_hooks
|
490
|
+
context = SourceProviderHooksContext.generate
|
491
|
+
HooksManager.run(:source_provider, context, plugins)
|
492
|
+
context.sources
|
493
|
+
end
|
494
|
+
|
495
|
+
# Run the deintegrator against all projects in the installation root if the
|
496
|
+
# current CocoaPods major version part is different than the one in the
|
497
|
+
# lockfile.
|
498
|
+
#
|
499
|
+
# @return [void]
|
500
|
+
#
|
501
|
+
def deintegrate_if_different_major_version
|
502
|
+
return unless lockfile
|
503
|
+
return if lockfile.cocoapods_version.major == Version.create(VERSION).major
|
504
|
+
UI.section('Re-creating CocoaPods due to major version update.') do
|
505
|
+
projects = Pathname.glob(config.installation_root + '*.xcodeproj').map { |path| Xcodeproj::Project.open(path) }
|
506
|
+
deintegrator = Deintegrator.new
|
507
|
+
projects.each do |project|
|
508
|
+
config.with_changes(:silent => true) { deintegrator.deintegrate_project(project) }
|
509
|
+
project.save if project.dirty?
|
510
|
+
end
|
511
|
+
end
|
512
|
+
end
|
513
|
+
|
514
|
+
# Ensures that all plugins specified in the {#podfile} are loaded.
|
515
|
+
#
|
516
|
+
# @return [void]
|
517
|
+
#
|
518
|
+
def ensure_plugins_are_installed!
|
519
|
+
require 'claide/command/plugin_manager'
|
520
|
+
|
521
|
+
loaded_plugins = Command::PluginManager.specifications.map(&:name)
|
522
|
+
|
523
|
+
podfile.plugins.keys.each do |plugin|
|
524
|
+
unless loaded_plugins.include? plugin
|
525
|
+
raise Informative, "Your Podfile requires that the plugin `#{plugin}` be installed. Please install it and try installation again."
|
526
|
+
end
|
527
|
+
end
|
528
|
+
end
|
529
|
+
|
530
|
+
DEFAULT_PLUGINS = { 'cocoapods-stats' => {} }
|
531
|
+
|
532
|
+
# Returns the plugins that should be run, as indicated by the default
|
533
|
+
# plugins and the podfile's plugins
|
534
|
+
#
|
535
|
+
# @return [Hash<String, Hash>] The plugins to be used
|
536
|
+
#
|
537
|
+
def plugins
|
538
|
+
if use_default_plugins?
|
539
|
+
DEFAULT_PLUGINS.merge(podfile.plugins)
|
540
|
+
else
|
541
|
+
podfile.plugins
|
542
|
+
end
|
543
|
+
end
|
544
|
+
|
545
|
+
# Prints a warning for any pods that are deprecated
|
546
|
+
#
|
547
|
+
# @return [void]
|
548
|
+
#
|
549
|
+
def warn_for_deprecations
|
550
|
+
deprecated_pods = root_specs.select do |spec|
|
551
|
+
spec.deprecated || spec.deprecated_in_favor_of
|
552
|
+
end
|
553
|
+
deprecated_pods.each do |spec|
|
554
|
+
if spec.deprecated_in_favor_of
|
555
|
+
UI.warn "#{spec.name} has been deprecated in " \
|
556
|
+
"favor of #{spec.deprecated_in_favor_of}"
|
557
|
+
else
|
558
|
+
UI.warn "#{spec.name} has been deprecated"
|
559
|
+
end
|
560
|
+
end
|
561
|
+
end
|
562
|
+
|
563
|
+
# Prints a warning for any pods that included script phases
|
564
|
+
#
|
565
|
+
# @return [void]
|
566
|
+
#
|
567
|
+
def warn_for_installed_script_phases
|
568
|
+
pods_to_install = sandbox_state.added | sandbox_state.changed
|
569
|
+
pod_targets.group_by(&:pod_name).each do |name, pod_targets|
|
570
|
+
if pods_to_install.include?(name)
|
571
|
+
script_phase_count = pod_targets.inject(0) { |sum, target| sum + target.script_phases.count }
|
572
|
+
unless script_phase_count.zero?
|
573
|
+
UI.warn "#{name} has added #{script_phase_count} #{'script phase'.pluralize(script_phase_count)}. " \
|
574
|
+
'Please inspect before executing a build. See `https://guides.cocoapods.org/syntax/podspec.html#script_phases` for more information.'
|
575
|
+
end
|
576
|
+
end
|
577
|
+
end
|
578
|
+
end
|
579
|
+
|
580
|
+
# Writes the Podfile and the lock files.
|
581
|
+
#
|
582
|
+
# @todo Pass the checkout options to the Lockfile.
|
583
|
+
#
|
584
|
+
# @return [void]
|
585
|
+
#
|
586
|
+
def write_lockfiles
|
587
|
+
external_source_pods = analysis_result.podfile_dependency_cache.podfile_dependencies.select(&:external_source).map(&:root_name).uniq
|
588
|
+
checkout_options = sandbox.checkout_sources.select { |root_name, _| external_source_pods.include? root_name }
|
589
|
+
@lockfile = Lockfile.generate(podfile, analysis_result.specifications, checkout_options, analysis_result.specs_by_source)
|
590
|
+
|
591
|
+
UI.message "- Writing Lockfile in #{UI.path config.lockfile_path}" do
|
592
|
+
@lockfile.write_to_disk(config.lockfile_path)
|
593
|
+
end
|
594
|
+
|
595
|
+
UI.message "- Writing Manifest in #{UI.path sandbox.manifest_path}" do
|
596
|
+
sandbox.manifest_path.open('w') do |f|
|
597
|
+
f.write config.lockfile_path.read
|
598
|
+
end
|
599
|
+
end
|
600
|
+
end
|
601
|
+
|
602
|
+
# Integrates the user projects adding the dependencies on the CocoaPods
|
603
|
+
# libraries, setting them up to use the xcconfigs and performing other
|
604
|
+
# actions. This step is also responsible of creating the workspace if
|
605
|
+
# needed.
|
606
|
+
#
|
607
|
+
# @return [void]
|
608
|
+
#
|
609
|
+
# @todo [#397] The libraries should be cleaned and the re-added on every
|
610
|
+
# installation. Maybe a clean_user_project phase should be added.
|
611
|
+
# In any case it appears to be a good idea store target definition
|
612
|
+
# information in the lockfile.
|
613
|
+
#
|
614
|
+
def integrate_user_project
|
615
|
+
UI.section "Integrating client #{'project'.pluralize(aggregate_targets.map(&:user_project_path).uniq.count)}" do
|
616
|
+
installation_root = config.installation_root
|
617
|
+
integrator = UserProjectIntegrator.new(podfile, sandbox, installation_root, aggregate_targets)
|
618
|
+
integrator.integrate!
|
619
|
+
end
|
620
|
+
end
|
621
|
+
|
622
|
+
#-------------------------------------------------------------------------#
|
623
|
+
|
624
|
+
private
|
625
|
+
|
626
|
+
# @!group Hooks
|
627
|
+
|
628
|
+
# Runs the pre install hooks of the installed specs and of the Podfile.
|
629
|
+
#
|
630
|
+
# @return [void]
|
631
|
+
#
|
632
|
+
def run_podfile_pre_install_hooks
|
633
|
+
UI.message '- Running pre install hooks' do
|
634
|
+
executed = run_podfile_pre_install_hook
|
635
|
+
UI.message '- Podfile' if executed
|
636
|
+
end
|
637
|
+
end
|
638
|
+
|
639
|
+
# Runs the pre install hook of the Podfile
|
640
|
+
#
|
641
|
+
# @raise Raises an informative if the hooks raises.
|
642
|
+
#
|
643
|
+
# @return [Boolean] Whether the hook was run.
|
644
|
+
#
|
645
|
+
def run_podfile_pre_install_hook
|
646
|
+
podfile.pre_install!(self)
|
647
|
+
rescue => e
|
648
|
+
raise Informative, 'An error occurred while processing the pre-install ' \
|
649
|
+
'hook of the Podfile.' \
|
650
|
+
"\n\n#{e.message}\n\n#{e.backtrace * "\n"}"
|
651
|
+
end
|
652
|
+
|
653
|
+
# Runs the post install hooks of the installed specs and of the Podfile.
|
654
|
+
#
|
655
|
+
# @note Post install hooks run _before_ saving of project, so that they
|
656
|
+
# can alter it before it is written to the disk.
|
657
|
+
#
|
658
|
+
# @return [void]
|
659
|
+
#
|
660
|
+
def run_podfile_post_install_hooks
|
661
|
+
UI.message '- Running post install hooks' do
|
662
|
+
executed = run_podfile_post_install_hook
|
663
|
+
UI.message '- Podfile' if executed
|
664
|
+
end
|
665
|
+
end
|
666
|
+
|
667
|
+
# Runs the post install hook of the Podfile
|
668
|
+
#
|
669
|
+
# @raise Raises an informative if the hooks raises.
|
670
|
+
#
|
671
|
+
# @return [Boolean] Whether the hook was run.
|
672
|
+
#
|
673
|
+
def run_podfile_post_install_hook
|
674
|
+
podfile.post_install!(self)
|
675
|
+
rescue => e
|
676
|
+
raise Informative, 'An error occurred while processing the post-install ' \
|
677
|
+
'hook of the Podfile.' \
|
678
|
+
"\n\n#{e.message}\n\n#{e.backtrace * "\n"}"
|
679
|
+
end
|
680
|
+
|
681
|
+
#-------------------------------------------------------------------------#
|
682
|
+
|
683
|
+
public
|
684
|
+
|
685
|
+
# @return [Array<PodTarget>] The targets of the development pods generated by
|
686
|
+
# the installation process. This can be used as a convenience method for external scripts.
|
687
|
+
#
|
688
|
+
def development_pod_targets
|
689
|
+
pod_targets.select do |pod_target|
|
690
|
+
sandbox.local?(pod_target.pod_name)
|
691
|
+
end
|
692
|
+
end
|
693
|
+
|
694
|
+
#-------------------------------------------------------------------------#
|
695
|
+
|
696
|
+
private
|
697
|
+
|
698
|
+
# @!group Private helpers
|
699
|
+
|
700
|
+
# @return [Array<Specification>] All the root specifications of the
|
701
|
+
# installation.
|
702
|
+
#
|
703
|
+
def root_specs
|
704
|
+
analysis_result.specifications.map(&:root).uniq
|
705
|
+
end
|
706
|
+
|
707
|
+
# @return [SpecsState] The state of the sandbox returned by the analyzer.
|
708
|
+
#
|
709
|
+
def sandbox_state
|
710
|
+
analysis_result.sandbox_state
|
711
|
+
end
|
712
|
+
|
713
|
+
#-------------------------------------------------------------------------#
|
714
|
+
end
|
715
|
+
end
|