28
28
'getsockopt.c' , 'setsockopt.c' , 'freeaddrinfo.c' ,
29
29
'in6addr_any.c' , 'in6addr_loopback.c' , 'accept4.c' ]
30
30
31
+ # Experimental: Setting EMCC_USE_NINJA will cause system libraries to get built with ninja rather
32
+ # than simple subprocesses. The primary benefit here is that we get accurate dependency tracking.
33
+ # This means we can avoid completely rebuilding a library and just rebiuld based on what changed.
34
+ #
35
+ # Setting EMCC_USE_NINJA=2 means that ninja will automatically be run for each library needed at
36
+ # link time.
37
+ USE_NINJA = int (os .environ .get ('EMCC_USE_NINJA' , '0' ))
38
+
31
39
32
40
def files_in_path (path , filenames ):
33
41
srcdir = utils .path_from_root (path )
@@ -239,23 +247,26 @@ def can_build(self):
239
247
return True
240
248
241
249
def erase (self ):
242
- shared .Cache .erase_file (shared .Cache .get_lib_name (self .get_filename ()))
250
+ shared .Cache .erase_file (self .get_path ())
251
+
252
+ def get_path (self , absolute = False ):
253
+ return shared .Cache .get_lib_name (self .get_filename (), absolute = absolute )
243
254
244
- def get_path (self ):
255
+ def build (self ):
245
256
"""
246
257
Gets the cached path of this library.
247
258
248
259
This will trigger a build if this library is not in the cache.
249
260
"""
250
- return shared .Cache .get_lib (self .get_filename (), self .build )
261
+ return shared .Cache .get (self .get_path (), self .do_build , force = USE_NINJA == 2 , quiet = USE_NINJA )
251
262
252
263
def get_link_flag (self ):
253
264
"""
254
265
Gets the link flags needed to use the library.
255
266
256
267
This will trigger a build if this library is not in the cache.
257
268
"""
258
- fullpath = self .get_path ()
269
+ fullpath = self .build ()
259
270
# For non-libaries (e.g. crt1.o) we pass the entire path to the linker
260
271
if self .get_ext () != '.a' :
261
272
return fullpath
@@ -281,6 +292,100 @@ def get_files(self):
281
292
282
293
raise NotImplementedError ()
283
294
295
+ def write_ninja_file (self , filename , libname ):
296
+ cflags = self .get_cflags ()
297
+ asflags = get_base_cflags ()
298
+ # TODO(sbc) There is an llvm bug that causes a crash when `-g` is used with
299
+ # assembly files that define wasm globals.
300
+ asflags = [arg for arg in asflags if arg != '-g' ]
301
+ cflags_asm = [arg for arg in cflags if arg != '-g' ]
302
+
303
+ def join (flags ):
304
+ return ' ' .join (flags )
305
+
306
+ out = f'''\
307
+ # Automatically generated by tools/system_libs.py. DO NOT EDIT
308
+
309
+ ninja_required_version = 1.5
310
+
311
+ ASFLAGS = { join (asflags )}
312
+ CFLAGS = { join (cflags )}
313
+ CFLAGS_ASM = { join (cflags_asm )}
314
+ EMCC = { shared .EMCC }
315
+ EMXX = { shared .EMXX }
316
+ EMAR = { shared .EMAR }
317
+
318
+ rule cc
319
+ depfile = $out.d
320
+ command = $EMCC -MD -MF $out.d $CFLAGS -c $in -o $out
321
+ description = CC $in
322
+
323
+ rule cxx
324
+ depfile = $out.d
325
+ command = $EMXX -MD -MF $out.d $CFLAGS -c $in -o $out
326
+ description = CXX $out
327
+
328
+ rule asm
329
+ command = $EMCC $ASFLAGS -c $in -o $out
330
+ description = ASM $out
331
+
332
+ rule asm_cpp
333
+ depfile = $out.d
334
+ command = $EMCC -MD -MF $out.d $CFLAGS_ASM -c $in -o $out
335
+ description = ASM $out
336
+
337
+ rule direct_cc
338
+ depfile = $with_depfile
339
+ command = $EMCC -MD -MF $with_depfile $CFLAGS -c $in -o $out
340
+ description = CC $out
341
+
342
+ rule archive
343
+ command = $EMAR cr $out $in
344
+ description = AR $out
345
+
346
+ '''
347
+ suffix = shared .suffix (libname )
348
+ input_files = self .get_files ()
349
+
350
+ if suffix == '.o' :
351
+ assert len (input_files ) == 1
352
+ depfile = shared .unsuffixed_basename (input_files [0 ]) + '.d'
353
+ out += f'build { libname } : direct_cc { input_files [0 ]} \n '
354
+ out += f' with_depfile = { depfile } \n '
355
+ else :
356
+ objects = []
357
+ for src in input_files :
358
+ o = shared .unsuffixed_basename (src ) + '.o'
359
+ object_uuid = 0
360
+ # Find a unique basename
361
+ while o in objects :
362
+ object_uuid += 1
363
+ o = f'{ object_basename } __{ object_uuid } .o'
364
+ objects .append (o )
365
+ ext = shared .suffix (src )
366
+ if ext == '.s' :
367
+ out += f'build { o } : asm { src } \n '
368
+ flags = asflags
369
+ elif ext == '.S' :
370
+ out += f'build { o } : asm_cpp { src } \n '
371
+ flags = cflags_asm
372
+ elif ext == '.c' :
373
+ out += f'build { o } : cc { src } \n '
374
+ flags = cflags
375
+ else :
376
+ out += f'build { o } : cxx { src } \n '
377
+ flags = cflags
378
+ custom_flags = self .customize_build_flags (src , flags )
379
+ if custom_flags != flags :
380
+ out += f' CFLAGS = { join (custom_flags )} '
381
+ out += '\n '
382
+
383
+ objects = ' ' .join (objects )
384
+ out += f'build { libname } : archive { objects } \n '
385
+
386
+ if not os .path .exists (filename ) or utils .read_file (filename ) != out :
387
+ utils .write_file (filename , out )
388
+
284
389
def build_objects (self , build_dir ):
285
390
"""
286
391
Returns a list of compiled object files for this library.
@@ -307,40 +412,52 @@ def build_objects(self, build_dir):
307
412
o = os .path .join (build_dir , f'{ object_basename } __{ object_uuid } .o' )
308
413
ext = shared .suffix (src )
309
414
if ext in ('.s' , '.S' , '.c' ):
310
- cmd = [ shared .EMCC ]
415
+ compiler = shared .EMCC
311
416
else :
312
- cmd = [ shared .EMXX ]
417
+ compiler = shared .EMXX
313
418
314
419
if ext == '.s' :
315
420
# .s files are processed directly by the assembler. In this case we can't pass
316
421
# pre-processor flags such as `-I` and `-D` but we still want core flags such as
317
422
# `-sMEMORY64`.
318
- cmd + = get_base_cflags ()
423
+ flags = get_base_cflags ()
319
424
else :
320
- cmd + = cflags
425
+ flags = cflags
321
426
if ext in ('.s' , '.S' ):
322
427
# TODO(sbc) There is an llvm bug that causes a crash when `-g` is used with
323
428
# assembly files that define wasm globals.
324
- cmd = [arg for arg in cmd if arg != '-g' ]
325
- cmd = self .customize_build_cmd ( cmd , src )
326
- commands .append (cmd + ['-c' , src , '-o' , o ])
429
+ flags = [arg for arg in flags if arg != '-g' ]
430
+ flags = self .customize_build_flags ( src , flags )
431
+ commands .append ([ compiler ] + flags + ['-c' , src , '-o' , o ])
327
432
objects .append (o )
328
433
run_build_commands (commands )
434
+ logger .info ('compiled %d inputs' % len (objects ))
329
435
return objects
330
436
331
- def customize_build_cmd (self , cmd , filename ): # noqa
437
+ def customize_build_flags (self , filename , flags ): # noqa
332
438
"""Allows libraries to customize the build command used on per-file basis.
333
439
334
440
For example, libc uses this to replace -Oz with -O2 for some subset of files."""
335
- return cmd
441
+ return flags
336
442
337
- def build (self , out_filename ):
443
+ def do_build (self , out_filename ):
338
444
"""Builds the library and returns the path to the file."""
339
- build_dir = shared .Cache .get_path (os .path .join ('build' , self .get_base_name ()))
340
- utils .safe_ensure_dirs (build_dir )
341
- create_lib (out_filename , self .build_objects (build_dir ))
342
- if not shared .DEBUG :
343
- utils .delete_dir (build_dir )
445
+ assert out_filename == self .get_path (absolute = True )
446
+ build_dir = os .path .join (shared .Cache .get_path ('build' ), self .get_base_name ())
447
+ if USE_NINJA :
448
+ ensure_sysroot ()
449
+ utils .safe_ensure_dirs (build_dir )
450
+ ninja_file = os .path .join (build_dir , 'build.ninja' )
451
+ self .write_ninja_file (ninja_file , out_filename )
452
+ shared .check_call (['ninja' , '-C' , build_dir ], env = clean_env ())
453
+ else :
454
+ # Use a seperate build directory to the ninja flavor so that building without
455
+ # EMCC_USE_NINJA doesn't clobber the ninja build tree
456
+ build_dir += '-tmp'
457
+ utils .safe_ensure_dirs (build_dir )
458
+ create_lib (out_filename , self .build_objects (build_dir ))
459
+ if not shared .DEBUG :
460
+ utils .delete_dir (build_dir )
344
461
345
462
@classmethod
346
463
def _inherit_list (cls , attr ):
@@ -1002,18 +1119,18 @@ def get_files(self):
1002
1119
1003
1120
return libc_files
1004
1121
1005
- def customize_build_cmd (self , cmd , filename ):
1006
- if filename in self .non_lto_files :
1122
+ def customize_build_flags (self , filename , flags ):
1123
+ if filename not in self .non_lto_files :
1007
1124
# These files act more like the part of compiler-rt in that
1008
1125
# references to them can be generated at compile time.
1009
1126
# Treat them like compiler-rt in as much as never compile
1010
1127
# them as LTO and build them with -O2 rather then -Os (which
1011
1128
# use used for the rest of libc) because this set of files
1012
1129
# also contains performance sensitive math functions.
1013
- cmd = [a for a in cmd if not a .startswith ('-flto' )]
1014
- cmd = [a for a in cmd if not a .startswith ('-O' )]
1015
- cmd += ['-O2' ]
1016
- return cmd
1130
+ flags = [a for a in flags if not a .startswith ('-flto' )]
1131
+ flags = [a for a in flags if not a .startswith ('-O' )]
1132
+ flags += ['-O2' ]
1133
+ return flags
1017
1134
1018
1135
1019
1136
# Contains the files from libc that are optimized differently in -Oz mode, where
@@ -1029,7 +1146,7 @@ def __init__(self, **kwargs):
1029
1146
self .non_lto_files = self .get_libcall_files ()
1030
1147
1031
1148
def get_libcall_files (self ):
1032
- # see comments in libc.customize_build_cmd
1149
+ # see comments in libc.customize_build_flags
1033
1150
1034
1151
# some files also appear in libc, and a #define affects them
1035
1152
mem_files = files_in_path (
@@ -1054,13 +1171,13 @@ def get_files(self):
1054
1171
1055
1172
return libcall_files + mem_files
1056
1173
1057
- def customize_build_cmd (self , cmd , filename ):
1174
+ def customize_build_flags (self , filename , flags ):
1058
1175
if filename in self .non_lto_files :
1059
- # see comments in libc.customize_build_cmd
1060
- cmd = [a for a in cmd if not a .startswith ('-flto' )]
1061
- cmd = [a for a in cmd if not a .startswith ('-O' )]
1062
- cmd += ['-O2' ]
1063
- return cmd
1176
+ # see comments in libc.customize_build_flags
1177
+ flags = [f for f in flags if not f .startswith ('-flto' )]
1178
+ flags = [f for f in flags if not f .startswith ('-O' )]
1179
+ flags += ['-O2' ]
1180
+ return flags
1064
1181
1065
1182
def can_use (self ):
1066
1183
# Because libc_optz overrides parts of libc, it is not compatible with
0 commit comments