Files
swift-mirror/utils/download-unpublished-toolchains.py
Max Desiatov 2fad5aac58 Fix utils/download-unpublished-toolchains.py URL parsing (#81939)
Current `.removeprefix` invocation is not resilient against any input preceding the `Toolchain: ` pattern, which means logs that include timestamps (IIUC all of them) can't be parsed.

Additionally, for easier testing this script now supports a new `--dry-run` flag that infers toolchain URL, but doesn't download it.
2025-06-04 13:35:13 -07:00

128 lines
3.3 KiB
Python
Executable File

#!/usr/bin/env python3
# This source file is part of the Swift.org open source project
#
# Copyright (c) 2024 Apple Inc. and the Swift project authors
# Licensed under Apache License v2.0 with Runtime Library Exception
#
# See https://swift.org/LICENSE.txt for license information
# See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
"""
Utility to download unpublished toolchains
"""
import argparse
import os
import re
import sys
import tarfile
import urllib.request
_CI_URL_ = "https://ci.swift.org/job/oss-swift-package-"
def get_build_url(platform, arch):
if arch == "x86_64" or arch == "Universal":
arch = ""
else:
arch = f"-{arch}"
return f"{_CI_URL_}{platform}{arch}/lastSuccessfulBuild/consoleText"
def get_latest_toolchain_url(build_url):
toolchain_url = None
with urllib.request.urlopen(build_url) as response:
body = response.read().decode('utf-8')
lines = body.split('\n')
toolchain = [x for x in lines if 'Toolchain:' in x]
if len(toolchain) > 0:
toolchain_url = re.sub(r'.+Toolchain: (.+)$', r'\1', toolchain[0])
return toolchain_url
def download_toolchain(toolchain_url, output_dir):
file_name = toolchain_url.rsplit('/', 1)[-1]
output_path = os.path.join(output_dir, file_name)
urllib.request.urlretrieve(toolchain_url, output_path)
return output_path
def untar_toolchain(output_dir, tar_path):
with tarfile.open(tar_path, "r") as tf:
tf.extractall(path=output_dir)
def parse_args():
parser = argparse.ArgumentParser()
parser.add_argument(
"--output-dir",
required=True,
help="Output dir to download the toolchain",
)
parser.add_argument(
"--platform",
required=True,
choices=["amazon-linux-2",
"debian-12",
"fedora-39",
"macos",
"ubi-9",
"ubuntu-20_04",
"ubuntu-22_04",
"ubuntu-24_04"],
help="Platform",
)
parser.add_argument(
"--arch",
required=True,
choices=["x86_64", "aarch64", "Universal"],
help="architectures",
)
parser.add_argument(
"--untar",
action=argparse.BooleanOptionalAction,
help="Untar the toolchain in the output directory",
)
parser.add_argument(
"--dry-run",
default=False,
action='store_true',
help="Infer the toolchain URL but don't download it")
return parser.parse_args()
def main():
args = parse_args()
if not os.path.isdir(args.output_dir):
print(f"Error: Directory does not exist at {args.output_dir}.")
return -1
build_url = get_build_url(args.platform, args.arch)
toolchain_url = get_latest_toolchain_url(build_url)
if not toolchain_url:
print(f"Error: Unable to find toolchain for {args.platform}.")
return -1
print(f"[Toolchain URL] {toolchain_url}")
if not args.dry_run:
output_path = download_toolchain(toolchain_url, args.output_dir)
print(f"[Toolchain] {output_path}")
if args.untar:
untar_toolchain(args.output_dir, output_path)
print(f"[Extracted] {args.output_dir}/usr")
if __name__ == "__main__":
sys.exit(main())