scripts: Add renode download script

Download renode from https://builds.renode.io
By default the script will download the latest release, but we can also
query a specific release name (or git commit sha)

Change-Id: I016ca4dcc70128407e5c676f2903842944a192ad
diff --git a/download_renode.py b/download_renode.py
new file mode 100755
index 0000000..bf461ac
--- /dev/null
+++ b/download_renode.py
@@ -0,0 +1,150 @@
+#! /usr/bin/env python3
+"""Download and install renode release package."""
+
+import argparse
+import os
+import re
+import sys
+import time
+import tarfile
+import urllib
+
+from pathlib import Path
+
+import wget
+
+
+def download_artifact(url, artifact_name, out_dir):
+    """Download the artifact from url."""
+    if not os.path.isdir(out_dir):
+        os.mkdir(out_dir)
+    out_file = os.path.join(out_dir, artifact_name)
+    download_url = os.path.join(url, artifact_name)
+    num_retries = 3
+    for i in range(num_retries + 1):
+        try:
+            wget.download(download_url, out=out_file)
+            break
+        except urllib.error.HTTPError as exception:
+            if i == num_retries:
+                raise
+            print(f"{exception}\nDownload failed. Retrying...")
+            time.sleep(5)
+    return out_file
+
+
+def strip_top_directory(tar):
+    """Renode packages the release with a top directory. Need to strip it."""
+    members = tar.getmembers()
+    out_members = []
+    for member in members[1:]:
+        if member.path.startswith(members[0].name + "/"):
+            member.path = os.path.join(
+                *(member.name.split(os.path.sep)[1:]))
+            out_members.append(member)
+
+    tag = re.search("(.+?)_portable", members[0].name).group(1)
+    return tag, out_members
+
+
+def main():
+    """Download and install renode release package."""
+    out_dir = os.getenv("OUT")
+    if not out_dir:
+        out_dir = "/tmp"
+
+    parser = argparse.ArgumentParser(
+        description="Download Renode from Antmicro releases")
+    parser.add_argument(
+        "--release_name", action="store", default="",
+        help="Release to be downloaded. If not set, download the latest")
+    parser.add_argument(
+        "--release_url", action="store",
+        default="https://dl.antmicro.com/projects/renode/builds/",
+        help=("URL to check the IREE release."
+              "(default: https://dl.antmicro.com/projects/renode/builds/)")
+    )
+    parser.add_argument(
+        "--renode_dir", action="store", required=True,
+        default="",
+        help=("Renode installed directory")
+    )
+
+    args = parser.parse_args()
+
+    with urllib.request.urlopen(args.release_url) as website:
+        html_list = website.read().decode("utf-8")
+    files = re.findall('href="(.*linux-portable.tar.gz)"', html_list)
+
+    release_found = False
+    release_name = None
+    if args.release_name:
+        for file in files:
+            if args.release_name in file:
+                release_found = True
+                release_name = re.search(
+                    "(.+?).linux-portable.tar.gz", file).group(1)
+                break
+    else:
+        # The latest release is the symlink of the second entry.
+        release_found = True
+        release_name = re.search(
+            "(.+?).linux-portable.tar.gz", files[1]).group(1)
+
+    if not release_found:
+        print(f"!!!!!Renode can't be found with release {args.release_name}, please try a "
+              "different release!!!!!")
+        sys.exit(1)
+
+    print(f"Release: {release_name}")
+    commit_sha = re.search("git(.+?)$", release_name).group(1)
+
+    renode_dir = Path(args.renode_dir)
+    if not os.path.isdir(renode_dir):
+        os.mkdir(renode_dir)
+    tag_file = renode_dir / "tag"
+
+    # Check the tag of the existing download.
+    release_match = False
+    if os.path.isfile(tag_file):
+        with open(tag_file, "r", encoding="utf-8") as file:
+            for line in file:
+                if commit_sha in line.replace("\n", ""):
+                    release_match = True
+                    break
+
+    if release_match:
+        print("Renode is up-to-date")
+        sys.exit(0)
+
+    tmp_dir = Path(out_dir) / "tmp"
+    artifact_name = release_name + ".linux-portable.tar.gz"
+    tar_file = download_artifact(args.release_url, artifact_name, tmp_dir)
+
+    # Extract the tarball
+    tar = tarfile.open(tar_file)
+    _, members = strip_top_directory(tar)
+    tar.extractall(members=members, path=renode_dir)
+    tar.close()
+
+    # Make the release has the same interface as build from the source
+    bin_dir = Path(renode_dir) / "bin"
+    if not os.path.isdir(bin_dir):
+        os.mkdir(bin_dir)
+    os.system(f"ln -sfr {renode_dir}/*.so {bin_dir}")
+    os.system(f"ln -sfr {renode_dir}/renode {bin_dir}/Renode.exe")
+    with open(renode_dir / "renode.sh", "w", encoding="utf-8") as file:
+        file.write("#! /bin/sh\n")
+        file.write("cd ${ROOTDIR} && %s/Renode.exe \"$@\"\n" % bin_dir)
+    os.chmod(renode_dir / "renode.sh", 0o755)
+
+    os.remove(tar_file)
+    print("\nRenode is installed")
+
+    # Add tag file for future checks
+    with open(tag_file, "w", encoding="utf-8") as file:
+        file.write(f"{release_name}\ncommit_sha: {commit_sha}\n")
+
+
+if __name__ == "__main__":
+    main()