xcocoapods 1.5.3
Sign up to get free protection for your applications and to get access to all the features.
- 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,158 @@
|
|
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
|
+
option :warn_for_multiple_pod_sources, true
|
110
|
+
option :share_schemes_for_development_pods, false
|
111
|
+
|
112
|
+
module Mixin
|
113
|
+
module ClassMethods
|
114
|
+
# Delegates the creation of {#installation_options} to the `Podfile`
|
115
|
+
# returned by the given block.
|
116
|
+
#
|
117
|
+
# @param blk a block that returns the `Podfile` to create
|
118
|
+
# installation options from.
|
119
|
+
#
|
120
|
+
# @return [Void]
|
121
|
+
#
|
122
|
+
def delegate_installation_options(&blk)
|
123
|
+
define_method(:installation_options) do
|
124
|
+
@installation_options ||= InstallationOptions.from_podfile(instance_eval(&blk))
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
# Delegates the installation options attributes directly to
|
129
|
+
# {#installation_options}.
|
130
|
+
#
|
131
|
+
# @return [Void]
|
132
|
+
#
|
133
|
+
def delegate_installation_option_attributes!
|
134
|
+
define_method(:respond_to_missing?) do |name, *args|
|
135
|
+
installation_options.respond_to?(name, *args) || super
|
136
|
+
end
|
137
|
+
|
138
|
+
define_method(:method_missing) do |name, *args, &blk|
|
139
|
+
if installation_options.respond_to?(name)
|
140
|
+
installation_options.send(name, *args, &blk)
|
141
|
+
else
|
142
|
+
super
|
143
|
+
end
|
144
|
+
end
|
145
|
+
end
|
146
|
+
end
|
147
|
+
|
148
|
+
# @return [InstallationOptions] The installation options.
|
149
|
+
#
|
150
|
+
attr_accessor :installation_options
|
151
|
+
|
152
|
+
def self.included(mod)
|
153
|
+
mod.extend(ClassMethods)
|
154
|
+
end
|
155
|
+
end
|
156
|
+
end
|
157
|
+
end
|
158
|
+
end
|
@@ -0,0 +1,202 @@
|
|
1
|
+
require 'active_support/core_ext/string/strip'
|
2
|
+
|
3
|
+
module Pod
|
4
|
+
class Installer
|
5
|
+
# Controller class responsible of installing the activated specifications
|
6
|
+
# of a single Pod.
|
7
|
+
#
|
8
|
+
# @note This class needs to consider all the activated specs of a Pod.
|
9
|
+
#
|
10
|
+
class PodSourceInstaller
|
11
|
+
UNENCRYPTED_PROTOCOLS = %w(http git).freeze
|
12
|
+
|
13
|
+
# @return [Sandbox] The installation target.
|
14
|
+
#
|
15
|
+
attr_reader :sandbox
|
16
|
+
|
17
|
+
# @return [Hash{Symbol=>Array}] The specifications that need to be
|
18
|
+
# installed grouped by platform.
|
19
|
+
#
|
20
|
+
attr_reader :specs_by_platform
|
21
|
+
|
22
|
+
# @return [Boolean] Whether the installer is allowed to touch the cache.
|
23
|
+
#
|
24
|
+
attr_reader :can_cache
|
25
|
+
alias_method :can_cache?, :can_cache
|
26
|
+
|
27
|
+
# Initialize a new instance
|
28
|
+
#
|
29
|
+
# @param [Sandbox] sandbox @see #sandbox
|
30
|
+
# @param [Hash{Symbol=>Array}] specs_by_platform @see #specs_by_platform
|
31
|
+
# @param [Boolean] can_cache @see #can_cache
|
32
|
+
#
|
33
|
+
def initialize(sandbox, specs_by_platform, can_cache: true)
|
34
|
+
@sandbox = sandbox
|
35
|
+
@specs_by_platform = specs_by_platform
|
36
|
+
@can_cache = can_cache
|
37
|
+
end
|
38
|
+
|
39
|
+
# @return [String] A string suitable for debugging.
|
40
|
+
#
|
41
|
+
def inspect
|
42
|
+
"<#{self.class} sandbox=#{sandbox.root} pod=#{root_spec.name}"
|
43
|
+
end
|
44
|
+
|
45
|
+
# @return [String] The name of the pod this installer is installing.
|
46
|
+
#
|
47
|
+
def name
|
48
|
+
root_spec.name
|
49
|
+
end
|
50
|
+
|
51
|
+
#-----------------------------------------------------------------------#
|
52
|
+
|
53
|
+
public
|
54
|
+
|
55
|
+
# @!group Installation
|
56
|
+
|
57
|
+
# Creates the target in the Pods project and the relative support files.
|
58
|
+
#
|
59
|
+
# @return [void]
|
60
|
+
#
|
61
|
+
def install!
|
62
|
+
download_source unless predownloaded? || local?
|
63
|
+
PodSourcePreparer.new(root_spec, root).prepare! if local?
|
64
|
+
end
|
65
|
+
|
66
|
+
# Cleans the installations if appropriate.
|
67
|
+
#
|
68
|
+
# @return [void]
|
69
|
+
#
|
70
|
+
def clean!
|
71
|
+
clean_installation unless local?
|
72
|
+
end
|
73
|
+
|
74
|
+
# Locks the source files if appropriate.
|
75
|
+
#
|
76
|
+
# @return [void]
|
77
|
+
#
|
78
|
+
def lock_files!(file_accessors)
|
79
|
+
return if local?
|
80
|
+
FileUtils.chmod('u-w', source_files(file_accessors))
|
81
|
+
end
|
82
|
+
|
83
|
+
# Unlocks the source files if appropriate.
|
84
|
+
#
|
85
|
+
# @return [void]
|
86
|
+
#
|
87
|
+
def unlock_files!(file_accessors)
|
88
|
+
return if local?
|
89
|
+
FileUtils.chmod('u+w', source_files(file_accessors))
|
90
|
+
end
|
91
|
+
|
92
|
+
#-----------------------------------------------------------------------#
|
93
|
+
|
94
|
+
private
|
95
|
+
|
96
|
+
# @!group Installation Steps
|
97
|
+
|
98
|
+
# Downloads the source of the Pod.
|
99
|
+
#
|
100
|
+
# @return [void]
|
101
|
+
#
|
102
|
+
def download_source
|
103
|
+
verify_source_is_secure(root_spec)
|
104
|
+
download_result = Downloader.download(download_request, root, :can_cache => can_cache?)
|
105
|
+
|
106
|
+
if (specific_source = download_result.checkout_options) && specific_source != root_spec.source
|
107
|
+
sandbox.store_checkout_source(root_spec.name, specific_source)
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
# Verify the source of the spec is secure, which is used to show a warning to the user if that isn't the case
|
112
|
+
# This method doesn't verify all protocols, but currently only prohibits unencrypted 'http://' and 'git://''
|
113
|
+
# connections.
|
114
|
+
#
|
115
|
+
# @return [void]
|
116
|
+
#
|
117
|
+
def verify_source_is_secure(root_spec)
|
118
|
+
return if root_spec.source.nil? || (root_spec.source[:http].nil? && root_spec.source[:git].nil?)
|
119
|
+
source = if !root_spec.source[:http].nil?
|
120
|
+
URI(root_spec.source[:http].to_s)
|
121
|
+
elsif !root_spec.source[:git].nil?
|
122
|
+
git_source = root_spec.source[:git].to_s
|
123
|
+
return unless git_source =~ /^#{URI.regexp}$/
|
124
|
+
URI(git_source)
|
125
|
+
end
|
126
|
+
if UNENCRYPTED_PROTOCOLS.include?(source.scheme)
|
127
|
+
UI.warn "'#{root_spec.name}' uses the unencrypted '#{source.scheme}' protocol to transfer the Pod. " \
|
128
|
+
'Please be sure you\'re in a safe network with only trusted hosts. ' \
|
129
|
+
'Otherwise, please reach out to the library author to notify them of this security issue.'
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
def download_request
|
134
|
+
Downloader::Request.new(
|
135
|
+
:spec => root_spec,
|
136
|
+
:released => released?,
|
137
|
+
)
|
138
|
+
end
|
139
|
+
|
140
|
+
# Removes all the files not needed for the installation according to the
|
141
|
+
# specs by platform.
|
142
|
+
#
|
143
|
+
# @return [void]
|
144
|
+
#
|
145
|
+
def clean_installation
|
146
|
+
cleaner = Sandbox::PodDirCleaner.new(root, specs_by_platform)
|
147
|
+
cleaner.clean!
|
148
|
+
end
|
149
|
+
|
150
|
+
#-----------------------------------------------------------------------#
|
151
|
+
|
152
|
+
private
|
153
|
+
|
154
|
+
# @!group Convenience methods.
|
155
|
+
|
156
|
+
# @return [Array<Specifications>] the specification of the Pod used in
|
157
|
+
# this installation.
|
158
|
+
#
|
159
|
+
def specs
|
160
|
+
specs_by_platform.values.flatten
|
161
|
+
end
|
162
|
+
|
163
|
+
# @return [Specification] the root specification of the Pod.
|
164
|
+
#
|
165
|
+
def root_spec
|
166
|
+
specs.first.root
|
167
|
+
end
|
168
|
+
|
169
|
+
# @return [Pathname] the folder where the source of the Pod is located.
|
170
|
+
#
|
171
|
+
def root
|
172
|
+
sandbox.pod_dir(root_spec.name)
|
173
|
+
end
|
174
|
+
|
175
|
+
# @return [Boolean] whether the source has been pre downloaded in the
|
176
|
+
# resolution process to retrieve its podspec.
|
177
|
+
#
|
178
|
+
def predownloaded?
|
179
|
+
sandbox.predownloaded_pods.include?(root_spec.name)
|
180
|
+
end
|
181
|
+
|
182
|
+
# @return [Boolean] whether the pod uses the local option and thus
|
183
|
+
# CocoaPods should not interfere with the files of the user.
|
184
|
+
#
|
185
|
+
def local?
|
186
|
+
sandbox.local?(root_spec.name)
|
187
|
+
end
|
188
|
+
|
189
|
+
def released?
|
190
|
+
!local? && !predownloaded? && sandbox.specification(root_spec.name) != root_spec
|
191
|
+
end
|
192
|
+
|
193
|
+
# @return [Array<Pathname>] The paths of the source files
|
194
|
+
#
|
195
|
+
def source_files(file_accessors)
|
196
|
+
file_accessors.flat_map(&:source_files)
|
197
|
+
end
|
198
|
+
|
199
|
+
#-----------------------------------------------------------------------#
|
200
|
+
end
|
201
|
+
end
|
202
|
+
end
|
@@ -0,0 +1,77 @@
|
|
1
|
+
module Pod
|
2
|
+
class Installer
|
3
|
+
# Controller class responsible of executing the prepare command
|
4
|
+
# of a single Pod.
|
5
|
+
#
|
6
|
+
class PodSourcePreparer
|
7
|
+
# @return [Specification] the root specification of the Pod.
|
8
|
+
#
|
9
|
+
attr_reader :spec
|
10
|
+
|
11
|
+
# @return [Pathname] the folder where the source of the Pod is located.
|
12
|
+
#
|
13
|
+
attr_reader :path
|
14
|
+
|
15
|
+
# Initialize a new instance
|
16
|
+
#
|
17
|
+
# @param [Specification] spec the root specification of the Pod.
|
18
|
+
# @param [Pathname] path the folder where the source of the Pod is located.
|
19
|
+
#
|
20
|
+
def initialize(spec, path)
|
21
|
+
raise "Given spec isn't a root spec, but must be." unless spec.root?
|
22
|
+
@spec = spec
|
23
|
+
@path = path
|
24
|
+
end
|
25
|
+
|
26
|
+
#-----------------------------------------------------------------------#
|
27
|
+
|
28
|
+
public
|
29
|
+
|
30
|
+
# @!group Preparation
|
31
|
+
|
32
|
+
# Executes the prepare command if there is one.
|
33
|
+
#
|
34
|
+
# @return [void]
|
35
|
+
#
|
36
|
+
def prepare!
|
37
|
+
run_prepare_command
|
38
|
+
end
|
39
|
+
|
40
|
+
#-----------------------------------------------------------------------#
|
41
|
+
|
42
|
+
private
|
43
|
+
|
44
|
+
# @!group Preparation Steps
|
45
|
+
|
46
|
+
extend Executable
|
47
|
+
executable :bash
|
48
|
+
|
49
|
+
# Runs the prepare command bash script of the spec.
|
50
|
+
#
|
51
|
+
# @note Unsets the `CDPATH` env variable before running the
|
52
|
+
# shell script to avoid issues with relative paths
|
53
|
+
# (issue #1694).
|
54
|
+
#
|
55
|
+
# @return [void]
|
56
|
+
#
|
57
|
+
def run_prepare_command
|
58
|
+
return unless spec.prepare_command
|
59
|
+
UI.section(' > Running prepare command', '', 1) do
|
60
|
+
Dir.chdir(path) do
|
61
|
+
begin
|
62
|
+
ENV.delete('CDPATH')
|
63
|
+
ENV['COCOAPODS_VERSION'] = Pod::VERSION
|
64
|
+
prepare_command = spec.prepare_command.strip_heredoc.chomp
|
65
|
+
full_command = "\nset -e\n" + prepare_command
|
66
|
+
bash!('-c', full_command)
|
67
|
+
ensure
|
68
|
+
ENV.delete('COCOAPODS_VERSION')
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
#-----------------------------------------------------------------------#
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
@@ -0,0 +1,139 @@
|
|
1
|
+
module Pod
|
2
|
+
class Installer
|
3
|
+
# Validate the podfile before installing to catch errors and
|
4
|
+
# problems
|
5
|
+
#
|
6
|
+
class PodfileValidator
|
7
|
+
# @return [Podfile] The podfile being validated
|
8
|
+
#
|
9
|
+
attr_reader :podfile
|
10
|
+
|
11
|
+
# @return [Array<String>] any errors that have occured during the validation
|
12
|
+
#
|
13
|
+
attr_reader :errors
|
14
|
+
|
15
|
+
# @return [Array<String>] any warnings that have occured during the validation
|
16
|
+
#
|
17
|
+
attr_reader :warnings
|
18
|
+
|
19
|
+
# Initialize a new instance
|
20
|
+
#
|
21
|
+
# @param [Podfile] podfile
|
22
|
+
# The podfile to validate
|
23
|
+
#
|
24
|
+
# @param [Analyzer::PodfileDependencyCache] podfile_dependency_cache
|
25
|
+
# An (optional) cache of all the dependencies in the podfile
|
26
|
+
#
|
27
|
+
def initialize(podfile, podfile_dependency_cache = Analyzer::PodfileDependencyCache.from_podfile(podfile))
|
28
|
+
@podfile = podfile
|
29
|
+
@podfile_dependency_cache = podfile_dependency_cache
|
30
|
+
@errors = []
|
31
|
+
@warnings = []
|
32
|
+
@validated = false
|
33
|
+
end
|
34
|
+
|
35
|
+
# Validate the podfile
|
36
|
+
# Errors are added to the errors array
|
37
|
+
#
|
38
|
+
def validate
|
39
|
+
validate_pod_directives
|
40
|
+
validate_no_abstract_only_pods!
|
41
|
+
validate_dependencies_are_present!
|
42
|
+
validate_no_duplicate_targets!
|
43
|
+
|
44
|
+
@validated = true
|
45
|
+
end
|
46
|
+
|
47
|
+
# Wether the podfile is valid is not
|
48
|
+
# NOTE: Will execute `validate` if the podfile
|
49
|
+
# has not yet been validated
|
50
|
+
#
|
51
|
+
def valid?
|
52
|
+
validate unless @validated
|
53
|
+
|
54
|
+
@validated && errors.empty?
|
55
|
+
end
|
56
|
+
|
57
|
+
# A message describing any errors in the
|
58
|
+
# validation
|
59
|
+
#
|
60
|
+
def message
|
61
|
+
errors.join("\n")
|
62
|
+
end
|
63
|
+
|
64
|
+
private
|
65
|
+
|
66
|
+
def add_error(error)
|
67
|
+
errors << error
|
68
|
+
end
|
69
|
+
|
70
|
+
def add_warning(warning)
|
71
|
+
warnings << warning
|
72
|
+
end
|
73
|
+
|
74
|
+
def validate_pod_directives
|
75
|
+
@podfile_dependency_cache.podfile_dependencies.each do |dependency|
|
76
|
+
validate_conflicting_external_sources!(dependency)
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
def validate_conflicting_external_sources!(dependency)
|
81
|
+
external_source = dependency.external_source
|
82
|
+
return false if external_source.nil?
|
83
|
+
|
84
|
+
available_downloaders = Downloader.downloader_class_by_key.keys
|
85
|
+
specified_downloaders = external_source.select { |key| available_downloaders.include?(key) }
|
86
|
+
if specified_downloaders.size > 1
|
87
|
+
add_error "The dependency `#{dependency.name}` specifies more than one download strategy(#{specified_downloaders.keys.join(',')})." \
|
88
|
+
'Only one is allowed'
|
89
|
+
end
|
90
|
+
|
91
|
+
pod_spec_or_path = external_source[:podspec].present? || external_source[:path].present?
|
92
|
+
if pod_spec_or_path && specified_downloaders.size > 0
|
93
|
+
add_error "The dependency `#{dependency.name}` specifies `podspec` or `path` in combination with other" \
|
94
|
+
' download strategies. This is not allowed'
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
# Warns the user if the podfile is empty.
|
99
|
+
#
|
100
|
+
# @note The workspace is created in any case and all the user projects
|
101
|
+
# are added to it, however the projects are not integrated as
|
102
|
+
# there is no way to discern between target definitions which are
|
103
|
+
# empty and target definitions which just serve the purpose to
|
104
|
+
# wrap other ones. This is not an issue because empty target
|
105
|
+
# definitions generate empty libraries.
|
106
|
+
#
|
107
|
+
# @return [void]
|
108
|
+
#
|
109
|
+
def validate_dependencies_are_present!
|
110
|
+
if @podfile_dependency_cache.target_definition_list.all?(&:empty?)
|
111
|
+
add_warning 'The Podfile does not contain any dependencies.'
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
# Verifies that no dependencies in the Podfile will end up not being built
|
116
|
+
# at all. In other words, all dependencies _must_ belong to a non-abstract
|
117
|
+
# target, or be inherited by a target where `inheritance == complete`.
|
118
|
+
#
|
119
|
+
def validate_no_abstract_only_pods!
|
120
|
+
all_dependencies = @podfile_dependency_cache.podfile_dependencies
|
121
|
+
concrete_dependencies = @podfile_dependency_cache.target_definition_list.reject(&:abstract?).flat_map { |td| @podfile_dependency_cache.target_definition_dependencies(td) }
|
122
|
+
abstract_only_dependencies = all_dependencies - concrete_dependencies
|
123
|
+
abstract_only_dependencies.each do |dep|
|
124
|
+
add_error "The dependency `#{dep}` is not used in any concrete target."
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
def validate_no_duplicate_targets!
|
129
|
+
@podfile_dependency_cache.target_definition_list.group_by { |td| [td.name, td.user_project_path] }.
|
130
|
+
each do |(name, project), definitions|
|
131
|
+
next unless definitions.size > 1
|
132
|
+
error = "The target `#{name}` is declared twice"
|
133
|
+
error << " for the project `#{project}`" if project
|
134
|
+
add_error(error << '.')
|
135
|
+
end
|
136
|
+
end
|
137
|
+
end
|
138
|
+
end
|
139
|
+
end
|