@@ -8,32 +8,21 @@ pub mod packages;
8
8
pub mod parse;
9
9
pub mod read_compile_state;
10
10
11
+ use crate :: build:: compile:: { mark_modules_with_deleted_deps_dirty, mark_modules_with_expired_deps_dirty} ;
11
12
use crate :: helpers:: emojis:: * ;
12
13
use crate :: helpers:: { self , get_workspace_root} ;
14
+ use ahash:: AHashSet ;
13
15
use build_types:: * ;
14
16
use console:: style;
15
17
use indicatif:: { ProgressBar , ProgressStyle } ;
16
18
use serde:: Serialize ;
17
19
use std:: io:: { stdout, Write } ;
18
20
use std:: path:: PathBuf ;
19
- use std:: process:: Command ;
20
- use std:: time:: Instant ;
21
+ use std:: time:: { Duration , Instant } ;
21
22
22
23
use self :: compile:: compiler_args;
23
24
use self :: parse:: parser_args;
24
25
25
- pub fn get_version ( bsc_path : & str ) -> String {
26
- let version_cmd = Command :: new ( bsc_path)
27
- . args ( [ "-v" ] )
28
- . output ( )
29
- . expect ( "failed to find version" ) ;
30
-
31
- std:: str:: from_utf8 ( & version_cmd. stdout )
32
- . expect ( "Could not read version from rescript" )
33
- . replace ( "\n " , "" )
34
- . replace ( "ReScript " , "" )
35
- }
36
-
37
26
fn is_dirty ( module : & Module ) -> bool {
38
27
match module. source_type {
39
28
SourceType :: SourceFile ( SourceFile {
@@ -45,7 +34,7 @@ fn is_dirty(module: &Module) -> bool {
45
34
..
46
35
} ) => true ,
47
36
SourceType :: SourceFile ( _) => false ,
48
- SourceType :: MlMap ( MlMap { dirty, .. } ) => module . compile_dirty || dirty,
37
+ SourceType :: MlMap ( MlMap { dirty, .. } ) => dirty,
49
38
}
50
39
}
51
40
@@ -65,11 +54,11 @@ pub fn get_compiler_args(path: &str) -> String {
65
54
packages:: get_package_name ( & workspace_root. to_owned ( ) . unwrap_or ( package_root. to_owned ( ) ) ) ;
66
55
let package_name = packages:: get_package_name ( & package_root) ;
67
56
let bsc_path = helpers:: get_bsc ( & package_root, workspace_root. to_owned ( ) ) ;
68
- let rescript_version = get_version ( & bsc_path) ;
57
+ let rescript_version = helpers :: get_rescript_version ( & bsc_path) ;
69
58
let packages = packages:: make (
70
59
& None ,
71
60
& workspace_root. to_owned ( ) . unwrap_or ( package_root. to_owned ( ) ) ,
72
- workspace_root. to_owned ( ) ,
61
+ & workspace_root,
73
62
) ;
74
63
// make PathBuf from package root and get the relative path for filename
75
64
let relative_filename = PathBuf :: from ( & filename)
@@ -111,18 +100,16 @@ pub fn get_compiler_args(path: &str) -> String {
111
100
. unwrap ( )
112
101
}
113
102
114
- pub fn build ( filter : & Option < regex:: Regex > , path : & str , no_timing : bool ) -> Result < BuildState , ( ) > {
115
- let timing_total = Instant :: now ( ) ;
103
+ pub fn initialize_build < ' a > (
104
+ default_timing : Option < Duration > ,
105
+ filter : & Option < regex:: Regex > ,
106
+ path : & str ,
107
+ ) -> Result < BuildState , ( ) > {
116
108
let project_root = helpers:: get_abs_path ( path) ;
117
109
let workspace_root = helpers:: get_workspace_root ( & project_root) ;
118
110
let bsc_path = helpers:: get_bsc ( & project_root, workspace_root. to_owned ( ) ) ;
119
111
let root_config_name = packages:: get_package_name ( & project_root) ;
120
- let rescript_version = get_version ( & bsc_path) ;
121
- let default_timing: Option < std:: time:: Duration > = if no_timing {
122
- Some ( std:: time:: Duration :: new ( 0.0 as u64 , 0.0 as u32 ) )
123
- } else {
124
- None
125
- } ;
112
+ let rescript_version = helpers:: get_rescript_version ( & bsc_path) ;
126
113
127
114
print ! (
128
115
"{} {} Building package tree..." ,
@@ -131,7 +118,7 @@ pub fn build(filter: &Option<regex::Regex>, path: &str, no_timing: bool) -> Resu
131
118
) ;
132
119
let _ = stdout ( ) . flush ( ) ;
133
120
let timing_package_tree = Instant :: now ( ) ;
134
- let packages = packages:: make ( & filter, & project_root, workspace_root. to_owned ( ) ) ;
121
+ let packages = packages:: make ( & filter, & project_root, & workspace_root) ;
135
122
let timing_package_tree_elapsed = timing_package_tree. elapsed ( ) ;
136
123
137
124
println ! (
@@ -156,9 +143,15 @@ pub fn build(filter: &Option<regex::Regex>, path: &str, no_timing: bool) -> Resu
156
143
LOOKING_GLASS
157
144
) ;
158
145
let _ = stdout ( ) . flush ( ) ;
159
- let mut build_state = BuildState :: new ( project_root, root_config_name, packages) ;
146
+ let mut build_state = BuildState :: new (
147
+ project_root,
148
+ root_config_name,
149
+ packages,
150
+ workspace_root,
151
+ rescript_version,
152
+ bsc_path,
153
+ ) ;
160
154
packages:: parse_packages ( & mut build_state) ;
161
- logs:: initialize ( & build_state. packages ) ;
162
155
let timing_source_files_elapsed = timing_source_files. elapsed ( ) ;
163
156
println ! (
164
157
"{}\r {} {}Found source files in {:.2}s" ,
@@ -171,53 +164,72 @@ pub fn build(filter: &Option<regex::Regex>, path: &str, no_timing: bool) -> Resu
171
164
) ;
172
165
173
166
print ! (
174
- "{} {} Cleaning up previous build ..." ,
167
+ "{} {} Reading compile state ..." ,
175
168
style( "[3/7]" ) . bold( ) . dim( ) ,
169
+ LOOKING_GLASS
170
+ ) ;
171
+ let _ = stdout ( ) . flush ( ) ;
172
+ let timing_compile_state = Instant :: now ( ) ;
173
+ let compile_assets_state = read_compile_state:: read ( & mut build_state) ;
174
+ let timing_compile_state_elapsed = timing_compile_state. elapsed ( ) ;
175
+ println ! (
176
+ "{}\r {} {}Read compile state {:.2}s" ,
177
+ LINE_CLEAR ,
178
+ style( "[3/7]" ) . bold( ) . dim( ) ,
179
+ CHECKMARK ,
180
+ default_timing
181
+ . unwrap_or( timing_compile_state_elapsed)
182
+ . as_secs_f64( )
183
+ ) ;
184
+
185
+ print ! (
186
+ "{} {} Cleaning up previous build..." ,
187
+ style( "[4/7]" ) . bold( ) . dim( ) ,
176
188
SWEEP
177
189
) ;
178
190
let timing_cleanup = Instant :: now ( ) ;
179
- let compile_assets_state = read_compile_state:: read ( & mut build_state) ;
180
- let ( diff_cleanup, total_cleanup, deleted_module_names) =
181
- clean:: cleanup_previous_build ( & mut build_state, compile_assets_state) ;
191
+ let ( diff_cleanup, total_cleanup) = clean:: cleanup_previous_build ( & mut build_state, compile_assets_state) ;
182
192
let timing_cleanup_elapsed = timing_cleanup. elapsed ( ) ;
183
193
println ! (
184
194
"{}\r {} {}Cleaned {}/{} {:.2}s" ,
185
195
LINE_CLEAR ,
186
- style( "[3 /7]" ) . bold( ) . dim( ) ,
196
+ style( "[4 /7]" ) . bold( ) . dim( ) ,
187
197
CHECKMARK ,
188
198
diff_cleanup,
189
199
total_cleanup,
190
200
default_timing. unwrap_or( timing_cleanup_elapsed) . as_secs_f64( )
191
201
) ;
202
+ Ok ( build_state)
203
+ }
192
204
205
+ pub fn incremental_build (
206
+ build_state : & mut BuildState ,
207
+ default_timing : Option < Duration > ,
208
+ initial_build : bool ,
209
+ ) -> Result < ( ) , ( ) > {
210
+ logs:: initialize ( & build_state. packages ) ;
193
211
let num_dirty_modules = build_state. modules . values ( ) . filter ( |m| is_dirty ( m) ) . count ( ) as u64 ;
194
212
195
213
let pb = ProgressBar :: new ( num_dirty_modules) ;
196
214
pb. set_style (
197
215
ProgressStyle :: with_template ( & format ! (
198
216
"{} {} Parsing... {{spinner}} {{pos}}/{{len}} {{msg}}" ,
199
- style( "[4 /7]" ) . bold( ) . dim( ) ,
217
+ style( "[5 /7]" ) . bold( ) . dim( ) ,
200
218
CODE
201
219
) )
202
220
. unwrap ( ) ,
203
221
) ;
204
222
205
223
let timing_ast = Instant :: now ( ) ;
206
- let result_asts = parse:: generate_asts (
207
- & rescript_version,
208
- & mut build_state,
209
- || pb. inc ( 1 ) ,
210
- & bsc_path,
211
- & workspace_root,
212
- ) ;
224
+ let result_asts = parse:: generate_asts ( build_state, || pb. inc ( 1 ) ) ;
213
225
let timing_ast_elapsed = timing_ast. elapsed ( ) ;
214
226
215
227
match result_asts {
216
228
Ok ( err) => {
217
229
println ! (
218
230
"{}\r {} {}Parsed {} source files in {:.2}s" ,
219
231
LINE_CLEAR ,
220
- style( "[4 /7]" ) . bold( ) . dim( ) ,
232
+ style( "[5 /7]" ) . bold( ) . dim( ) ,
221
233
CHECKMARK ,
222
234
num_dirty_modules,
223
235
default_timing. unwrap_or( timing_ast_elapsed) . as_secs_f64( )
@@ -229,87 +241,121 @@ pub fn build(filter: &Option<regex::Regex>, path: &str, no_timing: bool) -> Resu
229
241
println ! (
230
242
"{}\r {} {}Error parsing source files in {:.2}s" ,
231
243
LINE_CLEAR ,
232
- style( "[4 /7]" ) . bold( ) . dim( ) ,
244
+ style( "[5 /7]" ) . bold( ) . dim( ) ,
233
245
CROSS ,
234
246
default_timing. unwrap_or( timing_ast_elapsed) . as_secs_f64( )
235
247
) ;
236
248
print ! ( "{}" , & err) ;
237
- clean:: cleanup_after_build ( & build_state) ;
238
249
return Err ( ( ) ) ;
239
250
}
240
251
}
241
-
242
252
let timing_deps = Instant :: now ( ) ;
243
- deps:: get_deps ( & mut build_state, & deleted_module_names ) ;
253
+ deps:: get_deps ( build_state, & build_state . deleted_modules . to_owned ( ) ) ;
244
254
let timing_deps_elapsed = timing_deps. elapsed ( ) ;
245
255
246
256
println ! (
247
257
"{}\r {} {}Collected deps in {:.2}s" ,
248
258
LINE_CLEAR ,
249
- style( "[5 /7]" ) . bold( ) . dim( ) ,
259
+ style( "[6 /7]" ) . bold( ) . dim( ) ,
250
260
CHECKMARK ,
251
261
default_timing. unwrap_or( timing_deps_elapsed) . as_secs_f64( )
252
262
) ;
253
263
264
+ // track the compile dirty state, we reset it when the compile fails
265
+ let mut tracked_dirty_modules = AHashSet :: new ( ) ;
266
+ for ( module_name, module) in build_state. modules . iter ( ) {
267
+ if module. compile_dirty {
268
+ tracked_dirty_modules. insert ( module_name. to_owned ( ) ) ;
269
+ }
270
+ }
271
+ if initial_build {
272
+ // repair broken state
273
+ mark_modules_with_expired_deps_dirty ( build_state) ;
274
+ }
275
+ mark_modules_with_deleted_deps_dirty ( build_state) ;
276
+
277
+ // print all the compile_dirty modules
278
+ // for (module_name, module) in build_state.modules.iter() {
279
+ // if module.compile_dirty {
280
+ // println!("compile dirty: {}", module_name);
281
+ // }
282
+ // }
283
+
254
284
let start_compiling = Instant :: now ( ) ;
255
285
let pb = ProgressBar :: new ( build_state. modules . len ( ) . try_into ( ) . unwrap ( ) ) ;
256
286
pb. set_style (
257
287
ProgressStyle :: with_template ( & format ! (
258
288
"{} {} Compiling... {{spinner}} {{pos}}/{{len}} {{msg}}" ,
259
- style( "[6 /7]" ) . bold( ) . dim( ) ,
289
+ style( "[7 /7]" ) . bold( ) . dim( ) ,
260
290
SWORDS
261
291
) )
262
292
. unwrap ( ) ,
263
293
) ;
264
- let ( compile_errors, compile_warnings, num_compiled_modules) = compile:: compile (
265
- & mut build_state,
266
- & deleted_module_names,
267
- & rescript_version,
268
- || pb. inc ( 1 ) ,
269
- |size| pb. set_length ( size) ,
270
- & bsc_path,
271
- ) ;
294
+ let ( compile_errors, compile_warnings, num_compiled_modules) =
295
+ compile:: compile ( build_state, || pb. inc ( 1 ) , |size| pb. set_length ( size) ) ;
272
296
let compile_duration = start_compiling. elapsed ( ) ;
273
297
274
298
logs:: finalize ( & build_state. packages ) ;
275
299
pb. finish ( ) ;
276
- clean:: cleanup_after_build ( & build_state) ;
277
300
if compile_errors. len ( ) > 0 {
278
301
if helpers:: contains_ascii_characters ( & compile_warnings) {
279
302
println ! ( "{}" , & compile_warnings) ;
280
303
}
281
304
println ! (
282
305
"{}\r {} {}Compiled {} modules in {:.2}s" ,
283
306
LINE_CLEAR ,
284
- style( "[6 /7]" ) . bold( ) . dim( ) ,
307
+ style( "[7 /7]" ) . bold( ) . dim( ) ,
285
308
CROSS ,
286
309
num_compiled_modules,
287
310
default_timing. unwrap_or( compile_duration) . as_secs_f64( )
288
311
) ;
289
312
print ! ( "{}" , & compile_errors) ;
313
+ // mark the original files as dirty again, because we didn't complete a full build
314
+ for ( module_name, module) in build_state. modules . iter_mut ( ) {
315
+ if tracked_dirty_modules. contains ( module_name) {
316
+ module. compile_dirty = true ;
317
+ }
318
+ }
290
319
return Err ( ( ) ) ;
291
320
} else {
292
321
println ! (
293
322
"{}\r {} {}Compiled {} modules in {:.2}s" ,
294
323
LINE_CLEAR ,
295
- style( "[6 /7]" ) . bold( ) . dim( ) ,
324
+ style( "[7 /7]" ) . bold( ) . dim( ) ,
296
325
CHECKMARK ,
297
326
num_compiled_modules,
298
327
default_timing. unwrap_or( compile_duration) . as_secs_f64( )
299
328
) ;
300
329
if helpers:: contains_ascii_characters ( & compile_warnings) {
301
330
print ! ( "{}" , & compile_warnings) ;
302
331
}
332
+ Ok ( ( ) )
303
333
}
334
+ }
304
335
305
- let timing_total_elapsed = timing_total. elapsed ( ) ;
306
- println ! (
307
- "{}\r {} {}Finished Compilation in {:.2}s" ,
308
- LINE_CLEAR ,
309
- style( "[7/7]" ) . bold( ) . dim( ) ,
310
- CHECKMARK ,
311
- default_timing. unwrap_or( timing_total_elapsed) . as_secs_f64( )
312
- ) ;
313
-
314
- Ok ( build_state)
336
+ pub fn build ( filter : & Option < regex:: Regex > , path : & str , no_timing : bool ) -> Result < BuildState , ( ) > {
337
+ let default_timing: Option < std:: time:: Duration > = if no_timing {
338
+ Some ( std:: time:: Duration :: new ( 0.0 as u64 , 0.0 as u32 ) )
339
+ } else {
340
+ None
341
+ } ;
342
+ let timing_total = Instant :: now ( ) ;
343
+ let mut build_state = initialize_build ( default_timing, filter, path) ?;
344
+ match incremental_build ( & mut build_state, default_timing, true ) {
345
+ Ok ( _) => {
346
+ let timing_total_elapsed = timing_total. elapsed ( ) ;
347
+ println ! (
348
+ "{}\r {}Finished Compilation in {:.2}s" ,
349
+ LINE_CLEAR ,
350
+ CHECKMARK ,
351
+ default_timing. unwrap_or( timing_total_elapsed) . as_secs_f64( )
352
+ ) ;
353
+ clean:: cleanup_after_build ( & build_state) ;
354
+ Ok ( build_state)
355
+ }
356
+ Err ( _) => {
357
+ clean:: cleanup_after_build ( & build_state) ;
358
+ Err ( ( ) )
359
+ }
360
+ }
315
361
}
0 commit comments