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