3
3
// file, You can obtain one at https://mozilla.org/MPL/2.0/.
4
4
5
5
use anyhow:: Context ;
6
- use object:: elf:: { FileHeader32 , FileHeader64 } ;
7
- use object:: macho:: { MachHeader32 , MachHeader64 } ;
8
- use object:: read:: pe:: { PeFile32 , PeFile64 } ;
6
+ use futures:: StreamExt ;
7
+
9
8
use object:: FileKind ;
10
9
use std:: process:: { Command , Stdio } ;
10
+ use url:: Url ;
11
11
use {
12
12
crate :: json:: parse_python_json,
13
13
anyhow:: { anyhow, Result } ,
@@ -286,8 +286,8 @@ pub fn convert_to_install_only<W: Write>(reader: impl BufRead, writer: W) -> Res
286
286
}
287
287
288
288
/// Run `llvm-strip` over the given data, returning the stripped data.
289
- fn llvm_strip ( data : & [ u8 ] ) -> Result < Vec < u8 > > {
290
- let mut command = Command :: new ( "/opt/homebrew/opt/llvm/ bin/llvm-strip")
289
+ fn llvm_strip ( data : & [ u8 ] , llvm_dir : & Path ) -> Result < Vec < u8 > > {
290
+ let mut command = Command :: new ( llvm_dir . join ( " bin/llvm-strip") )
291
291
. arg ( "--strip-debug" )
292
292
. arg ( "-" )
293
293
. stdin ( Stdio :: piped ( ) )
@@ -313,7 +313,11 @@ fn llvm_strip(data: &[u8]) -> Result<Vec<u8>> {
313
313
}
314
314
315
315
/// Given an install-only .tar.gz archive, strip the underlying build.
316
- pub fn convert_to_stripped < W : Write > ( reader : impl BufRead , writer : W ) -> Result < W > {
316
+ pub fn convert_to_stripped < W : Write > (
317
+ reader : impl BufRead ,
318
+ writer : W ,
319
+ llvm_dir : & Path ,
320
+ ) -> Result < W > {
317
321
let dctx = flate2:: read:: GzDecoder :: new ( reader) ;
318
322
319
323
let mut tar_in = tar:: Archive :: new ( dctx) ;
@@ -331,18 +335,12 @@ pub fn convert_to_stripped<W: Write>(reader: impl BufRead, writer: W) -> Result<
331
335
let path = entry. path ( ) ?;
332
336
333
337
// Drop PDB files.
334
- match pdb:: PDB :: open ( std:: io:: Cursor :: new ( & data) ) {
335
- Ok ( _) => {
336
- println ! ( "removed PDB file: {}" , path. display( ) ) ;
337
- continue ;
338
- }
339
- Err ( err) => {
340
- if path. extension ( ) . is_some_and ( |ext| ext == "pdb" ) {
341
- println ! (
342
- "file with `.pdb` extension ({}) failed to parse as PDB :{err}" ,
343
- path. display( )
344
- ) ;
345
- }
338
+ if let Err ( err) = pdb:: PDB :: open ( std:: io:: Cursor :: new ( & data) ) {
339
+ if path. extension ( ) . is_some_and ( |ext| ext == "pdb" ) {
340
+ println ! (
341
+ "file with `.pdb` extension ({}) failed to parse as PDB :{err}" ,
342
+ path. display( )
343
+ ) ;
346
344
}
347
345
}
348
346
@@ -359,21 +357,11 @@ pub fn convert_to_stripped<W: Write>(reader: impl BufRead, writer: W) -> Result<
359
357
| FileKind :: Pe32
360
358
| FileKind :: Pe64 )
361
359
) {
362
- let size_before = data. len ( ) ;
363
-
364
- let data =
365
- llvm_strip ( & data) . with_context ( || format ! ( "failed to strip {}" , path. display( ) ) ) ?;
366
-
367
- let size_after = data. len ( ) ;
368
-
369
- println ! (
370
- "stripped {} from {size_before} to {size_after} bytes" ,
371
- path. display( )
372
- ) ;
360
+ data = llvm_strip ( & data, llvm_dir)
361
+ . with_context ( || format ! ( "failed to strip {}" , path. display( ) ) ) ?;
373
362
}
374
363
375
364
let header = entry. header ( ) . clone ( ) ;
376
-
377
365
builder. append ( & header, std:: io:: Cursor :: new ( data) ) ?;
378
366
}
379
367
@@ -409,11 +397,24 @@ pub fn produce_install_only(tar_zst_path: &Path) -> Result<PathBuf> {
409
397
Ok ( dest_path)
410
398
}
411
399
412
- pub fn produce_install_only_stripped ( tar_gz_path : & Path ) -> Result < PathBuf > {
400
+ pub fn produce_install_only_stripped ( tar_gz_path : & Path , llvm_dir : & Path ) -> Result < PathBuf > {
413
401
let buf = std:: fs:: read ( tar_gz_path) ?;
414
402
415
- let gz_data =
416
- convert_to_stripped ( std:: io:: Cursor :: new ( buf) , std:: io:: Cursor :: new ( vec ! [ ] ) ) ?. into_inner ( ) ;
403
+ let size_before = buf. len ( ) ;
404
+
405
+ let gz_data = convert_to_stripped (
406
+ std:: io:: Cursor :: new ( buf) ,
407
+ std:: io:: Cursor :: new ( vec ! [ ] ) ,
408
+ llvm_dir,
409
+ ) ?
410
+ . into_inner ( ) ;
411
+
412
+ let size_after = gz_data. len ( ) ;
413
+
414
+ println ! (
415
+ "stripped {} from {size_before} to {size_after} bytes" ,
416
+ tar_gz_path. display( )
417
+ ) ;
417
418
418
419
let filename = tar_gz_path
419
420
. file_name ( )
@@ -436,3 +437,72 @@ pub fn produce_install_only_stripped(tar_gz_path: &Path) -> Result<PathBuf> {
436
437
437
438
Ok ( dest_path)
438
439
}
440
+
441
+ /// URL from which to download LLVM.
442
+ ///
443
+ /// To be kept in sync with `pythonbuild/downloads.py`.
444
+ static LLVM_URL : Lazy < Url > = Lazy :: new ( || {
445
+ if cfg ! ( target_os = "macos" ) {
446
+ if std:: env:: consts:: ARCH == "aarch64" {
447
+ Url :: parse ( "https://github.com/indygreg/toolchain-tools/releases/download/toolchain-bootstrap%2F20240222/llvm-17.0.6+20240222-aarch64-apple-darwin.tar.zst" ) . unwrap ( )
448
+ } else if std:: env:: consts:: ARCH == "x86_64" {
449
+ Url :: parse ( "https://github.com/indygreg/toolchain-tools/releases/download/toolchain-bootstrap%2F20240222/llvm-17.0.6+20240222-x86_64-apple-darwin.tar.zst" ) . unwrap ( )
450
+ } else {
451
+ panic ! ( "unsupported macOS architecture" ) ;
452
+ }
453
+ } else if cfg ! ( target_os = "linux" ) {
454
+ Url :: parse ( "https://github.com/indygreg/toolchain-tools/releases/download/toolchain-bootstrap%2F20240222/llvm-17.0.6+20240222-gnu_only-x86_64-unknown-linux-gnu.tar.zst" ) . unwrap ( )
455
+ } else {
456
+ panic ! ( "unsupported platform" ) ;
457
+ }
458
+ } ) ;
459
+
460
+ /// Bootstrap `llvm` for the current platform.
461
+ ///
462
+ /// Returns the path to the top-level `llvm` directory.
463
+ pub async fn bootstrap_llvm ( ) -> Result < PathBuf > {
464
+ let url = & * LLVM_URL ;
465
+ let filename = url. path_segments ( ) . unwrap ( ) . last ( ) . unwrap ( ) ;
466
+
467
+ let llvm_dir = PathBuf :: from ( "llvm" ) ;
468
+
469
+ // If `llvm` is already available with the target version, return it.
470
+ if llvm_dir. join ( filename) . exists ( ) {
471
+ return Ok ( llvm_dir. join ( "llvm" ) ) ;
472
+ }
473
+
474
+ println ! ( "Downloading LLVM tarball from: {url}" ) ;
475
+
476
+ // Create a temporary directory to download and extract the LLVM tarball.
477
+ let temp_dir = tempfile:: TempDir :: new ( ) ?;
478
+
479
+ // Download the tarball.
480
+ let tarball_path = temp_dir
481
+ . path ( )
482
+ . join ( url. path_segments ( ) . unwrap ( ) . last ( ) . unwrap ( ) ) ;
483
+ let mut tarball_file = tokio:: fs:: File :: create ( & tarball_path) . await ?;
484
+ let mut bytes_stream = reqwest:: Client :: new ( )
485
+ . get ( url. clone ( ) )
486
+ . send ( )
487
+ . await ?
488
+ . bytes_stream ( ) ;
489
+ while let Some ( chunk) = bytes_stream. next ( ) . await {
490
+ tokio:: io:: copy ( & mut chunk?. as_ref ( ) , & mut tarball_file) . await ?;
491
+ }
492
+
493
+ // Decompress the tarball.
494
+ let tarball = std:: fs:: File :: open ( & tarball_path) ?;
495
+ let tar = zstd:: stream:: Decoder :: new ( std:: io:: BufReader :: new ( tarball) ) ?;
496
+ let mut archive = tar:: Archive :: new ( tar) ;
497
+ archive. unpack ( temp_dir. path ( ) ) ?;
498
+
499
+ // Persist the directory.
500
+ match tokio:: fs:: remove_dir_all ( & llvm_dir) . await {
501
+ Ok ( _) => { }
502
+ Err ( err) if err. kind ( ) == std:: io:: ErrorKind :: NotFound => { }
503
+ Err ( err) => return Err ( err) . context ( "failed to remove existing llvm directory" ) ,
504
+ }
505
+ tokio:: fs:: rename ( temp_dir. into_path ( ) , & llvm_dir) . await ?;
506
+
507
+ Ok ( llvm_dir. join ( "llvm" ) )
508
+ }
0 commit comments