| Build the APK with buck2, from pins rather than from a second checkout a71ef8f nandi 19d ago | 1 | #!/usr/bin/env python3 |
| 2 | """Generate buck2 http_archive data from the DotSlash files beside this script. |
| 3 | |
| 4 | DotSlash is where a tool's version, URL and digest are written down. buck needs |
| 5 | the same three facts to fetch that tool itself — it cannot use the shim, which |
| 6 | resolves on the machine at run time and so is neither an action input nor |
| 7 | anything a remote worker could be given. Rather than write them twice and let |
| 8 | them drift, this reads the manifests and emits the table buck reads. |
| 9 | |
| 10 | just sync-dist # after editing any scripts/*.dotslash |
| 11 | |
| 12 | The DotSlash format is JSON with a `//` shebang line on top: one entry per |
| 13 | platform, each with a url, a size, a digest and the path of the binary inside |
| 14 | the archive. Only the archive itself is wanted here; the path inside it is the |
| 15 | shim's business, and the toolchain rules name what they need. |
| 16 | """ |
| 17 | |
| 18 | import json |
| 19 | import pathlib |
| 20 | import sys |
| 21 | |
| 22 | HERE = pathlib.Path(__file__).resolve().parent |
| 23 | OUT = HERE.parent / "toolchains" / "dist" / "generated.bzl" |
| 24 | |
| 25 | # The tools buck fetches for itself. buck2 is not among them: it is the thing |
| 26 | # doing the fetching. jolt is not either — it is fetched by DotSlash on the |
| 27 | # machine that runs the boot image compile, which may not be this one. |
| 28 | TOOLS = { |
| 29 | # DotSlash itself, so an action can fetch what it needs where it runs |
| 30 | # rather than being handed it from here. See android/BUCK. |
| 31 | "dotslash": "dotslash", |
| 32 | # jolt-native's release, so the APK's UI half and the Jolt side of the |
| 33 | # tree ABI are fetched rather than built out of a second checkout. |
| 34 | "libvidya-android": "libvidya-android", |
| 35 | "glimmer-vidya": "glimmer-vidya", |
| 36 | "android-glue": "android-glue", |
| 37 | } |
| 38 | |
| 39 | |
| 40 | def load(path): |
| 41 | text = path.read_text() |
| 42 | return json.loads(text[text.index("{") :]) |
| 43 | |
| 44 | |
| 45 | def main(): |
| 46 | entries = {} |
| 47 | for stem, name in sorted(TOOLS.items()): |
| 48 | for candidate in (HERE / f"{stem}.dotslash", HERE / stem): |
| 49 | if candidate.exists(): |
| 50 | manifest = load(candidate) |
| 51 | break |
| 52 | else: |
| 53 | sys.exit(f"no DotSlash manifest for {stem}") |
| 54 | |
| 55 | platforms = {} |
| 56 | for platform, entry in sorted(manifest["platforms"].items()): |
| 57 | if entry["hash"] != "sha256": |
| 58 | # http_archive takes sha256 and nothing else. |
| 59 | continue |
| 60 | url = entry["providers"][0]["url"] |
| 61 | # The path of the binary inside the archive starts with the |
| 62 | # directory the tarball unpacks into, which is what to strip. |
| 63 | # A flat archive — one binary, no directory around it — has |
| 64 | # nothing to strip, and naming the binary would strip the whole |
| 65 | # payload. |
| 66 | head, _, rest = entry["path"].partition("/") |
| 67 | platforms[platform] = { |
| 68 | "url": url, |
| 69 | "sha256": entry["digest"], |
| 70 | "strip_prefix": head if rest else "", |
| 71 | "type": entry["format"], |
| 72 | } |
| 73 | entries[name] = platforms |
| 74 | |
| 75 | lines = [ |
| 76 | "# @generated by scripts/dotslash-to-buck — do not edit.", |
| 77 | "#", |
| 78 | "# The same archives scripts/*.dotslash pins, as facts buck can act on.", |
| 79 | "# Bump the DotSlash file and run `just sync-dist`.", |
| 80 | "", |
| 81 | "DIST = " + json.dumps(entries, indent=4).replace(": true", ": True"), |
| 82 | "", |
| 83 | ] |
| 84 | OUT.parent.mkdir(parents=True, exist_ok=True) |
| 85 | OUT.write_text("\n".join(lines)) |
| 86 | print(f"wrote {OUT.relative_to(HERE.parent)}") |
| 87 | |
| 88 | |
| 89 | if __name__ == "__main__": |
| 90 | main() |