pod-builder-y 2.3.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +19 -0
- data/Gemfile +8 -0
- data/LICENSE.txt +13 -0
- data/README.md +385 -0
- data/Rakefile +2 -0
- data/bin/console +16 -0
- data/bin/setup +8 -0
- data/exe/pod_builder_y +406 -0
- data/lib/core_ext/string.rb +5 -0
- data/lib/pod_builder/analyze.rb +59 -0
- data/lib/pod_builder/analyzer.rb +16 -0
- data/lib/pod_builder/command.rb +14 -0
- data/lib/pod_builder/command/build.rb +228 -0
- data/lib/pod_builder/command/build_all.rb +15 -0
- data/lib/pod_builder/command/clean.rb +75 -0
- data/lib/pod_builder/command/deintegrate.rb +101 -0
- data/lib/pod_builder/command/generate_lldbinit.rb +128 -0
- data/lib/pod_builder/command/generate_podspec.rb +22 -0
- data/lib/pod_builder/command/info.rb +18 -0
- data/lib/pod_builder/command/init.rb +148 -0
- data/lib/pod_builder/command/install_sources.rb +79 -0
- data/lib/pod_builder/command/none.rb +16 -0
- data/lib/pod_builder/command/restore_all.rb +33 -0
- data/lib/pod_builder/command/switch.rb +224 -0
- data/lib/pod_builder/command/sync_podfile.rb +34 -0
- data/lib/pod_builder/command/update.rb +43 -0
- data/lib/pod_builder/configuration.rb +300 -0
- data/lib/pod_builder/core.rb +222 -0
- data/lib/pod_builder/info.rb +90 -0
- data/lib/pod_builder/install.rb +505 -0
- data/lib/pod_builder/licenses.rb +73 -0
- data/lib/pod_builder/podfile.rb +700 -0
- data/lib/pod_builder/podfile/pre_actions_swizzles.rb +84 -0
- data/lib/pod_builder/podfile_cp.rb +99 -0
- data/lib/pod_builder/podfile_item.rb +530 -0
- data/lib/pod_builder/podspec.rb +269 -0
- data/lib/pod_builder/rome/post_install.rb +446 -0
- data/lib/pod_builder/rome/pre_install.rb +6 -0
- data/lib/pod_builder/templates/build_podfile.template +70 -0
- data/lib/pod_builder/templates/build_podspec.template +19 -0
- data/lib/pod_builder/version.rb +4 -0
- data/pod-builder.gemspec +37 -0
- metadata +240 -0
@@ -0,0 +1,222 @@
|
|
1
|
+
require 'cocoapods'
|
2
|
+
require 'fileutils'
|
3
|
+
require 'colored'
|
4
|
+
|
5
|
+
require 'pod_builder/podfile'
|
6
|
+
require 'pod_builder/podfile_cp'
|
7
|
+
require 'pod_builder/podfile_item'
|
8
|
+
require 'pod_builder/analyze'
|
9
|
+
require 'pod_builder/analyzer'
|
10
|
+
require 'pod_builder/install'
|
11
|
+
require 'pod_builder/info'
|
12
|
+
require 'pod_builder/configuration'
|
13
|
+
require 'pod_builder/podspec'
|
14
|
+
require 'pod_builder/licenses'
|
15
|
+
|
16
|
+
require 'core_ext/string'
|
17
|
+
|
18
|
+
module PodBuilder
|
19
|
+
@@xcodeproj_path = nil
|
20
|
+
@@xcodeworkspace_path = nil
|
21
|
+
|
22
|
+
def self.git_rootpath
|
23
|
+
return `git rev-parse --show-toplevel`.strip()
|
24
|
+
end
|
25
|
+
|
26
|
+
def self.safe_rm_rf(path)
|
27
|
+
unless File.exist?(path)
|
28
|
+
return
|
29
|
+
end
|
30
|
+
|
31
|
+
unless File.directory?(path)
|
32
|
+
FileUtils.rm(path)
|
33
|
+
|
34
|
+
return
|
35
|
+
end
|
36
|
+
|
37
|
+
current_dir = Dir.pwd
|
38
|
+
|
39
|
+
Dir.chdir(path)
|
40
|
+
|
41
|
+
rootpath = git_rootpath()
|
42
|
+
raise "\n\nNo git repository found in '#{path}', can't delete files!\n".red if rootpath.empty? && !path.start_with?(Configuration.build_base_path)
|
43
|
+
|
44
|
+
FileUtils.rm_rf(path)
|
45
|
+
|
46
|
+
if File.exist?(current_dir)
|
47
|
+
Dir.chdir(current_dir)
|
48
|
+
else
|
49
|
+
Dir.chdir(basepath)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
def self.gitignoredfiles
|
54
|
+
Dir.chdir(git_rootpath) do
|
55
|
+
return `git status --ignored -s | grep "^\!\!" | cut -c4-`.strip().split("\n")
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
def self.basepath(child = "")
|
60
|
+
if child.nil?
|
61
|
+
return nil
|
62
|
+
end
|
63
|
+
|
64
|
+
return "#{Configuration.base_path}/#{child}".gsub("//", "/").gsub(/\/$/, '')
|
65
|
+
end
|
66
|
+
|
67
|
+
def self.prebuiltpath(child = "")
|
68
|
+
if child.nil?
|
69
|
+
return nil
|
70
|
+
end
|
71
|
+
|
72
|
+
path = basepath("Prebuilt")
|
73
|
+
if child.length > 0
|
74
|
+
path += "/#{child}"
|
75
|
+
end
|
76
|
+
|
77
|
+
return path
|
78
|
+
end
|
79
|
+
|
80
|
+
def self.buildpath_prebuiltpath(child = "")
|
81
|
+
if child.nil?
|
82
|
+
return nil
|
83
|
+
end
|
84
|
+
|
85
|
+
path = "#{Configuration.build_path}/Prebuilt"
|
86
|
+
if child.length > 0
|
87
|
+
path += "/#{child}"
|
88
|
+
end
|
89
|
+
|
90
|
+
return path
|
91
|
+
end
|
92
|
+
|
93
|
+
def self.buildpath_dsympath(child = "")
|
94
|
+
if child.nil?
|
95
|
+
return nil
|
96
|
+
end
|
97
|
+
|
98
|
+
path = "#{Configuration.build_path}/dSYM"
|
99
|
+
if child.length > 0
|
100
|
+
path += "/#{child}"
|
101
|
+
end
|
102
|
+
|
103
|
+
return path
|
104
|
+
end
|
105
|
+
|
106
|
+
def self.dsympath(child = "")
|
107
|
+
if child.nil?
|
108
|
+
return nil
|
109
|
+
end
|
110
|
+
|
111
|
+
path = basepath("dSYM")
|
112
|
+
if child.length > 0
|
113
|
+
path += "/#{child}"
|
114
|
+
end
|
115
|
+
|
116
|
+
return path
|
117
|
+
end
|
118
|
+
|
119
|
+
def self.project_path(child = "")
|
120
|
+
project = PodBuilder::find_xcodeworkspace
|
121
|
+
|
122
|
+
return project ? "#{File.dirname(project)}/#{child}".gsub("//", "/").gsub(/\/$/, '') : nil
|
123
|
+
end
|
124
|
+
|
125
|
+
def self.find_xcodeproj
|
126
|
+
unless @@xcodeproj_path.nil?
|
127
|
+
return @@xcodeproj_path
|
128
|
+
end
|
129
|
+
project_name = File.basename(find_xcodeworkspace, ".*")
|
130
|
+
|
131
|
+
xcodeprojects = Dir.glob("#{home}/**/#{project_name}.xcodeproj").select { |x|
|
132
|
+
folder_in_home = x.gsub(home, "")
|
133
|
+
!folder_in_home.include?("/Pods/") && !x.include?(PodBuilder::basepath("Sources")) && !x.include?(PodBuilder::basepath + "/")
|
134
|
+
}
|
135
|
+
raise "\n\nxcodeproj not found!".red if xcodeprojects.count == 0
|
136
|
+
raise "\n\nFound multiple xcodeproj:\n#{xcodeprojects.join("\n")}".red if xcodeprojects.count > 1
|
137
|
+
|
138
|
+
@@xcodeproj_path = xcodeprojects.first
|
139
|
+
return @@xcodeproj_path
|
140
|
+
end
|
141
|
+
|
142
|
+
def self.find_xcodeworkspace
|
143
|
+
unless @@xcodeworkspace_path.nil?
|
144
|
+
return @@xcodeworkspace_path
|
145
|
+
end
|
146
|
+
|
147
|
+
xcworkspaces = Dir.glob("#{home}/**/#{Configuration.project_name}*.xcworkspace").select { |x|
|
148
|
+
folder_in_home = x.gsub(home, "")
|
149
|
+
!folder_in_home.include?("/Pods/") && !x.include?(PodBuilder::basepath("Sources")) && !x.include?(PodBuilder::basepath + "/") && !x.include?(".xcodeproj/")
|
150
|
+
}
|
151
|
+
raise "\n\nxcworkspace not found!".red if xcworkspaces.count == 0
|
152
|
+
raise "\n\nFound multiple xcworkspaces:\n#{xcworkspaces.join("\n")}".red if xcworkspaces.count > 1
|
153
|
+
|
154
|
+
@@xcodeworkspace_path = xcworkspaces.first
|
155
|
+
return @@xcodeworkspace_path
|
156
|
+
end
|
157
|
+
|
158
|
+
def self.prepare_basepath
|
159
|
+
workspace_path = PodBuilder::find_xcodeworkspace
|
160
|
+
project_path = PodBuilder::find_xcodeproj
|
161
|
+
if workspace_path && project_path
|
162
|
+
FileUtils.mkdir_p(basepath("Pods/Target Support Files"))
|
163
|
+
FileUtils.cp_r(workspace_path, basepath)
|
164
|
+
FileUtils.cp_r(project_path, basepath)
|
165
|
+
FileUtils.rm_f(basepath("Podfile.lock"))
|
166
|
+
end
|
167
|
+
end
|
168
|
+
|
169
|
+
def self.clean_basepath
|
170
|
+
if path = PodBuilder::find_xcodeproj
|
171
|
+
PodBuilder::safe_rm_rf(basepath(File.basename(path)))
|
172
|
+
end
|
173
|
+
if path = PodBuilder::find_xcodeworkspace
|
174
|
+
PodBuilder::safe_rm_rf(basepath(File.basename(path)))
|
175
|
+
end
|
176
|
+
|
177
|
+
PodBuilder::safe_rm_rf(basepath("Pods"))
|
178
|
+
end
|
179
|
+
|
180
|
+
def self.system_swift_version
|
181
|
+
swift_version = `swiftc --version | grep -o 'swiftlang-.*\s'`.strip()
|
182
|
+
raise "\n\nUnsupported swift compiler version, expecting `swiftlang` keyword in `swiftc --version`".red if swift_version.length == 0
|
183
|
+
return swift_version
|
184
|
+
end
|
185
|
+
|
186
|
+
def self.add_lockfile
|
187
|
+
lockfile_path = Configuration.lockfile_path
|
188
|
+
|
189
|
+
if File.exist?(lockfile_path)
|
190
|
+
if pid = File.read(lockfile_path)
|
191
|
+
begin
|
192
|
+
if Process.getpgid(pid)
|
193
|
+
if Configuration.deterministic_build
|
194
|
+
raise "\n\nAnother PodBuilder pending task is running\n".red
|
195
|
+
else
|
196
|
+
raise "\n\nAnother PodBuilder pending task is running on this project\n".red
|
197
|
+
end
|
198
|
+
end
|
199
|
+
rescue
|
200
|
+
end
|
201
|
+
end
|
202
|
+
end
|
203
|
+
|
204
|
+
File.write(lockfile_path, Process.pid, mode: "w")
|
205
|
+
end
|
206
|
+
|
207
|
+
def self.remove_lockfile
|
208
|
+
lockfile_path = Configuration.lockfile_path
|
209
|
+
|
210
|
+
if File.exist?(lockfile_path)
|
211
|
+
FileUtils.rm(lockfile_path)
|
212
|
+
end
|
213
|
+
end
|
214
|
+
|
215
|
+
private
|
216
|
+
|
217
|
+
def self.home
|
218
|
+
rootpath = git_rootpath
|
219
|
+
raise "\n\nNo git repository found in current folder `#{Dir.pwd}`!\n".red if rootpath.empty?
|
220
|
+
return rootpath
|
221
|
+
end
|
222
|
+
end
|
@@ -0,0 +1,90 @@
|
|
1
|
+
require 'json'
|
2
|
+
|
3
|
+
module PodBuilder
|
4
|
+
class Info
|
5
|
+
def self.generate_info
|
6
|
+
swift_version = PodBuilder::system_swift_version
|
7
|
+
result = {}
|
8
|
+
name = nil
|
9
|
+
|
10
|
+
Dir.glob(PodBuilder::prebuiltpath("**/#{Configuration.prebuilt_info_filename}")).each do |json_path|
|
11
|
+
name, prebuilt_info = prebuilt_info(json_path)
|
12
|
+
result[name] = prebuilt_info
|
13
|
+
end
|
14
|
+
|
15
|
+
return result
|
16
|
+
end
|
17
|
+
|
18
|
+
private
|
19
|
+
|
20
|
+
def self.pod_name_from_entry(line)
|
21
|
+
if (matches = line&.match(/pod '(.*?)'/)) && matches.size == 2
|
22
|
+
pod_name = matches[1]
|
23
|
+
|
24
|
+
return pod_name
|
25
|
+
end
|
26
|
+
|
27
|
+
return "unknown_podname"
|
28
|
+
end
|
29
|
+
|
30
|
+
def self.version_info_from_entry(line)
|
31
|
+
if (matches = line&.match(/pod '(.*)', '=(.*)'/)) && matches.size == 3
|
32
|
+
pod_name = matches[1]
|
33
|
+
tag = matches[2]
|
34
|
+
|
35
|
+
return { "tag": tag }
|
36
|
+
elsif (matches = line&.match(/pod '(.*)', :git => '(.*)', :commit => '(.*)'/)) && matches.size == 4
|
37
|
+
pod_name = matches[1]
|
38
|
+
repo = matches[2]
|
39
|
+
hash = matches[3]
|
40
|
+
|
41
|
+
return { "repo": repo, "hash": hash }
|
42
|
+
elsif (matches = line&.match(/pod '(.*)', :git => '(.*)', :branch => '(.*)'/)) && matches.size == 4
|
43
|
+
pod_name = matches[1]
|
44
|
+
repo = matches[2]
|
45
|
+
branch = matches[3]
|
46
|
+
|
47
|
+
return { "repo": repo, "branch": branch }
|
48
|
+
elsif (matches = line&.match(/pod '(.*)', :git => '(.*)', :tag => '(.*)'/)) && matches.size == 4
|
49
|
+
pod_name = matches[1]
|
50
|
+
repo = matches[2]
|
51
|
+
tag = matches[3]
|
52
|
+
|
53
|
+
return { "repo": repo, "tag": tag }
|
54
|
+
elsif (matches = line&.match(/pod '(.*)', :path => '(.*)'/)) && matches.size == 3
|
55
|
+
pod_name = matches[1]
|
56
|
+
|
57
|
+
return { "repo": "local" }
|
58
|
+
elsif (matches = line&.match(/pod '(.*)', :podspec => '(.*)'/)) && matches.size == 3
|
59
|
+
pod_name = matches[1]
|
60
|
+
|
61
|
+
return { "repo": "local" }
|
62
|
+
else
|
63
|
+
raise "\n\nFailed extracting version from line:\n#{line}\n\n".red
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
def self.prebuilt_info(path)
|
68
|
+
unless File.exist?(path)
|
69
|
+
return {}
|
70
|
+
end
|
71
|
+
|
72
|
+
data = JSON.load(File.read(path))
|
73
|
+
|
74
|
+
result = {}
|
75
|
+
if swift_version = data["swift_version"]
|
76
|
+
result.merge!({ "swift_version": swift_version})
|
77
|
+
end
|
78
|
+
|
79
|
+
pod_version = version_info_from_entry(data["entry"])
|
80
|
+
pod_name = pod_name_from_entry(data["entry"])
|
81
|
+
|
82
|
+
result.merge!({ "version": pod_version })
|
83
|
+
result.merge!({ "specs": (data["specs"] || []) })
|
84
|
+
result.merge!({ "is_static": (data["is_static"] || false) })
|
85
|
+
result.merge!({ "original_compile_path": (data["original_compile_path"] || "") })
|
86
|
+
|
87
|
+
return pod_name, result
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
@@ -0,0 +1,505 @@
|
|
1
|
+
require 'digest'
|
2
|
+
require 'colored'
|
3
|
+
require 'highline/import'
|
4
|
+
|
5
|
+
# The following begin/end clause contains a set of monkey patches of the original CP implementation
|
6
|
+
|
7
|
+
# The Pod::Target and Pod::Installer::Xcode::PodTargetDependencyInstaller swizzles patch
|
8
|
+
# the following issues:
|
9
|
+
# - https://github.com/CocoaPods/Rome/issues/81
|
10
|
+
# - https://github.com/leavez/cocoapods-binary/issues/50
|
11
|
+
begin
|
12
|
+
require 'cocoapods/installer/xcode/pods_project_generator/pod_target_dependency_installer.rb'
|
13
|
+
|
14
|
+
class Pod::Specification
|
15
|
+
Pod::Specification.singleton_class.send(:alias_method, :swz_from_hash, :from_hash)
|
16
|
+
Pod::Specification.singleton_class.send(:alias_method, :swz_from_string, :from_string)
|
17
|
+
|
18
|
+
def self.from_string(*args)
|
19
|
+
spec = swz_from_string(*args)
|
20
|
+
|
21
|
+
if overrides = PodBuilder::Configuration.spec_overrides[spec.name]
|
22
|
+
overrides.each do |k, v|
|
23
|
+
if spec.attributes_hash[k].is_a?(Hash)
|
24
|
+
current = spec.attributes_hash[k]
|
25
|
+
spec.attributes_hash[k] = current.merge(v)
|
26
|
+
else
|
27
|
+
spec.attributes_hash[k] = v
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
spec
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
class Pod::Target
|
37
|
+
attr_accessor :mock_dynamic_framework
|
38
|
+
|
39
|
+
alias_method :swz_build_type, :build_type
|
40
|
+
|
41
|
+
def build_type
|
42
|
+
if mock_dynamic_framework == true
|
43
|
+
if defined?(Pod::BuildType) # CocoaPods 1.9 and later
|
44
|
+
Pod::BuildType.new(linkage: :dynamic, packaging: :framework)
|
45
|
+
elsif defined?(Pod::Target::BuildType) # CocoaPods 1.7, 1.8
|
46
|
+
Pod::Target::BuildType.new(linkage: :dynamic, packaging: :framework)
|
47
|
+
else
|
48
|
+
raise "\n\nBuildType not found. Open an issue reporting your CocoaPods version".red
|
49
|
+
end
|
50
|
+
else
|
51
|
+
swz_build_type()
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
class Pod::PodTarget
|
57
|
+
@@modules_override = Hash.new
|
58
|
+
|
59
|
+
def self.modules_override= (x)
|
60
|
+
@@modules_override = x
|
61
|
+
end
|
62
|
+
|
63
|
+
def self.modules_override
|
64
|
+
return @@modules_override
|
65
|
+
end
|
66
|
+
|
67
|
+
alias_method :swz_defines_module?, :defines_module?
|
68
|
+
|
69
|
+
def defines_module?
|
70
|
+
return @@modules_override.has_key?(name) ? @@modules_override[name] : swz_defines_module?
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
# Starting from CocoaPods 1.10.0 and later resources are no longer copied inside the .framework
|
75
|
+
# when building static frameworks. While this is correct when using CP normally, for redistributable
|
76
|
+
# frameworks we require resources to be shipped along the binary
|
77
|
+
class Pod::Installer::Xcode::PodsProjectGenerator::PodTargetInstaller
|
78
|
+
alias_method :swz_add_files_to_build_phases, :add_files_to_build_phases
|
79
|
+
|
80
|
+
def add_files_to_build_phases(native_target, test_native_targets, app_native_targets)
|
81
|
+
target.mock_dynamic_framework = target.build_as_static_framework?
|
82
|
+
swz_add_files_to_build_phases(native_target, test_native_targets, app_native_targets)
|
83
|
+
target.mock_dynamic_framework = false
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
class Pod::Installer::Xcode::PodTargetDependencyInstaller
|
88
|
+
alias_method :swz_wire_resource_bundle_targets, :wire_resource_bundle_targets
|
89
|
+
|
90
|
+
def wire_resource_bundle_targets(resource_bundle_targets, native_target, pod_target)
|
91
|
+
pod_target.mock_dynamic_framework = pod_target.build_as_static_framework?
|
92
|
+
res = swz_wire_resource_bundle_targets(resource_bundle_targets, native_target, pod_target)
|
93
|
+
pod_target.mock_dynamic_framework = false
|
94
|
+
return res
|
95
|
+
end
|
96
|
+
end
|
97
|
+
rescue LoadError
|
98
|
+
# CocoaPods 1.6.2 or earlier
|
99
|
+
end
|
100
|
+
|
101
|
+
module PodBuilder
|
102
|
+
class InstallResult
|
103
|
+
# @return [Array<Hash>] The installed licenses
|
104
|
+
#
|
105
|
+
attr_reader :licenses
|
106
|
+
|
107
|
+
# @return [Hash] A hash containing the expected prebuilt_info filename and content
|
108
|
+
#
|
109
|
+
attr_reader :prebuilt_info
|
110
|
+
|
111
|
+
def initialize(licenses = [], prebuilt_info = Hash.new)
|
112
|
+
@licenses = licenses
|
113
|
+
@prebuilt_info = prebuilt_info
|
114
|
+
end
|
115
|
+
|
116
|
+
def +(obj)
|
117
|
+
merged_licenses = @licenses.dup + obj.licenses
|
118
|
+
merged_prebuilt_info = @prebuilt_info.dup
|
119
|
+
|
120
|
+
merged_prebuilt_info.each do |key, value|
|
121
|
+
if obj.prebuilt_info.has_key?(key)
|
122
|
+
specs = merged_prebuilt_info[key]["specs"] || []
|
123
|
+
specs += (obj.prebuilt_info[key]["specs"] || [])
|
124
|
+
merged_prebuilt_info[key]["specs"] = specs.uniq
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
merged_prebuilt_info = obj.prebuilt_info.merge(merged_prebuilt_info)
|
129
|
+
|
130
|
+
return InstallResult.new(merged_licenses, merged_prebuilt_info)
|
131
|
+
end
|
132
|
+
|
133
|
+
def write_prebuilt_info_files
|
134
|
+
prebuilt_info.each do |file_path, file_content|
|
135
|
+
File.write(file_path, JSON.pretty_generate(file_content))
|
136
|
+
end
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
140
|
+
class Install
|
141
|
+
# This method will generate prebuilt data by building from "/tmp/pod_builder/Podfile"
|
142
|
+
def self.podfile(podfile_content, podfile_items, build_configuration)
|
143
|
+
puts "Preparing build Podfile".yellow
|
144
|
+
|
145
|
+
PodBuilder::safe_rm_rf(Configuration.build_path)
|
146
|
+
FileUtils.mkdir_p(Configuration.build_path)
|
147
|
+
|
148
|
+
init_git(Configuration.build_path) # this is needed to be able to call safe_rm_rf
|
149
|
+
|
150
|
+
podfile_content = copy_development_pods_source_code(podfile_content, podfile_items)
|
151
|
+
|
152
|
+
podfile_content = Podfile.update_path_entries(podfile_content, Install.method(:podfile_path_transform))
|
153
|
+
podfile_content = Podfile.update_project_entries(podfile_content, Install.method(:podfile_path_transform))
|
154
|
+
podfile_content = Podfile.update_require_entries(podfile_content, Install.method(:podfile_path_transform))
|
155
|
+
|
156
|
+
podfile_path = File.join(Configuration.build_path, "Podfile")
|
157
|
+
File.write(podfile_path, podfile_content)
|
158
|
+
|
159
|
+
begin
|
160
|
+
lock_file = "#{Configuration.build_path}/pod_builder.lock"
|
161
|
+
FileUtils.touch(lock_file)
|
162
|
+
|
163
|
+
prebuilt_entries = use_prebuilt_entries_for_unchanged_pods(podfile_path, podfile_items)
|
164
|
+
|
165
|
+
install
|
166
|
+
|
167
|
+
copy_prebuilt_items(podfile_items - prebuilt_entries)
|
168
|
+
|
169
|
+
prebuilt_info = prebuilt_info(podfile_items)
|
170
|
+
licenses = license_specifiers()
|
171
|
+
|
172
|
+
if !OPTIONS.has_key?(:debug)
|
173
|
+
PodBuilder::safe_rm_rf(Configuration.build_path)
|
174
|
+
end
|
175
|
+
|
176
|
+
return InstallResult.new(licenses, prebuilt_info)
|
177
|
+
rescue Exception => e
|
178
|
+
if File.directory?("#{Configuration.build_path}/Pods/Pods.xcodeproj")
|
179
|
+
activate_pod_scheme()
|
180
|
+
|
181
|
+
if ENV["DEBUGGING"]
|
182
|
+
system("xed #{Configuration.build_path}/Pods")
|
183
|
+
elsif !OPTIONS.has_key?(:no_stdin_available)
|
184
|
+
confirm = ask("\n\nOh no! Something went wrong during prebuild phase! Do you want to open the prebuild project to debug the error, you will need to add and run the Pods-Dummy scheme? [Y/N] ".red) { |yn| yn.limit = 1, yn.validate = /[yn]/i }
|
185
|
+
|
186
|
+
if confirm.downcase == 'y'
|
187
|
+
system("xed #{Configuration.build_path}/Pods")
|
188
|
+
end
|
189
|
+
end
|
190
|
+
end
|
191
|
+
|
192
|
+
raise e
|
193
|
+
ensure
|
194
|
+
FileUtils.rm(lock_file) if File.exist?(lock_file)
|
195
|
+
end
|
196
|
+
end
|
197
|
+
|
198
|
+
def self.prebuilt_info(podfile_items)
|
199
|
+
gitignored_files = PodBuilder::gitignoredfiles
|
200
|
+
|
201
|
+
swift_version = PodBuilder::system_swift_version
|
202
|
+
|
203
|
+
write_prebuilt_info_filename_gitattributes
|
204
|
+
|
205
|
+
ret = Hash.new
|
206
|
+
root_names = podfile_items.reject(&:is_prebuilt).map(&:root_name).uniq
|
207
|
+
root_names.each do |prebuilt_name|
|
208
|
+
path = PodBuilder::prebuiltpath(prebuilt_name)
|
209
|
+
|
210
|
+
unless File.directory?(path)
|
211
|
+
puts "Prebuilt items for #{prebuilt_name} not found".blue
|
212
|
+
next
|
213
|
+
end
|
214
|
+
|
215
|
+
unless podfile_item = podfile_items.detect { |t| t.name == prebuilt_name } || podfile_items.detect { |t| t.root_name == prebuilt_name }
|
216
|
+
puts "Prebuilt items for #{prebuilt_name} not found #2".blue
|
217
|
+
next
|
218
|
+
end
|
219
|
+
|
220
|
+
podbuilder_file = File.join(path, Configuration.prebuilt_info_filename)
|
221
|
+
entry = podfile_item.entry(true, false)
|
222
|
+
|
223
|
+
data = {}
|
224
|
+
data["entry"] = entry
|
225
|
+
data["is_prebuilt"] = podfile_item.is_prebuilt
|
226
|
+
if Dir.glob(File.join(path, "#{podfile_item.prebuilt_rel_path}/Headers/*-Swift.h")).count > 0
|
227
|
+
data["swift_version"] = swift_version
|
228
|
+
end
|
229
|
+
|
230
|
+
specs = podfile_items.select { |x| x.module_name == podfile_item.module_name }
|
231
|
+
subspecs_deps = specs.map(&:dependency_names).flatten
|
232
|
+
subspec_self_deps = subspecs_deps.select { |x| x.start_with?("#{prebuilt_name}/") }
|
233
|
+
data["specs"] = (specs.map(&:name) + subspec_self_deps).uniq
|
234
|
+
data["is_static"] = podfile_item.is_static
|
235
|
+
data["original_compile_path"] = Pathname.new(Configuration.build_path).realpath.to_s
|
236
|
+
if hash = build_folder_hash(podfile_item, gitignored_files)
|
237
|
+
data["build_folder_hash"] = hash
|
238
|
+
end
|
239
|
+
|
240
|
+
ret.merge!({ podbuilder_file => data })
|
241
|
+
end
|
242
|
+
|
243
|
+
return ret
|
244
|
+
end
|
245
|
+
private
|
246
|
+
|
247
|
+
def self.license_specifiers
|
248
|
+
acknowledge_file = "#{Configuration.build_path}/Pods/Target Support Files/Pods-DummyTarget/Pods-DummyTarget-acknowledgements.plist"
|
249
|
+
unless File.exist?(acknowledge_file)
|
250
|
+
raise "\n\nLicense file not found".red
|
251
|
+
end
|
252
|
+
|
253
|
+
plist = CFPropertyList::List.new(:file => acknowledge_file)
|
254
|
+
data = CFPropertyList.native_types(plist.value)
|
255
|
+
|
256
|
+
return data["PreferenceSpecifiers"] || []
|
257
|
+
end
|
258
|
+
|
259
|
+
def self.copy_development_pods_source_code(podfile_content, podfile_items)
|
260
|
+
# Development pods are normally built/integrated without moving files from their original paths.
|
261
|
+
# It is important that CocoaPods compiles the files under Configuration.build_path in order that
|
262
|
+
# DWARF debug info reference to this constant path. Doing otherwise breaks the assumptions that
|
263
|
+
# makes the `generate_lldbinit` command work.
|
264
|
+
development_pods = podfile_items.select { |x| x.is_development_pod }
|
265
|
+
development_pods.each do |podfile_item|
|
266
|
+
destination_path = "#{Configuration.build_path}/Pods/#{podfile_item.name}"
|
267
|
+
FileUtils.mkdir_p(destination_path)
|
268
|
+
|
269
|
+
if Pathname.new(podfile_item.path).absolute?
|
270
|
+
FileUtils.cp_r("#{podfile_item.path}/.", destination_path)
|
271
|
+
else
|
272
|
+
FileUtils.cp_r("#{PodBuilder::basepath(podfile_item.path)}/.", destination_path)
|
273
|
+
end
|
274
|
+
|
275
|
+
unless Configuration.build_using_repo_paths
|
276
|
+
podfile_content.gsub!("'#{podfile_item.path}'", "'#{destination_path}'")
|
277
|
+
end
|
278
|
+
end
|
279
|
+
|
280
|
+
return podfile_content
|
281
|
+
end
|
282
|
+
|
283
|
+
def self.use_prebuilt_entries_for_unchanged_pods(podfile_path, podfile_items)
|
284
|
+
podfile_content = File.read(podfile_path)
|
285
|
+
|
286
|
+
replaced_items = []
|
287
|
+
|
288
|
+
if OPTIONS.has_key?(:force_rebuild)
|
289
|
+
podfile_content.gsub!("%%%prebuilt_root_paths%%%", "{}")
|
290
|
+
else
|
291
|
+
download # Copy files under #{Configuration.build_path}/Pods so that we can determine build folder hashes
|
292
|
+
|
293
|
+
gitignored_files = PodBuilder::gitignoredfiles
|
294
|
+
|
295
|
+
prebuilt_root_paths = Hash.new
|
296
|
+
|
297
|
+
# Replace prebuilt entries in Podfile for Pods that have no changes in source code which will avoid rebuilding them
|
298
|
+
items = podfile_items.group_by { |t| t.root_name }.map { |k, v| v.first } # Return one podfile_item per root_name
|
299
|
+
items.each do |item|
|
300
|
+
podspec_path = item.prebuilt_podspec_path
|
301
|
+
if last_build_folder_hash = build_folder_hash_in_prebuilt_info_file(item)
|
302
|
+
if last_build_folder_hash == build_folder_hash(item, gitignored_files)
|
303
|
+
puts "No changes detected to '#{item.root_name}', will skip rebuild".blue
|
304
|
+
|
305
|
+
replaced_items.push(item)
|
306
|
+
|
307
|
+
podfile_items.select { |t| t.root_name == item.root_name }.each do |replace_item|
|
308
|
+
replace_regex = "pod '#{Regexp.quote(replace_item.name)}', .*"
|
309
|
+
replace_line_found = podfile_content =~ /#{replace_regex}/i
|
310
|
+
raise "\n\nFailed finding pod entry for '#{replace_item.name}'".red unless replace_line_found
|
311
|
+
podfile_content.gsub!(/#{replace_regex}/, replace_item.prebuilt_entry(true, true))
|
312
|
+
|
313
|
+
prebuilt_root_paths[replace_item.root_name] = PodBuilder::prebuiltpath
|
314
|
+
end
|
315
|
+
end
|
316
|
+
end
|
317
|
+
end
|
318
|
+
|
319
|
+
podfile_content.gsub!("%%%prebuilt_root_paths%%%", prebuilt_root_paths.to_s)
|
320
|
+
end
|
321
|
+
|
322
|
+
File.write(podfile_path, podfile_content)
|
323
|
+
|
324
|
+
return replaced_items
|
325
|
+
end
|
326
|
+
|
327
|
+
def self.install
|
328
|
+
puts "Preparing build".yellow
|
329
|
+
|
330
|
+
CLAide::Command::PluginManager.load_plugins("cocoapods")
|
331
|
+
|
332
|
+
Dir.chdir(Configuration.build_path) do
|
333
|
+
config = Pod::Config.new()
|
334
|
+
installer = Pod::Installer.new(config.sandbox, config.podfile, config.lockfile)
|
335
|
+
installer.repo_update = false
|
336
|
+
installer.update = false
|
337
|
+
|
338
|
+
install_start_time = Time.now
|
339
|
+
|
340
|
+
installer.install!
|
341
|
+
install_time = Time.now - install_start_time
|
342
|
+
|
343
|
+
puts "Build completed in #{install_time.to_i} seconds".blue
|
344
|
+
end
|
345
|
+
end
|
346
|
+
|
347
|
+
def self.download
|
348
|
+
puts "Downloading Pods source code".yellow
|
349
|
+
|
350
|
+
CLAide::Command::PluginManager.load_plugins("cocoapods")
|
351
|
+
|
352
|
+
Dir.chdir(Configuration.build_path) do
|
353
|
+
Pod::UserInterface::config.silent = true
|
354
|
+
|
355
|
+
config = Pod::Config.new()
|
356
|
+
installer = Pod::Installer.new(config.sandbox, config.podfile, config.lockfile)
|
357
|
+
installer.repo_update = false
|
358
|
+
installer.update = false
|
359
|
+
installer.prepare
|
360
|
+
installer.resolve_dependencies
|
361
|
+
installer.download_dependencies
|
362
|
+
|
363
|
+
Pod::UserInterface::config.silent = false
|
364
|
+
end
|
365
|
+
end
|
366
|
+
|
367
|
+
def self.copy_prebuilt_items(podfile_items)
|
368
|
+
FileUtils.mkdir_p(PodBuilder::prebuiltpath)
|
369
|
+
|
370
|
+
non_prebuilt_items = podfile_items.reject(&:is_prebuilt)
|
371
|
+
|
372
|
+
pod_names = non_prebuilt_items.map(&:root_name).uniq
|
373
|
+
|
374
|
+
pod_names.reject! { |t|
|
375
|
+
folder_path = PodBuilder::buildpath_prebuiltpath(t)
|
376
|
+
File.directory?(folder_path) && Dir.empty?(folder_path) # When using prebuilt items we end up with empty folders
|
377
|
+
}
|
378
|
+
|
379
|
+
pod_names.each do |pod_name|
|
380
|
+
root_name = pod_name.split("/").first
|
381
|
+
|
382
|
+
items_to_delete = Dir.glob("#{PodBuilder::prebuiltpath(root_name)}/**/*")
|
383
|
+
items_to_delete.each { |t| PodBuilder::safe_rm_rf(t) }
|
384
|
+
end
|
385
|
+
|
386
|
+
# Now copy
|
387
|
+
pod_names.each do |pod_name|
|
388
|
+
root_name = pod_name.split("/").first
|
389
|
+
source_path = PodBuilder::buildpath_prebuiltpath(root_name)
|
390
|
+
|
391
|
+
unless File.directory?(source_path)
|
392
|
+
puts "Prebuilt items for #{pod_name} not found".blue
|
393
|
+
next
|
394
|
+
end
|
395
|
+
|
396
|
+
unless Dir.glob("#{source_path}/**/*").select { |t| File.file?(t) }.empty?
|
397
|
+
destination_folder = PodBuilder::prebuiltpath(root_name)
|
398
|
+
FileUtils.mkdir_p(destination_folder)
|
399
|
+
FileUtils.cp_r("#{source_path}/.", destination_folder)
|
400
|
+
end
|
401
|
+
end
|
402
|
+
|
403
|
+
# Folder won't exist if no dSYM were generated (all static libs)
|
404
|
+
if File.directory?(PodBuilder::buildpath_dsympath)
|
405
|
+
FileUtils.mkdir_p(PodBuilder::dsympath)
|
406
|
+
FileUtils.cp_r(PodBuilder::buildpath_dsympath, PodBuilder::basepath)
|
407
|
+
end
|
408
|
+
end
|
409
|
+
|
410
|
+
def self.write_prebuilt_info_filename_gitattributes
|
411
|
+
gitattributes_path = PodBuilder::basepath(".gitattributes")
|
412
|
+
expected_attributes = ["#{Configuration.configuration_filename} binary"].join
|
413
|
+
unless File.exists?(gitattributes_path) && File.read(gitattributes_path).include?(expected_attributes)
|
414
|
+
File.write(gitattributes_path, expected_attributes, mode: 'a')
|
415
|
+
end
|
416
|
+
end
|
417
|
+
|
418
|
+
def self.init_git(path)
|
419
|
+
current_dir = Dir.pwd
|
420
|
+
|
421
|
+
Dir.chdir(path)
|
422
|
+
system("git init")
|
423
|
+
Dir.chdir(current_dir)
|
424
|
+
end
|
425
|
+
|
426
|
+
def self.build_folder_hash_in_prebuilt_info_file(podfile_item)
|
427
|
+
prebuilt_info_path = PodBuilder::prebuiltpath(File.join(podfile_item.root_name, Configuration.prebuilt_info_filename))
|
428
|
+
|
429
|
+
if File.exist?(prebuilt_info_path)
|
430
|
+
data = JSON.parse(File.read(prebuilt_info_path))
|
431
|
+
return data['build_folder_hash']
|
432
|
+
else
|
433
|
+
return nil
|
434
|
+
end
|
435
|
+
end
|
436
|
+
|
437
|
+
def self.build_folder_hash(podfile_item, exclude_files)
|
438
|
+
if podfile_item.is_development_pod
|
439
|
+
if Pathname.new(podfile_item.path).absolute?
|
440
|
+
item_path = podfile_item.path
|
441
|
+
else
|
442
|
+
item_path = PodBuilder::basepath(podfile_item.path)
|
443
|
+
end
|
444
|
+
|
445
|
+
rootpath = PodBuilder::git_rootpath
|
446
|
+
file_hashes = []
|
447
|
+
Dir.glob("#{item_path}/**/*", File::FNM_DOTMATCH) do |path|
|
448
|
+
unless File.file?(path)
|
449
|
+
next
|
450
|
+
end
|
451
|
+
|
452
|
+
path = File.expand_path(path)
|
453
|
+
rel_path = path.gsub(rootpath, "")[1..]
|
454
|
+
unless exclude_files.include?(rel_path)
|
455
|
+
file_hashes.push(Digest::MD5.hexdigest(File.read(path)))
|
456
|
+
end
|
457
|
+
end
|
458
|
+
|
459
|
+
return Digest::MD5.hexdigest(file_hashes.join)
|
460
|
+
else
|
461
|
+
# Pod folder might be under .gitignore
|
462
|
+
item_path = "#{Configuration.build_path}/Pods/#{podfile_item.root_name}"
|
463
|
+
if File.directory?(item_path)
|
464
|
+
return `find '#{item_path}' -type f -print0 | sort -z | xargs -0 shasum | shasum | cut -d' ' -f1`.strip()
|
465
|
+
else
|
466
|
+
return nil
|
467
|
+
end
|
468
|
+
end
|
469
|
+
end
|
470
|
+
|
471
|
+
def self.podfile_path_transform(path)
|
472
|
+
if Configuration.build_using_repo_paths
|
473
|
+
return File.expand_path(PodBuilder::basepath(path))
|
474
|
+
else
|
475
|
+
use_absolute_paths = true
|
476
|
+
podfile_path = File.join(Configuration.build_path, "Podfile")
|
477
|
+
original_basepath = PodBuilder::basepath
|
478
|
+
|
479
|
+
podfile_base_path = Pathname.new(File.dirname(podfile_path))
|
480
|
+
|
481
|
+
original_path = Pathname.new(File.join(original_basepath, path))
|
482
|
+
replace_path = original_path.relative_path_from(podfile_base_path)
|
483
|
+
if use_absolute_paths
|
484
|
+
replace_path = replace_path.expand_path(podfile_base_path)
|
485
|
+
end
|
486
|
+
|
487
|
+
return replace_path
|
488
|
+
end
|
489
|
+
end
|
490
|
+
|
491
|
+
def self.activate_pod_scheme
|
492
|
+
if scheme_file = Dir.glob("#{Configuration.build_path}/Pods/**/xcschememanagement.plist").first
|
493
|
+
plist = CFPropertyList::List.new(:file => scheme_file)
|
494
|
+
data = CFPropertyList.native_types(plist.value)
|
495
|
+
|
496
|
+
if !data.dig("SchemeUserState", "Pods-DummyTarget.xcscheme").nil?
|
497
|
+
data["SchemeUserState"]["Pods-DummyTarget.xcscheme"]["isShown"] = true
|
498
|
+
end
|
499
|
+
|
500
|
+
plist.value = CFPropertyList.guess(data)
|
501
|
+
plist.save(scheme_file, CFPropertyList::List::FORMAT_BINARY)
|
502
|
+
end
|
503
|
+
end
|
504
|
+
end
|
505
|
+
end
|