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.
Files changed (38) hide show
  1. checksums.yaml +4 -4
  2. data/lib/pod/command.rb +2 -0
  3. data/lib/pod/command/dyinstall.rb +51 -0
  4. data/lib/pod/command/dyupdate.rb +106 -0
  5. data/lib/pod/command/fmwk.rb +4 -0
  6. data/lib/pod/command/lib/dylint.rb +1 -0
  7. data/lib/pod/gem_version.rb +1 -1
  8. data/lib/pod/installer.rb +715 -0
  9. data/lib/pod/installer/analyzer.rb +934 -0
  10. data/lib/pod/installer/analyzer/analysis_result.rb +57 -0
  11. data/lib/pod/installer/analyzer/locking_dependency_analyzer.rb +95 -0
  12. data/lib/pod/installer/analyzer/pod_variant.rb +68 -0
  13. data/lib/pod/installer/analyzer/pod_variant_set.rb +157 -0
  14. data/lib/pod/installer/analyzer/podfile_dependency_cache.rb +54 -0
  15. data/lib/pod/installer/analyzer/sandbox_analyzer.rb +251 -0
  16. data/lib/pod/installer/analyzer/specs_state.rb +84 -0
  17. data/lib/pod/installer/analyzer/target_inspection_result.rb +45 -0
  18. data/lib/pod/installer/analyzer/target_inspector.rb +254 -0
  19. data/lib/pod/installer/installation_options.rb +158 -0
  20. data/lib/pod/installer/pod_source_installer.rb +214 -0
  21. data/lib/pod/installer/pod_source_preparer.rb +77 -0
  22. data/lib/pod/installer/podfile_validator.rb +139 -0
  23. data/lib/pod/installer/post_install_hooks_context.rb +107 -0
  24. data/lib/pod/installer/pre_install_hooks_context.rb +42 -0
  25. data/lib/pod/installer/source_provider_hooks_context.rb +32 -0
  26. data/lib/pod/installer/user_project_integrator.rb +253 -0
  27. data/lib/pod/installer/user_project_integrator/target_integrator.rb +462 -0
  28. data/lib/pod/installer/user_project_integrator/target_integrator/xcconfig_integrator.rb +146 -0
  29. data/lib/pod/installer/xcode.rb +8 -0
  30. data/lib/pod/installer/xcode/pods_project_generator.rb +353 -0
  31. data/lib/pod/installer/xcode/pods_project_generator/aggregate_target_installer.rb +172 -0
  32. data/lib/pod/installer/xcode/pods_project_generator/file_references_installer.rb +367 -0
  33. data/lib/pod/installer/xcode/pods_project_generator/pod_target_installer.rb +718 -0
  34. data/lib/pod/installer/xcode/pods_project_generator/pod_target_integrator.rb +111 -0
  35. data/lib/pod/installer/xcode/pods_project_generator/target_installer.rb +265 -0
  36. data/lib/pod/installer/xcode/target_validator.rb +141 -0
  37. data/lib/pod/resolver.rb +632 -0
  38. metadata +34 -2
@@ -0,0 +1,214 @@
1
+ require 'active_support/core_ext/string/strip'
2
+
3
+ module Pod
4
+ class DyInstaller
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
+ # @return [Sandbox] The installation target.
12
+ #
13
+ attr_reader :sandbox
14
+
15
+ # @return [Hash{Symbol=>Array}] The specifications that need to be
16
+ # installed grouped by platform.
17
+ #
18
+ attr_reader :specs_by_platform
19
+
20
+ # @return [Boolean] Whether the installer is allowed to touch the cache.
21
+ #
22
+ attr_reader :can_cache
23
+ alias_method :can_cache?, :can_cache
24
+
25
+ # Initialize a new instance
26
+ #
27
+ # @param [Sandbox] sandbox @see sandbox
28
+ # @param [Hash{Symbol=>Array}] specs_by_platform @see specs_by_platform
29
+ # @param [Boolean] can_cache @see can_cache
30
+ #
31
+ def initialize(sandbox, specs_by_platform, can_cache: true)
32
+ @sandbox = sandbox
33
+ @specs_by_platform = specs_by_platform
34
+ @can_cache = can_cache
35
+ end
36
+
37
+ # @return [String] A string suitable for debugging.
38
+ #
39
+ def inspect
40
+ "<#{self.class} sandbox=#{sandbox.root} pod=#{root_spec.name}"
41
+ end
42
+
43
+ # @return [String] The name of the pod this installer is installing.
44
+ #
45
+ def name
46
+ root_spec.name
47
+ end
48
+
49
+ #-----------------------------------------------------------------------#
50
+
51
+ public
52
+
53
+ # @!group Installation
54
+
55
+ # Creates the target in the Pods project and the relative support files.
56
+ #
57
+ # @return [void]
58
+ #
59
+ def install!
60
+ download_source unless predownloaded? || local?
61
+ PodSourcePreparer.new(root_spec, root).prepare! if local?
62
+ end
63
+
64
+ # Cleans the installations if appropriate.
65
+ #
66
+ # @todo As the pre install hooks need to run before cleaning this
67
+ # method should be refactored.
68
+ #
69
+ # @return [void]
70
+ #
71
+ def clean!
72
+ clean_installation unless local?
73
+ end
74
+
75
+ # Locks the source files if appropriate.
76
+ #
77
+ # @todo As the pre install hooks need to run before cleaning this
78
+ # method should be refactored.
79
+ #
80
+ # @return [void]
81
+ #
82
+ def lock_files!(file_accessors)
83
+ return if local?
84
+ each_source_file(file_accessors) do |source_file|
85
+ FileUtils.chmod('u-w', source_file)
86
+ end
87
+ end
88
+
89
+ # Unlocks the source files if appropriate.
90
+ #
91
+ # @todo As the pre install hooks need to run before cleaning this
92
+ # method should be refactored.
93
+ #
94
+ # @return [void]
95
+ #
96
+ def unlock_files!(file_accessors)
97
+ return if local?
98
+ each_source_file(file_accessors) do |source_file|
99
+ FileUtils.chmod('u+w', source_file)
100
+ end
101
+ end
102
+
103
+ # @return [Hash] @see Downloader#checkout_options
104
+ #
105
+ attr_reader :specific_source
106
+
107
+ #-----------------------------------------------------------------------#
108
+
109
+ private
110
+
111
+ # @!group Installation Steps
112
+
113
+ # Downloads the source of the Pod. It also stores the specific options
114
+ # needed to recreate the same exact installation if needed in
115
+ # `#specific_source`.
116
+ #
117
+ # @return [void]
118
+ #
119
+ def download_source
120
+ verify_source_is_secure(root_spec)
121
+ download_result = Downloader.download(download_request, root, :can_cache => can_cache?)
122
+
123
+ if (@specific_source = download_result.checkout_options) && specific_source != root_spec.source
124
+ sandbox.store_checkout_source(root_spec.name, specific_source)
125
+ end
126
+ end
127
+
128
+ # Verify the source of the spec is secure, which is used to
129
+ # show a warning to the user if that isn't the case
130
+ # This method doesn't verify all protocols, but currently
131
+ # only prohibits unencrypted http:// connections
132
+ #
133
+ def verify_source_is_secure(root_spec)
134
+ return if root_spec.source.nil? || root_spec.source[:http].nil?
135
+ http_source = URI(root_spec.source[:http])
136
+ return if http_source.scheme == 'https' || http_source.scheme == 'file'
137
+ UI.warn "'#{root_spec.name}' uses the unencrypted http protocol to transfer the Pod. " \
138
+ 'Please be sure you\'re in a safe network with only trusted hosts in there. ' \
139
+ 'Please reach out to the library author to notify them of this security issue.'
140
+ end
141
+
142
+ def download_request
143
+ Downloader::Request.new(
144
+ :spec => root_spec,
145
+ :released => released?,
146
+ )
147
+ end
148
+
149
+ # Removes all the files not needed for the installation according to the
150
+ # specs by platform.
151
+ #
152
+ # @return [void]
153
+ #
154
+ def clean_installation
155
+ cleaner = Sandbox::PodDirCleaner.new(root, specs_by_platform)
156
+ cleaner.clean!
157
+ end
158
+
159
+ #-----------------------------------------------------------------------#
160
+
161
+ private
162
+
163
+ # @!group Convenience methods.
164
+
165
+ # @return [Array<Specifications>] the specification of the Pod used in
166
+ # this installation.
167
+ #
168
+ def specs
169
+ specs_by_platform.values.flatten
170
+ end
171
+
172
+ # @return [Specification] the root specification of the Pod.
173
+ #
174
+ def root_spec
175
+ specs.first.root
176
+ end
177
+
178
+ # @return [Pathname] the folder where the source of the Pod is located.
179
+ #
180
+ def root
181
+ sandbox.pod_dir(root_spec.name)
182
+ end
183
+
184
+ # @return [Boolean] whether the source has been pre downloaded in the
185
+ # resolution process to retrieve its podspec.
186
+ #
187
+ def predownloaded?
188
+ sandbox.predownloaded_pods.include?(root_spec.name)
189
+ end
190
+
191
+ # @return [Boolean] whether the pod uses the local option and thus
192
+ # CocoaPods should not interfere with the files of the user.
193
+ #
194
+ def local?
195
+ sandbox.local?(root_spec.name)
196
+ end
197
+
198
+ def released?
199
+ !local? && !predownloaded? && sandbox.specification(root_spec.name) != root_spec
200
+ end
201
+
202
+ def each_source_file(file_accessors, &blk)
203
+ file_accessors.each do |file_accessor|
204
+ file_accessor.source_files.each do |source_file|
205
+ next unless source_file.exist?
206
+ blk[source_file]
207
+ end
208
+ end
209
+ end
210
+
211
+ #-----------------------------------------------------------------------#
212
+ end
213
+ end
214
+ end
@@ -0,0 +1,77 @@
1
+ module Pod
2
+ class DyInstaller
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 DyInstaller
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
@@ -0,0 +1,107 @@
1
+ module Pod
2
+ class DyInstaller
3
+ # Context object designed to be used with the HooksManager which describes
4
+ # the context of the installer.
5
+ #
6
+ class PostInstallHooksContext
7
+ # @return [String] The path to the sandbox root (`Pods` directory).
8
+ #
9
+ attr_accessor :sandbox_root
10
+
11
+ # @return [Project] The Pods Xcode project.
12
+ #
13
+ attr_accessor :pods_project
14
+
15
+ # @return [Sandbox] The Sandbox for the project.
16
+ #
17
+ attr_accessor :sandbox
18
+
19
+ # @return [Array<UmbrellaTargetDescription>] The list of
20
+ # the CocoaPods umbrella targets generated by the installer.
21
+ #
22
+ attr_accessor :umbrella_targets
23
+
24
+ # @return [PostInstallHooksContext] Convenience class generator method
25
+ #
26
+ # @param [Sandbox] sandbox
27
+ # The sandbox
28
+ #
29
+ # @param [Array<AggregateTarget>] aggregate_targets
30
+ # The aggregate targets, which will been presented by an adequate
31
+ # {UmbrellaTargetDescription} in the generated context.
32
+ #
33
+ # @return [HooksContext] Convenience class method to generate the
34
+ # static context.
35
+ #
36
+ def self.generate(sandbox, aggregate_targets)
37
+ umbrella_targets_descriptions = []
38
+ aggregate_targets.each do |umbrella|
39
+ desc = UmbrellaTargetDescription.new
40
+ desc.user_project = umbrella.user_project
41
+ desc.user_targets = umbrella.user_targets
42
+ desc.specs = umbrella.specs
43
+ desc.platform_name = umbrella.platform.name
44
+ desc.platform_deployment_target = umbrella.platform.deployment_target.to_s
45
+ desc.cocoapods_target_label = umbrella.label
46
+ umbrella_targets_descriptions << desc
47
+ end
48
+
49
+ result = new
50
+ result.sandbox_root = sandbox.root.to_s
51
+ result.pods_project = sandbox.project
52
+ result.sandbox = sandbox
53
+ result.umbrella_targets = umbrella_targets_descriptions
54
+ result
55
+ end
56
+
57
+ # Pure data class which describes and umbrella target.
58
+ #
59
+ class UmbrellaTargetDescription
60
+ # @return [Xcodeproj::Project] The user project into which this target
61
+ # is integrated.
62
+ #
63
+ attr_accessor :user_project
64
+
65
+ # @return [String] The path of the user project
66
+ # integrated by this target.
67
+ #
68
+ def user_project_path
69
+ user_project.path if user_project
70
+ end
71
+
72
+ # @return [Array<PBXNativeTarget>]
73
+ # The list of user targets integrated by this umbrella target.
74
+ #
75
+ attr_accessor :user_targets
76
+
77
+ # @return [Array<String>] The list of the UUIDs of the
78
+ # user targets integrated by this umbrella
79
+ # target. They can be used to find the
80
+ # targets opening the project They can be used
81
+ # to find the targets opening the project with
82
+ # Xcodeproj.
83
+ #
84
+ def user_target_uuids
85
+ user_targets.map(&:uuid)
86
+ end
87
+
88
+ # @return [Array<Specification>] The list of the
89
+ # specifications of the target.
90
+ #
91
+ attr_accessor :specs
92
+
93
+ # @return [Symbol] The platform (either `:ios`, `:watchos`, `:tvos`, or `:osx`).
94
+ #
95
+ attr_accessor :platform_name
96
+
97
+ # @return [String] The deployment target.
98
+ #
99
+ attr_accessor :platform_deployment_target
100
+
101
+ # @return [String] The label for the target.
102
+ #
103
+ attr_accessor :cocoapods_target_label
104
+ end
105
+ end
106
+ end
107
+ end