pod-builder-y 2.3.1

Sign up to get free protection for your applications and to get access to all the features.
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