lowRISC Contributors | 802543a | 2019-08-31 12:12:56 +0100 | [diff] [blame] | 1 | #!/usr/bin/env python3 |
| 2 | # Copyright lowRISC contributors. |
| 3 | # Licensed under the Apache License, Version 2.0, see LICENSE for details. |
| 4 | # SPDX-License-Identifier: Apache-2.0 |
| 5 | |
| 6 | import argparse |
| 7 | import json |
| 8 | import os |
Miguel Osorio | 1e5fc8e | 2019-09-23 17:33:54 -0700 | [diff] [blame] | 9 | from pathlib import Path |
| 10 | import re |
lowRISC Contributors | 802543a | 2019-08-31 12:12:56 +0100 | [diff] [blame] | 11 | import subprocess |
Miguel Osorio | 1e5fc8e | 2019-09-23 17:33:54 -0700 | [diff] [blame] | 12 | import shutil |
lowRISC Contributors | 802543a | 2019-08-31 12:12:56 +0100 | [diff] [blame] | 13 | import sys |
| 14 | import tempfile |
Miguel Osorio | 8e28280 | 2019-09-24 10:37:27 -0700 | [diff] [blame] | 15 | import time |
lowRISC Contributors | 802543a | 2019-08-31 12:12:56 +0100 | [diff] [blame] | 16 | from urllib.request import urlopen, urlretrieve |
| 17 | |
lowRISC Contributors | 802543a | 2019-08-31 12:12:56 +0100 | [diff] [blame] | 18 | ASSET_PREFIX = "lowrisc-toolchain-gcc-rv32imc-" |
Miguel Osorio | c705000 | 2019-09-20 00:16:04 -0700 | [diff] [blame] | 19 | ASSET_SUFFIX = ".tar.xz" |
Miguel Osorio | 1e5fc8e | 2019-09-23 17:33:54 -0700 | [diff] [blame] | 20 | BUILDINFO_FILENAME = "buildinfo" |
| 21 | RELEASES_URL_BASE = 'https://api.github.com/repos/lowRISC/lowrisc-toolchains/releases' |
lowRISC Contributors | 802543a | 2019-08-31 12:12:56 +0100 | [diff] [blame] | 22 | TARGET_DIR = '/tools/riscv' |
Miguel Osorio | 1e5fc8e | 2019-09-23 17:33:54 -0700 | [diff] [blame] | 23 | TOOLCHAIN_VERSION = 'latest' |
Miguel Osorio | 1fe4e95 | 2019-10-10 09:35:44 -0700 | [diff] [blame] | 24 | VERSION_RE=r"(lowRISC toolchain version|Version):\s*\n(?P<version>\d+(-\d+)?)" |
lowRISC Contributors | 802543a | 2019-08-31 12:12:56 +0100 | [diff] [blame] | 25 | |
| 26 | |
Miguel Osorio | 8e28280 | 2019-09-24 10:37:27 -0700 | [diff] [blame] | 27 | def prompt_yes_no(msg): |
| 28 | while True: |
| 29 | print(msg, end=" ") |
| 30 | response = input().lower() |
| 31 | if response in ('yes', 'y'): |
| 32 | return True |
| 33 | elif response in ('no', 'n'): |
| 34 | return False |
| 35 | else: |
| 36 | print('Invalid response. Valid options are "yes" or "no"') |
| 37 | |
| 38 | |
Miguel Osorio | 1e5fc8e | 2019-09-23 17:33:54 -0700 | [diff] [blame] | 39 | def get_release_info(toolchain_version): |
lowRISC Contributors | 802543a | 2019-08-31 12:12:56 +0100 | [diff] [blame] | 40 | if toolchain_version == 'latest': |
| 41 | releases_url = '%s/%s' % (RELEASES_URL_BASE, toolchain_version) |
| 42 | else: |
| 43 | releases_url = '%s/tags/%s' % (RELEASES_URL_BASE, toolchain_version) |
| 44 | with urlopen(releases_url) as f: |
Miguel Osorio | 1e5fc8e | 2019-09-23 17:33:54 -0700 | [diff] [blame] | 45 | return json.loads(f.read().decode('utf-8')) |
| 46 | |
| 47 | |
| 48 | def get_download_url(release_info): |
| 49 | return [ |
| 50 | a["browser_download_url"] for a in release_info["assets"] |
| 51 | if (a["name"].startswith(ASSET_PREFIX) and |
| 52 | a["name"].endswith(ASSET_SUFFIX)) |
| 53 | ][0] |
| 54 | |
| 55 | |
| 56 | def get_release_tag(release_info): |
| 57 | return release_info["tag_name"] |
| 58 | |
| 59 | |
| 60 | def get_release_tag_from_file(buildinfo_file): |
| 61 | """Extracts version tag from buildinfo file. |
| 62 | |
| 63 | Args: |
| 64 | buildinfo_file: Path to buildinfo_file. |
| 65 | Returns: |
| 66 | Release tag string if available, otherwise None. |
| 67 | """ |
| 68 | with open(buildinfo_file, 'r') as f: |
Miguel Osorio | 1fe4e95 | 2019-10-10 09:35:44 -0700 | [diff] [blame] | 69 | match = re.match(VERSION_RE, f.read(), re.M) |
Miguel Osorio | 1e5fc8e | 2019-09-23 17:33:54 -0700 | [diff] [blame] | 70 | if not match: |
| 71 | return None |
| 72 | return match.group("version") |
lowRISC Contributors | 802543a | 2019-08-31 12:12:56 +0100 | [diff] [blame] | 73 | |
| 74 | |
| 75 | def download(url): |
| 76 | print("Downloading toolchain from %s" % (url, )) |
| 77 | tmpfile = tempfile.mktemp() |
| 78 | urlretrieve(url, tmpfile) |
| 79 | return tmpfile |
| 80 | |
| 81 | |
Miguel Osorio | 8e28280 | 2019-09-24 10:37:27 -0700 | [diff] [blame] | 82 | def install(archive_file, target_dir): |
lowRISC Contributors | 802543a | 2019-08-31 12:12:56 +0100 | [diff] [blame] | 83 | os.makedirs(target_dir) |
| 84 | |
| 85 | cmd = [ |
| 86 | 'tar', '-x', '-f', archive_file, '--strip-components=1', '-C', |
| 87 | target_dir |
| 88 | ] |
| 89 | subprocess.run(cmd, check=True) |
| 90 | |
| 91 | |
| 92 | def main(): |
| 93 | parser = argparse.ArgumentParser() |
| 94 | parser.add_argument( |
| 95 | '--target-dir', |
| 96 | '-t', |
| 97 | required=False, |
| 98 | default=TARGET_DIR, |
| 99 | help="Target directory (must not exist) (default: %(default)s)") |
| 100 | parser.add_argument( |
| 101 | '--release-version', |
| 102 | '-r', |
| 103 | required=False, |
| 104 | default=TOOLCHAIN_VERSION, |
| 105 | help="Toolchain version (default: %(default)s)") |
Miguel Osorio | 1e5fc8e | 2019-09-23 17:33:54 -0700 | [diff] [blame] | 106 | parser.add_argument( |
| 107 | '--update', |
| 108 | '-u', |
| 109 | required=False, |
| 110 | default=False, |
| 111 | action='store_true', |
| 112 | help="Set to update to target version if needed (default: %(default)s)") |
Miguel Osorio | 8e28280 | 2019-09-24 10:37:27 -0700 | [diff] [blame] | 113 | parser.add_argument( |
| 114 | '--force', |
| 115 | '-f', |
| 116 | required=False, |
| 117 | default=False, |
| 118 | action='store_true', |
| 119 | help="Set to skip directory erase prompt when --update is set " |
| 120 | "(default: %(default)s)") |
lowRISC Contributors | 802543a | 2019-08-31 12:12:56 +0100 | [diff] [blame] | 121 | args = parser.parse_args() |
| 122 | |
Miguel Osorio | 1351b98 | 2019-09-24 18:05:37 -0700 | [diff] [blame] | 123 | target_dir = args.target_dir |
lowRISC Contributors | 802543a | 2019-08-31 12:12:56 +0100 | [diff] [blame] | 124 | toolchain_version = args.release_version |
| 125 | |
Miguel Osorio | 1e5fc8e | 2019-09-23 17:33:54 -0700 | [diff] [blame] | 126 | if not args.update and os.path.exists(args.target_dir): |
| 127 | sys.exit('Target directory %s already exists. Delete it first ' |
| 128 | 'it you want to re-download the toolchain.' % (target_dir, )) |
lowRISC Contributors | 802543a | 2019-08-31 12:12:56 +0100 | [diff] [blame] | 129 | |
Miguel Osorio | 1e5fc8e | 2019-09-23 17:33:54 -0700 | [diff] [blame] | 130 | release_info = get_release_info(toolchain_version) |
| 131 | |
| 132 | if args.update and os.path.exists(args.target_dir): |
Miguel Osorio | 1351b98 | 2019-09-24 18:05:37 -0700 | [diff] [blame] | 133 | buildinfo_file = str(Path(target_dir) / BUILDINFO_FILENAME) |
Miguel Osorio | 1e5fc8e | 2019-09-23 17:33:54 -0700 | [diff] [blame] | 134 | if not os.path.exists(buildinfo_file): |
| 135 | sys.exit('Unable to find buildinfo file at %s. Delete target ' |
| 136 | 'directory and try again.' % buildinfo_file) |
| 137 | current_release_tag = get_release_tag_from_file(buildinfo_file) |
Miguel Osorio | 1fe4e95 | 2019-10-10 09:35:44 -0700 | [diff] [blame] | 138 | if not current_release_tag and not args.force: |
| 139 | # If args.force is set then we can skip this error condition. The |
| 140 | # version check test condition will also fail, and the install |
| 141 | # will continue. |
Miguel Osorio | 8e28280 | 2019-09-24 10:37:27 -0700 | [diff] [blame] | 142 | sys.exit('Unable to extract current toolchain version from %s. ' |
Miguel Osorio | 1e5fc8e | 2019-09-23 17:33:54 -0700 | [diff] [blame] | 143 | 'Delete target directory and try again.' % buildinfo_file) |
| 144 | if get_release_tag(release_info) == current_release_tag: |
| 145 | print('Toolchain version %s already installed at %s. Skipping ' |
| 146 | 'install.' % (current_release_tag, target_dir)) |
| 147 | return |
Miguel Osorio | 1e5fc8e | 2019-09-23 17:33:54 -0700 | [diff] [blame] | 148 | |
| 149 | download_url = get_download_url(release_info) |
lowRISC Contributors | 802543a | 2019-08-31 12:12:56 +0100 | [diff] [blame] | 150 | try: |
| 151 | archive_file = download(download_url) |
Miguel Osorio | 8e28280 | 2019-09-24 10:37:27 -0700 | [diff] [blame] | 152 | if args.update and os.path.exists(args.target_dir): |
| 153 | # Warn the user before deleting the target directory. |
| 154 | warning_msg = 'WARNING: Removing directory: %s.' % target_dir |
| 155 | if not args.force: |
| 156 | if not prompt_yes_no(warning_msg + ' Continue [yes/no]:'): |
| 157 | sys.exit('Aborting update.') |
| 158 | else: |
| 159 | print(warning_msg) |
| 160 | shutil.rmtree(target_dir) |
| 161 | install(archive_file, target_dir) |
lowRISC Contributors | 802543a | 2019-08-31 12:12:56 +0100 | [diff] [blame] | 162 | finally: |
| 163 | os.remove(archive_file) |
| 164 | |
| 165 | print('Toolchain downloaded and installed to %s' % (target_dir, )) |
| 166 | |
| 167 | |
| 168 | if __name__ == "__main__": |
| 169 | main() |