pod-builder 2.0.0.beta.23 ā†’ 2.0.0.beta.28

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.
@@ -10,7 +10,9 @@ module PodBuilder
10
10
  installer, analyzer = Analyze.installer_at(PodBuilder::basepath, false)
11
11
  all_buildable_items = Analyze.podfile_items(installer, analyzer)
12
12
 
13
- Podspec::generate(all_buildable_items, analyzer)
13
+ install_using_frameworks = Podfile::install_using_frameworks(analyzer)
14
+
15
+ Podspec::generate(all_buildable_items, analyzer, install_using_frameworks)
14
16
 
15
17
  puts "\n\nšŸŽ‰ done!\n".green
16
18
  return 0
@@ -22,17 +22,8 @@ module PodBuilder
22
22
  FileUtils.mkdir_p("#{OPTIONS[:prebuild_path]}/.pod_builder")
23
23
  FileUtils.touch("#{OPTIONS[:prebuild_path]}/.pod_builder/pod_builder")
24
24
 
25
- source_path_rel_path = "Sources"
26
- development_pods_config_rel_path = Configuration.dev_pods_configuration_filename
27
-
28
- git_ignores = ["Pods/",
29
- "*.xcworkspace",
30
- "*.xcodeproj",
31
- "Podfile.lock",
32
- source_path_rel_path,
33
- development_pods_config_rel_path]
34
-
35
- File.write("#{OPTIONS[:prebuild_path]}/.gitignore", git_ignores.join("\n"))
25
+ write_gitignore
26
+ write_gitattributes
36
27
 
37
28
  project_podfile_path = PodBuilder::project_path("Podfile")
38
29
  prebuilt_podfile_path = File.join(OPTIONS[:prebuild_path], "Podfile")
@@ -45,7 +36,9 @@ module PodBuilder
45
36
  podfile_content = Podfile.update_project_entries(podfile_content, Init.method(:podfile_path_transform))
46
37
  podfile_content = Podfile.update_require_entries(podfile_content, Init.method(:podfile_path_transform))
47
38
 
48
- podfile_content = Podfile.prepare_for_react_native(podfile_content)
39
+ if podfile_content.include?("/node_modules/react-native/")
40
+ podfile_content = Podfile.prepare_for_react_native(podfile_content)
41
+ end
49
42
 
50
43
  File.write(prebuilt_podfile_path, podfile_content)
51
44
 
@@ -59,6 +52,27 @@ module PodBuilder
59
52
 
60
53
  private
61
54
 
55
+ def self.write_gitignore
56
+ source_path_rel_path = "Sources"
57
+ development_pods_config_rel_path = Configuration.dev_pods_configuration_filename
58
+
59
+ git_ignores = ["Pods/",
60
+ "*.xcworkspace",
61
+ "*.xcodeproj",
62
+ "Podfile.lock",
63
+ Configuration.lldbinit_name,
64
+ source_path_rel_path,
65
+ development_pods_config_rel_path]
66
+
67
+ File.write("#{OPTIONS[:prebuild_path]}/.gitignore", git_ignores.join("\n"))
68
+ end
69
+
70
+ def self.write_gitattributes
71
+ git_attributes = ["#{Configuration.prebuilt_info_filename} binary"]
72
+
73
+ File.write("#{OPTIONS[:prebuild_path]}/.gitattributes", git_attributes.join("\n"))
74
+ end
75
+
62
76
  def self.podfile_path_transform(path)
63
77
  use_absolute_paths = false
64
78
  podfile_path = File.join(OPTIONS[:prebuild_path], "Podfile")
@@ -22,12 +22,12 @@ module PodBuilder
22
22
  framework_files.each do |path|
23
23
  rel_path = Pathname.new(path).relative_path_from(Pathname.new(base_path)).to_s
24
24
 
25
- if podfile_spec = podfile_items.detect { |x| x.prebuilt_rel_path == rel_path }
25
+ if podfile_spec = podfile_items.detect { |x| "#{x.root_name}/#{x.prebuilt_rel_path}" == rel_path }
26
26
  update_repo(podfile_spec)
27
27
  end
28
28
  end
29
29
 
30
- Command::Clean::clean_sources(podspec_names)
30
+ Clean::install_sources(podfile_items)
31
31
 
32
32
  ARGV << PodBuilder::basepath("Sources")
33
33
 
@@ -17,13 +17,30 @@ module PodBuilder
17
17
  DEFAULT_SPEC_OVERRIDE = {
18
18
  "Google-Mobile-Ads-SDK" => {
19
19
  "module_name": "GoogleMobileAds"
20
+ },
21
+ "React-jsiexecutor" => {
22
+ "remove_module_maps": ["glog"]
23
+ },
24
+ "React-Core" => {
25
+ "remove_module_maps": ["glog"]
26
+ },
27
+ "React-Core/Default" => {
28
+ "public_headers": "React/**/*.{h}"
29
+ },
30
+ "React-jsi" => {
31
+ "remove_module_maps": ["glog"]
32
+ },
33
+ "React-cxxreact" => {
34
+ "remove_module_maps": ["glog"]
35
+ },
36
+ "Folly" => {
37
+ "remove_module_maps": ["glog"]
20
38
  }
21
39
  }.freeze
22
- DEFAULT_SKIP_PODS = ["GoogleMaps", "Flipper", "FlipperKit", "Flipper-DoubleConversion", "Flipper-Folly", "Flipper-Glog", "Flipper-PeerTalk", "Flipper-RSocket", "React-cxxreact"]
23
- DEFAULT_FORCE_PREBUILD_PODS = ["Firebase", "GoogleTagManager"]
40
+ DEFAULT_SKIP_PODS = ["GoogleMaps"]
41
+ DEFAULT_FORCE_PREBUILD_PODS = ["GoogleTagManager"]
24
42
  DEFAULT_BUILD_SYSTEM = "Legacy".freeze # either Latest (New build system) or Legacy (Standard build system)
25
43
  DEFAULT_LIBRARY_EVOLUTION_SUPPORT = false
26
- MIN_LFS_SIZE_KB = 256.freeze
27
44
  DEFAULT_PLATFORMS = ["iphoneos", "iphonesimulator", "appletvos", "appletvsimulator"].freeze
28
45
  DEFAULT_BUILD_FOR_APPLE_SILICON = false
29
46
  DEFAULT_BUILD_USING_REPO_PATHS = false
@@ -31,7 +48,6 @@ module PodBuilder
31
48
  private_constant :DEFAULT_BUILD_SETTINGS
32
49
  private_constant :DEFAULT_BUILD_SYSTEM
33
50
  private_constant :DEFAULT_LIBRARY_EVOLUTION_SUPPORT
34
- private_constant :MIN_LFS_SIZE_KB
35
51
 
36
52
  class <<self
37
53
  attr_accessor :allow_building_development_pods
@@ -51,9 +67,6 @@ module PodBuilder
51
67
  attr_accessor :build_path
52
68
  attr_accessor :configuration_filename
53
69
  attr_accessor :dev_pods_configuration_filename
54
- attr_accessor :lfs_min_file_size
55
- attr_accessor :lfs_update_gitattributes
56
- attr_accessor :lfs_include_pods_folder
57
70
  attr_accessor :project_name
58
71
  attr_accessor :restore_enabled
59
72
  attr_accessor :prebuilt_info_filename
@@ -65,6 +78,7 @@ module PodBuilder
65
78
  attr_accessor :build_for_apple_silicon
66
79
  attr_accessor :build_using_repo_paths
67
80
  attr_accessor :react_native_project
81
+ attr_accessor :lldbinit_name
68
82
  end
69
83
 
70
84
  @allow_building_development_pods = false
@@ -84,14 +98,12 @@ module PodBuilder
84
98
  @build_path = build_base_path
85
99
  @configuration_filename = "PodBuilder.json".freeze
86
100
  @dev_pods_configuration_filename = "PodBuilderDevPodsPaths.json".freeze
87
- @lfs_min_file_size = MIN_LFS_SIZE_KB
88
- @lfs_update_gitattributes = false
89
- @lfs_include_pods_folder = false
90
101
  @project_name = ""
91
102
  @restore_enabled = true
92
103
  @prebuilt_info_filename = "PodBuilder.json"
93
104
  @lockfile_name = "PodBuilder.lock"
94
105
  @lockfile_path = "/tmp/#{lockfile_name}"
106
+ @lldbinit_name = "lldbinit".freeze
95
107
 
96
108
  @use_bundler = false
97
109
  @deterministic_build = false
@@ -173,16 +185,6 @@ module PodBuilder
173
185
  Configuration.subspecs_to_split = value
174
186
  end
175
187
  end
176
- if value = json["lfs_update_gitattributes"]
177
- if [TrueClass, FalseClass].include?(value.class)
178
- Configuration.lfs_update_gitattributes = value
179
- end
180
- end
181
- if value = json["lfs_include_pods_folder"]
182
- if [TrueClass, FalseClass].include?(value.class)
183
- Configuration.lfs_include_pods_folder = value
184
- end
185
- end
186
188
  if value = json["project_name"]
187
189
  if value.is_a?(String) && value.length > 0
188
190
  Configuration.project_name = value
@@ -262,8 +264,6 @@ module PodBuilder
262
264
  config["library_evolution_support"] = Configuration.library_evolution_support
263
265
  config["license_filename"] = Configuration.license_filename
264
266
  config["subspecs_to_split"] = Configuration.subspecs_to_split
265
- config["lfs_update_gitattributes"] = Configuration.lfs_update_gitattributes
266
- config["lfs_include_pods_folder"] = Configuration.lfs_include_pods_folder
267
267
  config["restore_enabled"] = Configuration.restore_enabled
268
268
  config["allow_building_development_pods"] = Configuration.allow_building_development_pods
269
269
  config["use_bundler"] = Configuration.use_bundler
@@ -19,6 +19,10 @@ module PodBuilder
19
19
  @@xcodeproj_path = nil
20
20
  @@xcodeworkspace_path = nil
21
21
 
22
+ def self.git_rootpath
23
+ return `git rev-parse --show-toplevel`.strip()
24
+ end
25
+
22
26
  def self.safe_rm_rf(path)
23
27
  unless File.exist?(path)
24
28
  return
@@ -34,8 +38,8 @@ module PodBuilder
34
38
 
35
39
  Dir.chdir(path)
36
40
 
37
- h = `git rev-parse --show-toplevel`.strip()
38
- raise "\n\nNo git repository found in '#{path}', can't delete files!\n".red if h.empty? && !path.start_with?(Configuration.build_base_path)
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)
39
43
 
40
44
  FileUtils.rm_rf(path)
41
45
 
@@ -45,6 +49,12 @@ module PodBuilder
45
49
  Dir.chdir(basepath)
46
50
  end
47
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
48
58
 
49
59
  def self.basepath(child = "")
50
60
  if child.nil?
@@ -205,8 +215,8 @@ module PodBuilder
205
215
  private
206
216
 
207
217
  def self.home
208
- h = `git rev-parse --show-toplevel`.strip()
209
- raise "\n\nNo git repository found in current folder `#{Dir.pwd}`!\n".red if h.empty?
210
- return h
218
+ rootpath = git_rootpath
219
+ raise "\n\nNo git repository found in current folder `#{Dir.pwd}`!\n".red if rootpath.empty?
220
+ return rootpath
211
221
  end
212
222
  end
@@ -10,29 +10,29 @@ require 'highline/import'
10
10
  # - https://github.com/leavez/cocoapods-binary/issues/50
11
11
  begin
12
12
  require 'cocoapods/installer/xcode/pods_project_generator/pod_target_dependency_installer.rb'
13
-
13
+
14
14
  class Pod::Specification
15
15
  Pod::Specification.singleton_class.send(:alias_method, :swz_from_hash, :from_hash)
16
16
  Pod::Specification.singleton_class.send(:alias_method, :swz_from_string, :from_string)
17
-
17
+
18
18
  def self.from_string(*args)
19
19
  spec = swz_from_string(*args)
20
-
20
+
21
21
  if overrides = PodBuilder::Configuration.spec_overrides[spec.name]
22
22
  overrides.each do |k, v|
23
23
  spec.attributes_hash[k] = v
24
24
  end
25
25
  end
26
-
26
+
27
27
  spec
28
28
  end
29
29
  end
30
-
30
+
31
31
  class Pod::Target
32
32
  attr_accessor :mock_dynamic_framework
33
-
33
+
34
34
  alias_method :swz_build_type, :build_type
35
-
35
+
36
36
  def build_type
37
37
  if mock_dynamic_framework == true
38
38
  if defined?(Pod::BuildType) # CocoaPods 1.9 and later
@@ -47,23 +47,23 @@ begin
47
47
  end
48
48
  end
49
49
  end
50
-
50
+
51
51
  # Starting from CocoaPods 1.10.0 and later resources are no longer copied inside the .framework
52
52
  # when building static frameworks. While this is correct when using CP normally, for redistributable
53
53
  # frameworks we require resources to be shipped along the binary
54
54
  class Pod::Installer::Xcode::PodsProjectGenerator::PodTargetInstaller
55
55
  alias_method :swz_add_files_to_build_phases, :add_files_to_build_phases
56
-
56
+
57
57
  def add_files_to_build_phases(native_target, test_native_targets, app_native_targets)
58
58
  target.mock_dynamic_framework = target.build_as_static_framework?
59
59
  swz_add_files_to_build_phases(native_target, test_native_targets, app_native_targets)
60
60
  target.mock_dynamic_framework = false
61
61
  end
62
62
  end
63
-
63
+
64
64
  class Pod::Installer::Xcode::PodTargetDependencyInstaller
65
65
  alias_method :swz_wire_resource_bundle_targets, :wire_resource_bundle_targets
66
-
66
+
67
67
  def wire_resource_bundle_targets(resource_bundle_targets, native_target, pod_target)
68
68
  pod_target.mock_dynamic_framework = pod_target.build_as_static_framework?
69
69
  res = swz_wire_resource_bundle_targets(resource_bundle_targets, native_target, pod_target)
@@ -71,48 +71,110 @@ begin
71
71
  return res
72
72
  end
73
73
  end
74
+
75
+ class Pod::Target::BuildSettings
76
+ alias_method :swz_save_as, :save_as
77
+
78
+ @specs_remove_module_maps = Hash.new
79
+
80
+ class << self
81
+ attr_accessor :specs_remove_module_maps
82
+ end
83
+
84
+ def save_as(path)
85
+ Pod::Target::BuildSettings.specs_remove_module_maps.each do |root_name, module_maps_to_remove|
86
+ if target.name == root_name
87
+ module_maps_to_remove.each do |module_map_to_remove|
88
+ xcconfig.attributes["OTHER_CFLAGS"] = xcconfig.attributes["OTHER_CFLAGS"].gsub(/-fmodule-map-file=\S*#{module_map_to_remove}.modulemap.*?(\s|$)/, '')
89
+ end
90
+ end
91
+ end
92
+
93
+ swz_save_as(path)
94
+ end
95
+ end
74
96
  rescue LoadError
75
97
  # CocoaPods 1.6.2 or earlier
76
98
  end
77
99
 
78
100
  module PodBuilder
101
+ class InstallResult
102
+ # @return [Array<Hash>] The installed licenses
103
+ #
104
+ attr_reader :licenses
105
+
106
+ # @return [Hash] A hash containing the expected prebuilt_info filename and content
107
+ #
108
+ attr_reader :prebuilt_info
109
+
110
+ def initialize(licenses = [], prebuilt_info = Hash.new)
111
+ @licenses = licenses
112
+ @prebuilt_info = prebuilt_info
113
+ end
114
+
115
+ def +(obj)
116
+ merged_licenses = @licenses.dup + obj.licenses
117
+ merged_prebuilt_info = @prebuilt_info.dup
118
+
119
+ merged_prebuilt_info.each do |key, value|
120
+ if obj.prebuilt_info.has_key?(key)
121
+ specs = merged_prebuilt_info[key]["specs"] || []
122
+ specs += (obj.prebuilt_info[key]["specs"] || [])
123
+ merged_prebuilt_info[key]["specs"] = specs.uniq
124
+ end
125
+ end
126
+
127
+ merged_prebuilt_info = obj.prebuilt_info.merge(merged_prebuilt_info)
128
+
129
+ return InstallResult.new(merged_licenses, merged_prebuilt_info)
130
+ end
131
+
132
+ def write_prebuilt_info_files
133
+ prebuilt_info.each do |file_path, file_content|
134
+ File.write(file_path, JSON.pretty_generate(file_content))
135
+ end
136
+ end
137
+ end
138
+
79
139
  class Install
80
140
  # This method will generate prebuilt data by building from "/tmp/pod_builder/Podfile"
81
141
  def self.podfile(podfile_content, podfile_items, build_configuration)
82
142
  puts "Preparing build Podfile".yellow
83
-
143
+
84
144
  PodBuilder::safe_rm_rf(Configuration.build_path)
85
145
  FileUtils.mkdir_p(Configuration.build_path)
86
146
 
87
147
  init_git(Configuration.build_path) # this is needed to be able to call safe_rm_rf
88
-
148
+
89
149
  podfile_content = copy_development_pods_source_code(podfile_content, podfile_items)
90
-
150
+
91
151
  podfile_content = Podfile.update_path_entries(podfile_content, Install.method(:podfile_path_transform))
92
152
  podfile_content = Podfile.update_project_entries(podfile_content, Install.method(:podfile_path_transform))
93
153
  podfile_content = Podfile.update_require_entries(podfile_content, Install.method(:podfile_path_transform))
94
-
154
+
95
155
  podfile_path = File.join(Configuration.build_path, "Podfile")
96
156
  File.write(podfile_path, podfile_content)
97
-
157
+
98
158
  begin
99
159
  lock_file = "#{Configuration.build_path}/pod_builder.lock"
100
160
  FileUtils.touch(lock_file)
161
+
162
+ prebuilt_entries = use_prebuilt_entries_for_unchanged_pods(podfile_path, podfile_items)
101
163
 
102
- use_prebuilt_entries_for_unchanged_pods(podfile_path, podfile_items)
103
-
164
+ prepare_for_static_framework_workarounds(podfile_content, podfile_items)
165
+
104
166
  install
167
+
168
+ copy_prebuilt_items(podfile_items - prebuilt_entries)
105
169
 
106
- copy_prebuilt_items(podfile_items)
107
- add_prebuilt_info_file(podfile_items)
108
-
170
+ prebuilt_info = prebuilt_info(podfile_items)
109
171
  licenses = license_specifiers()
110
-
172
+
111
173
  if !OPTIONS.has_key?(:debug)
112
174
  PodBuilder::safe_rm_rf(Configuration.build_path)
113
175
  end
114
-
115
- return licenses
176
+
177
+ return InstallResult.new(licenses, prebuilt_info)
116
178
  rescue Exception => e
117
179
  if File.directory?("#{Configuration.build_path}/Pods/Pods.xcodeproj")
118
180
  if ENV['DEBUGGING']
@@ -124,87 +186,165 @@ module PodBuilder
124
186
  end
125
187
  end
126
188
  end
127
-
189
+
128
190
  raise e
129
191
  ensure
130
192
  FileUtils.rm(lock_file) if File.exist?(lock_file)
131
193
  end
132
194
  end
133
195
 
196
+ def self.prebuilt_info(podfile_items)
197
+ gitignored_files = PodBuilder::gitignoredfiles
198
+
199
+ swift_version = PodBuilder::system_swift_version
200
+
201
+ write_prebuilt_info_filename_gitattributes
202
+
203
+ ret = Hash.new
204
+ root_names = podfile_items.reject(&:is_prebuilt).map(&:root_name).uniq
205
+ root_names.each do |prebuilt_name|
206
+ path = PodBuilder::prebuiltpath(prebuilt_name)
207
+
208
+ unless File.directory?(path)
209
+ puts "Prebuilt items for #{prebuilt_name} not found".blue
210
+ next
211
+ end
212
+
213
+ unless podfile_item = podfile_items.detect { |t| t.name == prebuilt_name } || podfile_items.detect { |t| t.root_name == prebuilt_name }
214
+ puts "Prebuilt items for #{prebuilt_name} not found #2".blue
215
+ next
216
+ end
217
+
218
+ podbuilder_file = File.join(path, Configuration.prebuilt_info_filename)
219
+ entry = podfile_item.entry(true, false)
220
+ if Configuration.subspecs_to_split.include?(podfile_item.name)
221
+ entry.gsub!("'#{podfile_item.name}'", "'#{podfile_item.root_name}'")
222
+ end
223
+
224
+ data = {}
225
+ data["entry"] = entry
226
+ data["is_prebuilt"] = podfile_item.is_prebuilt
227
+ if Dir.glob(File.join(path, "#{podfile_item.prebuilt_rel_path}/Headers/*-Swift.h")).count > 0
228
+ data["swift_version"] = swift_version
229
+ end
230
+
231
+ specs = podfile_items.select { |x| x.module_name == podfile_item.module_name }
232
+ subspecs_deps = specs.map(&:dependency_names).flatten
233
+ subspec_self_deps = subspecs_deps.select { |x| x.start_with?("#{prebuilt_name}/") }
234
+ data["specs"] = (specs.map(&:name) + subspec_self_deps).uniq
235
+ data["is_static"] = podfile_item.is_static
236
+ data["original_compile_path"] = Configuration.build_path
237
+ if hash = build_folder_hash(podfile_item, gitignored_files)
238
+ data["build_folder_hash"] = hash
239
+ end
240
+
241
+ ret.merge!({ podbuilder_file => data })
242
+ end
243
+
244
+ return ret
245
+ end
134
246
  private
135
247
 
248
+ def self.prepare_for_static_framework_workarounds(podfile_content, podfile_items)
249
+ unless podfile_content.include?("use_modular_headers!")
250
+ return
251
+ end
252
+
253
+ podfile_items.each do |podfile_item|
254
+ Pod::Target::BuildSettings.specs_remove_module_maps[podfile_item.root_name] = podfile_item.remove_module_maps
255
+ end
256
+ end
257
+
136
258
  def self.license_specifiers
137
259
  acknowledge_file = "#{Configuration.build_path}/Pods/Target Support Files/Pods-DummyTarget/Pods-DummyTarget-acknowledgements.plist"
138
260
  unless File.exist?(acknowledge_file)
139
261
  raise "\n\nLicense file not found".red
140
262
  end
141
-
263
+
142
264
  plist = CFPropertyList::List.new(:file => acknowledge_file)
143
265
  data = CFPropertyList.native_types(plist.value)
144
-
266
+
145
267
  return data["PreferenceSpecifiers"] || []
146
268
  end
147
-
269
+
148
270
  def self.copy_development_pods_source_code(podfile_content, podfile_items)
149
271
  if Configuration.build_using_repo_paths
150
272
  return podfile_content
151
273
  end
152
-
274
+
153
275
  # Development pods are normally built/integrated without moving files from their original paths.
154
276
  # It is important that CocoaPods compiles the files under Configuration.build_path in order that
155
277
  # DWARF debug info reference to this constant path. Doing otherwise breaks the assumptions that
156
- # makes the `update_lldbinit` command work.
278
+ # makes the `generate_lldbinit` command work.
157
279
  development_pods = podfile_items.select { |x| x.is_development_pod }
158
280
  development_pods.each do |podfile_item|
159
281
  destination_path = "#{Configuration.build_path}/Pods/#{podfile_item.name}"
160
282
  FileUtils.mkdir_p(destination_path)
161
-
283
+
162
284
  if Pathname.new(podfile_item.path).absolute?
163
285
  FileUtils.cp_r("#{podfile_item.path}/.", destination_path)
164
286
  else
165
287
  FileUtils.cp_r("#{PodBuilder::basepath(podfile_item.path)}/.", destination_path)
166
288
  end
167
-
289
+
168
290
  podfile_content.gsub!("'#{podfile_item.path}'", "'#{destination_path}'")
169
291
  end
170
-
292
+
171
293
  return podfile_content
172
294
  end
173
-
295
+
174
296
  def self.use_prebuilt_entries_for_unchanged_pods(podfile_path, podfile_items)
175
- if OPTIONS.has_key?(:force_rebuild)
176
- return
177
- end
178
-
179
- download # Copy files under #{Configuration.build_path}/Pods so that we can determine build folder hashes
180
-
181
297
  podfile_content = File.read(podfile_path)
298
+
299
+ replaced_items = []
182
300
 
183
- # Replace prebuilt entries in Podfile for Pods that have no changes in source code which will avoid rebuilding them
184
- items = podfile_items.group_by { |t| t.root_name }.map { |k, v| v.first } # Return one podfile_item per root_name
185
- items.each do |item|
186
- podspec_path = item.prebuilt_podspec_path
187
- if last_build_folder_hash = build_folder_hash_in_prebuilt_info_file(item)
188
- if last_build_folder_hash == build_folder_hash(item)
189
- puts "No changes detected to '#{item.root_name}', will skip rebuild".blue
190
- podfile_items.select { |t| t.root_name == item.root_name }.each do |replace_item|
191
- replace_regex = "pod '#{Regexp.quote(replace_item.name)}', .*"
192
- replace_line_found = podfile_content =~ /#{replace_regex}/i
193
- raise "\n\nFailed finding pod entry for '#{replace_item.name}'".red unless replace_line_found
194
- podfile_content.gsub!(/#{replace_regex}/, replace_item.prebuilt_entry(true, true))
301
+ if OPTIONS.has_key?(:force_rebuild)
302
+ podfile_content.gsub!("%%%prebuilt_root_paths%%%", "{}")
303
+ else
304
+ download # Copy files under #{Configuration.build_path}/Pods so that we can determine build folder hashes
305
+
306
+ gitignored_files = PodBuilder::gitignoredfiles
307
+
308
+ prebuilt_root_paths = Hash.new
309
+
310
+ # Replace prebuilt entries in Podfile for Pods that have no changes in source code which will avoid rebuilding them
311
+ items = podfile_items.group_by { |t| t.root_name }.map { |k, v| v.first } # Return one podfile_item per root_name
312
+ items.each do |item|
313
+ podspec_path = item.prebuilt_podspec_path
314
+ if last_build_folder_hash = build_folder_hash_in_prebuilt_info_file(item)
315
+ if last_build_folder_hash == build_folder_hash(item, gitignored_files)
316
+ if Configuration.subspecs_to_split.include?(item.name)
317
+ puts "No changes detected to '#{item.name}', will skip rebuild".blue
318
+ else
319
+ puts "No changes detected to '#{item.root_name}', will skip rebuild".blue
320
+ end
321
+ replaced_items.push(item)
322
+
323
+ podfile_items.select { |t| t.root_name == item.root_name }.each do |replace_item|
324
+ replace_regex = "pod '#{Regexp.quote(replace_item.name)}', .*"
325
+ replace_line_found = podfile_content =~ /#{replace_regex}/i
326
+ raise "\n\nFailed finding pod entry for '#{replace_item.name}'".red unless replace_line_found
327
+ podfile_content.gsub!(/#{replace_regex}/, replace_item.prebuilt_entry(true, true))
328
+
329
+ prebuilt_root_paths[replace_item.root_name] = PodBuilder::prebuiltpath
330
+ end
195
331
  end
196
332
  end
197
333
  end
334
+
335
+ podfile_content.gsub!("%%%prebuilt_root_paths%%%", prebuilt_root_paths.to_s)
198
336
  end
199
337
 
200
338
  File.write(podfile_path, podfile_content)
201
- end
202
339
 
340
+ return replaced_items
341
+ end
342
+
203
343
  def self.install
204
344
  puts "Prebuilding items".yellow
205
-
345
+
206
346
  CLAide::Command::PluginManager.load_plugins("cocoapods")
207
-
347
+
208
348
  Dir.chdir(Configuration.build_path) do
209
349
  config = Pod::Config.new()
210
350
  installer = Pod::Installer.new(config.sandbox, config.podfile, config.lockfile)
@@ -212,21 +352,22 @@ module PodBuilder
212
352
  installer.update = false
213
353
 
214
354
  install_start_time = Time.now
355
+
215
356
  installer.install!
216
357
  install_time = Time.now - install_start_time
217
-
358
+
218
359
  puts "Build completed in #{install_time.to_i} seconds".blue
219
360
  end
220
361
  end
221
-
362
+
222
363
  def self.download
223
364
  puts "Downloading Pods source code".yellow
224
-
365
+
225
366
  CLAide::Command::PluginManager.load_plugins("cocoapods")
226
-
367
+
227
368
  Dir.chdir(Configuration.build_path) do
228
369
  Pod::UserInterface::config.silent = true
229
-
370
+
230
371
  config = Pod::Config.new()
231
372
  installer = Pod::Installer.new(config.sandbox, config.podfile, config.lockfile)
232
373
  installer.repo_update = false
@@ -234,86 +375,92 @@ module PodBuilder
234
375
  installer.prepare
235
376
  installer.resolve_dependencies
236
377
  installer.download_dependencies
237
-
378
+
238
379
  Pod::UserInterface::config.silent = false
239
380
  end
240
381
  end
241
-
382
+
242
383
  def self.copy_prebuilt_items(podfile_items)
243
384
  FileUtils.mkdir_p(PodBuilder::prebuiltpath)
244
385
 
245
- root_names = podfile_items.reject(&:is_prebuilt).map(&:root_name).uniq
246
- root_names.each do |prebuilt_name|
247
- source_path = PodBuilder::buildpath_prebuiltpath(prebuilt_name)
248
- unless File.directory?(source_path)
249
- puts "Prebuilt items for #{prebuilt_name} not found".blue
250
- next
251
- end
252
- if Dir.empty?(source_path)
253
- next # When using prebuilt items we end up with empty folders
254
- end
386
+ non_prebuilt_items = podfile_items.reject(&:is_prebuilt)
255
387
 
256
- PodBuilder::safe_rm_rf(PodBuilder::prebuiltpath(prebuilt_name))
257
- FileUtils.cp_r(source_path, PodBuilder::prebuiltpath)
258
- end
388
+ splitted_pods = non_prebuilt_items.map { |t| splitted_pod(t, podfile_items) }.flatten.uniq
389
+ splitted_pods_root_name = splitted_pods.map { |t| t.root_name }.uniq
259
390
 
260
- # Folder won't exist if no dSYM were generated (all static libs)
261
- if File.directory?(PodBuilder::buildpath_dsympath)
262
- FileUtils.mkdir_p(PodBuilder::dsympath)
263
- FileUtils.cp_r(PodBuilder::buildpath_dsympath, PodBuilder::basepath)
264
- end
265
- end
391
+ pod_names = non_prebuilt_items.reject { |t| splitted_pods_root_name.include?(t.root_name) }.map(&:root_name).uniq + splitted_pods.map(&:name)
266
392
 
267
- def self.add_prebuilt_info_file(podfile_items)
268
- swift_version = PodBuilder::system_swift_version
393
+ pod_names.reject! { |t|
394
+ folder_path = PodBuilder::buildpath_prebuiltpath(t)
395
+ File.directory?(folder_path) && Dir.empty?(folder_path) # When using prebuilt items we end up with empty folders
396
+ }
269
397
 
270
- root_names = podfile_items.reject(&:is_prebuilt).map(&:root_name).uniq
271
- root_names.each do |prebuilt_name|
272
- path = PodBuilder::prebuiltpath(prebuilt_name)
398
+ # Selectively delete destination folder.
399
+ # If it's a splitted spec we just need to wipe the Subspecs/#{pod_name}
400
+ # If it's not we need to wipe everything except the Subspecs folder
401
+ pod_names.each do |pod_name|
402
+ root_name = pod_name.split("/").first
403
+ if pod_name.include?("/") # Splitted pod
404
+ PodBuilder::safe_rm_rf(PodBuilder::prebuiltpath("#{root_name}/Subspecs/#{pod_name.gsub("/", "_") }"))
405
+ else
406
+ items_to_delete = Dir.glob("#{PodBuilder::prebuiltpath(root_name)}/**/*")
407
+ items_to_delete.reject! { |t| t.include?(PodBuilder::prebuiltpath("#{root_name}/Subspecs")) }
273
408
 
274
- unless File.directory?(path)
275
- puts "Prebuilt items for #{prebuilt_name} not found".blue
276
- next
409
+ items_to_delete.each { |t| PodBuilder::safe_rm_rf(t) }
277
410
  end
411
+ end
278
412
 
279
- unless podfile_item = podfile_items.detect { |t| t.name == prebuilt_name } || podfile_items.detect { |t| t.root_name == prebuilt_name }
280
- puts "Prebuilt items for #{prebuilt_name} not found #2".blue
413
+ # Now copy
414
+ splitted_items_copied = false
415
+ pod_names.each do |pod_name|
416
+ root_name = pod_name.split("/").first
417
+ source_path = PodBuilder::buildpath_prebuiltpath(root_name)
418
+
419
+ unless File.directory?(source_path)
420
+ puts "Prebuilt items for #{pod_name} not found".blue
281
421
  next
282
422
  end
283
423
 
284
- podbuilder_file = File.join(path, Configuration.prebuilt_info_filename)
285
- entry = podfile_item.entry(true, false)
286
-
287
- data = {}
288
- data['entry'] = entry
289
- data['is_prebuilt'] = podfile_item.is_prebuilt
290
- if Dir.glob(File.join(path, "#{podfile_item.module_name}/Headers/*-Swift.h")).count > 0
291
- data['swift_version'] = swift_version
424
+ if Configuration.subspecs_to_split.include?(pod_name)
425
+ destination_folder = PodBuilder::prebuiltpath("#{root_name}/Subspecs/#{pod_name.gsub("/", "_") }")
426
+ FileUtils.mkdir_p(destination_folder)
427
+ unless splitted_items_copied
428
+ FileUtils.cp_r("#{source_path}/.", destination_folder)
429
+ splitted_items_copied = true
430
+ end
431
+ else
432
+ destination_folder = PodBuilder::prebuiltpath(root_name)
433
+ FileUtils.mkdir_p(destination_folder)
434
+ FileUtils.cp_r("#{source_path}/.", destination_folder)
292
435
  end
293
-
294
- specs = podfile_items.select { |x| x.module_name == podfile_item.module_name }
295
- subspecs_deps = specs.map(&:dependency_names).flatten
296
- subspec_self_deps = subspecs_deps.select { |x| x.start_with?("#{prebuilt_name}/") }
297
- data['specs'] = (specs.map(&:name) + subspec_self_deps).uniq
298
- data['is_static'] = podfile_item.is_static
299
- data['original_compile_path'] = Pathname.new(Configuration.build_path).realpath.to_s
300
- data['build_folder_hash'] = build_folder_hash(podfile_item)
301
-
302
- File.write(podbuilder_file, JSON.pretty_generate(data))
436
+ end
437
+
438
+ # Folder won't exist if no dSYM were generated (all static libs)
439
+ if File.directory?(PodBuilder::buildpath_dsympath)
440
+ FileUtils.mkdir_p(PodBuilder::dsympath)
441
+ FileUtils.cp_r(PodBuilder::buildpath_dsympath, PodBuilder::basepath)
303
442
  end
304
443
  end
305
444
 
445
+ def self.write_prebuilt_info_filename_gitattributes
446
+ gitattributes_path = PodBuilder::basepath(".gitattributes")
447
+ expected_attributes = ["#{Configuration.configuration_filename} binary"].join
448
+ unless File.exists?(gitattributes_path) && File.read(gitattributes_path).include?(expected_attributes)
449
+ File.write(gitattributes_path, expected_attributes, mode: 'a')
450
+ end
451
+ end
452
+
306
453
  def self.init_git(path)
307
454
  current_dir = Dir.pwd
308
-
455
+
309
456
  Dir.chdir(path)
310
457
  system("git init")
311
458
  Dir.chdir(current_dir)
312
459
  end
313
-
460
+
314
461
  def self.build_folder_hash_in_prebuilt_info_file(podfile_item)
315
462
  prebuilt_info_path = PodBuilder::prebuiltpath(File.join(podfile_item.root_name, Configuration.prebuilt_info_filename))
316
-
463
+
317
464
  if File.exist?(prebuilt_info_path)
318
465
  data = JSON.parse(File.read(prebuilt_info_path))
319
466
  return data['build_folder_hash']
@@ -321,19 +468,39 @@ module PodBuilder
321
468
  return nil
322
469
  end
323
470
  end
324
-
325
- def self.build_folder_hash(podfile_item)
471
+
472
+ def self.build_folder_hash(podfile_item, exclude_files)
326
473
  if podfile_item.is_development_pod
327
474
  if Pathname.new(podfile_item.path).absolute?
328
475
  item_path = podfile_item.path
329
476
  else
330
477
  item_path = PodBuilder::basepath(podfile_item.path)
331
478
  end
479
+
480
+ rootpath = PodBuilder::git_rootpath
481
+ file_hashes = []
482
+ Dir.glob("#{item_path}/**/*", File::FNM_DOTMATCH) do |path|
483
+ unless File.file?(path)
484
+ next
485
+ end
486
+
487
+ path = File.expand_path(path)
488
+ rel_path = path.gsub(rootpath, "")[1..]
489
+ unless exclude_files.include?(rel_path)
490
+ file_hashes.push(Digest::MD5.hexdigest(File.read(path)))
491
+ end
492
+ end
493
+
494
+ return Digest::MD5.hexdigest(file_hashes.join)
332
495
  else
496
+ # Pod folder might be under .gitignore
333
497
  item_path = "#{Configuration.build_path}/Pods/#{podfile_item.root_name}"
498
+ if File.directory?(item_path)
499
+ return `find '#{item_path}' -type f -print0 | sort -z | xargs -0 shasum | shasum | cut -d' ' -f1`.strip()
500
+ else
501
+ return nil
502
+ end
334
503
  end
335
-
336
- return `find '#{item_path}' -type f -print0 | sort -z | xargs -0 shasum | shasum | cut -d' ' -f1`.strip()
337
504
  end
338
505
 
339
506
  def self.podfile_path_transform(path)
@@ -343,17 +510,21 @@ module PodBuilder
343
510
  use_absolute_paths = true
344
511
  podfile_path = File.join(Configuration.build_path, "Podfile")
345
512
  original_basepath = PodBuilder::basepath
346
-
513
+
347
514
  podfile_base_path = Pathname.new(File.dirname(podfile_path))
348
-
515
+
349
516
  original_path = Pathname.new(File.join(original_basepath, path))
350
517
  replace_path = original_path.relative_path_from(podfile_base_path)
351
518
  if use_absolute_paths
352
519
  replace_path = replace_path.expand_path(podfile_base_path)
353
520
  end
354
-
521
+
355
522
  return replace_path
356
523
  end
357
- end
524
+ end
525
+
526
+ def self.splitted_pod(podfile_item, podfile_items)
527
+ return podfile_items.select { |t| t.root_name == podfile_item.root_name && Configuration.subspecs_to_split.include?(t.name) }
528
+ end
358
529
  end
359
530
  end