1*8975f5c5SAndroid Build Coastguard Worker#!/usr/bin/env vpython3 2*8975f5c5SAndroid Build Coastguard Worker 3*8975f5c5SAndroid Build Coastguard Worker# Copyright 2021 The Chromium Authors 4*8975f5c5SAndroid Build Coastguard Worker# Use of this source code is governed by a BSD-style license that can be 5*8975f5c5SAndroid Build Coastguard Worker# found in the LICENSE file. 6*8975f5c5SAndroid Build Coastguard Worker 7*8975f5c5SAndroid Build Coastguard Worker# This is a wrapper script which runs a Cargo build.rs build script 8*8975f5c5SAndroid Build Coastguard Worker# executable in a Cargo-like environment. Build scripts can do arbitrary 9*8975f5c5SAndroid Build Coastguard Worker# things and we can't support everything. Moreover, we do not WANT 10*8975f5c5SAndroid Build Coastguard Worker# to support everything because that means the build is not deterministic. 11*8975f5c5SAndroid Build Coastguard Worker# Code review processes must be applied to ensure that the build script 12*8975f5c5SAndroid Build Coastguard Worker# depends upon only these inputs: 13*8975f5c5SAndroid Build Coastguard Worker# 14*8975f5c5SAndroid Build Coastguard Worker# * The environment variables set by Cargo here: 15*8975f5c5SAndroid Build Coastguard Worker# https://doc.rust-lang.org/cargo/reference/environment-variables.html#environment-variables-cargo-sets-for-build-scripts 16*8975f5c5SAndroid Build Coastguard Worker# * Output from rustc commands, e.g. to figure out the Rust version. 17*8975f5c5SAndroid Build Coastguard Worker# 18*8975f5c5SAndroid Build Coastguard Worker# Similarly, the only allowable output from such a build script 19*8975f5c5SAndroid Build Coastguard Worker# is currently: 20*8975f5c5SAndroid Build Coastguard Worker# 21*8975f5c5SAndroid Build Coastguard Worker# * Generated .rs files 22*8975f5c5SAndroid Build Coastguard Worker# * cargo:rustc-cfg output. 23*8975f5c5SAndroid Build Coastguard Worker# 24*8975f5c5SAndroid Build Coastguard Worker# That's it. We don't even support the other standard cargo:rustc- 25*8975f5c5SAndroid Build Coastguard Worker# output messages. 26*8975f5c5SAndroid Build Coastguard Worker 27*8975f5c5SAndroid Build Coastguard Workerimport argparse 28*8975f5c5SAndroid Build Coastguard Workerimport io 29*8975f5c5SAndroid Build Coastguard Workerimport os 30*8975f5c5SAndroid Build Coastguard Workerimport platform 31*8975f5c5SAndroid Build Coastguard Workerimport re 32*8975f5c5SAndroid Build Coastguard Workerimport subprocess 33*8975f5c5SAndroid Build Coastguard Workerimport sys 34*8975f5c5SAndroid Build Coastguard Workerimport tempfile 35*8975f5c5SAndroid Build Coastguard Worker 36*8975f5c5SAndroid Build Coastguard Worker# Set up path to be able to import action_helpers 37*8975f5c5SAndroid Build Coastguard Workersys.path.append( 38*8975f5c5SAndroid Build Coastguard Worker os.path.join(os.path.dirname(os.path.abspath(__file__)), os.pardir, 39*8975f5c5SAndroid Build Coastguard Worker os.pardir, 'build')) 40*8975f5c5SAndroid Build Coastguard Workerimport action_helpers 41*8975f5c5SAndroid Build Coastguard Worker 42*8975f5c5SAndroid Build Coastguard Worker 43*8975f5c5SAndroid Build Coastguard WorkerRUSTC_VERSION_LINE = re.compile(r"(\w+): (.*)") 44*8975f5c5SAndroid Build Coastguard Worker 45*8975f5c5SAndroid Build Coastguard Worker 46*8975f5c5SAndroid Build Coastguard Workerdef rustc_name(): 47*8975f5c5SAndroid Build Coastguard Worker if platform.system() == 'Windows': 48*8975f5c5SAndroid Build Coastguard Worker return "rustc.exe" 49*8975f5c5SAndroid Build Coastguard Worker else: 50*8975f5c5SAndroid Build Coastguard Worker return "rustc" 51*8975f5c5SAndroid Build Coastguard Worker 52*8975f5c5SAndroid Build Coastguard Worker 53*8975f5c5SAndroid Build Coastguard Workerdef host_triple(rustc_path): 54*8975f5c5SAndroid Build Coastguard Worker """ Works out the host rustc target. """ 55*8975f5c5SAndroid Build Coastguard Worker args = [rustc_path, "-vV"] 56*8975f5c5SAndroid Build Coastguard Worker known_vars = dict() 57*8975f5c5SAndroid Build Coastguard Worker proc = subprocess.Popen(args, stdout=subprocess.PIPE) 58*8975f5c5SAndroid Build Coastguard Worker for line in io.TextIOWrapper(proc.stdout, encoding="utf-8"): 59*8975f5c5SAndroid Build Coastguard Worker m = RUSTC_VERSION_LINE.match(line.rstrip()) 60*8975f5c5SAndroid Build Coastguard Worker if m: 61*8975f5c5SAndroid Build Coastguard Worker known_vars[m.group(1)] = m.group(2) 62*8975f5c5SAndroid Build Coastguard Worker return known_vars["host"] 63*8975f5c5SAndroid Build Coastguard Worker 64*8975f5c5SAndroid Build Coastguard Worker 65*8975f5c5SAndroid Build Coastguard Worker# Before 1.77, the format was `cargo:rustc-cfg=`. As of 1.77 the format is now 66*8975f5c5SAndroid Build Coastguard Worker# `cargo::rustc-cfg=`. 67*8975f5c5SAndroid Build Coastguard WorkerRUSTC_CFG_LINE = re.compile("cargo::?rustc-cfg=(.*)") 68*8975f5c5SAndroid Build Coastguard Worker 69*8975f5c5SAndroid Build Coastguard Worker 70*8975f5c5SAndroid Build Coastguard Workerdef main(): 71*8975f5c5SAndroid Build Coastguard Worker parser = argparse.ArgumentParser(description='Run Rust build script.') 72*8975f5c5SAndroid Build Coastguard Worker parser.add_argument('--build-script', 73*8975f5c5SAndroid Build Coastguard Worker required=True, 74*8975f5c5SAndroid Build Coastguard Worker help='build script to run') 75*8975f5c5SAndroid Build Coastguard Worker parser.add_argument('--output', 76*8975f5c5SAndroid Build Coastguard Worker required=True, 77*8975f5c5SAndroid Build Coastguard Worker help='where to write output rustc flags') 78*8975f5c5SAndroid Build Coastguard Worker parser.add_argument('--target', help='rust target triple') 79*8975f5c5SAndroid Build Coastguard Worker parser.add_argument('--target-abi', help='rust target_abi') 80*8975f5c5SAndroid Build Coastguard Worker parser.add_argument('--pointer-width', help='rust target pointer width') 81*8975f5c5SAndroid Build Coastguard Worker parser.add_argument('--features', help='features', nargs='+') 82*8975f5c5SAndroid Build Coastguard Worker parser.add_argument('--env', help='environment variable', nargs='+') 83*8975f5c5SAndroid Build Coastguard Worker parser.add_argument('--rustflags', 84*8975f5c5SAndroid Build Coastguard Worker help=('path to a file of newline-separated command line ' 85*8975f5c5SAndroid Build Coastguard Worker 'flags for rustc')) 86*8975f5c5SAndroid Build Coastguard Worker parser.add_argument('--rust-prefix', required=True, help='rust path prefix') 87*8975f5c5SAndroid Build Coastguard Worker parser.add_argument('--generated-files', nargs='+', help='any generated file') 88*8975f5c5SAndroid Build Coastguard Worker parser.add_argument('--out-dir', required=True, help='target out dir') 89*8975f5c5SAndroid Build Coastguard Worker parser.add_argument('--src-dir', required=True, help='target source dir') 90*8975f5c5SAndroid Build Coastguard Worker 91*8975f5c5SAndroid Build Coastguard Worker args = parser.parse_args() 92*8975f5c5SAndroid Build Coastguard Worker 93*8975f5c5SAndroid Build Coastguard Worker rustc_path = os.path.join(args.rust_prefix, rustc_name()) 94*8975f5c5SAndroid Build Coastguard Worker 95*8975f5c5SAndroid Build Coastguard Worker # We give the build script an OUT_DIR of a temporary directory, 96*8975f5c5SAndroid Build Coastguard Worker # and copy out only any files which gn directives say that it 97*8975f5c5SAndroid Build Coastguard Worker # should generate. Mostly this is to ensure we can atomically 98*8975f5c5SAndroid Build Coastguard Worker # create those files, but it also serves to avoid side-effects 99*8975f5c5SAndroid Build Coastguard Worker # from the build script. 100*8975f5c5SAndroid Build Coastguard Worker # In the future, we could consider isolating this build script 101*8975f5c5SAndroid Build Coastguard Worker # into a chroot jail or similar on some platforms, but ultimately 102*8975f5c5SAndroid Build Coastguard Worker # we are always going to be reliant on code review to ensure the 103*8975f5c5SAndroid Build Coastguard Worker # build script is deterministic and trustworthy, so this would 104*8975f5c5SAndroid Build Coastguard Worker # really just be a backup to humans. 105*8975f5c5SAndroid Build Coastguard Worker with tempfile.TemporaryDirectory() as tempdir: 106*8975f5c5SAndroid Build Coastguard Worker env = {} # try to avoid build scripts depending on other things 107*8975f5c5SAndroid Build Coastguard Worker env["RUSTC"] = os.path.abspath(rustc_path) 108*8975f5c5SAndroid Build Coastguard Worker env["OUT_DIR"] = tempdir 109*8975f5c5SAndroid Build Coastguard Worker env["CARGO_MANIFEST_DIR"] = os.path.abspath(args.src_dir) 110*8975f5c5SAndroid Build Coastguard Worker env["HOST"] = host_triple(rustc_path) 111*8975f5c5SAndroid Build Coastguard Worker env["CARGO_CFG_TARGET_POINTER_WIDTH"] = args.pointer_width 112*8975f5c5SAndroid Build Coastguard Worker if args.target is None: 113*8975f5c5SAndroid Build Coastguard Worker env["TARGET"] = env["HOST"] 114*8975f5c5SAndroid Build Coastguard Worker else: 115*8975f5c5SAndroid Build Coastguard Worker env["TARGET"] = args.target 116*8975f5c5SAndroid Build Coastguard Worker target_components = env["TARGET"].split("-") 117*8975f5c5SAndroid Build Coastguard Worker if len(target_components) == 2: 118*8975f5c5SAndroid Build Coastguard Worker env["CARGO_CFG_TARGET_ARCH"] = target_components[0] 119*8975f5c5SAndroid Build Coastguard Worker env["CARGO_CFG_TARGET_VENDOR"] = '' 120*8975f5c5SAndroid Build Coastguard Worker env["CARGO_CFG_TARGET_OS"] = target_components[1] 121*8975f5c5SAndroid Build Coastguard Worker env["CARGO_CFG_TARGET_ENV"] = '' 122*8975f5c5SAndroid Build Coastguard Worker elif len(target_components) == 3: 123*8975f5c5SAndroid Build Coastguard Worker env["CARGO_CFG_TARGET_ARCH"] = target_components[0] 124*8975f5c5SAndroid Build Coastguard Worker env["CARGO_CFG_TARGET_VENDOR"] = target_components[1] 125*8975f5c5SAndroid Build Coastguard Worker env["CARGO_CFG_TARGET_OS"] = target_components[2] 126*8975f5c5SAndroid Build Coastguard Worker env["CARGO_CFG_TARGET_ENV"] = '' 127*8975f5c5SAndroid Build Coastguard Worker elif len(target_components) == 4: 128*8975f5c5SAndroid Build Coastguard Worker env["CARGO_CFG_TARGET_ARCH"] = target_components[0] 129*8975f5c5SAndroid Build Coastguard Worker env["CARGO_CFG_TARGET_VENDOR"] = target_components[1] 130*8975f5c5SAndroid Build Coastguard Worker env["CARGO_CFG_TARGET_OS"] = target_components[2] 131*8975f5c5SAndroid Build Coastguard Worker env["CARGO_CFG_TARGET_ENV"] = target_components[3] 132*8975f5c5SAndroid Build Coastguard Worker else: 133*8975f5c5SAndroid Build Coastguard Worker print(f'Invalid TARGET {env["TARGET"]}') 134*8975f5c5SAndroid Build Coastguard Worker sys.exit(1) 135*8975f5c5SAndroid Build Coastguard Worker # See https://crbug.com/325543500 for background. 136*8975f5c5SAndroid Build Coastguard Worker # Cargo sets CARGO_CFG_TARGET_OS to "android" even when targeting 137*8975f5c5SAndroid Build Coastguard Worker # *-androideabi. 138*8975f5c5SAndroid Build Coastguard Worker if env["CARGO_CFG_TARGET_OS"].startswith("android"): 139*8975f5c5SAndroid Build Coastguard Worker env["CARGO_CFG_TARGET_OS"] = "android" 140*8975f5c5SAndroid Build Coastguard Worker elif env["CARGO_CFG_TARGET_OS"] == "darwin": 141*8975f5c5SAndroid Build Coastguard Worker env["CARGO_CFG_TARGET_OS"] = "macos" 142*8975f5c5SAndroid Build Coastguard Worker env["CARGO_CFG_TARGET_ENDIAN"] = "little" 143*8975f5c5SAndroid Build Coastguard Worker if env["CARGO_CFG_TARGET_OS"] == "windows": 144*8975f5c5SAndroid Build Coastguard Worker env["CARGO_CFG_TARGET_FAMILY"] = "windows" 145*8975f5c5SAndroid Build Coastguard Worker else: 146*8975f5c5SAndroid Build Coastguard Worker env["CARGO_CFG_TARGET_FAMILY"] = "unix" 147*8975f5c5SAndroid Build Coastguard Worker env["CARGO_CFG_TARGET_ABI"] = args.target_abi if args.target_abi else "" 148*8975f5c5SAndroid Build Coastguard Worker if args.features: 149*8975f5c5SAndroid Build Coastguard Worker for f in args.features: 150*8975f5c5SAndroid Build Coastguard Worker feature_name = f.upper().replace("-", "_") 151*8975f5c5SAndroid Build Coastguard Worker env["CARGO_FEATURE_%s" % feature_name] = "1" 152*8975f5c5SAndroid Build Coastguard Worker if args.rustflags: 153*8975f5c5SAndroid Build Coastguard Worker with open(args.rustflags) as flags: 154*8975f5c5SAndroid Build Coastguard Worker env["CARGO_ENCODED_RUSTFLAGS"] = '\x1f'.join(flags.readlines()) 155*8975f5c5SAndroid Build Coastguard Worker if args.env: 156*8975f5c5SAndroid Build Coastguard Worker for e in args.env: 157*8975f5c5SAndroid Build Coastguard Worker (k, v) = e.split("=") 158*8975f5c5SAndroid Build Coastguard Worker env[k] = v 159*8975f5c5SAndroid Build Coastguard Worker # Pass through a couple which are useful for diagnostics 160*8975f5c5SAndroid Build Coastguard Worker if os.environ.get("RUST_BACKTRACE"): 161*8975f5c5SAndroid Build Coastguard Worker env["RUST_BACKTRACE"] = os.environ.get("RUST_BACKTRACE") 162*8975f5c5SAndroid Build Coastguard Worker if os.environ.get("RUST_LOG"): 163*8975f5c5SAndroid Build Coastguard Worker env["RUST_LOG"] = os.environ.get("RUST_LOG") 164*8975f5c5SAndroid Build Coastguard Worker 165*8975f5c5SAndroid Build Coastguard Worker # In the future we should, set all the variables listed here: 166*8975f5c5SAndroid Build Coastguard Worker # https://doc.rust-lang.org/cargo/reference/environment-variables.html#environment-variables-cargo-sets-for-build-scripts 167*8975f5c5SAndroid Build Coastguard Worker 168*8975f5c5SAndroid Build Coastguard Worker proc = subprocess.run([os.path.abspath(args.build_script)], 169*8975f5c5SAndroid Build Coastguard Worker env=env, 170*8975f5c5SAndroid Build Coastguard Worker cwd=args.src_dir, 171*8975f5c5SAndroid Build Coastguard Worker encoding='utf8', 172*8975f5c5SAndroid Build Coastguard Worker stdout=subprocess.PIPE, 173*8975f5c5SAndroid Build Coastguard Worker stderr=subprocess.PIPE) 174*8975f5c5SAndroid Build Coastguard Worker 175*8975f5c5SAndroid Build Coastguard Worker if proc.stderr.rstrip(): 176*8975f5c5SAndroid Build Coastguard Worker print(proc.stderr.rstrip(), file=sys.stderr) 177*8975f5c5SAndroid Build Coastguard Worker proc.check_returncode() 178*8975f5c5SAndroid Build Coastguard Worker 179*8975f5c5SAndroid Build Coastguard Worker flags = "" 180*8975f5c5SAndroid Build Coastguard Worker for line in proc.stdout.split("\n"): 181*8975f5c5SAndroid Build Coastguard Worker m = RUSTC_CFG_LINE.match(line.rstrip()) 182*8975f5c5SAndroid Build Coastguard Worker if m: 183*8975f5c5SAndroid Build Coastguard Worker flags = "%s--cfg\n%s\n" % (flags, m.group(1)) 184*8975f5c5SAndroid Build Coastguard Worker 185*8975f5c5SAndroid Build Coastguard Worker # AtomicOutput will ensure we only write to the file on disk if what we 186*8975f5c5SAndroid Build Coastguard Worker # give to write() is different than what's currently on disk. 187*8975f5c5SAndroid Build Coastguard Worker with action_helpers.atomic_output(args.output) as output: 188*8975f5c5SAndroid Build Coastguard Worker output.write(flags.encode("utf-8")) 189*8975f5c5SAndroid Build Coastguard Worker 190*8975f5c5SAndroid Build Coastguard Worker # Copy any generated code out of the temporary directory, 191*8975f5c5SAndroid Build Coastguard Worker # atomically. 192*8975f5c5SAndroid Build Coastguard Worker if args.generated_files: 193*8975f5c5SAndroid Build Coastguard Worker for generated_file in args.generated_files: 194*8975f5c5SAndroid Build Coastguard Worker in_path = os.path.join(tempdir, generated_file) 195*8975f5c5SAndroid Build Coastguard Worker out_path = os.path.join(args.out_dir, generated_file) 196*8975f5c5SAndroid Build Coastguard Worker out_dir = os.path.dirname(out_path) 197*8975f5c5SAndroid Build Coastguard Worker if not os.path.exists(out_dir): 198*8975f5c5SAndroid Build Coastguard Worker os.makedirs(out_dir) 199*8975f5c5SAndroid Build Coastguard Worker with open(in_path, 'rb') as input: 200*8975f5c5SAndroid Build Coastguard Worker with action_helpers.atomic_output(out_path) as output: 201*8975f5c5SAndroid Build Coastguard Worker content = input.read() 202*8975f5c5SAndroid Build Coastguard Worker output.write(content) 203*8975f5c5SAndroid Build Coastguard Worker 204*8975f5c5SAndroid Build Coastguard Worker 205*8975f5c5SAndroid Build Coastguard Workerif __name__ == '__main__': 206*8975f5c5SAndroid Build Coastguard Worker sys.exit(main()) 207