cocoapods-ppbuild 0.0.1

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,236 @@
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
+ if @prebuild_pods_changes.nil?
26
+ changes = local_manifest.detect_changes_with_podfile(podfile)
27
+ @prebuild_pods_changes = Analyzer::SpecsState.new(changes)
28
+ # save the chagnes info for later stage
29
+ Pod::Prebuild::Passer.prebuild_pods_changes = @prebuild_pods_changes
30
+ end
31
+ @prebuild_pods_changes
32
+ end
33
+
34
+
35
+ public
36
+
37
+ # check if need to prebuild
38
+ def have_exact_prebuild_cache?
39
+ # check if need build frameworks
40
+ return false if local_manifest == nil
41
+
42
+ changes = prebuild_pods_changes
43
+ added = changes.added
44
+ changed = changes.changed
45
+ unchanged = changes.unchanged
46
+ deleted = changes.deleted
47
+
48
+ exsited_framework_pod_names = sandbox.exsited_framework_pod_names
49
+ missing = unchanged.select do |pod_name|
50
+ not exsited_framework_pod_names.include?(pod_name)
51
+ end
52
+
53
+ needed = (added + changed + deleted + missing)
54
+ return needed.empty?
55
+ end
56
+
57
+
58
+ # The install method when have completed cache
59
+ def install_when_cache_hit!
60
+ # just print log
61
+ self.sandbox.exsited_framework_target_names.each do |name|
62
+ UI.puts "Using #{name}"
63
+ end
64
+ end
65
+
66
+
67
+ # Build the needed framework files
68
+ def prebuild_frameworks!
69
+ # build options
70
+ sandbox_path = sandbox.root
71
+ existed_framework_folder = sandbox.generate_framework_path
72
+ bitcode_enabled = Pod::Podfile::DSL.bitcode_enabled
73
+ use_static_framework = Pod::Podfile::DSL.static_binary
74
+ targets = []
75
+
76
+ if local_manifest != nil
77
+ changes = prebuild_pods_changes
78
+ added = changes.added
79
+ changed = changes.changed
80
+ unchanged = changes.unchanged
81
+ deleted = changes.deleted
82
+
83
+ existed_framework_folder.mkdir unless existed_framework_folder.exist?
84
+ exsited_framework_pod_names = sandbox.exsited_framework_pod_names
85
+
86
+ # additions
87
+ missing = unchanged.select do |pod_name|
88
+ not exsited_framework_pod_names.include?(pod_name)
89
+ end
90
+
91
+
92
+ root_names_to_update = (added + changed + missing)
93
+
94
+ # transform names to targets
95
+ cache = []
96
+ targets = root_names_to_update.map do |pod_name|
97
+ tars = Pod.fast_get_targets_for_pod_name(pod_name, self.pod_targets, cache)
98
+ if tars.nil? || tars.empty?
99
+ raise "There's no target named (#{pod_name}) in Pod.xcodeproj.\n #{self.pod_targets.map(&:name)}" if t.nil?
100
+ end
101
+ tars
102
+ end.flatten
103
+
104
+ # add the dendencies
105
+ dependency_targets = targets.map {|t| t.recursive_dependent_targets }.flatten.uniq || []
106
+ targets = (targets + dependency_targets).uniq
107
+ else
108
+ targets = self.pod_targets
109
+ end
110
+
111
+ targets = targets.reject {|pod_target| sandbox.local?(pod_target.pod_name) }
112
+
113
+
114
+ # build!
115
+ Pod::UI.puts "Prebuild frameworks (total #{targets.count})"
116
+ Pod::Prebuild.remove_build_dir(sandbox_path)
117
+ targets.each do |target|
118
+ if !target.should_build?
119
+ UI.puts "Prebuilding #{target.label}"
120
+ next
121
+ end
122
+
123
+ output_path = sandbox.framework_folder_path_for_target_name(target.name)
124
+ output_path.mkpath unless output_path.exist?
125
+ Pod::Prebuild.build(sandbox_path, target, output_path, bitcode_enabled, Podfile::DSL.custom_build_options, Podfile::DSL.custom_build_options_simulator)
126
+
127
+ # save the resource paths for later installing,动态库需要将frameworkwork中资源链接到pod上
128
+ if target.static_framework? and !target.resource_paths.empty?
129
+ framework_path = output_path + target.framework_name
130
+ standard_sandbox_path = sandbox.standard_sanbox_path
131
+
132
+ resources = begin
133
+ if Pod::VERSION.start_with? "1.5"
134
+ target.resource_paths
135
+ else
136
+ # resource_paths is Hash{String=>Array<String>} on 1.6 and above
137
+ # (use AFNetworking to generate a demo data)
138
+ target.resource_paths.values.flatten
139
+ end
140
+ end
141
+ raise "Wrong type: #{resources}" unless resources.kind_of? Array
142
+
143
+ path_objects = resources.flat_map do |path|
144
+ real_path = framework_path + File.basename(path)
145
+ # 静态库资源目录处理
146
+ if use_static_framework
147
+ real_bundle_path = sandbox.real_bundle_path_for_pod(path)
148
+ if File.exists?(real_bundle_path)
149
+ FileUtils.cp_r(real_bundle_path, framework_path, :remove_destination => true)
150
+ end
151
+ end
152
+ object = Prebuild::Passer::ResourcePath.new
153
+ if File.exists?(real_path)
154
+ object.real_file_path = real_path
155
+ end
156
+ object.target_file_path = path.gsub('${PODS_ROOT}', standard_sandbox_path.to_s) if path.start_with? '${PODS_ROOT}'
157
+ object.target_file_path = path.gsub("${PODS_CONFIGURATION_BUILD_DIR}", standard_sandbox_path.to_s) if path.start_with? "${PODS_CONFIGURATION_BUILD_DIR}"
158
+ object
159
+ end
160
+ Prebuild::Passer.resources_to_copy_for_static_framework[target.name] = path_objects
161
+ end
162
+ end
163
+ Pod::Prebuild.remove_build_dir(sandbox_path)
164
+
165
+ # copy vendored libraries and frameworks
166
+ targets.each do |target|
167
+ root_path = self.sandbox.pod_dir(target.name)
168
+ target_folder = sandbox.framework_folder_path_for_target_name(target.name)
169
+
170
+ # If target shouldn't build, we copy all the original files
171
+ # This is for target with only .a and .h files
172
+ if not target.should_build?
173
+ Prebuild::Passer.target_names_to_skip_integration_framework << target.name
174
+ FileUtils.cp_r(root_path, target_folder, :remove_destination => true)
175
+ next
176
+ end
177
+
178
+ target.spec_consumers.each do |consumer|
179
+ file_accessor = Sandbox::FileAccessor.new(root_path, consumer)
180
+ lib_paths = file_accessor.vendored_frameworks || []
181
+ lib_paths += file_accessor.vendored_libraries
182
+ # @TODO dSYM files
183
+ lib_paths.each do |lib_path|
184
+ relative = lib_path.relative_path_from(root_path)
185
+ destination = target_folder + relative
186
+ destination.dirname.mkpath unless destination.dirname.exist?
187
+ FileUtils.cp_r(lib_path, destination, :remove_destination => true)
188
+ end
189
+ end
190
+ end
191
+
192
+ # save the pod_name for prebuild framwork in sandbox
193
+ targets.each do |target|
194
+ sandbox.save_pod_name_for_target target
195
+ end
196
+
197
+ # Remove useless files
198
+ # remove useless pods
199
+ all_needed_names = self.pod_targets.map(&:name).uniq
200
+ useless_target_names = sandbox.exsited_framework_target_names.reject do |name|
201
+ all_needed_names.include? name
202
+ end
203
+ useless_target_names.each do |name|
204
+ path = sandbox.framework_folder_path_for_target_name(name)
205
+ path.rmtree if path.exist?
206
+ end
207
+
208
+ if Podfile::DSL.dont_remove_source_code
209
+ # just remove the tmp files
210
+ path = sandbox.root + 'Manifest.lock.tmp'
211
+ path.rmtree if path.exist?
212
+ else
213
+ # only keep manifest.lock and framework folder in _Prebuild
214
+ to_remain_files = ["Manifest.lock", File.basename(existed_framework_folder)]
215
+ to_delete_files = sandbox_path.children.select do |file|
216
+ filename = File.basename(file)
217
+ not to_remain_files.include?(filename)
218
+ end
219
+ to_delete_files.each do |path|
220
+ path.rmtree if path.exist?
221
+ end
222
+ end
223
+ end
224
+
225
+ # patch the post install hook
226
+ old_method2 = instance_method(:run_plugins_post_install_hooks)
227
+ define_method(:run_plugins_post_install_hooks) do
228
+ old_method2.bind(self).()
229
+ if Pod::is_prebuild_stage
230
+ self.prebuild_frameworks!
231
+ end
232
+ end
233
+
234
+
235
+ end
236
+ end
@@ -0,0 +1,216 @@
1
+ module Pod
2
+ class Command
3
+ #
4
+ class Ppbuild < Command
5
+ self.summary = '通过将二进制对应源码放置在临时目录中,让二进制出现断点时可以跳到对应的源码,方便调试。'
6
+
7
+ self.description = <<-DESC
8
+ 通过将二进制对应源码放置在临时目录中,让二进制出现断点时可以跳到对应的源码,方便调试。
9
+ 在不删除二进制的情况下为某个组件添加源码调试能力,多个组件名称用空格分隔
10
+ DESC
11
+
12
+ self.arguments = [
13
+ CLAide::Argument.new('NAME', false)
14
+ ]
15
+ def self.options
16
+ [
17
+ ['--all-clean', '删除所有已经下载的源码'],
18
+ ['--clean', '删除所有指定下载的源码'],
19
+ ['--list', '展示所有一级下载的源码以及其大小'],
20
+ ['--source', '源码路径,本地路径,会去自动链接本地源码']
21
+ ]
22
+ end
23
+
24
+ def initialize(argv)
25
+ @codeSource = argv.option('source') || nil
26
+ @names = argv.arguments! unless argv.arguments.empty?
27
+ @list = argv.flag?('list', false )
28
+ @all_clean = argv.flag?('all-clean', false )
29
+ @clean = argv.flag?('clean', false )
30
+ @config = Pod::Config.instance
31
+ @analyzer = nil
32
+ @podfile_dependency_cache = Pod::Installer::Analyzer::PodfileDependencyCache.from_podfile(@config.podfile)
33
+ super
34
+ end
35
+
36
+
37
+ def run
38
+ podfile_lock = File.join(Pathname.pwd,"Podfile.lock")
39
+ raise "podfile.lock,不存在,请先pod install/update" unless File.exist?(podfile_lock)
40
+ @lockfile ||= Lockfile.from_file(Pathname.new(podfile_lock) )
41
+
42
+ if @list
43
+ list
44
+ elsif @clean
45
+ clean
46
+ elsif @all_clean
47
+ all_clean
48
+ elsif @names
49
+ add
50
+ end
51
+
52
+ if @list && @clean && @names
53
+ raise "请选择您要执行的命令。"
54
+ end
55
+ end
56
+
57
+ #==========================begin add ==============
58
+
59
+ def add
60
+ if @names == nil
61
+ raise "请输入要调试组件名,多个组件名称用空格分隔"
62
+ end
63
+ @names.each do |name|
64
+ lib_file = get_lib_path(name)
65
+ unless File.exist?(lib_file)
66
+ raise "找不到 #{lib_file}"
67
+ end
68
+ UI.puts "#{lib_file}"
69
+
70
+ target_path = @codeSource || download_source(name)
71
+
72
+ link(lib_file,target_path,name)
73
+ end
74
+ end
75
+
76
+ #下载源码到本地
77
+ def download_source(name)
78
+ target_path = File.join(source_root, name)
79
+ UI.puts target_path
80
+ FileUtils.rm_rf(target_path)
81
+ find_dependency = find_dependency(name)
82
+ spec = fetch_external_source(find_dependency, @config.podfile,@config.lockfile, @config.sandbox,true )
83
+
84
+ download_request = Pod::Downloader::Request.new(:name => name, :spec => spec)
85
+ Downloader.download(download_request, Pathname.new(target_path), :can_cache => true)
86
+
87
+ target_path
88
+ end
89
+
90
+ #找出依赖
91
+ def find_dependency (name)
92
+ find_dependency = nil
93
+
94
+ @config.podfile.dependencies.each do |dependency|
95
+ if dependency.root_name.downcase == name.downcase
96
+ find_dependency = dependency
97
+ break
98
+ end
99
+ end
100
+ if find_dependency.external_source.nil?
101
+
102
+ end
103
+ find_dependency
104
+ end
105
+
106
+ # 获取external_source 下的仓库
107
+ # @return spec
108
+ def fetch_external_source(dependency ,podfile , lockfile, sandbox,use_lockfile_options)
109
+ checkout_options = lockfile.checkout_options_for_pod_named(dependency.root_name)
110
+ source = if use_lockfile_options && lockfile && checkout_options
111
+ ExternalSources.from_params(checkout_options, dependency, podfile.defined_in_file, podfile.installation_options.clean?)
112
+ else
113
+ ExternalSources.from_dependency(dependency, podfile.defined_in_file, podfile.installation_options.clean?)
114
+ end
115
+ source.fetch(sandbox)
116
+ end
117
+
118
+
119
+ #==========================link begin ==============
120
+
121
+ #链接,.a文件位置, 源码目录
122
+ def link(lib_file,target_path,basename)
123
+ check(lib_file,dir,basename)
124
+ end
125
+
126
+ def check(lib_file,dir,basename)
127
+ file = `dwarfdump "#{lib_file}" | grep -E "DW_AT_decl_file.*#{basename}.*\\.m|\\.c" | head -1 | cut -d \\" -f2`
128
+ if File.exist?(file)
129
+ raise "#{file} 不存在 请检测代码源是否正确~"
130
+ end
131
+ UI.puts "link successfully!"
132
+ UI.puts "view linked source at path: #{dir}"
133
+ end
134
+
135
+ def get_lib_path(name)
136
+ dir = Pathname.new(File.join(Pathname.pwd,"Pods",name))
137
+ dir = get_framework_path(dir, name)
138
+ if !Dir.exist?(dir)
139
+ raise "找不到组件:#{name}, 请输入正确名称!! 者将组件名在git上提issues"
140
+ end
141
+ lib_path = File.join(dir, name)
142
+ lib_path
143
+ end
144
+
145
+ def get_framework_path(basepath, name)
146
+ dir = Pathname.new(File.join(basepath, "#{name}.framework"))
147
+ if !Dir.exist?(dir)
148
+ dir = dir.children.first
149
+ if Dir.exist?(dir)
150
+ return get_framework_path(dir, name)
151
+ end
152
+ end
153
+ return dir
154
+ end
155
+
156
+
157
+ #源码地址
158
+ # def get_gitlib_iOS_path(name)
159
+ # "git@gitlab.xxx.com:iOS/#{name}.git"
160
+ # end
161
+ #要转换的地址,Github-iOS默认都是静态库
162
+ # def git_gitlib_iOS_path
163
+ # 'git@gitlab.xxx.com:Github-iOS/'
164
+ # end
165
+
166
+
167
+ #要转换的地址,Github-iOS默认都是静态库
168
+ # def http_gitlib_GitHub_iOS_path
169
+ # 'https://gitlab.xxx.com/Github-iOS/'
170
+ # end
171
+
172
+ #要转换的地址,iOS默认都是静态库
173
+ # def http_gitlib_iOS_path
174
+ # 'https://gitlab.xxx.com/iOS/'
175
+ # end
176
+
177
+ #==========================list begin ==============
178
+
179
+ def list
180
+ Dir.entries(source_root).each do |sub|
181
+ UI.puts "- #{sub}" unless sub.include?('.')
182
+ end
183
+ UI.puts "加载完成"
184
+ end
185
+
186
+
187
+ #==========================clean begin ==============
188
+ def all_clean
189
+ FileUtils.rm_rf(source_root) if File.directory?(source_root)
190
+ UI.puts "清理完成 #{source_root}"
191
+ end
192
+
193
+ def clean
194
+ raise "请输入要删除的组件库" if @names.nil?
195
+ @names.each do |name|
196
+ full_path = File.join(source_root,name)
197
+ if File.directory?(full_path)
198
+ FileUtils.rm_rf(full_path)
199
+ else
200
+ UI.puts "找不到 #{full_path}".yellow
201
+ end
202
+ end
203
+ UI.puts "清理完成 #{@names.to_s}"
204
+ end
205
+
206
+ private
207
+
208
+ def source_root
209
+ dir = File.join(@config.cache_root,"Source")
210
+ FileUtils.mkdir_p(dir) unless File.exist? dir
211
+ UI.puts "缓存目录 #{dir}"
212
+ dir
213
+ end
214
+ end
215
+ end
216
+ end
@@ -0,0 +1 @@
1
+ require 'cocoapods-ppbuild/command/ppbuild'
@@ -0,0 +1,3 @@
1
+ module CocoapodsPpbuild
2
+ VERSION = "0.0.1"
3
+ end
@@ -0,0 +1,97 @@
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 ':ppbuild => 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
+ old_method.bind(self).(name, *args)
42
+ end
43
+ end
44
+ end
45
+ end
46
+
47
+
48
+ # a force disable option for integral
49
+ class Installer
50
+ def self.force_disable_integration(value)
51
+ @@force_disable_integration = value
52
+ end
53
+
54
+ old_method = instance_method(:integrate_user_project)
55
+ define_method(:integrate_user_project) do
56
+ if @@force_disable_integration
57
+ return
58
+ end
59
+ old_method.bind(self).()
60
+ end
61
+ end
62
+
63
+ # a option to disable install complete message
64
+ class Installer
65
+ def self.disable_install_complete_message(value)
66
+ @@disable_install_complete_message = value
67
+ end
68
+
69
+ old_method = instance_method(:print_post_install_message)
70
+ define_method(:print_post_install_message) do
71
+ if @@disable_install_complete_message
72
+ return
73
+ end
74
+ old_method.bind(self).()
75
+ end
76
+ end
77
+
78
+ # option to disable write lockfiles
79
+ class Config
80
+
81
+ @@force_disable_write_lockfile = false
82
+ def self.force_disable_write_lockfile(value)
83
+ @@force_disable_write_lockfile = value
84
+ end
85
+
86
+ old_method = instance_method(:lockfile_path)
87
+ define_method(:lockfile_path) do
88
+ if @@force_disable_write_lockfile
89
+ # As config is a singleton, sandbox_root refer to the standard sandbox.
90
+ return PrebuildSandbox.from_standard_sanbox_path(sandbox_root).root + 'Manifest.lock.tmp'
91
+ else
92
+ return old_method.bind(self).()
93
+ end
94
+ end
95
+ end
96
+
97
+ 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