zabel 1.0.0 → 1.0.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 40a25da43fd7d7baad52891f28711a40becb1fecc3994cc55012947a3533b7b4
4
- data.tar.gz: d6e31ca241e61654e1ef27142be0c670c13d8f9391c3a81118b9cf73605ed87d
3
+ metadata.gz: 6b4bd5a362ce7f4aa9c80a6e1f385f21cdd60b07c7d76bd27aaacb127a5eb037
4
+ data.tar.gz: 842369d90e5a1f878d51b9d439c62de4c123f3fbfd7bd63bca2735f24ac302d9
5
5
  SHA512:
6
- metadata.gz: 4d3c4baa573dc08a5c5515deaf48741f2d1b8c6df5865ba621484d7481af25bc91323bb6e9cac0343712eb973eda9f0d0b97399428cee0f790bd7a83bb937d7e
7
- data.tar.gz: 3e406539303eda08035c1a0179d09c427827f12d7f08f3c41bd576edb1cfbf25159294df450dae9d0fb318fa190e26c0d88b488494464d953111e9eaf3e06242
6
+ metadata.gz: e44882f989a91d79be7467f1c81af6625f74509219ea8e761814eba5bafa9413263b9e0708ea9d2d42f637da03ccea990eaecfdaf337d540ee12576750aece13
7
+ data.tar.gz: 6dbf765804557e693cc30e569ea7cf17ceffaf1c6cfb516eb745e1d972248551bb4490fc14a660c2de55296fedd67f7f3afb585427729623995f5ca479fe97e5
data/.gitignore CHANGED
@@ -5,14 +5,14 @@
5
5
  /doc/
6
6
  /pkg/
7
7
  /spec/reports/
8
- /tmp/
8
+ tmp
9
9
  Pods
10
- build
11
- build-*
10
+ build*
12
11
  cache
13
12
  logs
14
13
  *.xcodeproj
15
14
  *.xcworkspace
15
+ *.xcarchive
16
16
  *.lock
17
17
  *.log
18
18
  *.gem
data/README.md CHANGED
@@ -17,6 +17,8 @@ Zabel, is a build cacher for Xcode, using Xcodeproj and MD5, to detect and cache
17
17
  - support development pods
18
18
  - support different build path
19
19
  - support dependent files and implicit dependent targets
20
+ - support xcodebuild build or archive
21
+ - support fastlane build or archive
20
22
 
21
23
  ## Installation
22
24
 
@@ -36,21 +38,20 @@ Or install it yourself as:
36
38
 
37
39
  ## Usage
38
40
 
39
- Simply add zabel before your xcodebuild command.
41
+ Simply add zabel before your xcodebuild/fastlane command. Please ensure that your command can work without zabel.
40
42
 
41
43
  ```
42
- pod install/update
43
- zabel xcodebuild -workspace app.xcworkspace -scheme app -configuration Release -sdk iphonesimulator
44
+ zabel xcodebuild/fastlane xxx
44
45
  ```
45
46
 
46
47
  ## Advanced usage
47
48
 
48
- You can controll your cache keys, which can be more or less.
49
+ You can controll your cache keys, which can be more or less. Please ensure that your arguments are same in pre and post.
49
50
 
50
51
  ```
51
- zabel pre -configuration Release -sdk iphonesimulator
52
- xcodebuild -workspace app.xcworkspace -scheme app -configuration Release -sdk iphonesimulator
53
- zabel post -configuration Release -sdk iphonesimulator
52
+ zabel pre -configuration Release abc
53
+ xcodebuild/fastlane xxx
54
+ zabel post -configuration Release abc
54
55
  ```
55
56
 
56
57
  ## Development
@@ -65,9 +66,9 @@ To install this gem onto your local machine, run `bundle exec rake install`. To
65
66
  # test all cases
66
67
  ruby test/all.rb
67
68
  # test one case
68
- ruby test/case/development_pods/test.rb
69
+ ruby test/one.rb test/case/simple/Podfile
69
70
  # test one todo case
70
- ruby test/todo/modulemap_file/test.rb
71
+ ruby test/one.rb test/todo/modulemap_file/Podfile
71
72
  ```
72
73
 
73
74
  ## TODO
@@ -92,7 +93,7 @@ Everyone interacting in the Zabel project’s codebases, issue trackers, chat ro
92
93
 
93
94
  ## FAQ
94
95
 
95
- Q: Must I set -configuration ?
96
+ Q: Must I set configuration ?
96
97
 
97
98
  A: Yes, for now.
98
99
 
data/bin/zabel CHANGED
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env ruby
2
2
 
3
3
  require 'zabel'
4
-
4
+
5
5
  if ARGV[0] == Zabel::STAGE_CLEAN
6
6
  Zabel::zabel_clean
7
7
  elsif ARGV[0] == Zabel::STAGE_EXTRACT
@@ -12,19 +12,20 @@ elsif ARGV[0] == Zabel::STAGE_PRE
12
12
  Zabel::zabel_pre(ARGV[1..-1])
13
13
  elsif ARGV[0] == Zabel::STAGE_POST
14
14
  Zabel::zabel_post(ARGV[1..-1])
15
- elsif ARGV[0] and ARGV[0].include? "xcodebuild"
15
+ elsif ARGV.include?("-configuration") or ARGV.include?("--configuration")
16
16
  total_start_time = Time.now
17
17
 
18
18
  Zabel::zabel_pre(ARGV)
19
19
 
20
20
  build_start_time = Time.now
21
- raise unless system(*ARGV)
21
+ exit 1 unless system(*ARGV)
22
22
  puts "[ZABEL]<INFO> duration = #{(Time.now - build_start_time).to_i} s in stage build"
23
23
 
24
24
  Zabel::zabel_post(ARGV)
25
25
 
26
26
  puts "[ZABEL]<INFO> duration = #{(Time.now - total_start_time).to_i} s in stage all"
27
27
  else
28
- puts Zabel::VERSION
29
- puts "https://github.com/WeijunDeng/Zabel"
28
+ puts "version: #{Zabel::VERSION}"
29
+ puts "exec: #{$0}"
30
+ puts "url: https://github.com/WeijunDeng/Zabel"
30
31
  end
data/lib/zabel.rb CHANGED
@@ -9,900 +9,896 @@ require 'yaml'
9
9
  require 'pathname'
10
10
 
11
11
  module Zabel
12
- class Error < StandardError; end
12
+ class Error < StandardError; end
13
13
 
14
- BUILD_KEY_SYMROOT = "SYMROOT"
15
- BUILD_KEY_TARGET_BUILD_DIR = "TARGET_BUILD_DIR"
16
- BUILD_KEY_OBJROOT = "OBJROOT"
17
- BUILD_KEY_TARGET_TEMP_DIR = "TARGET_TEMP_DIR"
18
- BUILD_KEY_PODS_XCFRAMEWORKS_BUILD_DIR = "PODS_XCFRAMEWORKS_BUILD_DIR"
19
- BUILD_KEY_MODULEMAP_FILE = "MODULEMAP_FILE"
20
- BUILD_KEY_SRCROOT = "SRCROOT"
21
- BUILD_KEY_WRAPPER_NAME = "WRAPPER_NAME"
14
+ BUILD_KEY_SYMROOT = "SYMROOT"
15
+ BUILD_KEY_CONFIGURATION_BUILD_DIR = "CONFIGURATION_BUILD_DIR"
16
+ BUILD_KEY_OBJROOT = "OBJROOT"
17
+ BUILD_KEY_TARGET_TEMP_DIR = "TARGET_TEMP_DIR"
18
+ BUILD_KEY_PODS_XCFRAMEWORKS_BUILD_DIR = "PODS_XCFRAMEWORKS_BUILD_DIR"
19
+ BUILD_KEY_MODULEMAP_FILE = "MODULEMAP_FILE"
20
+ BUILD_KEY_SRCROOT = "SRCROOT"
21
+ BUILD_KEY_FULL_PRODUCT_NAME = "FULL_PRODUCT_NAME"
22
22
 
23
- STATUS_HIT = "hit"
24
- STATUS_MISS = "miss"
23
+ STATUS_HIT = "hit"
24
+ STATUS_MISS = "miss"
25
25
 
26
- STAGE_CLEAN = "clean"
27
- STAGE_EXTRACT = "extract"
28
- STAGE_PRINTENV = "printenv"
29
- STAGE_PRE = "pre"
30
- STAGE_POST = "post"
26
+ STAGE_CLEAN = "clean"
27
+ STAGE_EXTRACT = "extract"
28
+ STAGE_PRINTENV = "printenv"
29
+ STAGE_PRE = "pre"
30
+ STAGE_POST = "post"
31
31
 
32
- FILE_NAME_MESSAGE = "message.txt"
33
- FILE_NAME_CONTEXT = "context.yml"
34
- FILE_NAME_PRODUCT = "product.tar"
35
- FILE_NAME_TARGET_CONTEXT = "zabel_target_context.yml"
36
-
37
- def self.zabel_get_cache_root
38
- cache_root = ENV["ZABEL_CACHE_ROOT"]
39
- if cache_root and cache_root.size > 0
40
- return cache_root
41
- end
42
-
43
- return Dir.home + "/zabel"
44
- end
45
-
46
- def self.zabel_get_cache_count
47
- cache_count = ENV["ZABEL_CACHE_COUNT"]
48
- if cache_count and cache_count.to_i.to_s == cache_count
49
- return cache_count.to_i
50
- end
51
- return 10000
52
- end
53
-
54
- def self.zabel_should_not_detect_module_map_dependency
55
- # By default, zabel detects module map dependency.
56
- # However, there are bugs of xcodebuild or swift-frontend, which emits unnecessary and incorrect modulemap dependencies.
57
- # To test by run "ruby test/todo/modulemap_file/test.rb"
58
- # To avoid by set "export ZABEL_NOT_DETECT_MODULE_MAP_DEPENDENCY=YES"
59
- zabel_should_not_detect_module_map_dependency = ENV["ZABEL_NOT_DETECT_MODULE_MAP_DEPENDENCY"]
60
- if zabel_should_not_detect_module_map_dependency == "YES"
61
- return true
62
- end
63
- return false
64
- end
32
+ FILE_NAME_MESSAGE = "message.txt"
33
+ FILE_NAME_CONTEXT = "context.yml"
34
+ FILE_NAME_PRODUCT = "product.tar"
35
+ FILE_NAME_TARGET_CONTEXT = "zabel_target_context.yml"
36
+
37
+ def self.zabel_get_cache_root
38
+ cache_root = ENV["ZABEL_CACHE_ROOT"]
39
+ if cache_root and cache_root.size > 0
40
+ return cache_root
41
+ end
42
+
43
+ return Dir.home + "/zabel"
44
+ end
45
+
46
+ def self.zabel_get_cache_count
47
+ cache_count = ENV["ZABEL_CACHE_COUNT"]
48
+ if cache_count and cache_count.to_i.to_s == cache_count
49
+ return cache_count.to_i
50
+ end
51
+ return 10000
52
+ end
53
+
54
+ def self.zabel_should_not_detect_module_map_dependency
55
+ # By default, zabel detects module map dependency.
56
+ # However, there are bugs of xcodebuild or swift-frontend, which emits unnecessary and incorrect modulemap dependencies.
57
+ # To test by run "ruby test/one.rb test/todo/modulemap_file/Podfile"
58
+ # To avoid by set "export ZABEL_NOT_DETECT_MODULE_MAP_DEPENDENCY=YES"
59
+ zabel_should_not_detect_module_map_dependency = ENV["ZABEL_NOT_DETECT_MODULE_MAP_DEPENDENCY"]
60
+ if zabel_should_not_detect_module_map_dependency == "YES"
61
+ return true
62
+ end
63
+ return false
64
+ end
65
65
 
66
- def self.zabel_get_min_source_file_count
67
- # By default, zable caches targets which count of source files is greater than or equal 1.
68
- # You can set this value to 0 or more than 1 to achieve higher speed.
69
- min_source_file_count = ENV["ZABEL_MIN_SOURCE_FILE_COUNT"]
70
- if min_source_file_count and min_source_file_count.to_i.to_s == min_source_file_count
71
- return min_source_file_count.to_i
72
- end
73
- return 1
74
- end
66
+ def self.zabel_get_min_source_file_count
67
+ # By default, zable caches targets which count of source files is greater than or equal 1.
68
+ # You can set this value to 0 or more than 1 to achieve higher speed.
69
+ min_source_file_count = ENV["ZABEL_MIN_SOURCE_FILE_COUNT"]
70
+ if min_source_file_count and min_source_file_count.to_i.to_s == min_source_file_count
71
+ return min_source_file_count.to_i
72
+ end
73
+ return 1
74
+ end
75
75
 
76
- def self.zabel_should_extract_once
77
- # By default, to achieve better compatibility, zabel extracts target cache ondemand,
78
- # which means it depends on original dependencies of targets and it is in parallel.
79
- # However, extracting once in a shell script build phase rather than multiple shell script build phases,
80
- # is a little bit faster in some cases.
81
- # You can enable this by set "export ZABEL_EXTRACT_ONCE=YES"
82
- should_extract_once = ENV["ZABEL_EXTRACT_ONCE"]
83
- if should_extract_once == "YES"
84
- return true
85
- end
86
- return false
87
- end
76
+ def self.zabel_should_extract_once
77
+ # By default, to achieve better compatibility, zabel extracts target cache ondemand,
78
+ # which means it depends on original dependencies of targets and it is in parallel.
79
+ # However, extracting once in a shell script build phase rather than multiple shell script build phases,
80
+ # is a little bit faster in some cases.
81
+ # You can enable this by set "export ZABEL_EXTRACT_ONCE=YES"
82
+ should_extract_once = ENV["ZABEL_EXTRACT_ONCE"]
83
+ if should_extract_once == "YES"
84
+ return true
85
+ end
86
+ return false
87
+ end
88
88
 
89
- def self.zabel_get_projects
90
- # TODO: to support more project, not only Pods
91
- pods_project = Xcodeproj::Project.open("Pods/Pods.xcodeproj")
92
- wrapper_project_paths = zabel_get_wrapper_project_paths(pods_project)
93
- wrapper_projects = []
94
- wrapper_project_paths.each do | path |
95
- next if path.end_with? "Pods/Pods.xcodeproj"
96
- project = Xcodeproj::Project.open(path)
97
- wrapper_projects.push project
98
- end
99
- return (wrapper_projects + [pods_project])
100
- end
101
-
102
- def self.zabel_get_wrapper_project_paths(project)
103
- wrapper_projects = project.files.select{|file|file.last_known_file_type=="wrapper.pb-project"}
104
- wrapper_project_paths = []
105
- wrapper_projects.each do | wrapper_project_file |
106
- wrapper_project_file_path = wrapper_project_file.real_path.to_s
107
- wrapper_project_paths.push wrapper_project_file_path
108
- end
109
- return wrapper_project_paths.uniq
110
- end
111
-
112
- def self.zabel_can_cache_target(target)
113
- if target.name.start_with? "Pods-"
114
- return false
115
- end
116
- if target.class == Xcodeproj::Project::Object::PBXNativeTarget
117
- # see https://github.com/CocoaPods/Xcodeproj/blob/master/lib/xcodeproj/constants.rb#L145
118
- if target.product_type == "com.apple.product-type.bundle" or
119
- target.product_type == "com.apple.product-type.library.static" or
120
- target.product_type == "com.apple.product-type.framework"
121
- return true
122
- end
123
- end
124
- return false
125
- end
126
-
127
- def self.zabel_get_dependency_files(target, intermediate_dir, product_dir, xcframeworks_build_dir)
128
- dependency_files = []
129
- Dir.glob("#{intermediate_dir}/**/*.d").each do | dependency_file |
130
- content = File.read(dependency_file)
131
- # see https://github.com/ccache/ccache/blob/master/src/Depfile.cpp#L141
132
- # and this is a simple regex parser enough to get all files, as far as I know.
133
- files = content.scan(/(?:\S(?:\\ )*)+/).flatten.uniq
134
- files = files - ["dependencies:", "\\", ":"]
135
-
136
- files.each do | file |
137
- file = file.gsub("\\ ", " ")
138
-
139
- unless File.exist? file
140
- puts "[ZABEL]<ERROR> #{target.name} #{file} should exist in dependency file #{dependency_file}"
141
- return []
142
- end
143
-
144
- if file.start_with? intermediate_dir + "/" or
145
- file.start_with? product_dir + "/" or
146
- file.start_with? xcframeworks_build_dir + "/"
147
- next
148
- end
149
-
150
- dependency_files.push file
151
- end
152
- end
153
- return dependency_files.uniq
154
- end
155
-
156
- def self.zabel_get_target_source_files(target)
157
- files = []
158
- target.source_build_phase.files.each do | file |
159
- file_path = file.file_ref.real_path.to_s
160
- files.push file_path
161
- end
162
- target.headers_build_phase.files.each do | file |
163
- file_path = file.file_ref.real_path.to_s
164
- files.push file_path
165
- end
166
- target.resources_build_phase.files.each do | file |
167
- file_path = file.file_ref.real_path.to_s
168
- files.push file_path
169
- end
170
- expand_files = []
171
- files.uniq.each do | file |
172
- next unless File.exist? file
173
- if File.file? file
174
- expand_files.push file
175
- else
176
- Find.find(file).each do | file_in_dir |
177
- if File.file? file_in_dir
178
- expand_files.push file_in_dir
179
- end
180
- end
181
- end
182
- end
183
- return expand_files.uniq
184
- end
185
-
186
- def self.zabel_get_content_without_pwd(content)
187
- content = content.gsub("#{Dir.pwd}/", "").gsub(/#{Dir.pwd}(\W|$)/, '\1')
188
- return content
189
- end
190
-
191
- $zabel_file_md5_hash = {}
192
-
193
- def self.zabel_get_file_md5(file)
194
- if $zabel_file_md5_hash.has_key? file
195
- return $zabel_file_md5_hash[file]
196
- end
197
- md5 = Digest::MD5.hexdigest(File.read(file))
198
- $zabel_file_md5_hash[file] = md5
199
- return md5
200
- end
89
+ def self.zabel_get_projects
90
+ # TODO: to support more project, not only Pods
91
+ pods_project = Xcodeproj::Project.open("Pods/Pods.xcodeproj")
92
+ wrapper_project_paths = zabel_get_wrapper_project_paths(pods_project)
93
+ wrapper_projects = []
94
+ wrapper_project_paths.each do | path |
95
+ next if path.end_with? "Pods/Pods.xcodeproj"
96
+ project = Xcodeproj::Project.open(path)
97
+ wrapper_projects.push project
98
+ end
99
+ return (wrapper_projects + [pods_project])
100
+ end
101
+
102
+ def self.zabel_get_wrapper_project_paths(project)
103
+ wrapper_projects = project.files.select{|file|file.last_known_file_type=="wrapper.pb-project"}
104
+ wrapper_project_paths = []
105
+ wrapper_projects.each do | wrapper_project_file |
106
+ wrapper_project_file_path = wrapper_project_file.real_path.to_s
107
+ wrapper_project_paths.push wrapper_project_file_path
108
+ end
109
+ return wrapper_project_paths.uniq
110
+ end
111
+
112
+ def self.zabel_can_cache_target(target)
113
+ if target.name.start_with? "Pods-"
114
+ return false
115
+ end
116
+ if target.class == Xcodeproj::Project::Object::PBXNativeTarget
117
+ # see https://github.com/CocoaPods/Xcodeproj/blob/master/lib/xcodeproj/constants.rb#L145
118
+ if target.product_type == "com.apple.product-type.bundle" or
119
+ target.product_type == "com.apple.product-type.library.static" or
120
+ target.product_type == "com.apple.product-type.framework"
121
+ return true
122
+ end
123
+ end
124
+ return false
125
+ end
126
+
127
+ def self.zabel_get_dependency_files(target, intermediate_dir, product_dir, xcframeworks_build_dir)
128
+ dependency_files = []
129
+ Dir.glob("#{intermediate_dir}/**/*.d").each do | dependency_file |
130
+ content = File.read(dependency_file)
131
+ # see https://github.com/ccache/ccache/blob/master/src/Depfile.cpp#L141
132
+ # and this is a simple regex parser enough to get all files, as far as I know.
133
+ files = content.scan(/(?:\S(?:\\ )*)+/).flatten.uniq
134
+ files = files - ["dependencies:", "\\", ":"]
135
+
136
+ files.each do | file |
137
+ file = file.gsub("\\ ", " ")
138
+
139
+ unless File.exist? file
140
+ puts "[ZABEL]<ERROR> #{target.name} #{file} should exist in dependency file #{dependency_file}"
141
+ return []
142
+ end
143
+
144
+ if file.start_with? intermediate_dir + "/" or
145
+ file.start_with? product_dir + "/" or
146
+ file.start_with? xcframeworks_build_dir + "/"
147
+ next
148
+ end
149
+
150
+ dependency_files.push file
151
+ end
152
+ end
153
+ return dependency_files.uniq
154
+ end
155
+
156
+ def self.zabel_get_target_source_files(target)
157
+ files = []
158
+ target.source_build_phase.files.each do | file |
159
+ file_path = file.file_ref.real_path.to_s
160
+ files.push file_path
161
+ end
162
+ target.headers_build_phase.files.each do | file |
163
+ file_path = file.file_ref.real_path.to_s
164
+ files.push file_path
165
+ end
166
+ target.resources_build_phase.files.each do | file |
167
+ file_path = file.file_ref.real_path.to_s
168
+ files.push file_path
169
+ end
170
+ expand_files = []
171
+ files.uniq.each do | file |
172
+ next unless File.exist? file
173
+ if File.file? file
174
+ expand_files.push file
175
+ else
176
+ Find.find(file).each do | file_in_dir |
177
+ if File.file? file_in_dir
178
+ expand_files.push file_in_dir
179
+ end
180
+ end
181
+ end
182
+ end
183
+ return expand_files.uniq
184
+ end
185
+
186
+ def self.zabel_get_content_without_pwd(content)
187
+ content = content.gsub("#{Dir.pwd}/", "").gsub(/#{Dir.pwd}(\W|$)/, '\1')
188
+ return content
189
+ end
190
+
191
+ $zabel_file_md5_hash = {}
192
+
193
+ def self.zabel_get_file_md5(file)
194
+ if $zabel_file_md5_hash.has_key? file
195
+ return $zabel_file_md5_hash[file]
196
+ end
197
+ md5 = Digest::MD5.hexdigest(File.read(file))
198
+ $zabel_file_md5_hash[file] = md5
199
+ return md5
200
+ end
201
201
 
202
- def self.zabel_keep
203
- file_list = Dir.glob("#{zabel_get_cache_root}/*")
204
- file_time_hash = {}
205
- file_list.each do | file |
206
- file_time_hash[file] = File.mtime(file)
207
- end
208
- file_list = file_list.sort_by {|file| - file_time_hash[file].to_f}
209
- puts "[ZABEL]<INFO> keep cache " + file_list.size.to_s + " " + Open3.capture3("du -sh #{zabel_get_cache_root}")[0].to_s
210
-
211
- if file_list.size > 1
212
- puts "[ZABEL]<INFO> keep oldest " + file_time_hash[file_list.last].to_s + " " + file_list.last
213
- puts "[ZABEL]<INFO> keep newest " + file_time_hash[file_list.first].to_s + " " + file_list.first
214
- end
215
-
216
- if file_list.size > zabel_get_cache_count
217
- file_list_remove = file_list[zabel_get_cache_count..(file_list.size-1)]
218
- file_list_remove.each do | file |
219
- raise unless system "rm -rf \"#{file}\""
220
- end
221
- end
222
- end
223
-
224
- def self.zabel_clean_backup_project(project)
225
- command = "rm -rf \"#{project.path}/project.zabel_backup_pbxproj\""
226
- raise unless system command
227
- end
228
-
229
-
230
- def self.zabel_backup_project(project)
231
- command = "cp \"#{project.path}/project.pbxproj\" \"#{project.path}/project.zabel_backup_pbxproj\""
232
- raise unless system command
233
- end
234
-
235
- def self.zabel_restore_project(project)
236
- if File.exist? "#{project.path}/project.zabel_backup_pbxproj"
237
- command = "mv \"#{project.path}/project.zabel_backup_pbxproj\" \"#{project.path}/project.pbxproj\""
238
- raise unless system command
239
- end
240
- end
241
-
242
- $zabel_podfile_spec_checksums = nil
243
-
244
- def self.zabel_get_target_md5_content(project, target, configuration_name, argv, source_files)
245
-
246
- unless $zabel_podfile_spec_checksums
247
- if File.exist? "Podfile.lock"
248
- podfile_lock = YAML.load(File.read("Podfile.lock"))
249
- $zabel_podfile_spec_checksums = podfile_lock["SPEC CHECKSUMS"]
250
- end
251
- end
252
-
253
- project_configuration = project.build_configurations.detect { | config | config.name == configuration_name}
254
- project_configuration_content = project_configuration.pretty_print.to_yaml
255
- project_xcconfig = ""
256
- if project_configuration.base_configuration_reference
257
- config_file_path = project_configuration.base_configuration_reference.real_path
258
- if File.exist? config_file_path
259
- project_xcconfig = File.read(config_file_path).lines.reject{|line|line.include? "_SEARCH_PATHS"}.sort.join("")
260
- end
261
- end
262
-
263
- target_configuration = target.build_configurations.detect { | config | config.name == configuration_name}
264
- target_configuration_content = target_configuration.pretty_print.to_yaml
265
- target_xcconfig = ""
266
- if target_configuration.base_configuration_reference
267
- config_file_path = target_configuration.base_configuration_reference.real_path
268
- if File.exist? config_file_path
269
- target_xcconfig = File.read(config_file_path).lines.reject{|line|line.include? "_SEARCH_PATHS"}.sort.join("")
270
- end
271
- end
272
-
273
- first_configuration = []
274
- build_phases = []
275
- build_phases.push target.source_build_phase if target.methods.include? :source_build_phase
276
- build_phases.push target.resources_build_phase if target.methods.include? :resources_build_phase
277
- build_phases.each do | build_phase |
278
- target.source_build_phase.files_references.each do | files_reference |
279
- files_reference.build_files.each do |build_file|
280
- if build_file.settings and build_file.settings.class == Hash
281
- first_configuration.push File.basename(build_file.file_ref.real_path.to_s) + "\n" + build_file.settings.to_yaml
282
- end
283
- end
284
- end
285
- end
286
- first_configuration_content = first_configuration.sort.uniq.join("\n")
287
-
288
- key_argv = []
289
-
290
- # TODO: to add more and test more
291
- # However, you can control your cache keys manually by using pre and post.
292
- temp_path_list = ["-derivedDataPath", "-archivePath", "-exportPath", "-packageCachePath"]
293
- argv.each_with_index do | arg, index |
294
- next if temp_path_list.include? arg
295
- next if index > 0 and temp_path_list.include? argv[index-1]
296
- next if arg.start_with? "DSTROOT="
297
- next if arg.start_with? "OBJROOT="
298
- next if arg.start_with? "SYMROOT="
299
- key_argv.push arg
300
- end
301
-
302
- source_md5_list = []
303
- # zabel built-in verison, which will be changed for incompatibility in the future
304
- source_md5_list.push "Version : #{Zabel::VERSION}"
305
- source_md5_list.push "ARGV : #{key_argv.to_s}"
306
-
307
- has_found_checksum = false
308
- split_parts = target.name.split("-")
309
- split_parts.each_with_index do | part, index |
310
- spec_name = split_parts[0..index].join("-")
311
- # TODO: to get a explicit spec name from a target.
312
- # Now all potential spec names are push into md5 for safety.
313
- if $zabel_podfile_spec_checksums.has_key? spec_name
314
- source_md5_list.push "SPEC CHECKSUM : #{spec_name} #{$zabel_podfile_spec_checksums[spec_name]}"
315
- has_found_checksum = true
316
- end
317
- end
318
- unless has_found_checksum
319
- puts "[ZABEL]<ERROR> #{target.name} SPEC CHECKSUM should be found"
320
- end
321
-
322
- source_md5_list.push "Project : #{File.basename(project.path)}"
323
- source_md5_list.push "Project configuration : "
324
- source_md5_list.push project_configuration_content.strip
325
- source_md5_list.push "Project xcconfig : "
326
- source_md5_list.push project_xcconfig.strip
327
- source_md5_list.push "Target : #{target.name}"
328
- source_md5_list.push "Target type : #{target.product_type}"
329
- source_md5_list.push "Target configuration : "
330
- source_md5_list.push target_configuration_content.strip
331
- source_md5_list.push "Target xcconfig : "
332
- source_md5_list.push target_xcconfig.strip
333
- source_md5_list.push "Files settings : "
334
- source_md5_list.push first_configuration_content.strip
335
-
336
- source_md5_list.push "Files MD5 : "
337
- source_files.uniq.sort.each do | file |
338
- source_md5_list.push zabel_get_content_without_pwd(file) + " : " + zabel_get_file_md5(file)
339
- end
340
-
341
- source_md5_content = source_md5_list.join("\n")
342
- return source_md5_content
343
- end
344
-
345
- def self.zabel_clean_temp_files
346
- command = "rm -rf Pods/*.xcodeproj/project.zabel_backup_pbxproj"
347
- puts command
348
- raise unless system command
349
-
350
- command = "rm -rf Pods/*.xcodeproj/*.#{FILE_NAME_TARGET_CONTEXT}"
351
- puts command
352
- raise unless system command
353
-
354
- command = "rm -rf Pods/zabel.xcodeproj"
355
- puts command
356
- raise unless system command
357
- end
358
-
359
- def self.zabel_add_cache(target, target_context, message)
360
- target_md5 = target_context[:target_md5]
361
-
362
- product_dir = target_context[BUILD_KEY_TARGET_BUILD_DIR]
363
- intermediate_dir = target_context[BUILD_KEY_TARGET_TEMP_DIR]
364
- wrapper_name = target_context[BUILD_KEY_WRAPPER_NAME]
365
-
366
- target_cache_dir = zabel_get_cache_root + "/" + target.name + "-" + target_md5 + "-" + (Time.now.to_f * 1000).to_i.to_s
367
-
368
- Dir.glob("#{product_dir}/**/*.modulemap").each do | modulemap |
369
- modulemap_content = File.read(modulemap)
370
- if modulemap_content.include? File.dirname(modulemap) + "/"
371
- modulemap_content = modulemap_content.gsub(File.dirname(modulemap) + "/", "")
372
- File.write(modulemap, modulemap_content)
373
- end
374
- end
375
-
376
- if target.product_type == "com.apple.product-type.library.static"
377
- find_result = Open3.capture3("find #{product_dir}/*.a -maxdepth 0")
378
- unless find_result[2] == 0 and find_result[0].lines.size == 1
379
- puts "[ZABEL]<ERROR> #{target.name} #{product_dir}/*.a should exist"
380
- return false
381
- end
382
- elsif target.product_type == "com.apple.product-type.bundle" or target.product_type == "com.apple.product-type.framework"
383
- unless wrapper_name and wrapper_name.size > 0 and File.exist? "#{product_dir}/#{wrapper_name}"
384
- puts "[ZABEL]<ERROR> #{target.name} #{product_dir}/#{wrapper_name} should exist"
385
- return false
386
- end
387
- end
388
-
389
- zip_start_time = Time.now
390
-
391
- command = "cd \"#{File.dirname(product_dir)}\" && tar -cf #{target.name}.#{FILE_NAME_PRODUCT} #{File.basename(product_dir)}"
392
- if target.product_type == "com.apple.product-type.library.static"
393
- command = "cd \"#{File.dirname(product_dir)}\" && tar --exclude=*.bundle -cf #{target.name}.#{FILE_NAME_PRODUCT} #{File.basename(product_dir)}"
394
- elsif target.product_type == 'com.apple.product-type.bundle'
395
- if wrapper_name and wrapper_name.size > 0
396
- command = "cd \"#{File.dirname(product_dir)}\" && tar -cf #{target.name}.#{FILE_NAME_PRODUCT} #{File.basename(product_dir)}/#{wrapper_name}"
397
- else
398
- puts "[ZABEL]<ERROR> #{target.name} WRAPPER_NAME should be found"
399
- return false
400
- end
401
- end
402
-
403
- puts command
404
- unless system command
405
- puts "[ZABEL]<ERROR> #{command} should succeed"
406
- return false
407
- end
408
-
409
- if File.exist? target_cache_dir
410
- puts "[ZABEL]<ERROR> #{target_cache_dir} should not exist"
411
- raise unless system "rm -rf \"#{target_cache_dir}\""
412
- return false
413
- end
414
-
415
- command = "mkdir -p \"#{target_cache_dir}\""
416
- unless system command
417
- puts command
418
- puts "[ZABEL]<ERROR> #{command} should succeed"
419
- return false
420
- end
421
-
422
- cache_product_path = target_cache_dir + "/#{FILE_NAME_PRODUCT}"
423
-
424
- command = "mv \"#{File.dirname(product_dir)}/#{target.name}.#{FILE_NAME_PRODUCT}\" \"#{cache_product_path}\""
425
- puts command
426
- unless system command
427
- puts command
428
- puts "[ZABEL]<ERROR> #{command} should succeed"
429
- return false
430
- end
431
- unless File.exist? cache_product_path
432
- puts "[ZABEL]<ERROR> #{cache_product_path} should exist after mv"
433
- return false
434
- end
435
-
436
- target_context[:product_md5] = zabel_get_file_md5(cache_product_path)
437
- target_context[:target_build_dir_name] = target_context[BUILD_KEY_TARGET_BUILD_DIR].gsub(target_context[BUILD_KEY_SYMROOT] + "/", "")
438
- target_context[:target_temp_dir_name] = target_context[BUILD_KEY_TARGET_TEMP_DIR].gsub(target_context[BUILD_KEY_OBJROOT] + "/", "")
439
- if target_context[BUILD_KEY_MODULEMAP_FILE]
440
- target_context[BUILD_KEY_MODULEMAP_FILE] = zabel_get_content_without_pwd target_context[BUILD_KEY_MODULEMAP_FILE]
441
- end
202
+ def self.zabel_keep
203
+ file_list = Dir.glob("#{zabel_get_cache_root}/*")
204
+ file_time_hash = {}
205
+ file_list.each do | file |
206
+ file_time_hash[file] = File.mtime(file)
207
+ end
208
+ file_list = file_list.sort_by {|file| - file_time_hash[file].to_f}
209
+ puts "[ZABEL]<INFO> keep cache " + file_list.size.to_s + " " + Open3.capture3("du -sh #{zabel_get_cache_root}")[0].to_s
210
+
211
+ if file_list.size > 1
212
+ puts "[ZABEL]<INFO> keep oldest " + file_time_hash[file_list.last].to_s + " " + file_list.last
213
+ puts "[ZABEL]<INFO> keep newest " + file_time_hash[file_list.first].to_s + " " + file_list.first
214
+ end
215
+
216
+ if file_list.size > zabel_get_cache_count
217
+ file_list_remove = file_list[zabel_get_cache_count..(file_list.size-1)]
218
+ file_list_remove.each do | file |
219
+ raise unless system "rm -rf \"#{file}\""
220
+ end
221
+ end
222
+ end
223
+
224
+ def self.zabel_clean_backup_project(project)
225
+ command = "rm -rf \"#{project.path}/project.zabel_backup_pbxproj\""
226
+ raise unless system command
227
+ end
228
+
229
+
230
+ def self.zabel_backup_project(project)
231
+ command = "cp \"#{project.path}/project.pbxproj\" \"#{project.path}/project.zabel_backup_pbxproj\""
232
+ raise unless system command
233
+ end
234
+
235
+ def self.zabel_restore_project(project)
236
+ if File.exist? "#{project.path}/project.zabel_backup_pbxproj"
237
+ command = "mv \"#{project.path}/project.zabel_backup_pbxproj\" \"#{project.path}/project.pbxproj\""
238
+ raise unless system command
239
+ end
240
+ end
241
+
242
+ $zabel_podfile_spec_checksums = nil
243
+
244
+ def self.zabel_get_target_md5_content(project, target, configuration_name, argv, source_files)
245
+
246
+ unless $zabel_podfile_spec_checksums
247
+ if File.exist? "Podfile.lock"
248
+ podfile_lock = YAML.load(File.read("Podfile.lock"))
249
+ $zabel_podfile_spec_checksums = podfile_lock["SPEC CHECKSUMS"]
250
+ end
251
+ end
252
+
253
+ project_configuration = project.build_configurations.detect { | config | config.name == configuration_name}
254
+ project_configuration_content = project_configuration.pretty_print.to_yaml
255
+ project_xcconfig = ""
256
+ if project_configuration.base_configuration_reference
257
+ config_file_path = project_configuration.base_configuration_reference.real_path
258
+ if File.exist? config_file_path
259
+ project_xcconfig = File.read(config_file_path).lines.reject{|line|line.include? "_SEARCH_PATHS"}.sort.join("")
260
+ end
261
+ end
262
+
263
+ target_configuration = target.build_configurations.detect { | config | config.name == configuration_name}
264
+ target_configuration_content = target_configuration.pretty_print.to_yaml
265
+ target_xcconfig = ""
266
+ if target_configuration.base_configuration_reference
267
+ config_file_path = target_configuration.base_configuration_reference.real_path
268
+ if File.exist? config_file_path
269
+ target_xcconfig = File.read(config_file_path).lines.reject{|line|line.include? "_SEARCH_PATHS"}.sort.join("")
270
+ end
271
+ end
272
+
273
+ first_configuration = []
274
+ build_phases = []
275
+ build_phases.push target.source_build_phase if target.methods.include? :source_build_phase
276
+ build_phases.push target.resources_build_phase if target.methods.include? :resources_build_phase
277
+ build_phases.each do | build_phase |
278
+ target.source_build_phase.files_references.each do | files_reference |
279
+ files_reference.build_files.each do |build_file|
280
+ if build_file.settings and build_file.settings.class == Hash
281
+ first_configuration.push File.basename(build_file.file_ref.real_path.to_s) + "\n" + build_file.settings.to_yaml
282
+ end
283
+ end
284
+ end
285
+ end
286
+ first_configuration_content = first_configuration.sort.uniq.join("\n")
287
+
288
+ key_argv = []
289
+
290
+ # TODO: to add more and test more
291
+ # However, you can control your cache keys manually by using pre and post.
292
+ temp_path_list = ["-derivedDataPath", "-archivePath", "--derived_data_path", "--archive_path", "--build_path"]
293
+ argv.each_with_index do | arg, index |
294
+ next if temp_path_list.include? arg
295
+ next if index > 0 and temp_path_list.include? argv[index-1]
296
+ next if arg.start_with? "DSTROOT="
297
+ next if arg.start_with? "OBJROOT="
298
+ next if arg.start_with? "SYMROOT="
299
+ key_argv.push arg
300
+ end
301
+
302
+ source_md5_list = []
303
+ # zabel built-in verison, which will be changed for incompatibility in the future
304
+ source_md5_list.push "Zabel version : #{Zabel::VERSION}"
305
+ source_md5_list.push "ARGV : #{key_argv.to_s}"
306
+
307
+ has_found_checksum = false
308
+ split_parts = target.name.split("-")
309
+ split_parts.each_with_index do | part, index |
310
+ spec_name = split_parts[0..index].join("-")
311
+ # TODO: to get a explicit spec name from a target.
312
+ # Now all potential spec names are push into md5 for safety.
313
+ if $zabel_podfile_spec_checksums.has_key? spec_name
314
+ source_md5_list.push "SPEC CHECKSUM : #{spec_name} #{$zabel_podfile_spec_checksums[spec_name]}"
315
+ has_found_checksum = true
316
+ end
317
+ end
318
+ unless has_found_checksum
319
+ puts "[ZABEL]<ERROR> #{target.name} SPEC CHECKSUM should be found"
320
+ end
321
+
322
+ source_md5_list.push "Project : #{File.basename(project.path)}"
323
+ source_md5_list.push "Project configuration : "
324
+ source_md5_list.push project_configuration_content.strip
325
+ source_md5_list.push "Project xcconfig : "
326
+ source_md5_list.push project_xcconfig.strip
327
+ source_md5_list.push "Target : #{target.name}"
328
+ source_md5_list.push "Target type : #{target.product_type}"
329
+ source_md5_list.push "Target configuration : "
330
+ source_md5_list.push target_configuration_content.strip
331
+ source_md5_list.push "Target xcconfig : "
332
+ source_md5_list.push target_xcconfig.strip
333
+ source_md5_list.push "Files settings : "
334
+ source_md5_list.push first_configuration_content.strip
335
+
336
+ source_md5_list.push "Files MD5 : "
337
+ source_files.uniq.sort.each do | file |
338
+ source_md5_list.push zabel_get_content_without_pwd(file) + " : " + zabel_get_file_md5(file)
339
+ end
340
+
341
+ source_md5_content = source_md5_list.join("\n")
342
+ return source_md5_content
343
+ end
344
+
345
+ def self.zabel_clean_temp_files
346
+ command = "rm -rf Pods/*.xcodeproj/project.zabel_backup_pbxproj"
347
+ puts command
348
+ raise unless system command
349
+
350
+ command = "rm -rf Pods/*.xcodeproj/*.#{FILE_NAME_TARGET_CONTEXT}"
351
+ puts command
352
+ raise unless system command
353
+
354
+ command = "rm -rf Pods/zabel.xcodeproj"
355
+ puts command
356
+ raise unless system command
357
+ end
358
+
359
+ def self.zabel_add_cache(target, target_context, message)
360
+ target_md5 = target_context[:target_md5]
361
+
362
+ product_dir = target_context[BUILD_KEY_CONFIGURATION_BUILD_DIR]
363
+ intermediate_dir = target_context[BUILD_KEY_TARGET_TEMP_DIR]
364
+ full_product_name = target_context[BUILD_KEY_FULL_PRODUCT_NAME]
365
+
366
+ target_cache_dir = zabel_get_cache_root + "/" + target.name + "-" + target_md5 + "-" + (Time.now.to_f * 1000).to_i.to_s
367
+
368
+ Dir.glob("#{product_dir}/**/*.modulemap").each do | modulemap |
369
+ modulemap_content = File.read(modulemap)
370
+ if modulemap_content.include? File.dirname(modulemap) + "/"
371
+ modulemap_content = modulemap_content.gsub(File.dirname(modulemap) + "/", "")
372
+ File.write(modulemap, modulemap_content)
373
+ end
374
+ end
375
+
376
+ unless full_product_name and full_product_name.size > 0 and File.exist? "#{product_dir}/#{full_product_name}"
377
+ puts "[ZABEL]<ERROR> #{target.name} #{product_dir}/#{full_product_name} should exist"
378
+ return false
379
+ end
380
+
381
+ zip_start_time = Time.now
382
+
383
+ command = "cd \"#{File.dirname(product_dir)}\" && tar -L -c -f #{target.name}.#{FILE_NAME_PRODUCT} #{File.basename(product_dir)}/#{full_product_name}"
384
+ if target.product_type == "com.apple.product-type.library.static"
385
+ command = "cd \"#{File.dirname(product_dir)}\" && tar --exclude=*.bundle --exclude=*.framework -L -c -f #{target.name}.#{FILE_NAME_PRODUCT} #{File.basename(product_dir)}"
386
+ end
387
+
388
+ puts command
389
+ unless system command
390
+ puts "[ZABEL]<ERROR> #{command} should succeed"
391
+ return false
392
+ end
393
+
394
+ if File.exist? target_cache_dir
395
+ puts "[ZABEL]<ERROR> #{target_cache_dir} should not exist"
396
+ raise unless system "rm -rf \"#{target_cache_dir}\""
397
+ return false
398
+ end
399
+
400
+ command = "mkdir -p \"#{target_cache_dir}\""
401
+ unless system command
402
+ puts command
403
+ puts "[ZABEL]<ERROR> #{command} should succeed"
404
+ return false
405
+ end
406
+
407
+ cache_product_path = target_cache_dir + "/#{FILE_NAME_PRODUCT}"
408
+
409
+ command = "mv \"#{File.dirname(product_dir)}/#{target.name}.#{FILE_NAME_PRODUCT}\" \"#{cache_product_path}\""
410
+ puts command
411
+ unless system command
412
+ puts command
413
+ puts "[ZABEL]<ERROR> #{command} should succeed"
414
+ return false
415
+ end
416
+ unless File.exist? cache_product_path
417
+ puts "[ZABEL]<ERROR> #{cache_product_path} should exist after mv"
418
+ return false
419
+ end
420
+
421
+ target_context[:product_md5] = zabel_get_file_md5(cache_product_path)
422
+ target_context[:build_product_dir] = target_context[BUILD_KEY_CONFIGURATION_BUILD_DIR].gsub(target_context[BUILD_KEY_SYMROOT] + "/", "")
423
+ target_context[:build_intermediate_dir] = target_context[BUILD_KEY_TARGET_TEMP_DIR].gsub(target_context[BUILD_KEY_OBJROOT] + "/", "")
424
+ if target_context[BUILD_KEY_MODULEMAP_FILE]
425
+ target_context[BUILD_KEY_MODULEMAP_FILE] = zabel_get_content_without_pwd target_context[BUILD_KEY_MODULEMAP_FILE]
426
+ end
442
427
 
443
- target_context = target_context.clone
444
- target_context.delete(:dependency_files)
445
- target_context.delete(:target_status)
446
- target_context.delete(:potential_hit_target_cache_dirs)
447
- target_context.delete(:target_md5_content)
448
- [BUILD_KEY_SYMROOT, BUILD_KEY_TARGET_BUILD_DIR, BUILD_KEY_OBJROOT, BUILD_KEY_TARGET_TEMP_DIR, BUILD_KEY_PODS_XCFRAMEWORKS_BUILD_DIR, BUILD_KEY_SRCROOT].each do | key |
449
- target_context.delete(key)
450
- end
451
-
452
- File.write(target_cache_dir + "/" + FILE_NAME_CONTEXT, target_context.to_yaml)
453
- File.write(target_cache_dir + "/" + FILE_NAME_MESSAGE, message)
454
-
455
- return true
456
- end
457
-
458
- def self.zabel_post(argv)
459
-
460
- unless argv.index("-configuration")
461
- raise "[ZABEL]<ERROR> -configuration should be set"
462
- end
463
- configuration_name = argv[argv.index("-configuration") + 1]
428
+ target_context = target_context.clone
429
+ target_context.delete(:dependency_files)
430
+ target_context.delete(:target_status)
431
+ target_context.delete(:potential_hit_target_cache_dirs)
432
+ target_context.delete(:target_md5_content)
433
+ [BUILD_KEY_SYMROOT, BUILD_KEY_CONFIGURATION_BUILD_DIR, BUILD_KEY_OBJROOT, BUILD_KEY_TARGET_TEMP_DIR, BUILD_KEY_PODS_XCFRAMEWORKS_BUILD_DIR, BUILD_KEY_SRCROOT].each do | key |
434
+ target_context.delete(key)
435
+ end
436
+
437
+ File.write(target_cache_dir + "/" + FILE_NAME_CONTEXT, target_context.to_yaml)
438
+ File.write(target_cache_dir + "/" + FILE_NAME_MESSAGE, message)
439
+
440
+ return true
441
+ end
442
+
443
+ def self.zabel_post(argv)
444
+
445
+ configuration_name = nil
464
446
 
465
- start_time = Time.now
466
-
467
- add_count = 0
468
-
469
- projects = zabel_get_projects
470
-
471
- post_targets_context = {}
472
-
473
- projects.each do | project |
474
- project.native_targets.each do | target |
475
- if zabel_can_cache_target(target)
476
-
477
- target_context_file = "#{project.path}/#{target.name}.#{FILE_NAME_TARGET_CONTEXT}"
478
- unless File.exist? target_context_file
479
- next
480
- end
481
-
482
- target_context = YAML.load(File.read(target_context_file))
483
-
484
- if target_context[:target_status] == STATUS_MISS
485
- source_files = zabel_get_target_source_files(target)
486
-
487
- product_dir = target_context[BUILD_KEY_TARGET_BUILD_DIR]
488
- intermediate_dir = target_context[BUILD_KEY_TARGET_TEMP_DIR]
489
- xcframeworks_build_dir = target_context[BUILD_KEY_PODS_XCFRAMEWORKS_BUILD_DIR]
490
-
491
- dependency_files = zabel_get_dependency_files(target, intermediate_dir, product_dir, xcframeworks_build_dir)
492
- if source_files.size > 0 and dependency_files.size == 0 and target.product_type != "com.apple.product-type.bundle"
493
- puts "[ZABEL]<ERROR> #{target.name} should have dependent files"
494
- next
495
- end
496
- target_context[:dependency_files] = dependency_files - source_files
497
- target_md5_content = zabel_get_target_md5_content(project, target, configuration_name, argv, source_files)
498
- target_context[:target_md5_content] = target_md5_content
499
- target_md5 = Digest::MD5.hexdigest(target_md5_content)
500
- unless target_context[:target_md5] == target_md5
501
- puts "[ZABEL]<ERROR> #{target.name} md5 should not be changed after build"
502
- next
503
- end
504
- if target_context[BUILD_KEY_SRCROOT] and target_context[BUILD_KEY_SRCROOT].size > 0 and
505
- target_context[BUILD_KEY_MODULEMAP_FILE] and target_context[BUILD_KEY_MODULEMAP_FILE].size > 0
506
- if File.exist? Dir.pwd + "/" + zabel_get_content_without_pwd("#{target_context[BUILD_KEY_SRCROOT]}/#{target_context[BUILD_KEY_MODULEMAP_FILE]}")
507
- target_context[BUILD_KEY_MODULEMAP_FILE] = zabel_get_content_without_pwd("#{target_context[BUILD_KEY_SRCROOT]}/#{target_context[BUILD_KEY_MODULEMAP_FILE]}")
508
- else
509
- puts "[ZABEL]<ERROR> #{target.name} #{target_context[BUILD_KEY_MODULEMAP_FILE]} should be supported"
510
- next
511
- end
512
- end
513
- elsif target_context[:target_status] == STATUS_HIT
514
- if target_context[BUILD_KEY_MODULEMAP_FILE] and target_context[BUILD_KEY_MODULEMAP_FILE].size > 0
515
- if not File.exist? Dir.pwd + "/" + target_context[BUILD_KEY_MODULEMAP_FILE]
516
- puts "[ZABEL]<ERROR> #{target.name} #{target_context[BUILD_KEY_MODULEMAP_FILE]} should be supported"
517
- next
518
- end
519
- end
520
- else
521
- puts "[ZABEL]<ERROR> #{target.name} should be hit or miss"
522
- next
523
- end
524
-
525
- post_targets_context[target] = target_context
526
- end
527
- end
528
- end
529
-
530
- projects.each do | project |
531
- project.native_targets.each do | target |
532
- if post_targets_context.has_key? target
533
- target_context = post_targets_context[target]
534
- next unless target_context[:target_status] == STATUS_MISS
535
-
536
- dependency_targets_set = Set.new
537
- implicit_dependencies = []
538
-
539
- post_targets_context.each do | other_target, other_target_context |
540
- next if other_target == target
541
-
542
- next if target.product_type == "com.apple.product-type.bundle"
543
- next if other_target.product_type == "com.apple.product-type.bundle"
544
-
545
- target_context[:dependency_files].each do | dependency |
546
-
547
- if other_target_context[BUILD_KEY_TARGET_BUILD_DIR] and other_target_context[BUILD_KEY_TARGET_BUILD_DIR].size > 0 and
548
- dependency.start_with? other_target_context[BUILD_KEY_TARGET_BUILD_DIR] + "/"
549
- dependency_targets_set.add other_target
550
- implicit_dependencies.push dependency
551
- elsif other_target_context[BUILD_KEY_TARGET_TEMP_DIR] and other_target_context[BUILD_KEY_TARGET_TEMP_DIR].size > 0 and
552
- dependency.start_with? other_target_context[BUILD_KEY_TARGET_TEMP_DIR] + "/"
553
- dependency_targets_set.add other_target
554
- implicit_dependencies.push dependency
555
- elsif other_target_context[:target_build_dir_name] and other_target_context[:target_build_dir_name].size > 0 and
556
- dependency.start_with? target_context[BUILD_KEY_SYMROOT] + "/" + other_target_context[:target_build_dir_name] + "/"
557
- dependency_targets_set.add other_target
558
- implicit_dependencies.push dependency
559
- elsif other_target_context[:target_temp_dir_name] and other_target_context[:target_temp_dir_name].size > 0 and
560
- dependency.start_with? target_context[BUILD_KEY_OBJROOT] + "/" + other_target_context[:target_temp_dir_name] + "/"
561
- dependency_targets_set.add other_target
562
- implicit_dependencies.push dependency
563
- end
564
-
565
- unless zabel_should_not_detect_module_map_dependency
566
- if other_target_context[BUILD_KEY_MODULEMAP_FILE] and other_target_context[BUILD_KEY_MODULEMAP_FILE].size > 0 and
567
- dependency == Dir.pwd + "/" + other_target_context[BUILD_KEY_MODULEMAP_FILE]
568
- dependency_targets_set.add other_target
569
- end
570
- end
571
- end
572
-
573
- target_context[:dependency_files] = target_context[:dependency_files] - implicit_dependencies
574
-
575
- end
576
-
577
- target_context[:dependency_files] = target_context[:dependency_files] - implicit_dependencies
578
- dependency_files_md5 = []
579
- target_context[:dependency_files].each do | file |
580
- dependency_files_md5.push [zabel_get_content_without_pwd(file), zabel_get_file_md5(file)]
581
- end
582
- target_context[:dependency_files_md5] = dependency_files_md5.sort.uniq
583
-
584
- dependency_targets_md5 = dependency_targets_set.to_a.map { | target | [target.name, post_targets_context[target][:target_md5]]}
585
- target_context[:dependency_targets_md5] = dependency_targets_md5
586
-
587
- message = target_context[:target_md5_content]
588
-
589
- if zabel_add_cache(target, target_context, message)
590
- add_count = add_count + 1
591
- end
592
- end
593
- end
594
- end
595
-
596
- projects.each do | project |
597
- zabel_restore_project(project)
598
- end
599
-
600
- zabel_keep
601
-
602
- puts "[ZABEL]<INFO> total add #{add_count}"
603
-
604
- puts "[ZABEL]<INFO> duration = #{(Time.now - start_time).to_i} s in stage post"
605
-
606
- end
607
-
608
- def self.zabel_get_potential_hit_target_cache_dirs(target, target_md5)
609
- dependency_start_time = Time.now
610
- target_cache_dirs = Dir.glob(zabel_get_cache_root + "/" + target.name + "-" + target_md5 + "-*")
611
- file_time_hash = {}
612
- target_cache_dirs.each do | file |
613
- file_time_hash[file] = File.mtime(file)
614
- end
615
- target_cache_dirs = target_cache_dirs.sort_by {|file| - file_time_hash[file].to_f}
616
- potential_hit_target_cache_dirs = []
617
- target_cache_dirs.each do | target_cache_dir |
618
- next unless File.exist? target_cache_dir + "/" + FILE_NAME_PRODUCT
619
- next unless File.exist? target_cache_dir + "/" + FILE_NAME_CONTEXT
620
- target_context = YAML.load(File.read(target_cache_dir + "/" + FILE_NAME_CONTEXT))
621
- dependency_miss = false
622
- target_context[:dependency_files_md5].each do | item |
623
- dependency_file = item[0]
624
- dependency_md5 = item[1]
625
-
626
- unless File.exist? dependency_file
627
- puts "[ZABEL]<WARNING> #{target.name} #{dependency_file} file should exist to be hit"
628
- dependency_miss = true
629
- break
630
- end
631
- unless zabel_get_file_md5(dependency_file) == dependency_md5
632
- puts "[ZABEL]<WARNING> #{target.name} #{dependency_file} md5 should match to be hit"
633
- dependency_miss = true
634
- break
635
- end
636
- end
637
- if not dependency_miss
638
- if not target_context[:target_md5] == target_md5
639
- command = "rm -rf \"#{target_cache_dir}\""
640
- raise unless system command
641
- puts "[ZABEL]<ERROR> #{target.name} #{target_cache_dir} target md5 should match to be verified"
642
- dependency_miss = false
643
- next
644
- end
645
- if not target_context[:product_md5] == zabel_get_file_md5(target_cache_dir + "/" + FILE_NAME_PRODUCT)
646
- command = "rm -rf \"#{target_cache_dir}\""
647
- raise unless system command
648
- puts "[ZABEL]<ERROR> #{target.name} #{target_cache_dir} product md5 should match to be verified"
649
- dependency_miss = false
650
- next
651
- end
652
-
653
- potential_hit_target_cache_dirs.push target_cache_dir
654
- if target_context[:dependency_targets_md5].size == 0
655
- break
656
- end
657
- if potential_hit_target_cache_dirs.size > 10
658
- break
659
- end
660
- end
661
- end
662
- return potential_hit_target_cache_dirs
663
- end
664
-
665
- # see https://github.com/CocoaPods/Xcodeproj/blob/master/lib/xcodeproj/project/object/native_target.rb#L239
666
- # and this is faster, without searching deeply.
667
- def self.zabel_fast_add_dependency(project, target_target, target, subproject_reference)
668
- container_proxy = project.new(Xcodeproj::Project::PBXContainerItemProxy)
669
- container_proxy.container_portal = subproject_reference.uuid
670
- container_proxy.proxy_type = Xcodeproj::Constants::PROXY_TYPES[:native_target]
671
- container_proxy.remote_global_id_string = target.uuid
672
- container_proxy.remote_info = target.name
673
-
674
- dependency = project.new(Xcodeproj::Project::PBXTargetDependency)
675
- dependency.name = target.name
676
- dependency.target_proxy = container_proxy
677
-
678
- target_target.dependencies << dependency
679
- end
680
-
681
- def self.zabel_disable_build_and_inject_extract(project, target, inject_project, inject_target, inject_scripts, target_context)
682
- target_cache_dir = target_context[:hit_target_cache_dir]
447
+ if argv.include?("-configuration")
448
+ configuration_name = argv[argv.index("-configuration") + 1]
449
+ elsif argv.include?("--configuration")
450
+ configuration_name = argv[argv.index("--configuration") + 1]
451
+ end
452
+ unless configuration_name and configuration_name.size > 0
453
+ raise "[ZABEL]<ERROR> -configuration or --configuration should be set"
454
+ end
683
455
 
684
- # touch to update mtime
685
- raise unless system "touch \"#{target_cache_dir}\""
686
-
687
- # delete build phases to disable build command
688
- target.build_phases.delete_if { | build_phase |
689
- build_phase.class == Xcodeproj::Project::Object::PBXHeadersBuildPhase or
690
- build_phase.class == Xcodeproj::Project::Object::PBXSourcesBuildPhase or
691
- build_phase.class == Xcodeproj::Project::Object::PBXResourcesBuildPhase
692
- }
456
+ start_time = Time.now
457
+
458
+ add_count = 0
459
+
460
+ projects = zabel_get_projects
461
+
462
+ post_targets_context = {}
463
+
464
+ projects.each do | project |
465
+ project.native_targets.each do | target |
466
+ if zabel_can_cache_target(target)
467
+
468
+ target_context_file = "#{project.path}/#{target.name}.#{FILE_NAME_TARGET_CONTEXT}"
469
+ unless File.exist? target_context_file
470
+ next
471
+ end
472
+
473
+ target_context = YAML.load(File.read(target_context_file))
474
+
475
+ if target_context[:target_status] == STATUS_MISS
476
+ source_files = zabel_get_target_source_files(target)
477
+
478
+ product_dir = target_context[BUILD_KEY_CONFIGURATION_BUILD_DIR]
479
+ intermediate_dir = target_context[BUILD_KEY_TARGET_TEMP_DIR]
480
+ xcframeworks_build_dir = target_context[BUILD_KEY_PODS_XCFRAMEWORKS_BUILD_DIR]
693
481
 
694
- extract_script = "zabel #{STAGE_EXTRACT} \"#{target_cache_dir}\" \"#{target_context[:target_build_dir_name]}\" \"#{target_context[:target_temp_dir_name]}\""
482
+ dependency_files = zabel_get_dependency_files(target, intermediate_dir, product_dir, xcframeworks_build_dir)
483
+ if source_files.size > 0 and dependency_files.size == 0 and target.product_type != "com.apple.product-type.bundle"
484
+ puts "[ZABEL]<ERROR> #{target.name} should have dependent files"
485
+ next
486
+ end
487
+ target_context[:dependency_files] = dependency_files - source_files
488
+ target_md5_content = zabel_get_target_md5_content(project, target, configuration_name, argv, source_files)
489
+ target_context[:target_md5_content] = target_md5_content
490
+ target_md5 = Digest::MD5.hexdigest(target_md5_content)
491
+ unless target_context[:target_md5] == target_md5
492
+ puts "[ZABEL]<ERROR> #{target.name} md5 should not be changed after build"
493
+ next
494
+ end
495
+ if target_context[BUILD_KEY_SRCROOT] and target_context[BUILD_KEY_SRCROOT].size > 0 and
496
+ target_context[BUILD_KEY_MODULEMAP_FILE] and target_context[BUILD_KEY_MODULEMAP_FILE].size > 0
497
+ if File.exist? Dir.pwd + "/" + zabel_get_content_without_pwd("#{target_context[BUILD_KEY_SRCROOT]}/#{target_context[BUILD_KEY_MODULEMAP_FILE]}")
498
+ target_context[BUILD_KEY_MODULEMAP_FILE] = zabel_get_content_without_pwd("#{target_context[BUILD_KEY_SRCROOT]}/#{target_context[BUILD_KEY_MODULEMAP_FILE]}")
499
+ else
500
+ puts "[ZABEL]<ERROR> #{target.name} #{target_context[BUILD_KEY_MODULEMAP_FILE]} should be supported"
501
+ next
502
+ end
503
+ end
504
+ elsif target_context[:target_status] == STATUS_HIT
505
+ if target_context[BUILD_KEY_MODULEMAP_FILE] and target_context[BUILD_KEY_MODULEMAP_FILE].size > 0
506
+ if not File.exist? Dir.pwd + "/" + target_context[BUILD_KEY_MODULEMAP_FILE]
507
+ puts "[ZABEL]<ERROR> #{target.name} #{target_context[BUILD_KEY_MODULEMAP_FILE]} should be supported"
508
+ next
509
+ end
510
+ end
511
+ else
512
+ puts "[ZABEL]<ERROR> #{target.name} should be hit or miss"
513
+ next
514
+ end
515
+
516
+ post_targets_context[target] = target_context
517
+ end
518
+ end
519
+ end
520
+
521
+ projects.each do | project |
522
+ project.native_targets.each do | target |
523
+ if post_targets_context.has_key? target
524
+ target_context = post_targets_context[target]
525
+ next unless target_context[:target_status] == STATUS_MISS
526
+
527
+ dependency_targets_set = Set.new
528
+ implicit_dependencies = []
529
+
530
+ post_targets_context.each do | other_target, other_target_context |
531
+ next if other_target == target
532
+
533
+ next if target.product_type == "com.apple.product-type.bundle"
534
+ next if other_target.product_type == "com.apple.product-type.bundle"
535
+
536
+ target_context[:dependency_files].each do | dependency |
537
+
538
+ if other_target_context[BUILD_KEY_CONFIGURATION_BUILD_DIR] and other_target_context[BUILD_KEY_CONFIGURATION_BUILD_DIR].size > 0 and
539
+ dependency.start_with? other_target_context[BUILD_KEY_CONFIGURATION_BUILD_DIR] + "/"
540
+ dependency_targets_set.add other_target
541
+ implicit_dependencies.push dependency
542
+ elsif other_target_context[BUILD_KEY_TARGET_TEMP_DIR] and other_target_context[BUILD_KEY_TARGET_TEMP_DIR].size > 0 and
543
+ dependency.start_with? other_target_context[BUILD_KEY_TARGET_TEMP_DIR] + "/"
544
+ dependency_targets_set.add other_target
545
+ implicit_dependencies.push dependency
546
+ elsif other_target_context[:build_product_dir] and other_target_context[:build_product_dir].size > 0 and
547
+ dependency.start_with? target_context[BUILD_KEY_SYMROOT] + "/" + other_target_context[:build_product_dir] + "/"
548
+ dependency_targets_set.add other_target
549
+ implicit_dependencies.push dependency
550
+ elsif other_target_context[:build_intermediate_dir] and other_target_context[:build_intermediate_dir].size > 0 and
551
+ dependency.start_with? target_context[BUILD_KEY_OBJROOT] + "/" + other_target_context[:build_intermediate_dir] + "/"
552
+ dependency_targets_set.add other_target
553
+ implicit_dependencies.push dependency
554
+ end
555
+
556
+ unless zabel_should_not_detect_module_map_dependency
557
+ if other_target_context[BUILD_KEY_MODULEMAP_FILE] and other_target_context[BUILD_KEY_MODULEMAP_FILE].size > 0 and
558
+ dependency == Dir.pwd + "/" + other_target_context[BUILD_KEY_MODULEMAP_FILE]
559
+ dependency_targets_set.add other_target
560
+ end
561
+ end
562
+ end
563
+
564
+ target_context[:dependency_files] = target_context[:dependency_files] - implicit_dependencies
565
+
566
+ end
567
+
568
+ target_context[:dependency_files] = target_context[:dependency_files] - implicit_dependencies
569
+ dependency_files_md5 = []
570
+ target_context[:dependency_files].each do | file |
571
+ dependency_files_md5.push [zabel_get_content_without_pwd(file), zabel_get_file_md5(file)]
572
+ end
573
+ target_context[:dependency_files_md5] = dependency_files_md5.sort.uniq
574
+
575
+ dependency_targets_md5 = dependency_targets_set.to_a.map { | target | [target.name, post_targets_context[target][:target_md5]]}
576
+ target_context[:dependency_targets_md5] = dependency_targets_md5
577
+
578
+ message = target_context[:target_md5_content]
579
+
580
+ if zabel_add_cache(target, target_context, message)
581
+ add_count = add_count + 1
582
+ end
583
+ end
584
+ end
585
+ end
586
+
587
+ projects.each do | project |
588
+ zabel_restore_project(project)
589
+ end
590
+
591
+ zabel_keep
592
+
593
+ puts "[ZABEL]<INFO> total add #{add_count}"
594
+
595
+ puts "[ZABEL]<INFO> duration = #{(Time.now - start_time).to_i} s in stage post"
596
+
597
+ end
598
+
599
+ def self.zabel_get_potential_hit_target_cache_dirs(target, target_md5)
600
+ dependency_start_time = Time.now
601
+ target_cache_dirs = Dir.glob(zabel_get_cache_root + "/" + target.name + "-" + target_md5 + "-*")
602
+ file_time_hash = {}
603
+ target_cache_dirs.each do | file |
604
+ file_time_hash[file] = File.mtime(file)
605
+ end
606
+ target_cache_dirs = target_cache_dirs.sort_by {|file| - file_time_hash[file].to_f}
607
+ potential_hit_target_cache_dirs = []
608
+ target_cache_dirs.each do | target_cache_dir |
609
+ next unless File.exist? target_cache_dir + "/" + FILE_NAME_PRODUCT
610
+ next unless File.exist? target_cache_dir + "/" + FILE_NAME_CONTEXT
611
+ target_context = YAML.load(File.read(target_cache_dir + "/" + FILE_NAME_CONTEXT))
612
+ dependency_miss = false
613
+ target_context[:dependency_files_md5].each do | item |
614
+ dependency_file = item[0]
615
+ dependency_md5 = item[1]
616
+
617
+ unless File.exist? dependency_file
618
+ puts "[ZABEL]<WARNING> #{target.name} #{dependency_file} file should exist to be hit"
619
+ dependency_miss = true
620
+ break
621
+ end
622
+ unless zabel_get_file_md5(dependency_file) == dependency_md5
623
+ puts "[ZABEL]<WARNING> #{target.name} #{dependency_file} md5 should match to be hit"
624
+ dependency_miss = true
625
+ break
626
+ end
627
+ end
628
+ if not dependency_miss
629
+ if not target_context[:target_md5] == target_md5
630
+ command = "rm -rf \"#{target_cache_dir}\""
631
+ raise unless system command
632
+ puts "[ZABEL]<ERROR> #{target.name} #{target_cache_dir} target md5 should match to be verified"
633
+ dependency_miss = false
634
+ next
635
+ end
636
+ if not target_context[:product_md5] == zabel_get_file_md5(target_cache_dir + "/" + FILE_NAME_PRODUCT)
637
+ command = "rm -rf \"#{target_cache_dir}\""
638
+ raise unless system command
639
+ puts "[ZABEL]<ERROR> #{target.name} #{target_cache_dir} product md5 should match to be verified"
640
+ dependency_miss = false
641
+ next
642
+ end
643
+
644
+ potential_hit_target_cache_dirs.push target_cache_dir
645
+ if target_context[:dependency_targets_md5].size == 0
646
+ break
647
+ end
648
+ if potential_hit_target_cache_dirs.size > 10
649
+ break
650
+ end
651
+ end
652
+ end
653
+ return potential_hit_target_cache_dirs
654
+ end
655
+
656
+ # see https://github.com/CocoaPods/Xcodeproj/blob/master/lib/xcodeproj/project/object/native_target.rb#L239
657
+ # and this is faster, without searching deeply.
658
+ def self.zabel_fast_add_dependency(project, target_target, target, subproject_reference)
659
+ container_proxy = project.new(Xcodeproj::Project::PBXContainerItemProxy)
660
+ container_proxy.container_portal = subproject_reference.uuid
661
+ container_proxy.proxy_type = Xcodeproj::Constants::PROXY_TYPES[:native_target]
662
+ container_proxy.remote_global_id_string = target.uuid
663
+ container_proxy.remote_info = target.name
664
+
665
+ dependency = project.new(Xcodeproj::Project::PBXTargetDependency)
666
+ dependency.name = target.name
667
+ dependency.target_proxy = container_proxy
668
+
669
+ target_target.dependencies << dependency
670
+ end
671
+
672
+ def self.zabel_disable_build_and_inject_extract(project, target, inject_project, inject_target, inject_scripts, target_context)
673
+ target_cache_dir = target_context[:hit_target_cache_dir]
695
674
 
696
- if zabel_should_extract_once
697
- subproject_reference = nil
698
- project.main_group.files.each do | file |
699
- if file.class == Xcodeproj::Project::Object::PBXFileReference and File.basename(file.path) == File.basename(inject_project.path)
700
- subproject_reference = file
701
- break
702
- end
703
- end
704
-
705
- unless subproject_reference
706
- subproject_reference = project.main_group.new_reference(inject_project.path, :group)
707
- end
708
-
709
- zabel_fast_add_dependency(project, target, inject_target, subproject_reference)
710
-
711
- inject_scripts.push extract_script
712
- else
713
- inject_phase = target.new_shell_script_build_phase("zabel_extract_#{target.name}")
714
- inject_phase.shell_script = extract_script
715
- inject_phase.show_env_vars_in_log = '0'
716
- end
717
- end
718
-
719
- def self.zabel_inject_printenv(project, target)
720
- inject_phase = target.new_shell_script_build_phase("zabel_printenv_#{target.name}")
721
- inject_phase.shell_script = "zabel #{STAGE_PRINTENV} #{target.name} \"#{project.path}\""
722
- inject_phase.show_env_vars_in_log = '0'
723
- end
724
-
725
- def self.zabel_pre(argv)
726
-
727
- unless argv.index("-configuration")
728
- raise "[ZABEL]<ERROR> -configuration should be set"
729
- end
730
- configuration_name = argv[argv.index("-configuration") + 1]
675
+ # touch to update mtime
676
+ raise unless system "touch \"#{target_cache_dir}\""
677
+
678
+ # delete build phases to disable build command
679
+ target.build_phases.delete_if { | build_phase |
680
+ build_phase.class == Xcodeproj::Project::Object::PBXHeadersBuildPhase or
681
+ build_phase.class == Xcodeproj::Project::Object::PBXSourcesBuildPhase or
682
+ build_phase.class == Xcodeproj::Project::Object::PBXResourcesBuildPhase
683
+ }
731
684
 
732
- start_time = Time.now
733
-
734
- if ENV["ZABEL_CLEAR_ALL"] == "YES"
735
- command = "rm -rf \"#{zabel_get_cache_root}\""
736
- puts command
737
- raise unless system command
738
- end
739
-
740
- zabel_clean_temp_files
741
-
742
- if zabel_should_extract_once
743
- inject_project = Xcodeproj::Project.new("Pods/zabel.xcodeproj")
744
- inject_target = inject_project.new_aggregate_target("zabel")
745
- inject_phase = inject_target.new_shell_script_build_phase("zabel_extract")
746
- inject_phase.show_env_vars_in_log = '0'
747
- inject_project.save
748
- inject_scripts = []
749
- end
750
-
751
- projects = zabel_get_projects
752
-
753
- pre_targets_context = {}
754
-
755
- hit_count = 0
756
- miss_count = 0
757
- hit_target_md5_cache_set = Set.new
758
- iteration_count = 0
759
-
760
- projects.each do | project |
761
- project.native_targets.each do | target |
762
- if zabel_can_cache_target(target)
763
- source_files = zabel_get_target_source_files(target)
764
- next unless source_files.size >= zabel_get_min_source_file_count
765
- target_md5_content = zabel_get_target_md5_content(project, target, configuration_name, argv, source_files)
766
- target_md5 = Digest::MD5.hexdigest(target_md5_content)
767
- potential_hit_target_cache_dirs = zabel_get_potential_hit_target_cache_dirs(target, target_md5)
768
-
769
- target_context = {}
770
- target_context[:target_md5] = target_md5
771
- target_context[:potential_hit_target_cache_dirs] = potential_hit_target_cache_dirs
772
- if potential_hit_target_cache_dirs.size == 0
773
- puts "[ZABEL]<INFO> miss #{target.name} #{target_md5} in iteration #{iteration_count}"
774
- target_context[:target_status] = STATUS_MISS
775
- miss_count = miss_count + 1
776
- end
777
- pre_targets_context[target] = target_context
778
- end
779
- end
780
- end
781
-
782
- while true
783
- iteration_count = iteration_count + 1
784
- confirm_count = hit_count + miss_count
785
- projects.each do | project |
786
- project.native_targets.each do | target |
787
- next unless pre_targets_context.has_key? target
788
- target_context = pre_targets_context[target]
789
- next if target_context[:target_status] == STATUS_MISS
790
- next if target_context[:target_status] == STATUS_HIT
791
- potential_hit_target_cache_dirs = target_context[:potential_hit_target_cache_dirs]
792
- next if potential_hit_target_cache_dirs.size == 0
793
-
794
- hit_target_cache_dir = nil
795
- potential_hit_target_cache_dirs.each do | target_cache_dir |
796
- next unless File.exist? target_cache_dir + "/" + FILE_NAME_CONTEXT
797
- hit_target_context = YAML.load(File.read(target_cache_dir + "/" + FILE_NAME_CONTEXT))
798
- hit_target_cache_dir = target_cache_dir
799
- hit_target_context[:dependency_targets_md5].each do | item |
800
- dependency_target = item[0]
801
- dependency_target_md5 = item[1]
802
-
803
- # cycle dependency targets will be miss every time.
804
- # TODO: to detect cycle dependency so that cache will not be added,
805
- # or to hit cache together with some kind of algorithms.
806
- unless hit_target_md5_cache_set.include? "#{dependency_target}-#{dependency_target_md5}"
807
- hit_target_cache_dir = nil
808
- break
809
- end
810
- end
811
- if hit_target_cache_dir
812
- target_context = target_context.merge!(hit_target_context)
813
- break
814
- end
815
- end
816
- if hit_target_cache_dir
817
- puts "[ZABEL]<INFO> hit #{target.name} #{target_context[:target_md5]} in iteration #{iteration_count} potential #{potential_hit_target_cache_dirs.size}"
818
- target_context[:target_status] = STATUS_HIT
819
- target_context[:hit_target_cache_dir] = hit_target_cache_dir
820
- hit_count = hit_count + 1
821
- hit_target_md5_cache_set.add "#{target.name}-#{target_context[:target_md5]}"
822
- end
823
- end
824
- end
825
- if hit_count + miss_count == confirm_count
826
- break
827
- end
828
- end
829
-
830
- projects.each do | project |
831
- should_save = false
832
- project.native_targets.each do | target |
833
- next unless pre_targets_context.has_key? target
834
- target_context = pre_targets_context[target]
835
-
836
- if target_context[:target_status] == STATUS_HIT
837
- zabel_disable_build_and_inject_extract(project, target, inject_project, inject_target, inject_scripts, target_context)
838
- else
839
- unless target_context[:target_status] == STATUS_MISS
840
- target_context[:target_status] = STATUS_MISS
841
- puts "[ZABEL]<INFO> miss #{target.name} #{target_context[:target_md5]} in iteration #{iteration_count}"
842
- miss_count = miss_count + 1
843
- end
844
- zabel_inject_printenv(project, target)
845
- end
846
- File.write("#{project.path}/#{target.name}.#{FILE_NAME_TARGET_CONTEXT}", target_context.to_yaml)
685
+ extract_script = "#{$0} #{STAGE_EXTRACT} \"#{target_cache_dir}\" \"#{target_context[:build_product_dir]}\" \"#{target_context[:build_intermediate_dir]}\""
847
686
 
848
- should_save = true
849
- end
687
+ if zabel_should_extract_once
688
+ subproject_reference = nil
689
+ project.main_group.files.each do | file |
690
+ if file.class == Xcodeproj::Project::Object::PBXFileReference and File.basename(file.path) == File.basename(inject_project.path)
691
+ subproject_reference = file
692
+ break
693
+ end
694
+ end
695
+
696
+ unless subproject_reference
697
+ subproject_reference = project.main_group.new_reference(inject_project.path, :group)
698
+ end
699
+
700
+ zabel_fast_add_dependency(project, target, inject_target, subproject_reference)
701
+
702
+ inject_scripts.push extract_script
703
+ else
704
+ inject_phase = target.new_shell_script_build_phase("zabel_extract_#{target.name}")
705
+ inject_phase.shell_script = extract_script
706
+ inject_phase.show_env_vars_in_log = '1'
707
+ end
708
+ end
709
+
710
+ def self.zabel_inject_printenv(project, target)
711
+ inject_phase = target.new_shell_script_build_phase("zabel_printenv_#{target.name}")
712
+ inject_phase.shell_script = "#{$0} #{STAGE_PRINTENV} #{target.name} \"#{project.path}\""
713
+ inject_phase.show_env_vars_in_log = '1'
714
+ end
715
+
716
+ def self.zabel_pre(argv)
717
+
718
+ configuration_name = nil
850
719
 
851
- if should_save
852
- zabel_backup_project(project)
853
- project.save
854
- else
855
- zabel_clean_backup_project(project)
856
- end
857
- end
858
-
859
- if zabel_should_extract_once and inject_scripts.size > 0
860
- inject_scripts = (["startTime_s=`date +%s`"] + inject_scripts + ["echo \"[ZABEL]<INFO> duration = $[ `date +%s` - $startTime_s ] s in stage #{STAGE_EXTRACT}\""]).flatten
861
- inject_phase.shell_script = inject_scripts.join("\n")
862
- inject_project.save
863
- end
720
+ if argv.include?("-configuration")
721
+ configuration_name = argv[argv.index("-configuration") + 1]
722
+ elsif argv.include?("--configuration")
723
+ configuration_name = argv[argv.index("--configuration") + 1]
724
+ end
725
+ unless configuration_name and configuration_name.size > 0
726
+ raise "[ZABEL]<ERROR> -configuration or --configuration should be set"
727
+ end
864
728
 
865
- puts "[ZABEL]<INFO> total #{hit_count + miss_count} hit #{hit_count} miss #{miss_count} iteration #{iteration_count}"
866
-
867
- puts "[ZABEL]<INFO> duration = #{(Time.now - start_time).to_i} s in stage pre"
868
- end
869
-
870
- def self.zabel_extract
871
- target_cache_dir = ARGV[1]
872
- product_path = ARGV[2]
873
- intermediate_path = ARGV[3]
874
-
875
- cache_product_path = target_cache_dir + "/#{FILE_NAME_PRODUCT}"
876
-
877
- start_time = Time.now
878
- command = "mkdir -p \"#{ENV[BUILD_KEY_SYMROOT]}/#{product_path}\" && cd \"#{ENV[BUILD_KEY_SYMROOT]}/#{product_path}/..\" && tar -xf \"#{cache_product_path}\""
879
- puts command
880
- raise unless system command
881
-
882
- end
883
-
884
- def self.zabel_printenv
885
- target_name = ARGV[1]
886
- project_path = ARGV[2]
887
-
888
- target_context = YAML.load(File.read("#{project_path}/#{target_name}.#{FILE_NAME_TARGET_CONTEXT}"))
889
-
890
- # see https://developer.apple.com/library/archive/documentation/DeveloperTools/Reference/XcodeBuildSettingRef/1-Build_Setting_Reference/build_setting_ref.html
891
- [BUILD_KEY_SYMROOT, BUILD_KEY_TARGET_BUILD_DIR, BUILD_KEY_OBJROOT, BUILD_KEY_TARGET_TEMP_DIR, BUILD_KEY_PODS_XCFRAMEWORKS_BUILD_DIR, BUILD_KEY_MODULEMAP_FILE, BUILD_KEY_SRCROOT, BUILD_KEY_WRAPPER_NAME].sort.each do | key |
892
- if ENV[key]
893
- target_context[key] = ENV[key]
894
- end
895
- end
896
- File.write("#{project_path}/#{target_name}.#{FILE_NAME_TARGET_CONTEXT}", target_context.to_yaml)
897
- end
898
-
899
- def self.zabel_clean
900
- if File.exist? "Pods/zabel.xcodeproj"
901
- command = "rm -rf Pods/*.xcodeproj"
902
- puts command
903
- raise unless system command
904
- end
905
- zabel_clean_temp_files
906
- end
729
+ start_time = Time.now
730
+
731
+ if ENV["ZABEL_CLEAR_ALL"] == "YES"
732
+ command = "rm -rf \"#{zabel_get_cache_root}\""
733
+ puts command
734
+ raise unless system command
735
+ end
736
+
737
+ zabel_clean_temp_files
738
+
739
+ if zabel_should_extract_once
740
+ inject_project = Xcodeproj::Project.new("Pods/zabel.xcodeproj")
741
+ inject_target = inject_project.new_aggregate_target("zabel")
742
+ inject_phase = inject_target.new_shell_script_build_phase("zabel_extract")
743
+ inject_phase.show_env_vars_in_log = '1'
744
+ inject_project.save
745
+ inject_scripts = []
746
+ end
747
+
748
+ projects = zabel_get_projects
749
+
750
+ pre_targets_context = {}
751
+
752
+ hit_count = 0
753
+ miss_count = 0
754
+ hit_target_md5_cache_set = Set.new
755
+ iteration_count = 0
756
+
757
+ projects.each do | project |
758
+ project.native_targets.each do | target |
759
+ if zabel_can_cache_target(target)
760
+ source_files = zabel_get_target_source_files(target)
761
+ next unless source_files.size >= zabel_get_min_source_file_count
762
+ target_md5_content = zabel_get_target_md5_content(project, target, configuration_name, argv, source_files)
763
+ target_md5 = Digest::MD5.hexdigest(target_md5_content)
764
+ potential_hit_target_cache_dirs = zabel_get_potential_hit_target_cache_dirs(target, target_md5)
765
+
766
+ target_context = {}
767
+ target_context[:target_md5] = target_md5
768
+ target_context[:potential_hit_target_cache_dirs] = potential_hit_target_cache_dirs
769
+ if potential_hit_target_cache_dirs.size == 0
770
+ puts "[ZABEL]<INFO> miss #{target.name} #{target_md5} in iteration #{iteration_count}"
771
+ target_context[:target_status] = STATUS_MISS
772
+ miss_count = miss_count + 1
773
+ end
774
+ pre_targets_context[target] = target_context
775
+ end
776
+ end
777
+ end
778
+
779
+ while true
780
+ iteration_count = iteration_count + 1
781
+ confirm_count = hit_count + miss_count
782
+ projects.each do | project |
783
+ project.native_targets.each do | target |
784
+ next unless pre_targets_context.has_key? target
785
+ target_context = pre_targets_context[target]
786
+ next if target_context[:target_status] == STATUS_MISS
787
+ next if target_context[:target_status] == STATUS_HIT
788
+ potential_hit_target_cache_dirs = target_context[:potential_hit_target_cache_dirs]
789
+ next if potential_hit_target_cache_dirs.size == 0
790
+
791
+ hit_target_cache_dir = nil
792
+ potential_hit_target_cache_dirs.each do | target_cache_dir |
793
+ next unless File.exist? target_cache_dir + "/" + FILE_NAME_CONTEXT
794
+ hit_target_context = YAML.load(File.read(target_cache_dir + "/" + FILE_NAME_CONTEXT))
795
+ hit_target_cache_dir = target_cache_dir
796
+ hit_target_context[:dependency_targets_md5].each do | item |
797
+ dependency_target = item[0]
798
+ dependency_target_md5 = item[1]
799
+
800
+ # cycle dependency targets will be miss every time.
801
+ # TODO: to detect cycle dependency so that cache will not be added,
802
+ # or to hit cache together with some kind of algorithms.
803
+ unless hit_target_md5_cache_set.include? "#{dependency_target}-#{dependency_target_md5}"
804
+ hit_target_cache_dir = nil
805
+ break
806
+ end
807
+ end
808
+ if hit_target_cache_dir
809
+ target_context = target_context.merge!(hit_target_context)
810
+ break
811
+ end
812
+ end
813
+ if hit_target_cache_dir
814
+ puts "[ZABEL]<INFO> hit #{target.name} #{target_context[:target_md5]} in iteration #{iteration_count} potential #{potential_hit_target_cache_dirs.size}"
815
+ target_context[:target_status] = STATUS_HIT
816
+ target_context[:hit_target_cache_dir] = hit_target_cache_dir
817
+ hit_count = hit_count + 1
818
+ hit_target_md5_cache_set.add "#{target.name}-#{target_context[:target_md5]}"
819
+ end
820
+ end
821
+ end
822
+ if hit_count + miss_count == confirm_count
823
+ break
824
+ end
825
+ end
826
+
827
+ projects.each do | project |
828
+ should_save = false
829
+ project.native_targets.each do | target |
830
+ next unless pre_targets_context.has_key? target
831
+ target_context = pre_targets_context[target]
832
+
833
+ if target_context[:target_status] == STATUS_HIT
834
+ zabel_disable_build_and_inject_extract(project, target, inject_project, inject_target, inject_scripts, target_context)
835
+ else
836
+ unless target_context[:target_status] == STATUS_MISS
837
+ target_context[:target_status] = STATUS_MISS
838
+ puts "[ZABEL]<INFO> miss #{target.name} #{target_context[:target_md5]} in iteration #{iteration_count}"
839
+ miss_count = miss_count + 1
840
+ end
841
+ zabel_inject_printenv(project, target)
842
+ end
843
+ File.write("#{project.path}/#{target.name}.#{FILE_NAME_TARGET_CONTEXT}", target_context.to_yaml)
844
+
845
+ should_save = true
846
+ end
847
+
848
+ if should_save
849
+ zabel_backup_project(project)
850
+ project.save
851
+ else
852
+ zabel_clean_backup_project(project)
853
+ end
854
+ end
855
+
856
+ if zabel_should_extract_once and inject_scripts.size > 0
857
+ inject_scripts = (["startTime_s=`date +%s`"] + inject_scripts + ["echo \"[ZABEL]<INFO> duration = $[ `date +%s` - $startTime_s ] s in stage #{STAGE_EXTRACT}\""]).flatten
858
+ inject_phase.shell_script = inject_scripts.join("\n")
859
+ inject_project.save
860
+ end
861
+
862
+ puts "[ZABEL]<INFO> total #{hit_count + miss_count} hit #{hit_count} miss #{miss_count} iteration #{iteration_count}"
863
+
864
+ puts "[ZABEL]<INFO> duration = #{(Time.now - start_time).to_i} s in stage pre"
865
+ end
866
+
867
+ def self.zabel_extract
868
+ target_cache_dir = ARGV[1]
869
+ product_path = ARGV[2]
870
+
871
+ cache_product_path = target_cache_dir + "/#{FILE_NAME_PRODUCT}"
872
+
873
+ start_time = Time.now
874
+ command = "mkdir -p \"#{ENV[BUILD_KEY_SYMROOT]}/#{product_path}\" && cd \"#{ENV[BUILD_KEY_SYMROOT]}/#{product_path}/..\" && tar -xf \"#{cache_product_path}\""
875
+ puts command
876
+ raise unless system command
877
+
878
+ end
879
+
880
+ def self.zabel_printenv
881
+ target_name = ARGV[1]
882
+ project_path = ARGV[2]
883
+
884
+ target_context = YAML.load(File.read("#{project_path}/#{target_name}.#{FILE_NAME_TARGET_CONTEXT}"))
885
+
886
+ # see https://developer.apple.com/library/archive/documentation/DeveloperTools/Reference/XcodeBuildSettingRef/1-Build_Setting_Reference/build_setting_ref.html
887
+ [BUILD_KEY_SYMROOT, BUILD_KEY_CONFIGURATION_BUILD_DIR, BUILD_KEY_OBJROOT, BUILD_KEY_TARGET_TEMP_DIR, BUILD_KEY_PODS_XCFRAMEWORKS_BUILD_DIR, BUILD_KEY_MODULEMAP_FILE, BUILD_KEY_SRCROOT, BUILD_KEY_FULL_PRODUCT_NAME].sort.each do | key |
888
+ if ENV[key]
889
+ target_context[key] = ENV[key]
890
+ end
891
+ end
892
+ File.write("#{project_path}/#{target_name}.#{FILE_NAME_TARGET_CONTEXT}", target_context.to_yaml)
893
+ end
894
+
895
+ def self.zabel_clean
896
+ if File.exist? "Pods/zabel.xcodeproj"
897
+ command = "rm -rf Pods/*.xcodeproj"
898
+ puts command
899
+ raise unless system command
900
+ end
901
+ zabel_clean_temp_files
902
+ end
907
903
 
908
904
  end