pod-builder-y 2.3.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.
Files changed (44) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +19 -0
  3. data/Gemfile +8 -0
  4. data/LICENSE.txt +13 -0
  5. data/README.md +385 -0
  6. data/Rakefile +2 -0
  7. data/bin/console +16 -0
  8. data/bin/setup +8 -0
  9. data/exe/pod_builder_y +406 -0
  10. data/lib/core_ext/string.rb +5 -0
  11. data/lib/pod_builder/analyze.rb +59 -0
  12. data/lib/pod_builder/analyzer.rb +16 -0
  13. data/lib/pod_builder/command.rb +14 -0
  14. data/lib/pod_builder/command/build.rb +228 -0
  15. data/lib/pod_builder/command/build_all.rb +15 -0
  16. data/lib/pod_builder/command/clean.rb +75 -0
  17. data/lib/pod_builder/command/deintegrate.rb +101 -0
  18. data/lib/pod_builder/command/generate_lldbinit.rb +128 -0
  19. data/lib/pod_builder/command/generate_podspec.rb +22 -0
  20. data/lib/pod_builder/command/info.rb +18 -0
  21. data/lib/pod_builder/command/init.rb +148 -0
  22. data/lib/pod_builder/command/install_sources.rb +79 -0
  23. data/lib/pod_builder/command/none.rb +16 -0
  24. data/lib/pod_builder/command/restore_all.rb +33 -0
  25. data/lib/pod_builder/command/switch.rb +224 -0
  26. data/lib/pod_builder/command/sync_podfile.rb +34 -0
  27. data/lib/pod_builder/command/update.rb +43 -0
  28. data/lib/pod_builder/configuration.rb +300 -0
  29. data/lib/pod_builder/core.rb +222 -0
  30. data/lib/pod_builder/info.rb +90 -0
  31. data/lib/pod_builder/install.rb +505 -0
  32. data/lib/pod_builder/licenses.rb +73 -0
  33. data/lib/pod_builder/podfile.rb +700 -0
  34. data/lib/pod_builder/podfile/pre_actions_swizzles.rb +84 -0
  35. data/lib/pod_builder/podfile_cp.rb +99 -0
  36. data/lib/pod_builder/podfile_item.rb +530 -0
  37. data/lib/pod_builder/podspec.rb +269 -0
  38. data/lib/pod_builder/rome/post_install.rb +446 -0
  39. data/lib/pod_builder/rome/pre_install.rb +6 -0
  40. data/lib/pod_builder/templates/build_podfile.template +70 -0
  41. data/lib/pod_builder/templates/build_podspec.template +19 -0
  42. data/lib/pod_builder/version.rb +4 -0
  43. data/pod-builder.gemspec +37 -0
  44. 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