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,295 @@
1
+ require_relative 'helper/podfile_options'
2
+ require_relative 'helper/feature_switches'
3
+ require_relative 'helper/prebuild_sandbox'
4
+ require_relative 'helper/passer'
5
+ require_relative 'helper/names'
6
+ require_relative 'helper/target_checker'
7
+
8
+
9
+ # NOTE:
10
+ # This file will only be loaded on normal pod install step
11
+ # so there's no need to check is_prebuild_stage
12
+
13
+
14
+
15
+ # Provide a special "download" process for prebuilded pods.
16
+ #
17
+ # As the frameworks is already exsited in local folder. We
18
+ # just create a symlink to the original target folder.
19
+ #
20
+ module Pod
21
+ class Installer
22
+ class PodSourceInstaller
23
+
24
+ def install_for_prebuild!(standard_sanbox)
25
+ return if standard_sanbox.local? self.name
26
+
27
+ # make a symlink to target folder
28
+ prebuild_sandbox = Pod::PrebuildSandbox.from_standard_sandbox(standard_sanbox)
29
+ # if spec used in multiple platforms, it may return multiple paths
30
+ target_names = prebuild_sandbox.existed_target_names_for_pod_name(self.name)
31
+
32
+ def walk(path, &action)
33
+ return unless path.exist?
34
+ path.children.each do |child|
35
+ result = action.call(child, &action)
36
+ if child.directory?
37
+ walk(child, &action) if result
38
+ end
39
+ end
40
+ end
41
+ def make_link(source, target)
42
+ source = Pathname.new(source)
43
+ target = Pathname.new(target)
44
+ target.parent.mkpath unless target.parent.exist?
45
+ relative_source = source.relative_path_from(target.parent)
46
+ FileUtils.ln_sf(relative_source, target)
47
+ end
48
+ def mirror_with_symlink(source, basefolder, target_folder)
49
+ target = target_folder + source.relative_path_from(basefolder)
50
+ make_link(source, target)
51
+ end
52
+
53
+ target_names.each do |name|
54
+
55
+ # symbol link copy all substructure
56
+ real_file_folder = prebuild_sandbox.framework_folder_path_for_target_name(name)
57
+
58
+ # If have only one platform, just place int the root folder of this pod.
59
+ # If have multiple paths, we use a sperated folder to store different
60
+ # platform frameworks. e.g. AFNetworking/AFNetworking-iOS/AFNetworking.framework
61
+
62
+ target_folder = standard_sanbox.pod_dir(self.name)
63
+ if target_names.count > 1
64
+ target_folder += real_file_folder.basename
65
+ end
66
+ target_folder.rmtree if target_folder.exist?
67
+ target_folder.mkpath
68
+
69
+
70
+ walk(real_file_folder) do |child|
71
+ source = child
72
+ # only make symlink to file and `.framework` folder
73
+ if child.directory? and [".framework", ".dSYM"].include? child.extname
74
+ mirror_with_symlink(source, real_file_folder, target_folder)
75
+ next false # return false means don't go deeper
76
+ elsif child.file?
77
+ mirror_with_symlink(source, real_file_folder, target_folder)
78
+ next true
79
+ else
80
+ next true
81
+ end
82
+ end
83
+
84
+
85
+ # symbol link copy resource for static framework
86
+ hash = Prebuild::Passer.resources_to_copy_for_static_framework || {}
87
+
88
+ path_objects = hash[name]
89
+ if path_objects != nil
90
+ path_objects.each do |object|
91
+ make_link(object.real_file_path, object.target_file_path)
92
+ end
93
+ end
94
+ end # of for each
95
+
96
+ end # of method
97
+
98
+ end
99
+ end
100
+ end
101
+
102
+
103
+ # Let cocoapods use the prebuild framework files in install process.
104
+ #
105
+ # the code only effect the second pod install process.
106
+ #
107
+ module Pod
108
+ class Installer
109
+
110
+
111
+ # Remove the old target files if prebuild frameworks changed
112
+ def remove_target_files_if_needed
113
+
114
+ changes = Pod::Prebuild::Passer.prebuild_pods_changes
115
+ updated_names = []
116
+ if changes == nil
117
+ updated_names = PrebuildSandbox.from_standard_sandbox(self.sandbox).exsited_framework_pod_names
118
+ else
119
+ added = changes.added
120
+ changed = changes.changed
121
+ deleted = changes.deleted
122
+ updated_names = added + changed + deleted
123
+ end
124
+
125
+ updated_names.each do |name|
126
+ root_name = Specification.root_name(name)
127
+ next if self.sandbox.local?(root_name)
128
+
129
+ # delete the cached files
130
+ target_path = self.sandbox.pod_dir(root_name)
131
+ target_path.rmtree if target_path.exist?
132
+
133
+ support_path = sandbox.target_support_files_dir(root_name)
134
+ support_path.rmtree if support_path.exist?
135
+ end
136
+
137
+ end
138
+
139
+
140
+ # Modify specification to use only the prebuild framework after analyzing
141
+ old_method2 = instance_method(:resolve_dependencies)
142
+ define_method(:resolve_dependencies) do
143
+
144
+ # Remove the old target files, else it will not notice file changes
145
+ self.remove_target_files_if_needed
146
+
147
+ # call original
148
+ old_method2.bind(self).()
149
+ # ...
150
+ # ...
151
+ # ...
152
+ # after finishing the very complex orginal function
153
+
154
+ # check the pods
155
+ # Although we have did it in prebuild stage, it's not sufficient.
156
+ # Same pod may appear in another target in form of source code.
157
+ # Prebuild.check_one_pod_should_have_only_one_target(self.prebuild_pod_targets)
158
+ self.validate_every_pod_only_have_one_form
159
+
160
+
161
+ # prepare
162
+ cache = []
163
+
164
+ def add_vendered_framework(spec, platform, added_framework_file_path)
165
+ if spec.attributes_hash[platform] == nil
166
+ spec.attributes_hash[platform] = {}
167
+ end
168
+ vendored_frameworks = spec.attributes_hash[platform]["vendored_frameworks"] || []
169
+ vendored_frameworks = [vendored_frameworks] if vendored_frameworks.kind_of?(String)
170
+ vendored_frameworks += [added_framework_file_path]
171
+ spec.attributes_hash[platform]["vendored_frameworks"] = vendored_frameworks
172
+ end
173
+ def empty_source_files(spec)
174
+ spec.attributes_hash["source_files"] = []
175
+ ["ios", "watchos", "tvos", "osx"].each do |plat|
176
+ if spec.attributes_hash[plat] != nil
177
+ spec.attributes_hash[plat]["source_files"] = []
178
+ end
179
+ end
180
+ end
181
+
182
+
183
+ specs = self.analysis_result.specifications
184
+
185
+ prebuilt_specs = (specs.select do |spec|
186
+
187
+ self.prebuild_pod_names.include? spec.root.name
188
+ end)
189
+
190
+ prebuilt_specs.each do |spec|
191
+
192
+ # Use the prebuild framworks as vendered frameworks
193
+ # get_corresponding_targets
194
+ targets = Pod.fast_get_targets_for_pod_name(spec.root.name, self.pod_targets, cache)
195
+
196
+ targets.each do |target|
197
+ # the framework_file_path rule is decided when `install_for_prebuild`,
198
+ # as to compitable with older version and be less wordy.
199
+ framework_file_path = target.framework_name
200
+ framework_file_path = target.name + "/" + framework_file_path if targets.count > 1
201
+ add_vendered_framework(spec, target.platform.name.to_s, framework_file_path)
202
+ end
203
+ # Clean the source files
204
+ # we just add the prebuilt framework to specific platform and set no source files
205
+ # for all platform, so it doesn't support the sence that 'a pod perbuild for one
206
+ # platform and not for another platform.'
207
+ empty_source_files(spec)
208
+
209
+ # to remove the resurce bundle target.
210
+ # When specify the "resource_bundles" in podspec, xcode will generate a bundle
211
+ # target after pod install. But the bundle have already built when the prebuit
212
+ # phase and saved in the framework folder. We will treat it as a normal resource
213
+ # file.
214
+ # https://github.com/leavez/cocoapods-binary/issues/29
215
+ if spec.attributes_hash["resource_bundles"]
216
+ bundle_names = spec.attributes_hash["resource_bundles"].keys
217
+ # 只对动态库处理,静态库由mainTarget处理resource_bundles,静态库不能清空resource_bundles
218
+ if not targets.first.build_as_static_framework?
219
+ spec.attributes_hash["resource_bundles"] = nil
220
+ spec.attributes_hash["resources"] ||= []
221
+ spec.attributes_hash["resources"] += bundle_names.map{|n| n+".bundle"}
222
+ end
223
+ end
224
+
225
+ # to avoid the warning of missing license
226
+ spec.attributes_hash["license"] = {}
227
+
228
+ end
229
+
230
+ end
231
+
232
+
233
+ # Override the download step to skip download and prepare file in target folder
234
+ old_method = instance_method(:install_source_of_pod)
235
+ define_method(:install_source_of_pod) do |pod_name|
236
+
237
+ # copy from original
238
+ pod_installer = create_pod_installer(pod_name)
239
+ # \copy from original
240
+
241
+ if self.prebuild_pod_names.include? pod_name
242
+ pod_installer.install_for_prebuild!(self.sandbox)
243
+ else
244
+ pod_installer.install!
245
+ end
246
+
247
+ # copy from original
248
+ @installed_specs.concat(pod_installer.specs_by_platform.values.flatten.uniq)
249
+ # \copy from original
250
+ end
251
+
252
+
253
+ end
254
+ end
255
+
256
+ # A fix in embeded frameworks script.
257
+ #
258
+ # The framework file in pod target folder is a symblink. The EmbedFrameworksScript use `readlink`
259
+ # to read the read path. As the symlink is a relative symlink, readlink cannot handle it well. So
260
+ # we override the `readlink` to a fixed version.
261
+ #
262
+ module Pod
263
+ module Generator
264
+ class EmbedFrameworksScript
265
+
266
+ old_method = instance_method(:script)
267
+ define_method(:script) do
268
+
269
+ script = old_method.bind(self).()
270
+ patch = <<-SH.strip_heredoc
271
+ #!/bin/sh
272
+
273
+ # ---- this is added by cocoapods-binary ---
274
+ # Readlink cannot handle relative symlink well, so we override it to a new one
275
+ # If the path isn't an absolute path, we add a realtive prefix.
276
+ old_read_link=`which readlink`
277
+ readlink () {
278
+ path=`$old_read_link "$1"`;
279
+ if [ $(echo "$path" | cut -c 1-1) = '/' ]; then
280
+ echo $path;
281
+ else
282
+ echo "`dirname $1`/$path";
283
+ fi
284
+ }
285
+ # ---
286
+ SH
287
+
288
+ # patch the rsync for copy dSYM symlink
289
+ script = script.gsub "rsync --delete", "rsync --copy-links --delete"
290
+
291
+ patch + script
292
+ end
293
+ end
294
+ end
295
+ end
@@ -0,0 +1,178 @@
1
+ # encoding: UTF-8
2
+ require_relative 'helper/podfile_options'
3
+ require_relative 'tool/tool'
4
+ require_relative 'gem_version'
5
+ require_relative 'command_option/command_option'
6
+
7
+ module Pod
8
+ class Podfile
9
+ module DSL
10
+
11
+ # Enable prebuiding for all pods
12
+ # it has a lower priority to other binary settings
13
+ def all_binary!
14
+ DSL.prebuild_all = true
15
+ end
16
+
17
+ def all_source!
18
+ DSL.framework_source_all = true
19
+ end
20
+
21
+ # Enable bitcode for prebuilt frameworks
22
+ def enable_bitcode_for_prebuilt_frameworks!
23
+ DSL.bitcode_enabled = true
24
+ end
25
+
26
+ # Don't remove source code of prebuilt pods
27
+ # It may speed up the pod install if git didn't
28
+ # include the `Pods` folder
29
+ def keep_source_code_for_prebuilt_frameworks!
30
+ DSL.dont_remove_source_code = true
31
+ end
32
+
33
+ # Add custom xcodebuild option to the prebuilding action
34
+ #
35
+ # You may use this for your special demands. For example: the default archs in dSYMs
36
+ # of prebuilt frameworks is 'arm64 armv7 x86_64', and no 'i386' for 32bit simulator.
37
+ # It may generate a warning when building for a 32bit simulator. You may add following
38
+ # to your podfile
39
+ #
40
+ # ` set_custom_xcodebuild_options_for_prebuilt_frameworks :simulator => "ARCHS=$(ARCHS_STANDARD)" `
41
+ #
42
+ # Another example to disable the generating of dSYM file:
43
+ #
44
+ # ` set_custom_xcodebuild_options_for_prebuilt_frameworks "DEBUG_INFORMATION_FORMAT=dwarf"`
45
+ #
46
+ #
47
+ # @param [String or Hash] options
48
+ #
49
+ # If is a String, it will apply for device and simulator. Use it just like in the commandline.
50
+ # If is a Hash, it should be like this: { :device => "XXXXX", :simulator => "XXXXX" }
51
+ #
52
+ def set_custom_xcodebuild_options_for_prebuilt_frameworks(options)
53
+ if options.kind_of? Hash
54
+ DSL.custom_build_options = [ options[:device] ] unless options[:device].nil?
55
+ DSL.custom_build_options_simulator = [ options[:simulator] ] unless options[:simulator].nil?
56
+ elsif options.kind_of? String
57
+ DSL.custom_build_options = [options]
58
+ DSL.custom_build_options_simulator = [options]
59
+ else
60
+ raise "Wrong type."
61
+ end
62
+ end
63
+
64
+ private
65
+ class_attr_accessor :prebuild_all
66
+ prebuild_all = false
67
+
68
+ class_attr_accessor :framework_source_all
69
+ framework_source_all = false
70
+
71
+ class_attr_accessor :bitcode_enabled
72
+ bitcode_enabled = false
73
+
74
+ class_attr_accessor :dont_remove_source_code
75
+ dont_remove_source_code = false
76
+
77
+ class_attr_accessor :custom_build_options
78
+ class_attr_accessor :custom_build_options_simulator
79
+ self.custom_build_options = []
80
+ self.custom_build_options_simulator = []
81
+ end
82
+ end
83
+ end
84
+
85
+ Pod::HooksManager.register('cocoapods-binary-bugfix', :pre_install) do |installer_context|
86
+
87
+ require_relative 'helper/feature_switches'
88
+ if Pod.is_prebuild_stage
89
+ next
90
+ end
91
+
92
+ Pod::UI.puts("当前版本: #{CocoapodsBinaryBel::VERSION}")
93
+
94
+ # [Check Environment]
95
+ # check user_framework is on
96
+ podfile = installer_context.podfile
97
+ podfile.target_definition_list.each do |target_definition|
98
+ next if target_definition.prebuild_framework_pod_names.empty?
99
+ if not target_definition.uses_frameworks?
100
+ STDERR.puts "[!] cocoapods-binary-bugfix requires `use_frameworks!`".red
101
+ exit
102
+ end
103
+ end
104
+
105
+
106
+ # -- step 1: prebuild framework ---
107
+ # Execute a sperated pod install, to generate targets for building framework,
108
+ # then compile them to framework files.
109
+ require_relative 'helper/prebuild_sandbox'
110
+ require_relative 'Prebuild'
111
+
112
+ Pod::UI.puts "🚀 Prebuild frameworks"
113
+
114
+ # Fetch original installer (which is running this pre-install hook) options,
115
+ # then pass them to our installer to perform update if needed
116
+ # Looks like this is the most appropriate way to figure out that something should be updated
117
+
118
+ update = nil
119
+ repo_update = nil
120
+
121
+ include ObjectSpace
122
+ ObjectSpace.each_object(Pod::Installer) { |installer|
123
+ update = installer.update
124
+ repo_update = installer.repo_update
125
+ }
126
+
127
+ # control features
128
+ Pod.is_prebuild_stage = true
129
+ Pod::Podfile::DSL.enable_prebuild_patch true # enable sikpping for prebuild targets
130
+ Pod::Installer.force_disable_integration true # don't integrate targets
131
+ Pod::Config.force_disable_write_lockfile true # disbale write lock file for perbuild podfile
132
+ Pod::Installer.disable_install_complete_message true # disable install complete message
133
+
134
+ Pod::Podfile::DSL.framework_source_all = Pod::Podfile::DSL.framework_source_all || Pod::Command::Install::all_use_source
135
+ Pod::UI.puts("全部使用源码") if Pod::Podfile::DSL.framework_source_all
136
+
137
+ # make another custom sandbox
138
+ standard_sandbox = installer_context.sandbox
139
+ prebuild_sandbox = Pod::PrebuildSandbox.from_standard_sandbox(standard_sandbox)
140
+
141
+ # get the podfile for prebuild
142
+ prebuild_podfile = Pod::Podfile.from_ruby(podfile.defined_in_file)
143
+
144
+ # install
145
+ lockfile = installer_context.lockfile
146
+ binary_installer = Pod::Installer.new(prebuild_sandbox, prebuild_podfile, lockfile)
147
+
148
+ if binary_installer.have_exact_prebuild_cache? && !update
149
+ binary_installer.install_when_cache_hit!
150
+ else
151
+ binary_installer.update = update
152
+ binary_installer.repo_update = repo_update
153
+ binary_installer.install!
154
+ end
155
+
156
+ binary_installer.delete_all_standard_sandbox_pod(standard_sandbox)
157
+
158
+
159
+ # reset the environment
160
+ Pod.is_prebuild_stage = false
161
+ Pod::Installer.force_disable_integration false
162
+ Pod::Podfile::DSL.enable_prebuild_patch false
163
+ Pod::Config.force_disable_write_lockfile false
164
+ Pod::Installer.disable_install_complete_message false
165
+ Pod::UserInterface.warnings = [] # clean the warning in the prebuild step, it's duplicated.
166
+
167
+
168
+
169
+
170
+
171
+ # -- step 2: pod install ---
172
+ # install
173
+ Pod::UI.puts "\n"
174
+ Pod::UI.puts "🤖 Pod Install"
175
+ require_relative 'Integration'
176
+ # go on the normal install step ...
177
+ end
178
+