Skip to content

Commit 68c26fd

Browse files
committed
Use llvm-strip
1 parent 961ec4e commit 68c26fd

File tree

8 files changed

+133
-41
lines changed

8 files changed

+133
-41
lines changed

.github/workflows/release.yml

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -31,10 +31,6 @@ jobs:
3131

3232
- uses: extractions/setup-just@v2
3333

34-
# Install `llvm-strip`.
35-
- name: Install llvm-strip
36-
run: sudo apt-get install -y llvm
37-
3834
# Perform a release in dry-run mode.
3935
- run: just release-dry-run ${{ secrets.GITHUB_TOKEN }} ${{ github.event.inputs.sha }} ${{ github.event.inputs.tag }}
4036
if: ${{ github.event.inputs.dry-run == 'true' }}

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,4 +4,5 @@ docs/_build/
44
dist/
55
target/
66
venv/
7+
llvm/
78
__pycache__/

Cargo.lock

Lines changed: 15 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ octocrab = { version = "0.34.1", features = ["rustls", "stream"] }
2424
once_cell = "1.19.0"
2525
pdb = "0.8.0"
2626
rayon = "1.8.1"
27-
reqwest = { version = "0.11.24", features = ["rustls"] }
27+
reqwest = { version = "0.11.24", features = ["rustls", "stream"] }
2828
scroll = "0.12.0"
2929
semver = "1.0.22"
3030
serde = { version = "1.0.197", features = ["derive"] }

pythonbuild/downloads.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -175,18 +175,21 @@
175175
"sha256": "04cb77c660f09df017a57738ae9635ef23a506024789f2f18da1304b45af2023",
176176
"version": "14.0.3+20220508",
177177
},
178+
# Remember to update LLVM_URL in src/release.rs whenever upgrading.
178179
"llvm-17-x86_64-linux": {
179180
"url": "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",
180181
"size": 229404408,
181182
"sha256": "fc5e9092a8915dde438c3b491c5e6321594de541245b619f391edba719e4bd4f",
182183
"version": "17.0.6+20240222",
183184
},
185+
# Remember to update LLVM_URL in src/release.rs whenever upgrading.
184186
"llvm-aarch64-macos": {
185187
"url": "https://github.com/indygreg/toolchain-tools/releases/download/toolchain-bootstrap%2F20240222/llvm-17.0.6+20240222-aarch64-apple-darwin.tar.zst",
186188
"size": 131303875,
187189
"sha256": "e1acf616780f32787b37b5534cb342c0e1e4a56b80cb9283f537fd8c9976e0d1",
188190
"version": "17.0.6+20240222",
189191
},
192+
# Remember to update LLVM_URL in src/release.rs whenever upgrading.
190193
"llvm-x86_64-macos": {
191194
"url": "https://github.com/indygreg/toolchain-tools/releases/download/toolchain-bootstrap%2F20240222/llvm-17.0.6+20240222-x86_64-apple-darwin.tar.zst",
192195
"size": 131306317,

src/github.rs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
// License, v. 2.0. If a copy of the MPL was not distributed with this
33
// file, You can obtain one at https://mozilla.org/MPL/2.0/.
44

5-
use crate::release::produce_install_only_stripped;
5+
use crate::release::{bootstrap_llvm, produce_install_only_stripped};
66
use {
77
crate::release::{produce_install_only, RELEASE_TRIPLES},
88
anyhow::{anyhow, Result},
@@ -257,6 +257,8 @@ pub async fn command_fetch_release_distributions(args: &ArgMatches) -> Result<()
257257
}
258258
}
259259

260+
let llvm_dir = bootstrap_llvm().await?;
261+
260262
install_paths
261263
.par_iter()
262264
.try_for_each(|path| -> Result<()> {
@@ -287,7 +289,7 @@ pub async fn command_fetch_release_distributions(args: &ArgMatches) -> Result<()
287289
.to_string_lossy()
288290
);
289291

290-
let dest_path = produce_install_only_stripped(&dest_path)?;
292+
let dest_path = produce_install_only_stripped(&dest_path, &llvm_dir)?;
291293

292294
println!(
293295
"releasing {}",

src/main.rs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -193,8 +193,13 @@ fn main_impl() -> Result<()> {
193193
Ok(())
194194
}
195195
Some(("convert-install-only-stripped", args)) => {
196+
let llvm_dir = tokio::runtime::Builder::new_current_thread()
197+
.enable_all()
198+
.build()
199+
.unwrap()
200+
.block_on(release::bootstrap_llvm())?;
196201
for path in args.get_many::<PathBuf>("path").unwrap() {
197-
let dest_path = release::produce_install_only_stripped(path)?;
202+
let dest_path = release::produce_install_only_stripped(path, &llvm_dir)?;
198203
println!("wrote {}", dest_path.display());
199204
}
200205

src/release.rs

Lines changed: 103 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,11 @@
33
// file, You can obtain one at https://mozilla.org/MPL/2.0/.
44

55
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+
98
use object::FileKind;
109
use std::process::{Command, Stdio};
10+
use url::Url;
1111
use {
1212
crate::json::parse_python_json,
1313
anyhow::{anyhow, Result},
@@ -286,8 +286,8 @@ pub fn convert_to_install_only<W: Write>(reader: impl BufRead, writer: W) -> Res
286286
}
287287

288288
/// 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"))
291291
.arg("--strip-debug")
292292
.arg("-")
293293
.stdin(Stdio::piped())
@@ -313,7 +313,11 @@ fn llvm_strip(data: &[u8]) -> Result<Vec<u8>> {
313313
}
314314

315315
/// 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> {
317321
let dctx = flate2::read::GzDecoder::new(reader);
318322

319323
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<
331335
let path = entry.path()?;
332336

333337
// 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+
);
346344
}
347345
}
348346

@@ -359,21 +357,11 @@ pub fn convert_to_stripped<W: Write>(reader: impl BufRead, writer: W) -> Result<
359357
| FileKind::Pe32
360358
| FileKind::Pe64)
361359
) {
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()))?;
373362
}
374363

375364
let header = entry.header().clone();
376-
377365
builder.append(&header, std::io::Cursor::new(data))?;
378366
}
379367

@@ -409,11 +397,24 @@ pub fn produce_install_only(tar_zst_path: &Path) -> Result<PathBuf> {
409397
Ok(dest_path)
410398
}
411399

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> {
413401
let buf = std::fs::read(tar_gz_path)?;
414402

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+
);
417418

418419
let filename = tar_gz_path
419420
.file_name()
@@ -436,3 +437,72 @@ pub fn produce_install_only_stripped(tar_gz_path: &Path) -> Result<PathBuf> {
436437

437438
Ok(dest_path)
438439
}
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

Comments
 (0)