1*523fa7a6SAndroid Build Coastguard Worker#!/usr/bin/env python3 2*523fa7a6SAndroid Build Coastguard Worker# Copyright (c) Meta Platforms, Inc. and affiliates. 3*523fa7a6SAndroid Build Coastguard Worker# Copyright 2024 Arm Limited and/or its affiliates. 4*523fa7a6SAndroid Build Coastguard Worker# All rights reserved. 5*523fa7a6SAndroid Build Coastguard Worker# 6*523fa7a6SAndroid Build Coastguard Worker# This source code is licensed under the BSD-style license found in the 7*523fa7a6SAndroid Build Coastguard Worker# LICENSE file in the root directory of this source tree. 8*523fa7a6SAndroid Build Coastguard Worker 9*523fa7a6SAndroid Build Coastguard Workerimport argparse 10*523fa7a6SAndroid Build Coastguard Workerimport os 11*523fa7a6SAndroid Build Coastguard Workerimport platform 12*523fa7a6SAndroid Build Coastguard Workerimport stat 13*523fa7a6SAndroid Build Coastguard Workerimport sys 14*523fa7a6SAndroid Build Coastguard Workerimport tempfile 15*523fa7a6SAndroid Build Coastguard Workerimport urllib.request 16*523fa7a6SAndroid Build Coastguard Worker 17*523fa7a6SAndroid Build Coastguard Workerfrom dataclasses import dataclass 18*523fa7a6SAndroid Build Coastguard Workerfrom pathlib import Path 19*523fa7a6SAndroid Build Coastguard Workerfrom typing import Sequence, Union 20*523fa7a6SAndroid Build Coastguard Worker 21*523fa7a6SAndroid Build Coastguard Workerimport buck_util 22*523fa7a6SAndroid Build Coastguard Workerimport zstd 23*523fa7a6SAndroid Build Coastguard Worker 24*523fa7a6SAndroid Build Coastguard Worker""" 25*523fa7a6SAndroid Build Coastguard WorkerLocate or download the version of buck2 needed to build ExecuTorch. 26*523fa7a6SAndroid Build Coastguard WorkerIt is intended to be invoked from the CMake build logic, and it returns 27*523fa7a6SAndroid Build Coastguard Workerthe path to 'buck2' via stdout. Log messages are written to stderr. 28*523fa7a6SAndroid Build Coastguard Worker 29*523fa7a6SAndroid Build Coastguard WorkerIt uses the following logic, in order of precedence, to locate or download 30*523fa7a6SAndroid Build Coastguard Workerbuck2: 31*523fa7a6SAndroid Build Coastguard Worker 32*523fa7a6SAndroid Build Coastguard Worker 1) If BUCK2 argument is set explicitly, use it. Warn if the version is 33*523fa7a6SAndroid Build Coastguard Worker incorrect. 34*523fa7a6SAndroid Build Coastguard Worker 2) Look for a binary named buck2 on the system path. Take it if it is 35*523fa7a6SAndroid Build Coastguard Worker the correct version. 36*523fa7a6SAndroid Build Coastguard Worker 3) Check for a previously downloaded buck2 binary (from step 4). 37*523fa7a6SAndroid Build Coastguard Worker 4) Download and cache correct version of buck2. 38*523fa7a6SAndroid Build Coastguard Worker 39*523fa7a6SAndroid Build Coastguard Worker""" 40*523fa7a6SAndroid Build Coastguard Worker 41*523fa7a6SAndroid Build Coastguard Worker# Path to the file containing BUCK2 version (build date) for ExecuTorch. 42*523fa7a6SAndroid Build Coastguard Worker# Note that this path is relative to this script file, not the working 43*523fa7a6SAndroid Build Coastguard Worker# directory. 44*523fa7a6SAndroid Build Coastguard WorkerBUCK_VERSION_FILE = "../.ci/docker/ci_commit_pins/buck2.txt" 45*523fa7a6SAndroid Build Coastguard Worker 46*523fa7a6SAndroid Build Coastguard Worker 47*523fa7a6SAndroid Build Coastguard Worker@dataclass 48*523fa7a6SAndroid Build Coastguard Workerclass BuckInfo: 49*523fa7a6SAndroid Build Coastguard Worker archive_name: str 50*523fa7a6SAndroid Build Coastguard Worker target_versions: Sequence[str] 51*523fa7a6SAndroid Build Coastguard Worker 52*523fa7a6SAndroid Build Coastguard Worker 53*523fa7a6SAndroid Build Coastguard Worker# Mapping of os family and architecture to buck2 binary versions. The 54*523fa7a6SAndroid Build Coastguard Worker# target version is the hash given by running 'buck2 --version'. The 55*523fa7a6SAndroid Build Coastguard Worker# archive name is the archive file name to download, as seen under 56*523fa7a6SAndroid Build Coastguard Worker# https://github.com/facebook/buck2/releases/. 57*523fa7a6SAndroid Build Coastguard Worker# 58*523fa7a6SAndroid Build Coastguard Worker# To add or update versions, download the appropriate version of buck2 59*523fa7a6SAndroid Build Coastguard Worker# and run 'buck2 --version'. Add the corresponding entry to the platform 60*523fa7a6SAndroid Build Coastguard Worker# map below, and if adding new os families or architectures, update the 61*523fa7a6SAndroid Build Coastguard Worker# platform detection logic in resolve_buck2(). 62*523fa7a6SAndroid Build Coastguard Worker# 63*523fa7a6SAndroid Build Coastguard Worker# Some platforms (linux) provide multiple binaries (GNU and MUSL). All 64*523fa7a6SAndroid Build Coastguard Worker# versions in the list are accepted when validating a user-provided or 65*523fa7a6SAndroid Build Coastguard Worker# system buck2. 66*523fa7a6SAndroid Build Coastguard WorkerBUCK_PLATFORM_MAP = { 67*523fa7a6SAndroid Build Coastguard Worker ("linux", "x86_64"): BuckInfo( 68*523fa7a6SAndroid Build Coastguard Worker archive_name="buck2-x86_64-unknown-linux-musl.zst", 69*523fa7a6SAndroid Build Coastguard Worker target_versions=[ 70*523fa7a6SAndroid Build Coastguard Worker # MUSL 71*523fa7a6SAndroid Build Coastguard Worker "3bbde7daa94987db468d021ad625bc93dc62ba7fcb16945cb09b64aab077f284", 72*523fa7a6SAndroid Build Coastguard Worker # GNU 73*523fa7a6SAndroid Build Coastguard Worker "029b0bcc6f8e399185c1d0f574eba204934722b5", 74*523fa7a6SAndroid Build Coastguard Worker ], 75*523fa7a6SAndroid Build Coastguard Worker ), 76*523fa7a6SAndroid Build Coastguard Worker ("linux", "aarch64"): BuckInfo( 77*523fa7a6SAndroid Build Coastguard Worker archive_name="buck2-aarch64-unknown-linux-gnu.zst", 78*523fa7a6SAndroid Build Coastguard Worker target_versions=["49670bee56a7d8a7696409ca6fbf7551d2469787"], 79*523fa7a6SAndroid Build Coastguard Worker ), 80*523fa7a6SAndroid Build Coastguard Worker ("darwin", "aarch64"): BuckInfo( 81*523fa7a6SAndroid Build Coastguard Worker archive_name="buck2-aarch64-apple-darwin.zst", 82*523fa7a6SAndroid Build Coastguard Worker target_versions=["99773fe6f7963a72ae5f7b737c02836e"], 83*523fa7a6SAndroid Build Coastguard Worker ), 84*523fa7a6SAndroid Build Coastguard Worker ("darwin", "x86_64"): BuckInfo( 85*523fa7a6SAndroid Build Coastguard Worker archive_name="buck2-x86_64-apple-darwin.zst", 86*523fa7a6SAndroid Build Coastguard Worker target_versions=["3eb1ae97ea963086866b4d2d9ffa966d"], 87*523fa7a6SAndroid Build Coastguard Worker ), 88*523fa7a6SAndroid Build Coastguard Worker} 89*523fa7a6SAndroid Build Coastguard Worker 90*523fa7a6SAndroid Build Coastguard Worker 91*523fa7a6SAndroid Build Coastguard Workerdef parse_args() -> argparse.Namespace: 92*523fa7a6SAndroid Build Coastguard Worker parser = argparse.ArgumentParser( 93*523fa7a6SAndroid Build Coastguard Worker description="Locates or downloads the appropriate version of buck2.", 94*523fa7a6SAndroid Build Coastguard Worker ) 95*523fa7a6SAndroid Build Coastguard Worker parser.add_argument( 96*523fa7a6SAndroid Build Coastguard Worker "--buck2", 97*523fa7a6SAndroid Build Coastguard Worker default="", 98*523fa7a6SAndroid Build Coastguard Worker help="Optional user-provided 'buck2' path. If provided, it will be " 99*523fa7a6SAndroid Build Coastguard Worker "used. If the version is incorrect, a warning will be logged.", 100*523fa7a6SAndroid Build Coastguard Worker ) 101*523fa7a6SAndroid Build Coastguard Worker parser.add_argument( 102*523fa7a6SAndroid Build Coastguard Worker "--cache_dir", 103*523fa7a6SAndroid Build Coastguard Worker help="Directory to cache downloaded versions of buck2.", 104*523fa7a6SAndroid Build Coastguard Worker ) 105*523fa7a6SAndroid Build Coastguard Worker return parser.parse_args() 106*523fa7a6SAndroid Build Coastguard Worker 107*523fa7a6SAndroid Build Coastguard Worker 108*523fa7a6SAndroid Build Coastguard Worker# Returns the path to buck2 on success or a return code on failure. 109*523fa7a6SAndroid Build Coastguard Workerdef resolve_buck2(args: argparse.Namespace) -> Union[str, int]: 110*523fa7a6SAndroid Build Coastguard Worker # Find buck2, in order of priority: 111*523fa7a6SAndroid Build Coastguard Worker # 1) Explicit buck2 argument. 112*523fa7a6SAndroid Build Coastguard Worker # 2) System buck2 (if correct version). 113*523fa7a6SAndroid Build Coastguard Worker # 3) Cached buck2 (previously downloaded). 114*523fa7a6SAndroid Build Coastguard Worker # 3) Download buck2. 115*523fa7a6SAndroid Build Coastguard Worker 116*523fa7a6SAndroid Build Coastguard Worker # Read the target version (build date) from the CI pin file. Note that 117*523fa7a6SAndroid Build Coastguard Worker # this path is resolved relative to the directory containing this script. 118*523fa7a6SAndroid Build Coastguard Worker script_dir = os.path.dirname(__file__) 119*523fa7a6SAndroid Build Coastguard Worker version_file_path = Path(script_dir) / BUCK_VERSION_FILE 120*523fa7a6SAndroid Build Coastguard Worker with open(version_file_path.absolute().as_posix()) as f: 121*523fa7a6SAndroid Build Coastguard Worker target_buck_version = f.read().strip() 122*523fa7a6SAndroid Build Coastguard Worker 123*523fa7a6SAndroid Build Coastguard Worker # Determine the target buck2 version string according to the current 124*523fa7a6SAndroid Build Coastguard Worker # platform. If the platform isn't linux or darwin, we won't perform 125*523fa7a6SAndroid Build Coastguard Worker # any version validation. 126*523fa7a6SAndroid Build Coastguard Worker machine = platform.machine().lower() 127*523fa7a6SAndroid Build Coastguard Worker arch = "unknown" 128*523fa7a6SAndroid Build Coastguard Worker if machine == "x86" or machine == "x86_64" or machine == "amd64": 129*523fa7a6SAndroid Build Coastguard Worker arch = "x86_64" 130*523fa7a6SAndroid Build Coastguard Worker elif machine == "arm64" or machine == "aarch64": 131*523fa7a6SAndroid Build Coastguard Worker arch = "aarch64" 132*523fa7a6SAndroid Build Coastguard Worker 133*523fa7a6SAndroid Build Coastguard Worker os_family = "unknown" 134*523fa7a6SAndroid Build Coastguard Worker if sys.platform.startswith("linux"): 135*523fa7a6SAndroid Build Coastguard Worker os_family = "linux" 136*523fa7a6SAndroid Build Coastguard Worker elif sys.platform.startswith("darwin"): 137*523fa7a6SAndroid Build Coastguard Worker os_family = "darwin" 138*523fa7a6SAndroid Build Coastguard Worker 139*523fa7a6SAndroid Build Coastguard Worker platform_key = (os_family, arch) 140*523fa7a6SAndroid Build Coastguard Worker if platform_key not in BUCK_PLATFORM_MAP: 141*523fa7a6SAndroid Build Coastguard Worker print( 142*523fa7a6SAndroid Build Coastguard Worker f"Unknown platform {platform_key}. Buck2 binary must be downloaded manually.", 143*523fa7a6SAndroid Build Coastguard Worker file=sys.stderr, 144*523fa7a6SAndroid Build Coastguard Worker ) 145*523fa7a6SAndroid Build Coastguard Worker return args.buck2 or "buck2" 146*523fa7a6SAndroid Build Coastguard Worker 147*523fa7a6SAndroid Build Coastguard Worker buck_info = BUCK_PLATFORM_MAP[platform_key] 148*523fa7a6SAndroid Build Coastguard Worker 149*523fa7a6SAndroid Build Coastguard Worker if args.buck2: 150*523fa7a6SAndroid Build Coastguard Worker # If we have an explicit buck2 arg, check the version and fail if 151*523fa7a6SAndroid Build Coastguard Worker # there is a mismatch. 152*523fa7a6SAndroid Build Coastguard Worker ver = buck_util.get_buck2_version(args.buck2) 153*523fa7a6SAndroid Build Coastguard Worker if ver in buck_info.target_versions: 154*523fa7a6SAndroid Build Coastguard Worker return args.buck2 155*523fa7a6SAndroid Build Coastguard Worker else: 156*523fa7a6SAndroid Build Coastguard Worker print( 157*523fa7a6SAndroid Build Coastguard Worker f'The provided buck2 binary "{args.buck2}" reports version ' 158*523fa7a6SAndroid Build Coastguard Worker f'"{ver}", but ExecuTorch needs version ' 159*523fa7a6SAndroid Build Coastguard Worker f'"{buck_info.target_versions[0]}". Ensure that the correct buck2' 160*523fa7a6SAndroid Build Coastguard Worker " version is installed or avoid explicitly passing the BUCK2 " 161*523fa7a6SAndroid Build Coastguard Worker "version to automatically download the correct version.", 162*523fa7a6SAndroid Build Coastguard Worker file=sys.stderr, 163*523fa7a6SAndroid Build Coastguard Worker ) 164*523fa7a6SAndroid Build Coastguard Worker 165*523fa7a6SAndroid Build Coastguard Worker # Return an error, since the build will fail later. This lets us 166*523fa7a6SAndroid Build Coastguard Worker # give the user a more useful error message. Note that an exit 167*523fa7a6SAndroid Build Coastguard Worker # code of 2 allows us to distinguish from an unexpected error, 168*523fa7a6SAndroid Build Coastguard Worker # such as a failed import, which exits with 1. 169*523fa7a6SAndroid Build Coastguard Worker return 2 170*523fa7a6SAndroid Build Coastguard Worker else: 171*523fa7a6SAndroid Build Coastguard Worker # Look for system buck2 and check version. Note that this can return 172*523fa7a6SAndroid Build Coastguard Worker # None. 173*523fa7a6SAndroid Build Coastguard Worker ver = buck_util.get_buck2_version("buck2") 174*523fa7a6SAndroid Build Coastguard Worker if ver in buck_info.target_versions: 175*523fa7a6SAndroid Build Coastguard Worker # Use system buck2. 176*523fa7a6SAndroid Build Coastguard Worker return "buck2" 177*523fa7a6SAndroid Build Coastguard Worker else: 178*523fa7a6SAndroid Build Coastguard Worker # Download buck2 or used previously cached download. 179*523fa7a6SAndroid Build Coastguard Worker cache_dir = Path(args.cache_dir) 180*523fa7a6SAndroid Build Coastguard Worker os.makedirs(cache_dir, exist_ok=True) 181*523fa7a6SAndroid Build Coastguard Worker 182*523fa7a6SAndroid Build Coastguard Worker buck2_local_path = ( 183*523fa7a6SAndroid Build Coastguard Worker (cache_dir / f"buck2-{buck_info.target_versions[0]}") 184*523fa7a6SAndroid Build Coastguard Worker .absolute() 185*523fa7a6SAndroid Build Coastguard Worker .as_posix() 186*523fa7a6SAndroid Build Coastguard Worker ) 187*523fa7a6SAndroid Build Coastguard Worker 188*523fa7a6SAndroid Build Coastguard Worker # Check for a previously cached buck2 binary. The filename includes 189*523fa7a6SAndroid Build Coastguard Worker # the version hash, so we don't have to worry about using an 190*523fa7a6SAndroid Build Coastguard Worker # outdated binary, in the event that the target version is updated. 191*523fa7a6SAndroid Build Coastguard Worker if os.path.isfile(buck2_local_path): 192*523fa7a6SAndroid Build Coastguard Worker return buck2_local_path 193*523fa7a6SAndroid Build Coastguard Worker 194*523fa7a6SAndroid Build Coastguard Worker buck2_archive_url = f"https://github.com/facebook/buck2/releases/download/{target_buck_version}/{buck_info.archive_name}" 195*523fa7a6SAndroid Build Coastguard Worker 196*523fa7a6SAndroid Build Coastguard Worker with tempfile.NamedTemporaryFile() as archive_file: 197*523fa7a6SAndroid Build Coastguard Worker print(f"Downloading buck2 from {buck2_archive_url}...", file=sys.stderr) 198*523fa7a6SAndroid Build Coastguard Worker urllib.request.urlretrieve(buck2_archive_url, archive_file.name) 199*523fa7a6SAndroid Build Coastguard Worker 200*523fa7a6SAndroid Build Coastguard Worker # Extract and chmod. 201*523fa7a6SAndroid Build Coastguard Worker with open(archive_file.name, "rb") as f: 202*523fa7a6SAndroid Build Coastguard Worker data = f.read() 203*523fa7a6SAndroid Build Coastguard Worker decompressed_bytes = zstd.decompress(data) 204*523fa7a6SAndroid Build Coastguard Worker 205*523fa7a6SAndroid Build Coastguard Worker with open(buck2_local_path, "wb") as f: 206*523fa7a6SAndroid Build Coastguard Worker f.write(decompressed_bytes) 207*523fa7a6SAndroid Build Coastguard Worker 208*523fa7a6SAndroid Build Coastguard Worker file_stat = os.stat(buck2_local_path) 209*523fa7a6SAndroid Build Coastguard Worker os.chmod(buck2_local_path, file_stat.st_mode | stat.S_IEXEC) 210*523fa7a6SAndroid Build Coastguard Worker 211*523fa7a6SAndroid Build Coastguard Worker return buck2_local_path 212*523fa7a6SAndroid Build Coastguard Worker 213*523fa7a6SAndroid Build Coastguard Worker 214*523fa7a6SAndroid Build Coastguard Workerdef main(): 215*523fa7a6SAndroid Build Coastguard Worker args = parse_args() 216*523fa7a6SAndroid Build Coastguard Worker resolved_path_or_error = resolve_buck2(args) 217*523fa7a6SAndroid Build Coastguard Worker if isinstance(resolved_path_or_error, str): 218*523fa7a6SAndroid Build Coastguard Worker print(resolved_path_or_error) 219*523fa7a6SAndroid Build Coastguard Worker else: 220*523fa7a6SAndroid Build Coastguard Worker sys.exit(resolved_path_or_error) 221*523fa7a6SAndroid Build Coastguard Worker 222*523fa7a6SAndroid Build Coastguard Worker 223*523fa7a6SAndroid Build Coastguard Workerif __name__ == "__main__": 224*523fa7a6SAndroid Build Coastguard Worker main() 225