cocoapods-dykit 0.5.2 → 0.5.3

Sign up to get free protection for your applications and to get access to all the features.
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