blob: 127629463b426055a9768adbd6f1616f8e15ff7f [file] [log] [blame]
#!/usr/bin/env python3
# Copyright lowRISC contributors.
# Licensed under the Apache License, Version 2.0, see LICENSE for details.
# SPDX-License-Identifier: Apache-2.0
import argparse
import json
import os
from pathlib import Path
import re
import subprocess
import shutil
import sys
import tempfile
import time
from urllib.request import urlopen, urlretrieve
ASSET_PREFIX = "lowrisc-toolchain-gcc-rv32imc-"
ASSET_SUFFIX = ".tar.xz"
BUILDINFO_FILENAME = "buildinfo"
RELEASES_URL_BASE = 'https://api.github.com/repos/lowRISC/lowrisc-toolchains/releases'
TARGET_DIR = '/tools/riscv'
TOOLCHAIN_VERSION = 'latest'
VERSION_RE=r"(lowRISC toolchain version|Version):\s*\n(?P<version>\d+(-\d+)?)"
def prompt_yes_no(msg):
while True:
print(msg, end=" ")
response = input().lower()
if response in ('yes', 'y'):
return True
elif response in ('no', 'n'):
return False
else:
print('Invalid response. Valid options are "yes" or "no"')
def get_release_info(toolchain_version):
if toolchain_version == 'latest':
releases_url = '%s/%s' % (RELEASES_URL_BASE, toolchain_version)
else:
releases_url = '%s/tags/%s' % (RELEASES_URL_BASE, toolchain_version)
with urlopen(releases_url) as f:
return json.loads(f.read().decode('utf-8'))
def get_download_url(release_info):
return [
a["browser_download_url"] for a in release_info["assets"]
if (a["name"].startswith(ASSET_PREFIX) and
a["name"].endswith(ASSET_SUFFIX))
][0]
def get_release_tag(release_info):
return release_info["tag_name"]
def get_release_tag_from_file(buildinfo_file):
"""Extracts version tag from buildinfo file.
Args:
buildinfo_file: Path to buildinfo_file.
Returns:
Release tag string if available, otherwise None.
"""
with open(buildinfo_file, 'r') as f:
match = re.match(VERSION_RE, f.read(), re.M)
if not match:
return None
return match.group("version")
def download(url):
print("Downloading toolchain from %s" % (url, ))
tmpfile = tempfile.mktemp()
urlretrieve(url, tmpfile)
return tmpfile
def install(archive_file, target_dir):
os.makedirs(target_dir)
cmd = [
'tar', '-x', '-f', archive_file, '--strip-components=1', '-C',
target_dir
]
subprocess.run(cmd, check=True)
def main():
parser = argparse.ArgumentParser()
parser.add_argument(
'--target-dir',
'-t',
required=False,
default=TARGET_DIR,
help="Target directory (must not exist) (default: %(default)s)")
parser.add_argument(
'--release-version',
'-r',
required=False,
default=TOOLCHAIN_VERSION,
help="Toolchain version (default: %(default)s)")
parser.add_argument(
'--update',
'-u',
required=False,
default=False,
action='store_true',
help="Set to update to target version if needed (default: %(default)s)")
parser.add_argument(
'--force',
'-f',
required=False,
default=False,
action='store_true',
help="Set to skip directory erase prompt when --update is set "
"(default: %(default)s)")
args = parser.parse_args()
target_dir = args.target_dir
toolchain_version = args.release_version
if not args.update and os.path.exists(args.target_dir):
sys.exit('Target directory %s already exists. Delete it first '
'it you want to re-download the toolchain.' % (target_dir, ))
release_info = get_release_info(toolchain_version)
if args.update and os.path.exists(args.target_dir):
buildinfo_file = str(Path(target_dir) / BUILDINFO_FILENAME)
if not os.path.exists(buildinfo_file):
sys.exit('Unable to find buildinfo file at %s. Delete target '
'directory and try again.' % buildinfo_file)
current_release_tag = get_release_tag_from_file(buildinfo_file)
if not current_release_tag and not args.force:
# If args.force is set then we can skip this error condition. The
# version check test condition will also fail, and the install
# will continue.
sys.exit('Unable to extract current toolchain version from %s. '
'Delete target directory and try again.' % buildinfo_file)
if get_release_tag(release_info) == current_release_tag:
print('Toolchain version %s already installed at %s. Skipping '
'install.' % (current_release_tag, target_dir))
return
download_url = get_download_url(release_info)
try:
archive_file = download(download_url)
if args.update and os.path.exists(args.target_dir):
# Warn the user before deleting the target directory.
warning_msg = 'WARNING: Removing directory: %s.' % target_dir
if not args.force:
if not prompt_yes_no(warning_msg + ' Continue [yes/no]:'):
sys.exit('Aborting update.')
else:
print(warning_msg)
shutil.rmtree(target_dir)
install(archive_file, target_dir)
finally:
os.remove(archive_file)
print('Toolchain downloaded and installed to %s' % (target_dir, ))
if __name__ == "__main__":
main()