cocoapods-binary-bugfix 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,370 @@
1
+ require_relative 'rome/build_framework'
2
+ require_relative 'helper/passer'
3
+ require_relative 'helper/target_checker'
4
+ require 'cfpropertylist'
5
+
6
+
7
+
8
+
9
+ # patch prebuild ability
10
+ module Pod
11
+ class Installer
12
+
13
+ include Config::Mixin
14
+
15
+ private
16
+
17
+ def local_manifest
18
+ if not @local_manifest_inited
19
+ @local_manifest_inited = true
20
+ raise "This method should be call before generate project" unless self.analysis_result == nil
21
+ @local_manifest = self.sandbox.manifest
22
+ end
23
+ @local_manifest
24
+ end
25
+ # @return [Analyzer::SpecsState]
26
+ def prebuild_pods_changes
27
+ local_manifest = self.sandbox.manifest
28
+ return nil if local_manifest.nil?
29
+ # if @prebuild_pods_changes.nil?
30
+ changes = local_manifest.detect_changes_with_podfile(podfile)
31
+ unchanged_pod_names = changes[:unchanged]
32
+ changed_pod_names = changes[:changed]
33
+ unchanged_pod_names.reverse_each do |name|
34
+ mainfest_pod_version = local_manifest.version(name).to_s
35
+ already_prebuild_version = prebuilded_framework_version(name) || "未找到"
36
+ if not compare_version(mainfest_pod_version, already_prebuild_version)
37
+ Pod::UI.puts("- #{name} 已编译版本 #{already_prebuild_version}, manifest中的版本: #{mainfest_pod_version}") if config.verbose
38
+ changed_pod_names = changed_pod_names.push(name)
39
+ unchanged_pod_names.delete(name)
40
+ end
41
+ end
42
+
43
+ changes[:changed] = changed_pod_names
44
+ changes[:unchanged] = unchanged_pod_names
45
+ Pod::UI.puts("Pre 需要重编译的framework : #{changed_pod_names.to_s}") if config.verbose
46
+ @prebuild_pods_changes = Analyzer::SpecsState.new(changes)
47
+ # save the chagnes info for later stage
48
+ Pod::Prebuild::Passer.prebuild_pods_changes = @prebuild_pods_changes
49
+ @prebuild_pods_changes
50
+ end
51
+
52
+ # compare version
53
+ # 1.2.0 == 1.2 => true
54
+ # 1.2.1 != 1.2.0 => false
55
+ def compare_version(first_version, second_version)
56
+ first_nums = first_version.split('.')
57
+ second_nums = second_version.split('.')
58
+
59
+ first_nums.pop until first_nums.last.to_i != 0
60
+ second_nums.pop until second_nums.last.to_i != 0
61
+
62
+ first_nums == second_nums
63
+ end
64
+
65
+
66
+ public
67
+
68
+ # pod update 之后 lockfile 文件更新,更新lockfile对象,再次检查与预编译的版本是否一致
69
+ def post_update_pods_changes
70
+ changes = lockfile.detect_changes_with_podfile(podfile)
71
+ unchanged_pod_names = changes[:unchanged]
72
+ changed_pod_names = changes[:changed]
73
+ add_pod_names = changes[:added]
74
+ removed_pod_names = changes[:removed]
75
+
76
+
77
+ unchanged_pod_names.reverse_each do |name|
78
+ mainfest_pod_version = lockfile.version(name).to_s
79
+ already_prebuild_version = prebuilded_framework_version(name) || "未找到"
80
+ if compare_version(mainfest_pod_version, already_prebuild_version)
81
+ # 已经编译了
82
+ removed_pod_names.delete(name) if removed_pod_names.include?(name)
83
+ add_pod_names.delete(name) if add_pod_names.include?(name)
84
+ changed_pod_names.delete(name) if changed_pod_names.include?(name)
85
+
86
+ else
87
+ # 未找到相对应的版本
88
+ if already_prebuild_version == "99999.99999.99999"
89
+ Pod::UI.puts("- #{name}: 未找到预编译文件, manifest中的版本: #{mainfest_pod_version}") if config.verbose
90
+ else
91
+ Pod::UI.puts("- #{name}: 已编译版 #{already_prebuild_version}, manifest中的版本: #{mainfest_pod_version}") if config.verbose
92
+ end
93
+ changed_pod_names = changed_pod_names.push(name)
94
+ unchanged_pod_names.delete(name) if unchanged_pod_names.include?(name)
95
+ end
96
+ end
97
+ changes[:removed] = removed_pod_names
98
+ changes[:added] = add_pod_names
99
+ changes[:changed] = changed_pod_names
100
+ changes[:unchanged] = unchanged_pod_names
101
+ Pod::UI.puts("post 需要重编译的framework : #{(changed_pod_names + removed_pod_names + add_pod_names ).to_a}") if config.verbose
102
+ @prebuild_pods_changes = Analyzer::SpecsState.new(changes)
103
+ # save the chagnes info for later stage
104
+ Pod::Prebuild::Passer.prebuild_pods_changes = @prebuild_pods_changes
105
+ @prebuild_pods_changes
106
+
107
+ end
108
+
109
+ # check if need to prebuild
110
+ def have_exact_prebuild_cache?
111
+
112
+ # check if need build frameworks
113
+ return false if local_manifest == nil
114
+
115
+ changes = prebuild_pods_changes
116
+ added = changes.added
117
+ changed = changes.changed
118
+ unchanged = changes.unchanged
119
+ deleted = changes.deleted
120
+
121
+ exsited_framework_pod_names = sandbox.exsited_framework_pod_names
122
+ missing = unchanged.select do |pod_name|
123
+ not exsited_framework_pod_names.include?(pod_name)
124
+ end
125
+
126
+ needed = (added + changed + deleted + missing)
127
+
128
+ return needed.empty?
129
+ end
130
+ # 当前已编译的framework的版本
131
+ def prebuilded_framework_version(name)
132
+ path = self.sandbox.plist_path_for_target_name(name)
133
+ framework_version = "99999.99999.99999"
134
+ if Pathname.new(path).exist?
135
+ plist_file = CFPropertyList::List.new(:file => path)
136
+ data = CFPropertyList.native_types(plist_file.value)
137
+ framework_version = data["CFBundleShortVersionString"]
138
+ end
139
+ framework_version
140
+ end
141
+
142
+
143
+ # The install method when have completed cache
144
+ def install_when_cache_hit!
145
+ # just print log
146
+ self.sandbox.exsited_framework_target_names.each do |name|
147
+ UI.puts "Using #{name}" if config.verbose
148
+ end
149
+
150
+ self.sandbox
151
+ #处理静态库resources 资源文件
152
+ self.resolve_dependencies
153
+ self.download_dependencies
154
+
155
+ self.handle_static_framework_resouces
156
+
157
+ end
158
+
159
+ def delete_standard_sand_box_pod(standard_sanbox)
160
+ if lockfile
161
+ changes = lockfile.detect_changes_with_podfile(podfile)
162
+ need_update_pods = (changes[:added] + changes[:changed] + changes[:removed]).to_a
163
+
164
+ need_update_pods.each do |pod_name|
165
+ pod_path = Pathname.new(standard_sanbox.root.to_s + "/#{pod_name}")
166
+ Pod::UI.puts("删除 #{pod_path.to_s}") if config.verbose
167
+ pod_path.rmtree if pod_path.exist?
168
+ end
169
+ end
170
+ end
171
+
172
+ def delete_all_standard_sandbox_pod(standard_sanbox)
173
+ if lockfile
174
+ changes = lockfile.detect_changes_with_podfile(podfile)
175
+ need_update_pods = (changes[:added] + changes[:changed] + changes[:removed] + changes[:unchanged]).to_a
176
+
177
+ need_update_pods.each do |pod_name|
178
+ pod_path = Pathname.new(standard_sanbox.root.to_s + "/#{pod_name}")
179
+ Pod::UI.puts("删除 #{pod_path.to_s}") if config.verbose
180
+ pod_path.rmtree if pod_path.exist?
181
+ end
182
+ end
183
+ end
184
+ # 处理静态库资源
185
+ def handle_static_framework_resouces
186
+ all_static_framework_targets = pod_targets.reject{|pod_target| not pod_target.static_framework? or pod_target.resource_paths.empty? }
187
+ all_static_framework_targets.each do |target|
188
+ output_path = sandbox.framework_folder_path_for_target_name(target.name)
189
+ if target.static_framework? and !target.resource_paths.empty?
190
+ framework_path = output_path + target.framework_name
191
+ standard_sandbox_path = sandbox.standard_sanbox_path
192
+ resources = begin
193
+ if Pod::VERSION.start_with? "1.5"
194
+ target.resource_paths
195
+ else
196
+ # resource_paths is Hash{String=>Array<String>} on 1.6 and above
197
+ # (use AFNetworking to generate a demo data)
198
+ # https://github.com/leavez/cocoapods-binary/issues/50
199
+ target.resource_paths.values.flatten
200
+ end
201
+ end
202
+ raise "Wrong type: #{resources}" unless resources.kind_of? Array
203
+
204
+ path_objects = resources.map do |path|
205
+ prebuild_real_path = (path.gsub('${PODS_ROOT}', sandbox.root.to_s) if path.start_with? '${PODS_ROOT}')|| ""
206
+ real_file_path = framework_path + File.basename(path)
207
+ if Pathname.new(prebuild_real_path).exist? and
208
+ Pathname.new(framework_path).exist? and
209
+ not Pathname.new(real_file_path).exist?
210
+ # 静态库的resource,拷贝至framework目录下
211
+ FileUtils.cp_r(prebuild_real_path, real_file_path, :remove_destination => true)
212
+ end
213
+ object = Prebuild::Passer::ResourcePath.new
214
+ object.real_file_path = real_file_path
215
+ object.target_file_path = path.gsub('${PODS_ROOT}', standard_sandbox_path.to_s) if path.start_with? '${PODS_ROOT}'
216
+ object.target_file_path = path.gsub("${PODS_CONFIGURATION_BUILD_DIR}", standard_sandbox_path.to_s) if path.start_with? "${PODS_CONFIGURATION_BUILD_DIR}"
217
+ object
218
+ end
219
+ Prebuild::Passer.resources_to_copy_for_static_framework[target.name] = path_objects
220
+ end
221
+ end
222
+ end
223
+
224
+
225
+ # Build the needed framework files
226
+ def prebuild_frameworks!(after_write_lock)
227
+ # build options
228
+ sandbox_path = sandbox.root
229
+ existed_framework_folder = sandbox.generate_framework_path
230
+ bitcode_enabled = Pod::Podfile::DSL.bitcode_enabled
231
+ targets = []
232
+
233
+ if local_manifest != nil
234
+
235
+ if after_write_lock
236
+ changes = post_update_pods_changes
237
+ elsif
238
+ changes = prebuild_pods_changes
239
+ end
240
+
241
+ added = changes.added
242
+ changed = changes.changed
243
+ unchanged = changes.unchanged
244
+ deleted = changes.deleted
245
+
246
+ existed_framework_folder.mkdir unless existed_framework_folder.exist?
247
+ exsited_framework_pod_names = sandbox.exsited_framework_pod_names
248
+
249
+ # additions
250
+ missing = unchanged.select do |pod_name|
251
+ not exsited_framework_pod_names.include?(pod_name)
252
+ end
253
+
254
+
255
+ root_names_to_update = (added + changed + missing)
256
+
257
+ # transform names to targets
258
+ cache = []
259
+ targets = root_names_to_update.map do |pod_name|
260
+ tars = Pod.fast_get_targets_for_pod_name(pod_name, self.pod_targets, cache)
261
+ if tars.nil? || tars.empty?
262
+ raise "There's no target named (#{pod_name}) in Pod.xcodeproj.\n #{self.pod_targets.map(&:name)}" if t.nil?
263
+ end
264
+ tars
265
+ end.flatten
266
+
267
+ # add the dendencies
268
+ # dependency_targets = targets.map {|t| t.recursive_dependent_targets }.flatten.uniq || []
269
+ # targets = (targets + dependency_targets).uniq
270
+ else
271
+ targets = self.pod_targets
272
+ end
273
+
274
+ targets = targets.reject {|pod_target| sandbox.local?(pod_target.pod_name) }
275
+
276
+ # build!
277
+ Pod::UI.puts "Prebuild frameworks (total #{targets.count})"
278
+ Pod::Prebuild.remove_build_dir(sandbox_path)
279
+ targets.each do |target|
280
+ if !target.should_build?
281
+ UI.puts "Prebuilding #{target.label}"
282
+ next
283
+ end
284
+
285
+ output_path = sandbox.framework_folder_path_for_target_name(target.name)
286
+ output_path.rmtree if output_path.exist?
287
+ output_path.mkpath unless output_path.exist?
288
+ Pod::Prebuild.build(sandbox_path, target, output_path, bitcode_enabled, Podfile::DSL.custom_build_options, Podfile::DSL.custom_build_options_simulator)
289
+ end
290
+ # check static_framework resources
291
+ self.handle_static_framework_resouces
292
+ Pod::Prebuild.remove_build_dir(sandbox_path)
293
+
294
+
295
+
296
+
297
+ # copy vendored libraries and frameworks
298
+ targets.each do |target|
299
+ root_path = self.sandbox.pod_dir(target.name)
300
+ target_folder = sandbox.framework_folder_path_for_target_name(target.name)
301
+
302
+ # If target shouldn't build, we copy all the original files
303
+ # This is for target with only .a and .h files
304
+ if not target.should_build?
305
+ Prebuild::Passer.target_names_to_skip_integration_framework << target.name
306
+ FileUtils.cp_r(root_path, target_folder, :remove_destination => true)
307
+ next
308
+ end
309
+
310
+ target.spec_consumers.each do |consumer|
311
+ file_accessor = Sandbox::FileAccessor.new(root_path, consumer)
312
+ lib_paths = file_accessor.vendored_frameworks || []
313
+ lib_paths += file_accessor.vendored_libraries
314
+ # @TODO dSYM files
315
+ lib_paths.each do |lib_path|
316
+ relative = lib_path.relative_path_from(root_path)
317
+ destination = target_folder + relative
318
+ destination.dirname.mkpath unless destination.dirname.exist?
319
+ FileUtils.cp_r(lib_path, destination, :remove_destination => true)
320
+ end
321
+ end
322
+ end
323
+
324
+ # save the pod_name for prebuild framwork in sandbox
325
+ targets.each do |target|
326
+ sandbox.save_pod_name_for_target target
327
+ end
328
+
329
+ # Remove useless files
330
+ # remove useless pods
331
+ all_needed_names = self.pod_targets.map(&:name).uniq
332
+ useless_target_names = sandbox.exsited_framework_target_names.reject do |name|
333
+ all_needed_names.include? name
334
+ end
335
+ # 不删除已经编译好的framework
336
+ # useless_target_names.each do |name|
337
+ # path = sandbox.framework_folder_path_for_target_name(name)
338
+ # path.rmtree if path.exist?
339
+ # end
340
+
341
+ if not Podfile::DSL.dont_remove_source_code
342
+ # only keep manifest.lock and framework folder in _Prebuild
343
+ to_remain_files = ["Manifest.lock", File.basename(existed_framework_folder)]
344
+ to_delete_files = sandbox_path.children.select do |file|
345
+ filename = File.basename(file)
346
+ not to_remain_files.include?(filename)
347
+ end
348
+
349
+ to_delete_files.each do |path|
350
+ path.rmtree if path.exist?
351
+ end
352
+ else
353
+ # just remove the tmp files
354
+ path = sandbox.root + 'Manifest.lock.tmp'
355
+ path.rmtree if path.exist?
356
+ end
357
+ end
358
+
359
+ # patch the post install hook
360
+ old_method2 = instance_method(:run_plugins_post_install_hooks)
361
+ define_method(:run_plugins_post_install_hooks) do
362
+ old_method2.bind(self).()
363
+ if Pod::is_prebuild_stage
364
+ self.prebuild_frameworks!(true)
365
+ end
366
+ end
367
+
368
+
369
+ end
370
+ end
@@ -0,0 +1,51 @@
1
+ module Pod
2
+ class Command
3
+ class Install < Command
4
+ @@use_source = false
5
+
6
+ class << self
7
+ alias :original_options :options
8
+ end
9
+ def self.options
10
+ [['--hsource', 'from cocoapods-binaryhqp, all frameworks use source code']].concat(original_options)
11
+ end
12
+
13
+ alias :original_initialize :initialize
14
+ def initialize(argv)
15
+ @@use_source = argv.flag?('hsource', false)
16
+ original_initialize(argv)
17
+ end
18
+
19
+ def self.all_use_source
20
+ @@use_source
21
+ end
22
+
23
+ def self.set_all_use_source(use)
24
+ @@use_source = use
25
+ end
26
+
27
+
28
+ end
29
+ end
30
+ end
31
+
32
+ module Pod
33
+ class Command
34
+ class Update < Command
35
+ class << self
36
+ alias :original_options :options
37
+ end
38
+ def self.options
39
+ [['--hsource', 'from cocoapods-binaryhqp, all frameworks use source code']].concat(original_options)
40
+ end
41
+
42
+ alias :original_initialize :initialize
43
+ def initialize(argv)
44
+ use = argv.flag?('hsource', false)
45
+ Pod::Command::Install.set_all_use_source(use)
46
+ original_initialize(argv)
47
+ end
48
+ end
49
+ end
50
+ end
51
+
@@ -0,0 +1,3 @@
1
+ module CocoapodsBinaryBugfix
2
+ VERSION = "0.0.1"
3
+ end
@@ -0,0 +1,101 @@
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 || (not Pod::Podfile::DSL.framework_source_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] && (not Pod::Podfile::DSL.framework_source_all)
37
+ local = (options[:path] != nil)
38
+ end
39
+
40
+ if Pod::Command::Install.all_use_source
41
+ should_prebuild = false
42
+ end
43
+
44
+ if should_prebuild and (not local)
45
+ old_method.bind(self).(name, *args)
46
+ end
47
+ end
48
+ end
49
+ end
50
+
51
+
52
+ # a force disable option for integral
53
+ class Installer
54
+ def self.force_disable_integration(value)
55
+ @@force_disable_integration = value
56
+ end
57
+
58
+ old_method = instance_method(:integrate_user_project)
59
+ define_method(:integrate_user_project) do
60
+ if @@force_disable_integration
61
+ return
62
+ end
63
+ old_method.bind(self).()
64
+ end
65
+ end
66
+
67
+ # a option to disable install complete message
68
+ class Installer
69
+ def self.disable_install_complete_message(value)
70
+ @@disable_install_complete_message = value
71
+ end
72
+
73
+ old_method = instance_method(:print_post_install_message)
74
+ define_method(:print_post_install_message) do
75
+ if @@disable_install_complete_message
76
+ return
77
+ end
78
+ old_method.bind(self).()
79
+ end
80
+ end
81
+
82
+ # option to disable write lockfiles
83
+ class Config
84
+
85
+ @@force_disable_write_lockfile = false
86
+ def self.force_disable_write_lockfile(value)
87
+ @@force_disable_write_lockfile = value
88
+ end
89
+
90
+ old_method = instance_method(:lockfile_path)
91
+ define_method(:lockfile_path) do
92
+ if @@force_disable_write_lockfile
93
+ # As config is a singleton, sandbox_root refer to the standard sandbox.
94
+ return PrebuildSandbox.from_standard_sanbox_path(sandbox_root).root + 'Manifest.lock.tmp'
95
+ else
96
+ return old_method.bind(self).()
97
+ end
98
+ end
99
+ end
100
+
101
+ end
@@ -0,0 +1,78 @@
1
+ # ABOUT NAMES
2
+ #
3
+ # There are many kinds of name in cocoapods. Two main names are widely used in this plugin.
4
+ # - root_spec.name (spec.root_name, targe.pod_name):
5
+ # aka "pod_name"
6
+ # the name we use in podfile. the concept.
7
+ #
8
+ # - target.name:
9
+ # aka "target_name"
10
+ # the name of the final target in xcode project. the final real thing.
11
+ #
12
+ # One pod may have multiple targets in xcode project, due to one pod can be used in mutiple
13
+ # platform simultaneously. So one `root_spec.name` may have multiple coresponding `target.name`s.
14
+ # Therefore, map a spec to/from targets is a little complecated. It's one to many.
15
+ #
16
+
17
+ # Tool to transform Pod_name to target efficiently
18
+ module Pod
19
+ def self.fast_get_targets_for_pod_name(pod_name, targets, cache)
20
+ pod_name_to_targets_hash = nil
21
+ if cache.empty?
22
+ pod_name_to_targets_hash = targets.reduce({}) do |sum, target|
23
+ array = sum[target.pod_name] || []
24
+ array << target
25
+ sum[target.pod_name] = array
26
+ sum
27
+ end
28
+ cache << pod_name_to_targets_hash
29
+ else
30
+ pod_name_to_targets_hash = cache.first
31
+ end
32
+
33
+ pod_name_to_targets_hash[pod_name] || []
34
+ end
35
+ end
36
+
37
+
38
+
39
+
40
+
41
+
42
+ # Target:
43
+
44
+ # def pod_name
45
+ # root_spec.name
46
+ # end
47
+
48
+ # def name
49
+ # pod_name + #{scope_suffix}
50
+ # end
51
+
52
+ # def product_module_name
53
+ # root_spec.module_name
54
+ # end
55
+
56
+ # def framework_name
57
+ # "#{product_module_name}.framework"
58
+ # end
59
+
60
+ # def product_name
61
+ # if requires_frameworks?
62
+ # framework_name
63
+ # else
64
+ # static_library_name
65
+ # end
66
+ # end
67
+
68
+ # def product_basename
69
+ # if requires_frameworks?
70
+ # product_module_name
71
+ # else
72
+ # label
73
+ # end
74
+ # end
75
+
76
+ # def framework_name
77
+ # "#{product_module_name}.framework"
78
+ # 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