1#!/usr/bin/env nix-shell
2#!nix-shell -p nix -p python3 -p git -i python
3# USAGE - just run the script: ./update.py
4# When editing this file, make also sure it passes the mypy typecheck
5# and is formatted with black.
6import fileinput
7import json
8import re
9import subprocess
10import tempfile
11import urllib.request
12from datetime import datetime
13from pathlib import Path
14from typing import Dict
15
16
17def sh(*args: str) -> str:
18 out = subprocess.check_output(list(args))
19 return out.strip().decode("utf-8")
20
21
22def prefetch_github(owner: str, repo: str, ref: str) -> str:
23 return sh(
24 "nix-prefetch-url",
25 "--unpack",
26 f"https://github.com/{owner}/{repo}/archive/{ref}.tar.gz",
27 )
28
29
30def get_radare2_rev() -> str:
31 url = "https://api.github.com/repos/radare/radare2/releases/latest"
32 with urllib.request.urlopen(url) as response:
33 release = json.load(response) # type: ignore
34 return release["tag_name"]
35
36
37def get_r2_cutter_rev() -> str:
38 url = "https://api.github.com/repos/radareorg/cutter/contents/"
39 with urllib.request.urlopen(url) as response:
40 data = json.load(response) # type: ignore
41 for entry in data:
42 if entry["name"] == "radare2":
43 return entry["sha"]
44 raise Exception("no radare2 submodule found in github.com/radareorg/cutter")
45
46
47def git(dirname: str, *args: str) -> str:
48 return sh("git", "-C", dirname, *args)
49
50
51def get_repo_info(dirname: str, rev: str) -> Dict[str, str]:
52 sha256 = prefetch_github("radare", "radare2", rev)
53
54 cs_tip = None
55 with open(Path(dirname).joinpath("shlr", "Makefile")) as makefile:
56 for l in makefile:
57 match = re.match("CS_TIP=(\S+)", l)
58 if match:
59 cs_tip = match.group(1)
60 assert cs_tip is not None
61
62 cs_sha256 = prefetch_github("aquynh", "capstone", cs_tip)
63
64 return dict(
65 rev=rev,
66 sha256=sha256,
67 version_commit=git(dirname, "rev-list", "--all", "--count"),
68 gittap=git(dirname, "describe", "--tags", "--match", "[0-9]*"),
69 gittip=git(dirname, "rev-parse", "HEAD"),
70 cs_tip=cs_tip,
71 cs_sha256=cs_sha256,
72 )
73
74
75def write_package_expr(version: str, info: Dict[str, str]) -> str:
76 return f"""generic {{
77 version_commit = "{info["version_commit"]}";
78 gittap = "{info["gittap"]}";
79 gittip = "{info["gittip"]}";
80 rev = "{info["rev"]}";
81 version = "{version}";
82 sha256 = "{info["sha256"]}";
83 cs_tip = "{info["cs_tip"]}";
84 cs_sha256 = "{info["cs_sha256"]}";
85 }}"""
86
87
88def main() -> None:
89 radare2_rev = get_radare2_rev()
90 r2_cutter_rev = get_r2_cutter_rev()
91
92 with tempfile.TemporaryDirectory() as dirname:
93 git(
94 dirname,
95 "clone",
96 "--branch",
97 radare2_rev,
98 "https://github.com/radare/radare2",
99 ".",
100 )
101 nix_file = str(Path(__file__).parent.joinpath("default.nix"))
102
103 radare2_info = get_repo_info(dirname, radare2_rev)
104
105 git(dirname, "checkout", r2_cutter_rev)
106
107 timestamp = git(dirname, "log", "-n1", "--format=%at")
108 r2_cutter_version = datetime.fromtimestamp(int(timestamp)).strftime("%Y-%m-%d")
109
110 r2_cutter_info = get_repo_info(dirname, r2_cutter_rev)
111
112 in_block = False
113 with fileinput.FileInput(nix_file, inplace=True) as f:
114 for l in f:
115 if "#<generated>" in l:
116 in_block = True
117 print(
118 f""" #<generated>
119 # DO NOT EDIT! Automatically generated by ./update.py
120 radare2 = {write_package_expr(radare2_rev, radare2_info)};
121 r2-for-cutter = {write_package_expr(r2_cutter_version, r2_cutter_info)};
122 #</generated>"""
123 )
124 elif "#</generated>" in l:
125 in_block = False
126 elif not in_block:
127 print(l, end="")
128
129
130if __name__ == "__main__":
131 main()