[sw] Prepare for GCC and Clang (Combined) Toolchain

Signed-off-by: Sam Elliott <selliott@lowrisc.org>
diff --git a/util/get-toolchain.py b/util/get-toolchain.py
index 73d82e3..278ea38 100755
--- a/util/get-toolchain.py
+++ b/util/get-toolchain.py
@@ -16,15 +16,28 @@
 
 log.basicConfig(level=log.INFO, format="%(levelname)s: %(message)s")
 
-ASSET_PREFIX = "lowrisc-toolchain-gcc-rv32imc-"
+# the keys in this dictionary specify valid toolchain kinds
+ASSET_PREFIXES = {
+    # kind : prefix,
+    "combined": "lowrisc-toolchain-rv32imc-",
+    "gcc-only": "lowrisc-toolchain-gcc-rv32imc-",
+}
 ASSET_SUFFIX = ".tar.xz"
 RELEASES_URL_BASE = 'https://api.github.com/repos/lowRISC/lowrisc-toolchains/releases'
 
 TARGET_DIR = '/tools/riscv'
 TOOLCHAIN_VERSION = 'latest'
+TOOLCHAIN_KIND = 'gcc-only'
+
+FILE_PATTERNS_TO_REWRITE = [
+    "riscv32-unknown-elf-*.cmake",
+    "meson-riscv32-unknown-elf-*.txt",
+]
 
 
-def get_available_toolchain_info(version):
+def get_available_toolchain_info(version, kind):
+    assert kind in ASSET_PREFIXES
+
     if version == 'latest':
         releases_url = '%s/%s' % (RELEASES_URL_BASE, version)
     else:
@@ -33,13 +46,20 @@
     with urlopen(releases_url) as f:
         release_info = json.loads(f.read().decode('utf-8'))
 
-    download_url = [
-        a["browser_download_url"] for a in release_info["assets"]
-        if (a["name"].startswith(ASSET_PREFIX) and
-            a["name"].endswith(ASSET_SUFFIX))
-    ][0]
+    for asset in release_info["assets"]:
+        if (asset["name"].startswith(ASSET_PREFIXES[kind]) and
+                asset["name"].endswith(ASSET_SUFFIX)):
+            return {
+                'download_url': asset['browser_download_url'],
+                'name': asset['name'],
+                'version': release_info['tag_name'],
+                'kind': kind
+            }
 
-    return {'download_url': download_url, 'version': release_info['tag_name']}
+    # No matching asset found for the toolchain kind requested
+    log.error("No available downloads found for %s toolchain version: %s",
+              kind, release_info['tag_name'])
+    raise SystemExit(1)
 
 
 def get_installed_toolchain_info(install_path):
@@ -49,6 +69,13 @@
         buildinfo = {}
         with open(str(install_path / 'buildinfo.json'), 'r') as f:
             buildinfo = json.loads(f.read())
+
+        # Toolchains before 20200602-4 contained a `buildinfo.json` without a
+        # 'kind' field. Setting it to 'unknown' will ensure we never skip
+        # updating because we think it's the same as the existing toolchain.
+        if 'kind' not in buildinfo:
+            buildinfo['kind'] = 'unknown'
+
         return buildinfo
     except Exception as e:
         # buildinfo.json might not exist in older builds
@@ -65,7 +92,7 @@
             log.warning("Unable extract version from %s",
                         str(buildinfo_txt_path))
             return None
-        return {'version': match.group("version")}
+        return {'version': match.group("version"), 'kind': 'unknown'}
     except Exception as e:
         log.error("Unable to read %s: %s", str(buildinfo_txt_path), str(e))
         return None
@@ -75,6 +102,7 @@
     log.info("Downloading toolchain from %s", url)
     tmpfile = tempfile.mkstemp()[1]
     urlretrieve(url, tmpfile)
+    log.info("Download complete")
     return Path(tmpfile)
 
 
@@ -82,12 +110,27 @@
     target_dir.mkdir(parents=True, exist_ok=True)
 
     cmd = [
-        'tar', '-x', '-f', str(archive_file), '--strip-components=1', '-C',
-        str(target_dir)
+        'tar', '-x', '-f', str(archive_file), '--strip-components=1', '-C', str(target_dir),
     ]
     subprocess.run(cmd, check=True)
 
 
+def postinstall_rewrite_configs(install_path):
+    """This rewrites the toolchain configuration files to point to install_path"""
+    if str(install_path) == TARGET_DIR:
+        return
+
+    for file_pattern in FILE_PATTERNS_TO_REWRITE:
+        for config_file_path in install_path.glob(file_pattern):
+            # Rewrite TARGET_DIR to the requested target dir.
+            log.info("Updating toolchain paths in %s",
+                        str(config_file_path))
+            with open(str(config_file_path)) as f:
+                original = f.read()
+            with open(str(config_file_path), "w") as f:
+                f.write(original.replace(TARGET_DIR, str(install_path)))
+
+
 def main():
     parser = argparse.ArgumentParser()
     parser.add_argument('--target-dir',
@@ -100,6 +143,11 @@
                         required=False,
                         default=TOOLCHAIN_VERSION,
                         help="Toolchain version (default: %(default)s)")
+    parser.add_argument('--kind',
+                        required=False,
+                        default=TOOLCHAIN_KIND,
+                        choices=ASSET_PREFIXES.keys(),
+                        help="Toolchain kind (default: %(default)s)")
     parser.add_argument(
         '--update',
         '-u',
@@ -111,28 +159,40 @@
 
     target_dir = Path(args.target_dir)
 
-    available_toolchain = get_available_toolchain_info(args.release_version)
+    available_toolchain = get_available_toolchain_info(args.release_version,
+                                                       args.kind)
+    log.info("Found available %s toolchain version %s, %s",
+             available_toolchain['kind'], available_toolchain['version'],
+             available_toolchain['name'])
 
     if args.update and target_dir.is_dir():
         installed_toolchain = get_installed_toolchain_info(target_dir)
         if installed_toolchain is None:
             sys.exit('Unable to extract current toolchain version. '
-                     'Delete target directory and try again.')
+                     'Delete target directory %s and try again.' %
+                     str(target_dir))
 
-        if available_toolchain['version'] == installed_toolchain['version']:
+        version_matches = available_toolchain['version'] == installed_toolchain['version']
+        kind_matches = available_toolchain['kind'] == installed_toolchain['kind']
+
+        if installed_toolchain['kind'] != 'unknown' and version_matches and kind_matches:
             log.info(
-                'Toolchain version %s already installed at %s. Skipping '
-                'install.', installed_toolchain['version'], str(target_dir))
+                'Downloaded %s toolchain is version %s, '
+                'same as the %s toolchain installed at %s (version %s).',
+                available_toolchain['kind'], available_toolchain['version'],
+                installed_toolchain['kind'], installed_toolchain['version'],
+                str(target_dir))
+            log.warning("Skipping install.")
             sys.exit(0)
 
         log.info(
-            "Found installed toolchain version %s, updating to version %s.",
-            installed_toolchain['version'], available_toolchain['version'])
+            "Found installed %s toolchain version %s, updating to %s toolchain version %s.",
+            installed_toolchain['kind'], installed_toolchain['version'],
+            available_toolchain['kind'], available_toolchain['version'])
     else:
         if target_dir.exists():
-            sys.exit(
-                'Target directory %s already exists. Delete it first, or use --update.'
-                % str(target_dir))
+            sys.exit('Target directory %s already exists. '
+                     'Delete it first, or use --update.' % str(target_dir))
 
     archive_file = None
     try:
@@ -144,12 +204,14 @@
             shutil.rmtree(target_dir)
 
         install(archive_file, target_dir)
+        postinstall_rewrite_configs(target_dir.resolve())
     finally:
         if archive_file:
             archive_file.unlink()
 
-    log.info('Toolchain version %s downloaded and installed to %s.',
-             available_toolchain['version'], str(target_dir))
+    log.info('Installed %s toolchain version %s to %s.',
+             available_toolchain['kind'], available_toolchain['version'],
+             str(target_dir))
 
 
 if __name__ == "__main__":