@@ -180,7 +180,7 @@ def VerifyStage0JsonHash():
180
180
sys .exit (1 )
181
181
182
182
183
- def Configure (llvm_bins_path , llvm_libs_root ):
183
+ def Configure (llvm_bins_path , llvm_libs_root , link_tensorflow ):
184
184
# Read the config.toml template file...
185
185
with open (RUST_CONFIG_TEMPLATE_PATH , 'r' ) as input :
186
186
template = string .Template (input .read ())
@@ -194,6 +194,11 @@ def quote_string(s: str):
194
194
subs ['LLVM_BIN' ] = quote_string (str (llvm_bins_path ))
195
195
subs ['PACKAGE_VERSION' ] = GetPackageVersionForBuild ()
196
196
197
+ if link_tensorflow :
198
+ subs ['RUSTC_LLVM_LDFLAGS' ] = '-ltf_xla_runtime'
199
+ else :
200
+ subs ['RUSTC_LLVM_LDFLAGS' ] = ''
201
+
197
202
# ...and apply substitutions, writing to config.toml in Rust tree.
198
203
with open (os .path .join (RUST_SRC_DIR , 'config.toml' ), 'w' ) as output :
199
204
output .write (template .substitute (subs ))
@@ -275,104 +280,125 @@ def CargoVendor(cargo_bin):
275
280
os .path .join (RUST_SRC_DIR , '.cargo' , 'config.toml' ))
276
281
277
282
278
- def RunXPy (sub , args , llvm_bins_path , zlib_path , libxml2_dirs , build_mac_arm ,
279
- gcc_toolchain_path , verbose ):
280
- ''' Run x.py, Rust's build script'''
281
- # We append to these flags, make sure they exist.
282
- ENV_FLAGS = [
283
- 'CFLAGS' ,
284
- 'CXXFLAGS' ,
285
- 'LDFLAGS' ,
286
- 'RUSTFLAGS_BOOTSTRAP' ,
287
- 'RUSTFLAGS_NOT_BOOTSTRAP' ,
288
- 'RUSTDOCFLAGS' ,
289
- ]
290
-
291
- RUSTENV = collections .defaultdict (str , os .environ )
292
- for f in ENV_FLAGS :
293
- RUSTENV .setdefault (f , '' )
294
-
295
- # The AR, CC, CXX flags control the C/C++ compiler used through the `cc`
296
- # crate. There are also C/C++ targets that are part of the Rust toolchain
297
- # build for which the tool is controlled from `config.toml`, so these must
298
- # be duplicated there.
299
-
300
- if sys .platform == 'win32' :
301
- RUSTENV ['AR' ] = os .path .join (llvm_bins_path , 'llvm-lib.exe' )
302
- RUSTENV ['CC' ] = os .path .join (llvm_bins_path , 'clang-cl.exe' )
303
- RUSTENV ['CXX' ] = os .path .join (llvm_bins_path , 'clang-cl.exe' )
304
- else :
305
- RUSTENV ['AR' ] = os .path .join (llvm_bins_path , 'llvm-ar' )
306
- RUSTENV ['CC' ] = os .path .join (llvm_bins_path , 'clang' )
307
- RUSTENV ['CXX' ] = os .path .join (llvm_bins_path , 'clang++' )
308
-
309
- if sys .platform == 'darwin' :
310
- # The system/xcode compiler would find system SDK correctly, but
311
- # the Clang we've built does not. See
312
- # https://github.com/llvm/llvm-project/issues/45225
313
- sdk_path = subprocess .check_output (['xcrun' , '--show-sdk-path' ],
314
- text = True ).rstrip ()
315
- RUSTENV ['CFLAGS' ] += f' -isysroot { sdk_path } '
316
- RUSTENV ['CXXFLAGS' ] += f' -isysroot { sdk_path } '
317
- RUSTENV ['LDFLAGS' ] += f' -isysroot { sdk_path } '
318
- RUSTENV ['RUSTFLAGS_BOOTSTRAP' ] += (
319
- f' -Clink-arg=-isysroot -Clink-arg={ sdk_path } ' )
320
- RUSTENV ['RUSTFLAGS_NOT_BOOTSTRAP' ] += (
321
- f' -Clink-arg=-isysroot -Clink-arg={ sdk_path } ' )
322
- # Rust compiletests don't get any of the RUSTFLAGS that we set here and
323
- # then the clang linker can't find `-lSystem`, unless we set the
324
- # `SDKROOT`.
325
- RUSTENV ['SDKROOT' ] = sdk_path
326
-
327
- if zlib_path :
328
- RUSTENV ['CFLAGS' ] += f' -I{ zlib_path } '
329
- RUSTENV ['CXXFLAGS' ] += f' -I{ zlib_path } '
330
- RUSTENV ['LDFLAGS' ] += f' { LD_PATH_FLAG } { zlib_path } '
331
- RUSTENV ['RUSTFLAGS_BOOTSTRAP' ] += (
332
- f' -Clink-arg={ LD_PATH_FLAG } { zlib_path } ' )
333
- RUSTENV ['RUSTFLAGS_NOT_BOOTSTRAP' ] += (
334
- f' -Clink-arg={ LD_PATH_FLAG } { zlib_path } ' )
335
-
336
- if libxml2_dirs :
337
- RUSTENV ['CFLAGS' ] += f' -I{ libxml2_dirs .include_dir } '
338
- RUSTENV ['CXXFLAGS' ] += f' -I{ libxml2_dirs .include_dir } '
339
- RUSTENV ['LDFLAGS' ] += f' { LD_PATH_FLAG } { libxml2_dirs .lib_dir } '
340
- RUSTENV ['RUSTFLAGS_BOOTSTRAP' ] += (
341
- f' -Clink-arg={ LD_PATH_FLAG } { libxml2_dirs .lib_dir } ' )
342
- RUSTENV ['RUSTFLAGS_NOT_BOOTSTRAP' ] += (
343
- f' -Clink-arg={ LD_PATH_FLAG } { libxml2_dirs .lib_dir } ' )
344
-
345
- if gcc_toolchain_path :
346
- # We use these flags to avoid linking with the system libstdc++.
347
- gcc_toolchain_flag = f'--gcc-toolchain={ gcc_toolchain_path } '
348
- RUSTENV ['CFLAGS' ] += f' { gcc_toolchain_flag } '
349
- RUSTENV ['CXXFLAGS' ] += f' { gcc_toolchain_flag } '
350
- RUSTENV ['LDFLAGS' ] += f' { gcc_toolchain_flag } '
351
- # A `-Clink-arg=<foo>` arg passes `foo`` to the linker invovation.
352
- RUSTENV ['RUSTFLAGS_BOOTSTRAP' ] += f' -Clink-arg={ gcc_toolchain_flag } '
353
- RUSTENV [
354
- 'RUSTFLAGS_NOT_BOOTSTRAP' ] += f' -Clink-arg={ gcc_toolchain_flag } '
355
- RUSTENV ['RUSTFLAGS_BOOTSTRAP' ] += (
356
- f' -L native={ gcc_toolchain_path } /lib64' )
357
- RUSTENV ['RUSTFLAGS_NOT_BOOTSTRAP' ] += (
358
- f' -L native={ gcc_toolchain_path } /lib64' )
359
-
360
- # Rustdoc should use our clang linker as well, as we pass flags that
361
- # the system linker may not understand.
362
- RUSTENV ['RUSTDOCFLAGS' ] += f' -Clinker={ RUSTENV ["CC" ]} '
363
-
364
- # Cargo normally stores files in $HOME. Override this.
365
- RUSTENV ['CARGO_HOME' ] = CARGO_HOME_DIR
366
-
367
- # This enables the compiler to produce Mac ARM binaries.
368
- if build_mac_arm :
369
- args .extend (['--target' , 'aarch64-apple-darwin' ])
370
-
371
- os .chdir (RUST_SRC_DIR )
372
- cmd = [sys .executable , 'x.py' , sub ]
373
- if verbose and verbose > 0 :
374
- cmd .append ('-' + verbose * 'v' )
375
- RunCommand (cmd + args , msvc_arch = 'x64' , env = RUSTENV )
283
+ class XPy :
284
+ ''' Runner for x.py, Rust's build script. Holds shared state between x.py
285
+ runs. '''
286
+
287
+ def __init__ (self , llvm_bins_path , zlib_path , libxml2_dirs , build_mac_arm ,
288
+ gcc_toolchain_path , verbose ):
289
+ self ._env = collections .defaultdict (str , os .environ )
290
+ self ._build_mac_arm = build_mac_arm
291
+ self ._verbose = verbose
292
+
293
+ # We append to these flags, make sure they exist.
294
+ ENV_FLAGS = [
295
+ 'CFLAGS' ,
296
+ 'CXXFLAGS' ,
297
+ 'LDFLAGS' ,
298
+ 'RUSTFLAGS_BOOTSTRAP' ,
299
+ 'RUSTFLAGS_NOT_BOOTSTRAP' ,
300
+ 'RUSTDOCFLAGS' ,
301
+ ]
302
+
303
+ self ._env = collections .defaultdict (str , os .environ )
304
+ for f in ENV_FLAGS :
305
+ self ._env .setdefault (f , '' )
306
+
307
+ # The AR, CC, CXX flags control the C/C++ compiler used through the `cc`
308
+ # crate. There are also C/C++ targets that are part of the Rust
309
+ # toolchain build for which the tool is controlled from `config.toml`,
310
+ # so these must be duplicated there.
311
+
312
+ if sys .platform == 'win32' :
313
+ self ._env ['AR' ] = os .path .join (llvm_bins_path , 'llvm-lib.exe' )
314
+ self ._env ['CC' ] = os .path .join (llvm_bins_path , 'clang-cl.exe' )
315
+ self ._env ['CXX' ] = os .path .join (llvm_bins_path , 'clang-cl.exe' )
316
+ else :
317
+ self ._env ['AR' ] = os .path .join (llvm_bins_path , 'llvm-ar' )
318
+ self ._env ['CC' ] = os .path .join (llvm_bins_path , 'clang' )
319
+ self ._env ['CXX' ] = os .path .join (llvm_bins_path , 'clang++' )
320
+
321
+ if sys .platform == 'darwin' :
322
+ # The system/xcode compiler would find system SDK correctly, but
323
+ # the Clang we've built does not. See
324
+ # https://github.com/llvm/llvm-project/issues/45225
325
+ sdk_path = subprocess .check_output (['xcrun' , '--show-sdk-path' ],
326
+ text = True ).rstrip ()
327
+ RUSTENV ['CFLAGS' ] += f' -isysroot { sdk_path } '
328
+ RUSTENV ['CXXFLAGS' ] += f' -isysroot { sdk_path } '
329
+ RUSTENV ['LDFLAGS' ] += f' -isysroot { sdk_path } '
330
+ RUSTENV ['RUSTFLAGS_BOOTSTRAP' ] += (
331
+ f' -Clink-arg=-isysroot -Clink-arg={ sdk_path } ' )
332
+ RUSTENV ['RUSTFLAGS_NOT_BOOTSTRAP' ] += (
333
+ f' -Clink-arg=-isysroot -Clink-arg={ sdk_path } ' )
334
+ # Rust compiletests don't get any of the RUSTFLAGS that we set here
335
+ # and then the clang linker can't find `-lSystem`, unless we set the
336
+ # `SDKROOT`.
337
+ RUSTENV ['SDKROOT' ] = sdk_path
338
+
339
+ if zlib_path :
340
+ self ._env ['CFLAGS' ] += f' -I{ zlib_path } '
341
+ self ._env ['CXXFLAGS' ] += f' -I{ zlib_path } '
342
+ self ._env ['LDFLAGS' ] += f' { LD_PATH_FLAG } { zlib_path } '
343
+ self ._env ['RUSTFLAGS_BOOTSTRAP' ] += (
344
+ f' -Clink-arg={ LD_PATH_FLAG } { zlib_path } ' )
345
+ self ._env ['RUSTFLAGS_NOT_BOOTSTRAP' ] += (
346
+ f' -Clink-arg={ LD_PATH_FLAG } { zlib_path } ' )
347
+
348
+ if libxml2_dirs :
349
+ self ._env ['CFLAGS' ] += f' -I{ libxml2_dirs .include_dir } '
350
+ self ._env ['CXXFLAGS' ] += f' -I{ libxml2_dirs .include_dir } '
351
+ self ._env ['LDFLAGS' ] += f' { LD_PATH_FLAG } { libxml2_dirs .lib_dir } '
352
+ self ._env ['RUSTFLAGS_BOOTSTRAP' ] += (
353
+ f' -Clink-arg={ LD_PATH_FLAG } { libxml2_dirs .lib_dir } ' )
354
+ self ._env ['RUSTFLAGS_NOT_BOOTSTRAP' ] += (
355
+ f' -Clink-arg={ LD_PATH_FLAG } { libxml2_dirs .lib_dir } ' )
356
+
357
+ if gcc_toolchain_path :
358
+ # We use these flags to avoid linking with the system libstdc++.
359
+ gcc_toolchain_flag = f'--gcc-toolchain={ gcc_toolchain_path } '
360
+ self ._env ['CFLAGS' ] += f' { gcc_toolchain_flag } '
361
+ self ._env ['CXXFLAGS' ] += f' { gcc_toolchain_flag } '
362
+ self ._env ['LDFLAGS' ] += f' { gcc_toolchain_flag } '
363
+ # A `-Clink-arg=<foo>` arg passes `foo`` to the linker invovation.
364
+ self ._env [
365
+ 'RUSTFLAGS_BOOTSTRAP' ] += f' -Clink-arg={ gcc_toolchain_flag } '
366
+ self ._env [
367
+ 'RUSTFLAGS_NOT_BOOTSTRAP' ] += f' -Clink-arg={ gcc_toolchain_flag } '
368
+ self ._env ['RUSTFLAGS_BOOTSTRAP' ] += (
369
+ f' -L native={ gcc_toolchain_path } /lib64' )
370
+ self ._env ['RUSTFLAGS_NOT_BOOTSTRAP' ] += (
371
+ f' -L native={ gcc_toolchain_path } /lib64' )
372
+
373
+ # Direct rustc to use Chromium's lld instead of the system linker. This
374
+ # is critical for stage 1 onward since we need to link libs with LLVM
375
+ # bitcode. It is also good for a hermetic build in general.
376
+ #
377
+ # The `--undefined-version` flag is needed due to a bug in libtest:
378
+ # https://github.com/rust-lang/rust/issues/105967
379
+ self ._env [
380
+ 'RUSTFLAGS_BOOTSTRAP' ] += ' -Clink-arg=-fuse-ld=lld -Clink-arg=-Wl,--undefined-version'
381
+ self ._env [
382
+ 'RUSTFLAGS_NOT_BOOTSTRAP' ] += ' -Clink-arg=-fuse-ld=lld -Clink-arg=-Wl,--undefined-version'
383
+
384
+ # Rustdoc should use our clang linker as well, as we pass flags that
385
+ # the system linker may not understand.
386
+ self ._env ['RUSTDOCFLAGS' ] += f' -Clinker={ self ._env ["CC" ]} '
387
+
388
+ # Cargo normally stores files in $HOME. Override this.
389
+ self ._env ['CARGO_HOME' ] = CARGO_HOME_DIR
390
+
391
+ def run (self , sub , args ):
392
+ ''' Run x.py subcommand with specified args. '''
393
+ # This enables the compiler to produce Mac ARM binaries.
394
+ if self ._build_mac_arm :
395
+ args .extend (['--target' , 'aarch64-apple-darwin' ])
396
+
397
+ os .chdir (RUST_SRC_DIR )
398
+ cmd = [sys .executable , 'x.py' , sub ]
399
+ if self ._verbose and self ._verbose > 0 :
400
+ cmd .append ('-' + self ._verbose * 'v' )
401
+ RunCommand (cmd + args , msvc_arch = 'x64' , env = self ._env )
376
402
377
403
378
404
# Get arguments to run desired test suites, minus disabled tests.
@@ -451,7 +477,8 @@ def main():
451
477
'--use-final-llvm-build-dir' ,
452
478
action = 'store_true' ,
453
479
help = 'use libs in LLVM_BUILD_DIR instead of LLVM_BOOTSTRAP_DIR. Useful '
454
- 'with --fetch-llvm-libs for local builds.' )
480
+ 'with --fetch-llvm-libs for local builds. When enabled, these '
481
+ 'libraries must not use LTO.' )
455
482
parser .add_argument (
456
483
'--run-xpy' ,
457
484
action = 'store_true' ,
@@ -468,14 +495,31 @@ def main():
468
495
print ('--build-mac-arm only valid on intel to cross-build arm' )
469
496
return 1
470
497
471
- # Get the LLVM root for libs. We use LLVM_BUILD_DIR tools either way.
498
+ # A production Rust toolchain will use our final-stage LLVM build. These
499
+ # LLVM libs are built with LTO enabled, using the same LLVM revision from an
500
+ # earlier build stage as backend. The Rust toolchain we start with (the
501
+ # upstream beta release) however cannot process these since it uses an
502
+ # earlier LLVM build as its backend.
503
+ #
504
+ # The Rust toolchain can be bootstrapped using earlier native-code LLVM
505
+ # artifacts for its first stage, then using the final LLVM libs for further
506
+ # stages. We do this because the bootstrap libs don't support targeting all
507
+ # platforms, while the final LLVM libs do.
472
508
#
473
- # TODO(https://crbug.com/1245714): use LTO libs from LLVM_BUILD_DIR for
474
- # stage 2+.
509
+ # Enable conditionally based on arguments and build host.
510
+ #
511
+ # TODO(https://crbug.com/1412187): hash out implementation issues on
512
+ # non-Linux and enable unconditionally (sans script argument).
513
+ use_lto_llvm = False
514
+ if not args .use_final_llvm_build_dir and sys .platform .startswith ('linux' ):
515
+ use_lto_llvm = True
516
+
517
+ # Get the LLVM root for the stage0 build. This normally comes from the LLVM
518
+ # bootstrap stage, but for local builds we support using LLVM_BUILD_DIR.
475
519
if args .use_final_llvm_build_dir :
476
- llvm_libs_root = LLVM_BUILD_DIR
520
+ llvm_bootstrap_root = LLVM_BUILD_DIR
477
521
else :
478
- llvm_libs_root = build .LLVM_BOOTSTRAP_DIR
522
+ llvm_bootstrap_root = build .LLVM_BOOTSTRAP_DIR
479
523
480
524
# If we're building for Mac ARM on an x86_64 Mac, we can't use the final
481
525
# clang binaries as they don't have x86_64 support. Building them with that
@@ -511,8 +555,9 @@ def main():
511
555
# `args.gcc_toolchain` if so.
512
556
build .MaybeDownloadHostGcc (args )
513
557
514
- # Set up config.toml in Rust source tree to configure build.
515
- Configure (llvm_bins_path , llvm_libs_root )
558
+ # Set up config.toml in Rust source tree to configure build for stage0.
559
+ # Normally, we will reconfigure later to use the production LLVM libs.
560
+ Configure (llvm_bins_path , llvm_bootstrap_root , link_tensorflow = False )
516
561
517
562
cargo_bin = FetchCargo (checkout_revision )
518
563
CargoVendor (cargo_bin )
@@ -538,25 +583,46 @@ def main():
538
583
# Cargo depends on OpenSSL.
539
584
AddOpenSSLToEnv (args .build_mac_arm )
540
585
586
+ xpy = XPy (llvm_bins_path , zlib_path , libxml2_dirs , args .build_mac_arm ,
587
+ args .gcc_toolchain , args .verbose )
588
+
541
589
if args .run_xpy :
542
590
if rest [0 ] == '--' :
543
591
rest = rest [1 :]
544
- RunXPy (rest [0 ], rest [1 :], llvm_bins_path , zlib_path , libxml2_dirs ,
545
- args .build_mac_arm , args .gcc_toolchain , args .verbose )
592
+ xpy .run (rest [0 ], rest [1 :])
546
593
return 0
547
594
else :
548
595
assert not rest
549
596
550
597
if not args .skip_clean :
551
598
print ('Cleaning build artifacts...' )
552
- RunXPy ('clean' , [], llvm_bins_path , zlib_path , libxml2_dirs ,
553
- args .build_mac_arm , args .gcc_toolchain , args .verbose )
599
+ xpy .run ('clean' , [])
600
+
601
+ # Run the stage0 build separately using the bootstrap LLVM libs. This will
602
+ # allow further stages to process the LTO-enabled LLVM libs.
603
+ #
604
+ # On the other hand, when `use_lto_llvm` is disabled, we always use the
605
+ # native-code bootstrap libs.
606
+ #
607
+ # The build has already been configured above for the bootstrap stage.
608
+ xpy .run ('build' , ['--stage' , '0' , 'compiler/rustc' ])
609
+
610
+ xpy_args = []
611
+ # Reconfigure to use production LLVM libs. After this point we must tell
612
+ # x.py to keep the stage0 artifacts, even though config.toml changed.
613
+ #
614
+ # Note we must link tensorflow into rustc_llvm on Linux, since our LLVM
615
+ # build includes it for MLGO. Unfortunately llvm-config does not report this
616
+ # dependency. See https://github.com/llvm/llvm-project/issues/60751.
617
+ if use_lto_llvm :
618
+ Configure (llvm_bins_path ,
619
+ LLVM_BUILD_DIR ,
620
+ link_tensorflow = sys .platform .startswith ('linux' ))
621
+ xpy_args = ['--keep-stage' , '0' ]
554
622
555
623
if not args .skip_test :
556
624
print ('Running stage 2 tests...' )
557
- # Run a subset of tests. Tell x.py to keep the rustc we already built.
558
- RunXPy ('test' , GetTestArgs (), llvm_bins_path , zlib_path , libxml2_dirs ,
559
- args .build_mac_arm , args .gcc_toolchain , args .verbose )
625
+ xpy .run ('test' , ['--stage' , '2' ] + xpy_args + GetTestArgs ())
560
626
561
627
targets = [
562
628
'library/proc_macro' , 'library/std' , 'src/tools/cargo' ,
@@ -566,8 +632,7 @@ def main():
566
632
# Build stage 2 compiler, tools, and libraries. This should reuse earlier
567
633
# stages from the test command (if run).
568
634
print ('Building stage 2 artifacts...' )
569
- RunXPy ('build' , ['--stage' , '2' ] + targets , llvm_bins_path , zlib_path ,
570
- libxml2_dirs , args .build_mac_arm , args .gcc_toolchain , args .verbose )
635
+ xpy .run ('build' , xpy_args + ['--stage' , '2' ] + targets )
571
636
572
637
if args .skip_install :
573
638
# Rust is fully built. We can quit.
@@ -578,8 +643,7 @@ def main():
578
643
if os .path .exists (RUST_TOOLCHAIN_OUT_DIR ):
579
644
shutil .rmtree (RUST_TOOLCHAIN_OUT_DIR )
580
645
581
- RunXPy ('install' , DISTRIBUTION_ARTIFACTS , llvm_bins_path , zlib_path ,
582
- libxml2_dirs , args .build_mac_arm , args .gcc_toolchain , args .verbose )
646
+ xpy .run ('install' , xpy_args + DISTRIBUTION_ARTIFACTS )
583
647
584
648
# Copy additional sources required for building stdlib out of
585
649
# RUST_TOOLCHAIN_SRC_DIST_DIR.
0 commit comments