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 +4 -4
- data/.gitignore +3 -3
- data/README.md +11 -10
- data/bin/zabel +6 -5
- data/lib/zabel.rb +874 -878
- data/lib/zabel/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 6b4bd5a362ce7f4aa9c80a6e1f385f21cdd60b07c7d76bd27aaacb127a5eb037
|
4
|
+
data.tar.gz: 842369d90e5a1f878d51b9d439c62de4c123f3fbfd7bd63bca2735f24ac302d9
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: e44882f989a91d79be7467f1c81af6625f74509219ea8e761814eba5bafa9413263b9e0708ea9d2d42f637da03ccea990eaecfdaf337d540ee12576750aece13
|
7
|
+
data.tar.gz: 6dbf765804557e693cc30e569ea7cf17ceffaf1c6cfb516eb745e1d972248551bb4490fc14a660c2de55296fedd67f7f3afb585427729623995f5ca479fe97e5
|
data/.gitignore
CHANGED
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
|
-
|
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
|
52
|
-
xcodebuild
|
53
|
-
zabel post -configuration Release
|
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/
|
69
|
+
ruby test/one.rb test/case/simple/Podfile
|
69
70
|
# test one todo case
|
70
|
-
ruby test/todo/modulemap_file/
|
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
|
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
|
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
|
-
|
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 "
|
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
|
-
|
12
|
+
class Error < StandardError; end
|
13
13
|
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
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
|
-
|
24
|
-
|
23
|
+
STATUS_HIT = "hit"
|
24
|
+
STATUS_MISS = "miss"
|
25
25
|
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
26
|
+
STAGE_CLEAN = "clean"
|
27
|
+
STAGE_EXTRACT = "extract"
|
28
|
+
STAGE_PRINTENV = "printenv"
|
29
|
+
STAGE_PRE = "pre"
|
30
|
+
STAGE_POST = "post"
|
31
31
|
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
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
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
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
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
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
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
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
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
|
225
|
-
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
|
233
|
-
|
234
|
-
|
235
|
-
|
236
|
-
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
|
241
|
-
|
242
|
-
|
243
|
-
|
244
|
-
|
245
|
-
|
246
|
-
|
247
|
-
|
248
|
-
|
249
|
-
|
250
|
-
|
251
|
-
|
252
|
-
|
253
|
-
|
254
|
-
|
255
|
-
|
256
|
-
|
257
|
-
|
258
|
-
|
259
|
-
|
260
|
-
|
261
|
-
|
262
|
-
|
263
|
-
|
264
|
-
|
265
|
-
|
266
|
-
|
267
|
-
|
268
|
-
|
269
|
-
|
270
|
-
|
271
|
-
|
272
|
-
|
273
|
-
|
274
|
-
|
275
|
-
|
276
|
-
|
277
|
-
|
278
|
-
|
279
|
-
|
280
|
-
|
281
|
-
|
282
|
-
|
283
|
-
|
284
|
-
|
285
|
-
|
286
|
-
|
287
|
-
|
288
|
-
|
289
|
-
|
290
|
-
|
291
|
-
|
292
|
-
|
293
|
-
|
294
|
-
|
295
|
-
|
296
|
-
|
297
|
-
|
298
|
-
|
299
|
-
|
300
|
-
|
301
|
-
|
302
|
-
|
303
|
-
|
304
|
-
|
305
|
-
|
306
|
-
|
307
|
-
|
308
|
-
|
309
|
-
|
310
|
-
|
311
|
-
|
312
|
-
|
313
|
-
|
314
|
-
|
315
|
-
|
316
|
-
|
317
|
-
|
318
|
-
|
319
|
-
|
320
|
-
|
321
|
-
|
322
|
-
|
323
|
-
|
324
|
-
|
325
|
-
|
326
|
-
|
327
|
-
|
328
|
-
|
329
|
-
|
330
|
-
|
331
|
-
|
332
|
-
|
333
|
-
|
334
|
-
|
335
|
-
|
336
|
-
|
337
|
-
|
338
|
-
|
339
|
-
|
340
|
-
|
341
|
-
|
342
|
-
|
343
|
-
|
344
|
-
|
345
|
-
|
346
|
-
|
347
|
-
|
348
|
-
|
349
|
-
|
350
|
-
|
351
|
-
|
352
|
-
|
353
|
-
|
354
|
-
|
355
|
-
|
356
|
-
|
357
|
-
|
358
|
-
|
359
|
-
|
360
|
-
|
361
|
-
|
362
|
-
|
363
|
-
|
364
|
-
|
365
|
-
|
366
|
-
|
367
|
-
|
368
|
-
|
369
|
-
|
370
|
-
|
371
|
-
|
372
|
-
|
373
|
-
|
374
|
-
|
375
|
-
|
376
|
-
|
377
|
-
|
378
|
-
|
379
|
-
|
380
|
-
|
381
|
-
|
382
|
-
|
383
|
-
|
384
|
-
|
385
|
-
|
386
|
-
|
387
|
-
|
388
|
-
|
389
|
-
|
390
|
-
|
391
|
-
|
392
|
-
|
393
|
-
|
394
|
-
|
395
|
-
|
396
|
-
|
397
|
-
|
398
|
-
|
399
|
-
|
400
|
-
|
401
|
-
|
402
|
-
|
403
|
-
|
404
|
-
|
405
|
-
|
406
|
-
|
407
|
-
|
408
|
-
|
409
|
-
|
410
|
-
|
411
|
-
|
412
|
-
|
413
|
-
|
414
|
-
|
415
|
-
|
416
|
-
|
417
|
-
|
418
|
-
|
419
|
-
|
420
|
-
|
421
|
-
|
422
|
-
|
423
|
-
|
424
|
-
|
425
|
-
|
426
|
-
|
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
|
-
|
444
|
-
|
445
|
-
|
446
|
-
|
447
|
-
|
448
|
-
|
449
|
-
|
450
|
-
|
451
|
-
|
452
|
-
|
453
|
-
|
454
|
-
|
455
|
-
|
456
|
-
|
457
|
-
|
458
|
-
|
459
|
-
|
460
|
-
|
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
|
-
|
466
|
-
|
467
|
-
|
468
|
-
|
469
|
-
|
470
|
-
|
471
|
-
|
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
|
-
|
685
|
-
|
686
|
-
|
687
|
-
|
688
|
-
|
689
|
-
|
690
|
-
|
691
|
-
|
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
|
-
|
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
|
-
|
697
|
-
|
698
|
-
|
699
|
-
|
700
|
-
|
701
|
-
|
702
|
-
|
703
|
-
|
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
|
-
|
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
|
-
|
849
|
-
|
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
|
-
|
852
|
-
|
853
|
-
|
854
|
-
|
855
|
-
|
856
|
-
|
857
|
-
|
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
|
-
|
866
|
-
|
867
|
-
|
868
|
-
|
869
|
-
|
870
|
-
|
871
|
-
|
872
|
-
|
873
|
-
|
874
|
-
|
875
|
-
|
876
|
-
|
877
|
-
|
878
|
-
|
879
|
-
|
880
|
-
|
881
|
-
|
882
|
-
|
883
|
-
|
884
|
-
|
885
|
-
|
886
|
-
|
887
|
-
|
888
|
-
|
889
|
-
|
890
|
-
|
891
|
-
|
892
|
-
|
893
|
-
|
894
|
-
|
895
|
-
|
896
|
-
|
897
|
-
|
898
|
-
|
899
|
-
|
900
|
-
|
901
|
-
|
902
|
-
|
903
|
-
|
904
|
-
|
905
|
-
|
906
|
-
|
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
|