cocoapods-binary-matchup 0.0.2 → 0.0.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.
@@ -0,0 +1,241 @@
1
+ require_relative 'rome/build_framework'
2
+ require_relative 'helper/passer'
3
+ require_relative 'helper/target_checker'
4
+
5
+
6
+ # patch prebuild ability
7
+ module Pod
8
+ class Installer
9
+
10
+
11
+ private
12
+
13
+ def local_manifest
14
+ if not @local_manifest_inited
15
+ @local_manifest_inited = true
16
+ raise "This method should be call before generate project" unless self.analysis_result == nil
17
+ @local_manifest = self.sandbox.manifest
18
+ end
19
+ @local_manifest
20
+ end
21
+
22
+ # @return [Analyzer::SpecsState]
23
+ def prebuild_pods_changes
24
+ return nil if local_manifest.nil?
25
+ UI.puts "--------------------------------"
26
+ UI.puts "prebuild_pods_changes"
27
+ UI.puts "--------------------------------"
28
+ if @prebuild_pods_changes.nil?
29
+ changes = local_manifest.detect_changes_with_podfile(podfile)
30
+ @prebuild_pods_changes = Analyzer::SpecsState.new(changes)
31
+ # save the chagnes info for later stage
32
+ Pod::Prebuild::Passer.prebuild_pods_changes = @prebuild_pods_changes
33
+ end
34
+ @prebuild_pods_changes
35
+ end
36
+
37
+
38
+ public
39
+
40
+ # check if need to prebuild
41
+ def have_exact_prebuild_cache?
42
+ # check if need build frameworks
43
+ return false if local_manifest == nil
44
+
45
+ changes = prebuild_pods_changes
46
+ added = changes.added
47
+ changed = changes.changed
48
+ unchanged = changes.unchanged
49
+ deleted = changes.deleted
50
+
51
+ unchange_framework_names = (added + unchanged)
52
+
53
+ exsited_framework_names = sandbox.exsited_framework_names
54
+ missing = unchanged.select do |pod_name|
55
+ not exsited_framework_names.include?(pod_name)
56
+ end
57
+
58
+ needed = (added + changed + deleted + missing)
59
+ return needed.empty?
60
+ end
61
+
62
+
63
+ # The install method when have completed cache
64
+ def install_when_cache_hit!
65
+ # just print log
66
+ self.sandbox.exsited_framework_names.each do |name|
67
+ UI.puts "Using #{name}"
68
+ end
69
+ end
70
+
71
+
72
+ # Build the needed framework files
73
+ def prebuild_frameworks!
74
+
75
+ # check
76
+ # give a early warning, instead of after compiling all the pods
77
+ Prebuild.check_one_pod_should_have_only_one_target(self.pod_targets)
78
+
79
+ # build options
80
+ sandbox_path = sandbox.root
81
+ existed_framework_folder = sandbox.generate_framework_path
82
+ bitcode_enabled = Pod::Podfile::DSL.bitcode_enabled
83
+ targets = []
84
+
85
+ if local_manifest != nil
86
+
87
+ changes = prebuild_pods_changes
88
+ added = changes.added
89
+ changed = changes.changed
90
+ unchanged = changes.unchanged
91
+ deleted = changes.deleted
92
+
93
+ existed_framework_folder.mkdir unless existed_framework_folder.exist?
94
+ exsited_framework_names = sandbox.exsited_framework_names
95
+
96
+ # additions
97
+ missing = unchanged.select do |pod_name|
98
+ not exsited_framework_names.include?(pod_name)
99
+ end
100
+
101
+
102
+ root_names_to_update = (added + changed + missing)
103
+
104
+ # transform names to targets
105
+ name_to_target_hash = self.pod_targets.reduce({}) do |sum, target|
106
+ sum[target.name] = target
107
+ sum
108
+ end
109
+ targets = root_names_to_update.map do |root_name|
110
+ t = name_to_target_hash[root_name]
111
+ raise "There's no target named (#{root_name}) in Pod.xcodeproj.\n #{name_to_target_hash.keys}" if t.nil?
112
+ t
113
+ end || []
114
+
115
+ # add the dendencies
116
+ dependency_targets = targets.map {|t| t.recursive_dependent_targets }.flatten.uniq || []
117
+ targets = (targets + dependency_targets).uniq
118
+ else
119
+ targets = self.pod_targets
120
+ end
121
+
122
+ targets = targets.reject {|pod_target| sandbox.local?(pod_target.pod_name) }
123
+
124
+
125
+ # build!
126
+ Pod::UI.puts "Prebuild frameworks (total #{targets.count})"
127
+ Pod::Prebuild.remove_build_dir(sandbox_path)
128
+ targets.each do |target|
129
+ if !target.should_build?
130
+ UI.puts "Prebuilding #{target.label}"
131
+ next
132
+ end
133
+
134
+ standard_sandbox_path = sandbox.standard_sanbox_path
135
+
136
+ if Pod::PrebuildCache::Cache.restore_from_pod_cache(target.pod_name, sandbox)
137
+ Pod::UI.puts "📦 Using exact cache from ~/.PodCache"
138
+ next
139
+ end
140
+
141
+ output_path = sandbox.framework_folder_path_for_pod_name(target.name)
142
+ output_path.mkpath unless output_path.exist?
143
+ Pod::Prebuild.build(sandbox_path, target, output_path, bitcode_enabled, Podfile::DSL.custom_build_options, Podfile::DSL.custom_build_options_simulator)
144
+
145
+ # save the resource paths for later installing
146
+ if target.static_framework? and !target.resource_paths.empty?
147
+ framework_path = output_path + target.framework_name
148
+ standard_sandbox_path = sandbox.standard_sanbox_path
149
+
150
+ resources = begin
151
+ if Pod::VERSION.start_with? "1.5"
152
+ target.resource_paths
153
+ else
154
+ # resource_paths is Hash{String=>Array<String>} on 1.6 and above
155
+ # (use AFNetworking to generate a demo data)
156
+ # https://github.com/leavez/cocoapods-binary/issues/50
157
+ target.resource_paths.values.flatten
158
+ end
159
+ end
160
+ raise "Wrong type: #{resources}" unless resources.kind_of? Array
161
+
162
+ path_objects = resources.map do |path|
163
+ object = Prebuild::Passer::ResourcePath.new
164
+ object.real_file_path = framework_path + File.basename(path)
165
+ object.target_file_path = path.gsub('${PODS_ROOT}', standard_sandbox_path.to_s) if path.start_with? '${PODS_ROOT}'
166
+ object.target_file_path = path.gsub("${PODS_CONFIGURATION_BUILD_DIR}", standard_sandbox_path.to_s) if path.start_with? "${PODS_CONFIGURATION_BUILD_DIR}"
167
+ object
168
+ end
169
+ Prebuild::Passer.resources_to_copy_for_static_framework[target.name] = path_objects
170
+ end
171
+ end
172
+ Pod::Prebuild.remove_build_dir(sandbox_path)
173
+
174
+
175
+ # copy vendored libraries and frameworks
176
+ targets.each do |target|
177
+ root_path = self.sandbox.pod_dir(target.name)
178
+ target_folder = sandbox.framework_folder_path_for_pod_name(target.name)
179
+
180
+ # If target shouldn't build, we copy all the original files
181
+ # This is for target with only .a and .h files
182
+ if not target.should_build?
183
+ Prebuild::Passer.target_names_to_skip_integration_framework << target.pod_name
184
+ FileUtils.cp_r(root_path, target_folder, :remove_destination => true)
185
+ next
186
+ end
187
+
188
+ target.spec_consumers.each do |consumer|
189
+ file_accessor = Sandbox::FileAccessor.new(root_path, consumer)
190
+ lib_paths = file_accessor.vendored_frameworks || []
191
+ lib_paths += file_accessor.vendored_libraries
192
+ # @TODO dSYM files
193
+ lib_paths.each do |lib_path|
194
+ relative = lib_path.relative_path_from(root_path)
195
+ destination = target_folder + relative
196
+ destination.dirname.mkpath unless destination.dirname.exist?
197
+ FileUtils.cp_r(lib_path, destination, :remove_destination => true)
198
+ end
199
+ end
200
+ end
201
+
202
+ # Remove useless files
203
+ # remove useless pods
204
+ all_needed_names = self.pod_targets.map(&:name).uniq
205
+ useless_names = sandbox.exsited_framework_names.reject do |name|
206
+ all_needed_names.include? name
207
+ end
208
+ useless_names.each do |name|
209
+ path = sandbox.framework_folder_path_for_pod_name(name)
210
+ path.rmtree if path.exist?
211
+ end
212
+
213
+ if not Podfile::DSL.dont_remove_source_code
214
+ # only keep manifest.lock and framework folder in _Prebuild
215
+ to_remain_files = ["Manifest.lock", File.basename(existed_framework_folder)]
216
+ to_delete_files = sandbox_path.children.select do |file|
217
+ filename = File.basename(file)
218
+ not to_remain_files.include?(filename)
219
+ end
220
+ to_delete_files.each do |path|
221
+ path.rmtree if path.exist?
222
+ end
223
+ else
224
+ # just remove the tmp files
225
+ path = sandbox.root + 'Manifest.lock.tmp'
226
+ path.rmtree if path.exist?
227
+ end
228
+
229
+ end
230
+
231
+ # patch the post install hook
232
+ old_method2 = instance_method(:run_plugins_post_install_hooks)
233
+ define_method(:run_plugins_post_install_hooks) do
234
+ old_method2.bind(self).()
235
+ if Pod::is_prebuild_stage
236
+ self.prebuild_frameworks!
237
+ end
238
+ end
239
+
240
+ end
241
+ end
@@ -0,0 +1,3 @@
1
+ module CocoapodsBinary
2
+ VERSION = "0.0.3"
3
+ end
@@ -0,0 +1,104 @@
1
+ require_relative '../tool/tool'
2
+ require_relative 'prebuild_sandbox'
3
+
4
+ module Pod
5
+
6
+ # a flag that indicate stages
7
+ class_attr_accessor :is_prebuild_stage
8
+
9
+
10
+ # a switch for the `pod` DSL to make it only valid for ':binary => true'
11
+ class Podfile
12
+ module DSL
13
+
14
+ @@enable_prebuild_patch = false
15
+
16
+ # when enable, `pod` function will skip all pods without 'prebuild => true'
17
+ def self.enable_prebuild_patch(value)
18
+ @@enable_prebuild_patch = value
19
+ end
20
+
21
+ # --- patch ---
22
+ old_method = instance_method(:pod)
23
+
24
+ define_method(:pod) do |name, *args|
25
+ if !@@enable_prebuild_patch
26
+ old_method.bind(self).(name, *args)
27
+ return
28
+ end
29
+
30
+ # patched content
31
+ should_prebuild = Pod::Podfile::DSL.prebuild_all
32
+ local = false
33
+
34
+ options = args.last
35
+ if options.is_a?(Hash) and options[Pod::Prebuild.keyword] != nil
36
+ should_prebuild = options[Pod::Prebuild.keyword]
37
+ local = (options[:path] != nil)
38
+ end
39
+
40
+ if should_prebuild and (not local)
41
+ if current_target_definition.platform == :watchos
42
+ # watchos isn't supported currently
43
+ Pod::UI.warn "Binary doesn't support watchos currently: #{name}. You can manually set `binary => false` for this pod to suppress this warning."
44
+ return
45
+ end
46
+ old_method.bind(self).(name, *args)
47
+ end
48
+ end
49
+ end
50
+ end
51
+
52
+
53
+ # a force disable option for integral
54
+ class Installer
55
+ def self.force_disable_integration(value)
56
+ @@force_disable_integration = value
57
+ end
58
+
59
+ old_method = instance_method(:integrate_user_project)
60
+ define_method(:integrate_user_project) do
61
+ if @@force_disable_integration
62
+ return
63
+ end
64
+ old_method.bind(self).()
65
+ end
66
+ end
67
+
68
+ # a option to disable install complete message
69
+ class Installer
70
+ def self.disable_install_complete_message(value)
71
+ @@disable_install_complete_message = value
72
+ end
73
+
74
+ old_method = instance_method(:print_post_install_message)
75
+ define_method(:print_post_install_message) do
76
+ if @@disable_install_complete_message
77
+ return
78
+ end
79
+ old_method.bind(self).()
80
+ end
81
+ end
82
+
83
+ # option to disable write lockfiles
84
+ class Config
85
+
86
+ @@force_disable_write_lockfile = false
87
+ def self.force_disable_write_lockfile(value)
88
+ @@force_disable_write_lockfile = value
89
+ end
90
+
91
+ old_method = instance_method(:lockfile_path)
92
+ define_method(:lockfile_path) do
93
+ if @@force_disable_write_lockfile
94
+ # As config is a singleton, sandbox_root refer to the standard sandbox.
95
+ manifest_lock_path = PrebuildSandbox.from_standard_sanbox_path(sandbox_root).root + 'Manifest.lock.tmp'
96
+ UI.puts "manifest_lock_path: #{manifest_lock_path}"
97
+ return manifest_lock_path
98
+ else
99
+ return old_method.bind(self).()
100
+ end
101
+ end
102
+ end
103
+
104
+ end
@@ -0,0 +1,41 @@
1
+ require_relative '../tool/tool'
2
+
3
+ module Pod
4
+ class Prebuild
5
+
6
+ # Pass the data between the 2 steps
7
+ #
8
+ # At step 2, the normal pod install, it needs some info of the
9
+ # prebuilt step. So we store it here.
10
+ #
11
+ class Passer
12
+
13
+ # indicate the add/remove/update of prebuit pods
14
+ # @return [Analyzer::SpecsState]
15
+ #
16
+ class_attr_accessor :prebuild_pods_changes
17
+
18
+
19
+ # represent the path of resurces to copy
20
+ class ResourcePath
21
+ attr_accessor :real_file_path
22
+ attr_accessor :target_file_path
23
+ end
24
+ # Save the resoures for static framework, and used when installing the prebuild framework
25
+ # static framework needs copy the resurces mannully
26
+ #
27
+ # @return [Hash<String, [Passer::ResourcePath]>]
28
+ class_attr_accessor :resources_to_copy_for_static_framework
29
+ self.resources_to_copy_for_static_framework = {}
30
+
31
+ # Some pod won't be build in prebuild stage even if it have `binary=>true`.
32
+ # The targets of this pods have `oshould_build? == true`.
33
+ # We should skip integration (patch spec) for this pods
34
+ #
35
+ # @return [Array<String>]
36
+ class_attr_accessor :target_names_to_skip_integration_framework
37
+ self.target_names_to_skip_integration_framework = []
38
+
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,120 @@
1
+ module Pod
2
+
3
+ class Prebuild
4
+ def self.keyword
5
+ :binary
6
+ end
7
+ end
8
+
9
+ class Podfile
10
+ class TargetDefinition
11
+
12
+ ## --- option for setting using prebuild framework ---
13
+ def parse_prebuild_framework(name, requirements)
14
+ should_prebuild = Pod::Podfile::DSL.prebuild_all
15
+
16
+ options = requirements.last
17
+ if options.is_a?(Hash) && options[Pod::Prebuild.keyword] != nil
18
+ should_prebuild = options.delete(Pod::Prebuild.keyword)
19
+ requirements.pop if options.empty?
20
+ end
21
+
22
+ pod_name = Specification.root_name(name)
23
+ set_prebuild_for_pod(pod_name, should_prebuild)
24
+ end
25
+
26
+ def set_prebuild_for_pod(pod_name, should_prebuild)
27
+
28
+ if should_prebuild == true
29
+ # watchos isn't supported currently
30
+ return if self.platform == :watchos
31
+
32
+ @prebuild_framework_names ||= []
33
+ @prebuild_framework_names.push pod_name
34
+ else
35
+ @should_not_prebuild_framework_names ||= []
36
+ @should_not_prebuild_framework_names.push pod_name
37
+ end
38
+ end
39
+
40
+ def prebuild_framework_names
41
+ names = @prebuild_framework_names || []
42
+ if parent != nil and parent.kind_of? TargetDefinition
43
+ names += parent.prebuild_framework_names
44
+ end
45
+ names
46
+ end
47
+ def should_not_prebuild_framework_names
48
+ names = @should_not_prebuild_framework_names || []
49
+ if parent != nil and parent.kind_of? TargetDefinition
50
+ names += parent.should_not_prebuild_framework_names
51
+ end
52
+ names
53
+ end
54
+
55
+ # ---- patch method ----
56
+ # We want modify `store_pod` method, but it's hard to insert a line in the
57
+ # implementation. So we patch a method called in `store_pod`.
58
+ old_method = instance_method(:parse_inhibit_warnings)
59
+
60
+ define_method(:parse_inhibit_warnings) do |name, requirements|
61
+ parse_prebuild_framework(name, requirements)
62
+ old_method.bind(self).(name, requirements)
63
+ end
64
+
65
+ end
66
+ end
67
+ end
68
+
69
+
70
+ module Pod
71
+ class Installer
72
+
73
+ def prebuild_pod_targets
74
+
75
+ all = []
76
+ aggregate_targets = (self.aggregate_targets || []).select { |a| a.platform != :watchos }
77
+ aggregate_targets.each do |aggregate_target|
78
+ target_definition = aggregate_target.target_definition
79
+ targets = aggregate_target.pod_targets || []
80
+
81
+ UI.puts "🔍 Processing #{aggregate_target.name}:"
82
+ UI.puts "🔍 pod_targets count: #{targets.count}"
83
+ UI.puts "🔍 pod_targets: #{targets.map(&:pod_name).join(', ')}" if targets.count < 10
84
+
85
+ # filter prebuild
86
+ prebuild_names = target_definition.prebuild_framework_names
87
+ if not Podfile::DSL.prebuild_all
88
+ targets = targets.select { |pod_target| prebuild_names.include?(pod_target.pod_name) }
89
+ end
90
+ dependency_targets = targets.map {|t| t.recursive_dependent_targets }.flatten.uniq || []
91
+ targets = (targets + dependency_targets).uniq
92
+
93
+ # filter should not prebuild
94
+ explict_should_not_names = target_definition.should_not_prebuild_framework_names
95
+ targets = targets.reject { |pod_target| explict_should_not_names.include?(pod_target.pod_name) }
96
+
97
+ all += targets
98
+ end
99
+
100
+ all = all.reject {|pod_target| sandbox.local?(pod_target.pod_name) }
101
+ UI.puts "🔍 prebuild_pod_targets: #{all.map(&:pod_name).join(', ')}"
102
+ all.uniq
103
+ end
104
+
105
+ # the root names who needs prebuild, including dependency pods.
106
+ def prebuild_pod_names
107
+ @prebuild_pod_names ||= self.prebuild_pod_targets.map(&:pod_name)
108
+ end
109
+
110
+ # 强制重新计算 prebuild_pod_names(用于主安装阶段)
111
+ def refresh_prebuild_pod_names!
112
+ @prebuild_pod_names = nil
113
+ prebuild_pod_names
114
+ end
115
+
116
+ end
117
+ end
118
+
119
+
120
+
@@ -0,0 +1,53 @@
1
+ module Pod
2
+ class PrebuildSandbox < Sandbox
3
+
4
+ # [String] standard_sandbox_path
5
+ def self.from_standard_sanbox_path(path)
6
+ # 将预构建目录设置到项目根目录,而不是Pods目录旁边
7
+ # 这样可以避免在删除Pods目录时丢失预构建的框架
8
+ project_root = Pathname.new(path).realpath.parent
9
+ prebuild_sandbox_path = project_root + "_Prebuild"
10
+ UI.puts "prebuild_sandbox_path: #{prebuild_sandbox_path}"
11
+ self.new(prebuild_sandbox_path)
12
+ end
13
+
14
+ def self.from_standard_sandbox(sandbox)
15
+ self.from_standard_sanbox_path(sandbox.root)
16
+ end
17
+
18
+ def standard_sanbox_path
19
+ self.root
20
+ end
21
+
22
+ def generate_framework_path
23
+ self.root + "GeneratedFrameworks"
24
+ end
25
+
26
+ def framework_folder_path_for_pod_name(name)
27
+ self.generate_framework_path + name
28
+ end
29
+
30
+ def exsited_framework_names
31
+ return [] unless generate_framework_path.exist?
32
+ generate_framework_path.children().map do |framework_name|
33
+ if framework_name.directory?
34
+ if not framework_name.children.empty?
35
+ File.basename(framework_name)
36
+ else
37
+ nil
38
+ end
39
+ else
40
+ nil
41
+ end
42
+ end.reject(&:nil?)
43
+ end
44
+
45
+ def framework_existed?(root_name)
46
+ return false unless generate_framework_path.exist?
47
+ generate_framework_path.children().any? do |child|
48
+ child.basename.to_s == root_name
49
+ end
50
+ end
51
+
52
+ end
53
+ end
@@ -0,0 +1,49 @@
1
+
2
+ module Pod
3
+ class Prebuild
4
+
5
+ # Check the targets, for the current limitation of the plugin
6
+ #
7
+ # @param [Array<PodTarget>] prebuilt_targets
8
+ def self.check_one_pod_should_have_only_one_target(prebuilt_targets)
9
+
10
+ targets_have_different_platforms = prebuilt_targets.select {|t| t.pod_name != t.name }
11
+
12
+ if targets_have_different_platforms.count > 0
13
+ names = targets_have_different_platforms.map(&:pod_name)
14
+ raw_names = targets_have_different_platforms.map(&:name)
15
+ message = "Oops, you came across a limitation of cocoapods-binary-matchup.
16
+
17
+ The plugin requires that one pod should have ONLY ONE target in the 'Pod.xcodeproj'. There are mainly 2 situations \
18
+ causing this problem:
19
+
20
+ 1. One pod integrates in 2 or more different platforms' targets. e.g.
21
+ ```
22
+ target 'iphoneApp' do
23
+ pod 'A', :binary => true
24
+ end
25
+ target 'watchApp' do
26
+ pod 'A'
27
+ end
28
+ ```
29
+
30
+ 2. Use different subspecs in multiple targets. e.g.
31
+ ```
32
+ target 'iphoneApp' do
33
+ pod 'A/core'
34
+ pod 'A/network'
35
+ end
36
+ target 'iphoneAppTest' do
37
+ pod 'A/core'
38
+ end
39
+ ```
40
+
41
+ Related pods: #{names}, target names: #{raw_names}
42
+ "
43
+ raise Informative, message
44
+ end
45
+ end
46
+
47
+
48
+ end
49
+ end