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,139 @@
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 && (not Pod::Podfile::DSL.framework_source_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) && (not Pod::Podfile::DSL.framework_source_all)
19
+ requirements.pop if options.empty?
20
+ end
21
+
22
+ if Pod::Command::Install.all_use_source
23
+ should_prebuild = false
24
+ end
25
+
26
+ pod_name = Specification.root_name(name)
27
+ set_prebuild_for_pod(pod_name, should_prebuild)
28
+ end
29
+
30
+ def set_prebuild_for_pod(pod_name, should_prebuild)
31
+
32
+ if should_prebuild == true
33
+ @prebuild_framework_pod_names ||= []
34
+ @prebuild_framework_pod_names.push pod_name
35
+ elsif should_prebuild == false
36
+ @should_not_prebuild_framework_pod_names ||= []
37
+ @should_not_prebuild_framework_pod_names.push pod_name
38
+ end
39
+ end
40
+
41
+ def prebuild_framework_pod_names
42
+ names = @prebuild_framework_pod_names || []
43
+ if parent != nil and parent.kind_of? TargetDefinition
44
+ names += parent.prebuild_framework_pod_names
45
+ end
46
+ names
47
+ end
48
+ def should_not_prebuild_framework_pod_names
49
+ names = @should_not_prebuild_framework_pod_names || []
50
+ if parent != nil and parent.kind_of? TargetDefinition
51
+ names += parent.should_not_prebuild_framework_pod_names
52
+ end
53
+ names
54
+ end
55
+
56
+ # ---- patch method ----
57
+ # We want modify `store_pod` method, but it's hard to insert a line in the
58
+ # implementation. So we patch a method called in `store_pod`.
59
+ old_method = instance_method(:parse_inhibit_warnings)
60
+
61
+ define_method(:parse_inhibit_warnings) do |name, requirements|
62
+ variables = requirements
63
+ parse_prebuild_framework(name, requirements)
64
+ old_method.bind(self).(name, variables)
65
+ end
66
+
67
+ end
68
+ end
69
+ end
70
+
71
+
72
+ module Pod
73
+ class Installer
74
+
75
+ def prebuild_pod_targets
76
+ @prebuild_pod_targets ||= (
77
+ all = []
78
+
79
+ aggregate_targets = self.aggregate_targets
80
+ aggregate_targets.each do |aggregate_target|
81
+ target_definition = aggregate_target.target_definition
82
+ targets = aggregate_target.pod_targets || []
83
+ # filter prebuild
84
+ prebuild_names = target_definition.prebuild_framework_pod_names
85
+ if not Podfile::DSL.prebuild_all
86
+ targets = targets.select { |pod_target| prebuild_names.include?(pod_target.pod_name) }
87
+ end
88
+
89
+ dependency_targets = targets.map {|t| t.recursive_dependent_targets }.flatten.uniq || []
90
+
91
+ targets = (targets + dependency_targets).uniq
92
+ # filter should not prebuild
93
+ explict_should_not_names = target_definition.should_not_prebuild_framework_pod_names
94
+ Pod::UI.puts(">>>>>>> 不需要二进制的framework: #{explict_should_not_names} ") if config.verbose
95
+ targets = targets.reject { |pod_target| explict_should_not_names.include?(pod_target.pod_name) }
96
+
97
+ all += targets
98
+
99
+ end
100
+ all = all.reject {|pod_target| sandbox.local?(pod_target.pod_name) }
101
+ Pod::UI.puts(">>>>>>> 需要二进制的framework: #{all}") if config.verbose
102
+ all.uniq
103
+
104
+
105
+
106
+ )
107
+ end
108
+
109
+ # the root names who needs prebuild, including dependency pods.
110
+ def prebuild_pod_names
111
+ @prebuild_pod_names ||= self.prebuild_pod_targets.map(&:pod_name)
112
+ end
113
+
114
+
115
+ def validate_every_pod_only_have_one_form
116
+
117
+ multi_targets_pods = self.pod_targets.group_by do |t|
118
+ t.pod_name
119
+ end.select do |k, v|
120
+ v.map{|t| t.platform.name }.count > 1
121
+ end
122
+
123
+ multi_targets_pods = multi_targets_pods.reject do |name, targets|
124
+ contained = targets.map{|t| self.prebuild_pod_targets.include? t }
125
+ contained.uniq.count == 1 # all equal
126
+ end
127
+
128
+ return if multi_targets_pods.empty?
129
+
130
+ warnings = "One pod can only be prebuilt or not prebuilt. These pod have different forms in multiple targets:\n"
131
+ warnings += multi_targets_pods.map{|name, targets| " #{name}: #{targets.map{|t|t.platform.name}}"}.join("\n")
132
+ raise Informative, warnings
133
+ end
134
+
135
+ end
136
+ end
137
+
138
+
139
+
@@ -0,0 +1,82 @@
1
+ require_relative "names"
2
+
3
+ module Pod
4
+ class PrebuildSandbox < Sandbox
5
+
6
+ # [String] standard_sandbox_path
7
+ def self.from_standard_sanbox_path(path)
8
+ prebuild_sandbox_path = Pathname.new(path).realpath + "_Prebuild"
9
+ self.new(prebuild_sandbox_path)
10
+ end
11
+
12
+ def self.from_standard_sandbox(sandbox)
13
+ self.from_standard_sanbox_path(sandbox.root)
14
+ end
15
+
16
+ def standard_sanbox_path
17
+ self.root.parent
18
+ end
19
+
20
+ def generate_framework_path
21
+ self.root + "GeneratedFrameworks"
22
+ end
23
+
24
+ # @param name [String] pass the target.name (may containing platform suffix)
25
+ # @return [Pathname] the folder containing the framework file.
26
+ def framework_folder_path_for_target_name(name)
27
+ self.generate_framework_path + name
28
+ end
29
+
30
+ def plist_path_for_target_name(name)
31
+ framework_path = framework_folder_path_for_target_name(name)
32
+ "#{framework_path}/#{name}.framework/Info.plist"
33
+ end
34
+
35
+ def source_framework_path(name)
36
+ self.root + name
37
+ end
38
+
39
+
40
+ def exsited_framework_target_names
41
+ exsited_framework_name_pairs.map {|pair| pair[0]}.uniq
42
+ end
43
+ def exsited_framework_pod_names
44
+ exsited_framework_name_pairs.map {|pair| pair[1]}.uniq
45
+ end
46
+ def existed_target_names_for_pod_name(pod_name)
47
+ exsited_framework_name_pairs.select {|pair| pair[1] == pod_name }.map { |pair| pair[0]}
48
+ end
49
+
50
+
51
+
52
+ def save_pod_name_for_target(target)
53
+ folder = framework_folder_path_for_target_name(target.name)
54
+ return unless folder.exist?
55
+ flag_file_path = folder + "#{target.pod_name}.pod_name"
56
+ File.write(flag_file_path.to_s, "")
57
+ end
58
+
59
+
60
+ private
61
+
62
+ def pod_name_for_target_folder(target_folder_path)
63
+ name = Pathname.new(target_folder_path).children.find do |child|
64
+ child.to_s.end_with? ".pod_name"
65
+ end
66
+ name = name.basename(".pod_name").to_s unless name.nil?
67
+ name ||= Pathname.new(target_folder_path).basename.to_s # for compatibility with older version
68
+ end
69
+
70
+ # Array<[target_name, pod_name]>
71
+ def exsited_framework_name_pairs
72
+ return [] unless generate_framework_path.exist?
73
+ generate_framework_path.children().map do |framework_path|
74
+ if framework_path.directory? && (not framework_path.children.empty?)
75
+ [framework_path.basename.to_s, pod_name_for_target_folder(framework_path)]
76
+ else
77
+ nil
78
+ end
79
+ end.reject(&:nil?).uniq
80
+ end
81
+ end
82
+ 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-bugfix.
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
@@ -0,0 +1,222 @@
1
+ require 'fourflusher'
2
+ require 'xcpretty'
3
+
4
+ CONFIGURATION = "Debug"
5
+ PLATFORMS = { 'iphonesimulator' => 'iOS',
6
+ 'appletvsimulator' => 'tvOS',
7
+ 'watchsimulator' => 'watchOS' }
8
+
9
+ # Build specific target to framework file
10
+ # @param [PodTarget] target
11
+ # a specific pod target
12
+ #
13
+ def build_for_iosish_platform(sandbox,
14
+ build_dir,
15
+ output_path,
16
+ target,
17
+ device,
18
+ simulator,
19
+ bitcode_enabled,
20
+ custom_build_options = [], # Array<String>
21
+ custom_build_options_simulator = [] # Array<String>
22
+ )
23
+
24
+ deployment_target = target.platform.deployment_target.to_s
25
+
26
+ target_label = target.label # name with platform if it's used in multiple platforms
27
+ Pod::UI.puts "Prebuilding #{target_label}..."
28
+
29
+ other_options = []
30
+ # bitcode enabled
31
+ other_options += ['BITCODE_GENERATION_MODE=bitcode'] if bitcode_enabled
32
+ # make less arch to iphone simulator for faster build
33
+ custom_build_options_simulator += ['ARCHS=x86_64', 'ONLY_ACTIVE_ARCH=NO'] if simulator == 'iphonesimulator'
34
+
35
+ is_succeed, _ = xcodebuild(sandbox, target_label, device, deployment_target, other_options + custom_build_options)
36
+ exit 1 unless is_succeed
37
+ is_succeed, _ = xcodebuild(sandbox, target_label, simulator, deployment_target, other_options + custom_build_options_simulator)
38
+ exit 1 unless is_succeed
39
+
40
+ # paths
41
+ target_name = target.name # equals target.label, like "AFNeworking-iOS" when AFNetworking is used in multiple platforms.
42
+ module_name = target.product_module_name
43
+ device_framework_path = "#{build_dir}/#{CONFIGURATION}-#{device}/#{target_name}/#{module_name}.framework"
44
+ simulator_framework_path = "#{build_dir}/#{CONFIGURATION}-#{simulator}/#{target_name}/#{module_name}.framework"
45
+
46
+ device_binary = device_framework_path + "/#{module_name}"
47
+ simulator_binary = simulator_framework_path + "/#{module_name}"
48
+ # if device_binary exist && simulator_binary exist 继续执行下面的代码
49
+ # else return
50
+ return unless File.file?(device_binary) || File.file?(simulator_binary)
51
+
52
+ # the device_lib path is the final output file path
53
+ # combine the binaries
54
+ tmp_lipoed_binary_path = "#{build_dir}/#{target_name}"
55
+ if File.file?(device_binary) && File.file?(simulator_binary)
56
+ lipo_log = `lipo -create -output #{tmp_lipoed_binary_path} #{device_binary} #{simulator_binary}`
57
+ elsif File.file?(device_binary)
58
+ lipo_log = `lipo -create -output #{tmp_lipoed_binary_path} #{device_binary}`
59
+ else
60
+ lipo_log = `lipo -create -output #{tmp_lipoed_binary_path} #{simulator_binary}`
61
+ end
62
+ puts lipo_log unless File.exist?(tmp_lipoed_binary_path)
63
+ FileUtils.mv tmp_lipoed_binary_path, device_binary, :force => true
64
+
65
+ # collect the swiftmodule file for various archs.
66
+ device_swiftmodule_path = device_framework_path + "/Modules/#{module_name}.swiftmodule"
67
+ simulator_swiftmodule_path = simulator_framework_path + "/Modules/#{module_name}.swiftmodule"
68
+ if File.exist?(simulator_swiftmodule_path) and not File.exist?(device_swiftmodule_path)
69
+ FileUtils.cp_r simulator_swiftmodule_path + "/.", device_swiftmodule_path
70
+ end
71
+
72
+ if File.exist?(device_swiftmodule_path) and not File.exist?(simulator_swiftmodule_path)
73
+ FileUtils.cp_r device_swiftmodule_path + "/.", simulator_swiftmodule_path
74
+ end
75
+
76
+ # combine the generated swift headers
77
+ # (In xcode 10.2, the generated swift headers vary for each archs)
78
+ # https://github.com/leavez/cocoapods-binary/issues/58
79
+ simulator_generated_swift_header_path = simulator_framework_path + "/Headers/#{module_name}-Swift.h"
80
+ device_generated_swift_header_path = device_framework_path + "/Headers/#{module_name}-Swift.h"
81
+
82
+ device_header = ""
83
+ if File.exist? simulator_generated_swift_header_path
84
+ device_header = File.read(device_generated_swift_header_path)
85
+ end
86
+
87
+ simulator_header = ""
88
+ if File.exist? device_generated_swift_header_path
89
+ simulator_header = File.read(device_generated_swift_header_path)
90
+ end
91
+
92
+ # https://github.com/Carthage/Carthage/issues/2718#issuecomment-473870461
93
+ combined_header_content = %Q{
94
+ #if TARGET_OS_SIMULATOR // merged by cocoapods-binary-bugfix
95
+
96
+ #{simulator_header}
97
+
98
+ #else // merged by cocoapods-binary-bugfix
99
+
100
+ #{device_header}
101
+
102
+ #endif // merged by cocoapods-binary-bugfix
103
+ }
104
+ File.write(device_generated_swift_header_path, combined_header_content.strip)
105
+
106
+ # # handle the dSYM files
107
+ # device_dsym = "#{device_framework_path}.dSYM"
108
+ # if File.exist? device_dsym
109
+ # # lipo the simulator dsym
110
+ # simulator_dsym = "#{simulator_framework_path}.dSYM"
111
+ # if File.exist? simulator_dsym
112
+ # tmp_lipoed_binary_path = "#{output_path}/#{module_name}.draft"
113
+ # lipo_log = `lipo -create -output #{tmp_lipoed_binary_path} #{device_dsym}/Contents/Resources/DWARF/#{module_name} #{simulator_dsym}/Contents/Resources/DWARF/#{module_name}`
114
+ # puts lipo_log unless File.exist?(tmp_lipoed_binary_path)
115
+ # FileUtils.mv tmp_lipoed_binary_path, "#{device_framework_path}.dSYM/Contents/Resources/DWARF/#{module_name}", :force => true
116
+ # end
117
+ # # move
118
+ # FileUtils.mv device_dsym, output_path, :force => true
119
+ # end
120
+
121
+ # # output
122
+ output_path.mkpath unless output_path.exist?
123
+ FileUtils.mv device_framework_path, output_path, :force => true
124
+
125
+ end
126
+
127
+ def xcodebuild(sandbox, target, sdk='macosx', deployment_target=nil, other_options=[])
128
+ args = %W(-project #{sandbox.project_path.realdirpath} -scheme #{target} -configuration #{CONFIGURATION} -sdk #{sdk} )
129
+ platform = PLATFORMS[sdk]
130
+ args += Fourflusher::SimControl.new.destination(:oldest, platform, deployment_target) unless platform.nil?
131
+ args += other_options
132
+ log = `xcodebuild #{args.join(" ")} 2>&1`
133
+ exit_code = $?.exitstatus # Process::Status
134
+ is_succeed = (exit_code == 0)
135
+
136
+ if !is_succeed
137
+ Pod::UI.puts("xcodebuild failed exitstatus:#{exit_code}")
138
+ begin
139
+ if log.include?('** BUILD FAILED **')
140
+ # use xcpretty to print build log
141
+ # 64 represent command invalid. http://www.manpagez.com/man/3/sysexits/
142
+ printer = XCPretty::Printer.new({:formatter => XCPretty::Simple, :colorize => 'auto'})
143
+ log.each_line do |line|
144
+ printer.pretty_print(line)
145
+ end
146
+ else
147
+ raise "shouldn't be handle by xcpretty"
148
+ end
149
+ rescue
150
+ puts log.red
151
+ end
152
+ end
153
+ [is_succeed, log]
154
+ end
155
+
156
+
157
+
158
+ module Pod
159
+ class Prebuild
160
+
161
+ # Build the frameworks with sandbox and targets
162
+ #
163
+ # @param [String] sandbox_root_path
164
+ # The sandbox root path where the targets project place
165
+ #
166
+ # [PodTarget] target
167
+ # The pod targets to build
168
+ #
169
+ # [Pathname] output_path
170
+ # output path for generated frameworks
171
+ #
172
+ def self.build(sandbox_root_path, target, output_path, bitcode_enabled = false, custom_build_options=[], custom_build_options_simulator=[])
173
+
174
+ return if target.nil?
175
+
176
+ sandbox_root = Pathname(sandbox_root_path)
177
+ sandbox = Pod::Sandbox.new(sandbox_root)
178
+ build_dir = self.build_dir(sandbox_root)
179
+
180
+ # -- build the framework
181
+ case target.platform.name
182
+ when :ios then build_for_iosish_platform(sandbox, build_dir, output_path, target, 'iphoneos', 'iphonesimulator', bitcode_enabled, custom_build_options, custom_build_options_simulator)
183
+ when :osx then xcodebuild(sandbox, target.label, 'macosx', nil, custom_build_options)
184
+ # when :tvos then build_for_iosish_platform(sandbox, build_dir, target, 'appletvos', 'appletvsimulator')
185
+ when :watchos then build_for_iosish_platform(sandbox, build_dir, output_path, target, 'watchos', 'watchsimulator', true, custom_build_options, custom_build_options_simulator)
186
+ else raise "Unsupported platform for '#{target.name}': '#{target.platform.name}'" end
187
+
188
+ raise Pod::Informative, 'The build directory was not found in the expected location.' unless build_dir.directory?
189
+
190
+ # # --- copy the vendored libraries and framework
191
+ # frameworks = build_dir.children.select{ |path| File.extname(path) == ".framework" }
192
+ # Pod::UI.puts "Built #{frameworks.count} #{'frameworks'.pluralize(frameworks.count)}"
193
+
194
+ # pod_target = target
195
+ # consumer = pod_target.root_spec.consumer(pod_target.platform.name)
196
+ # file_accessor = Pod::Sandbox::FileAccessor.new(sandbox.pod_dir(pod_target.pod_name), consumer)
197
+ # frameworks += file_accessor.vendored_libraries
198
+ # frameworks += file_accessor.vendored_frameworks
199
+
200
+ # frameworks.uniq!
201
+
202
+ # frameworks.each do |framework|
203
+ # FileUtils.mkdir_p destination
204
+ # FileUtils.cp_r framework, destination, :remove_destination => true
205
+ # end
206
+ # build_dir.rmtree if build_dir.directory?
207
+ end
208
+
209
+ def self.remove_build_dir(sandbox_root)
210
+ path = build_dir(sandbox_root)
211
+ path.rmtree if path.exist?
212
+ end
213
+
214
+ private
215
+
216
+ def self.build_dir(sandbox_root)
217
+ # don't know why xcode chose this folder
218
+ sandbox_root.parent + 'build'
219
+ end
220
+
221
+ end
222
+ end
@@ -0,0 +1,12 @@
1
+ # attr_accessor for class variable.
2
+ # usage:
3
+ #
4
+ # ```
5
+ # class Pod
6
+ # class_attr_accessor :is_prebuild_stage
7
+ # end
8
+ # ```
9
+ #
10
+ def class_attr_accessor(symbol)
11
+ self.class.send(:attr_accessor, symbol)
12
+ end
@@ -0,0 +1,2 @@
1
+ require 'cocoapods-binary-bugfix/Main'
2
+
@@ -0,0 +1,50 @@
1
+ require 'pathname'
2
+ ROOT = Pathname.new(File.expand_path('../../', __FILE__))
3
+ $:.unshift((ROOT + 'lib').to_s)
4
+ $:.unshift((ROOT + 'spec').to_s)
5
+
6
+ require 'bundler/setup'
7
+ require 'bacon'
8
+ require 'mocha-on-bacon'
9
+ require 'pretty_bacon'
10
+ require 'pathname'
11
+ require 'cocoapods'
12
+
13
+ Mocha::Configuration.prevent(:stubbing_non_existent_method)
14
+
15
+ require 'cocoapods_plugin'
16
+
17
+ #-----------------------------------------------------------------------------#
18
+
19
+ module Pod
20
+
21
+ # Disable the wrapping so the output is deterministic in the tests.
22
+ #
23
+ UI.disable_wrap = true
24
+
25
+ # Redirects the messages to an internal store.
26
+ #
27
+ module UI
28
+ @output = ''
29
+ @warnings = ''
30
+
31
+ class << self
32
+ attr_accessor :output
33
+ attr_accessor :warnings
34
+
35
+ def puts(message = '')
36
+ @output << "#{message}\n"
37
+ end
38
+
39
+ def warn(message = '', actions = [])
40
+ @warnings << "#{message}\n"
41
+ end
42
+
43
+ def print(message)
44
+ @output << message
45
+ end
46
+ end
47
+ end
48
+ end
49
+
50
+ #-----------------------------------------------------------------------------#