1*6777b538SAndroid Build Coastguard Worker#!/usr/bin/env python3 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 Workerimport argparse 8*6777b538SAndroid Build Coastguard Workerimport pathlib 9*6777b538SAndroid Build Coastguard Workerimport subprocess 10*6777b538SAndroid Build Coastguard Workerimport shlex 11*6777b538SAndroid Build Coastguard Workerimport os 12*6777b538SAndroid Build Coastguard Workerimport sys 13*6777b538SAndroid Build Coastguard Workerimport re 14*6777b538SAndroid Build Coastguard Worker 15*6777b538SAndroid Build Coastguard Worker# Set up path to be able to import action_helpers. 16*6777b538SAndroid Build Coastguard Workersys.path.append( 17*6777b538SAndroid Build Coastguard Worker os.path.join(os.path.dirname(os.path.abspath(__file__)), os.pardir, 18*6777b538SAndroid Build Coastguard Worker os.pardir, 'build')) 19*6777b538SAndroid Build Coastguard Workerimport action_helpers 20*6777b538SAndroid Build Coastguard Worker 21*6777b538SAndroid Build Coastguard Worker# This script wraps rustc for (currently) these reasons: 22*6777b538SAndroid Build Coastguard Worker# * To work around some ldflags escaping performed by ninja/gn 23*6777b538SAndroid Build Coastguard Worker# * To remove dependencies on some environment variables from the .d file. 24*6777b538SAndroid Build Coastguard Worker# * To enable use of .rsp files. 25*6777b538SAndroid Build Coastguard Worker# * To work around two gn bugs on Windows 26*6777b538SAndroid Build Coastguard Worker# 27*6777b538SAndroid Build Coastguard Worker# LDFLAGS ESCAPING 28*6777b538SAndroid Build Coastguard Worker# 29*6777b538SAndroid Build Coastguard Worker# This script performs a simple function to work around some of the 30*6777b538SAndroid Build Coastguard Worker# parameter escaping performed by ninja/gn. 31*6777b538SAndroid Build Coastguard Worker# 32*6777b538SAndroid Build Coastguard Worker# rustc invocations are given access to {{rustflags}} and {{ldflags}}. 33*6777b538SAndroid Build Coastguard Worker# We want to pass {{ldflags}} into rustc, using -Clink-args="{{ldflags}}". 34*6777b538SAndroid Build Coastguard Worker# Unfortunately, ninja assumes that each item in {{ldflags}} is an 35*6777b538SAndroid Build Coastguard Worker# independent command-line argument and will have escaped them appropriately 36*6777b538SAndroid Build Coastguard Worker# for use on a bare command line, instead of in a string. 37*6777b538SAndroid Build Coastguard Worker# 38*6777b538SAndroid Build Coastguard Worker# This script converts such {{ldflags}} into individual -Clink-arg=X 39*6777b538SAndroid Build Coastguard Worker# arguments to rustc. 40*6777b538SAndroid Build Coastguard Worker# 41*6777b538SAndroid Build Coastguard Worker# RUSTENV dependency stripping 42*6777b538SAndroid Build Coastguard Worker# 43*6777b538SAndroid Build Coastguard Worker# When Rust code depends on an environment variable at build-time 44*6777b538SAndroid Build Coastguard Worker# (using the env! macro), rustc spots that and adds it to the .d file. 45*6777b538SAndroid Build Coastguard Worker# Ninja then parses that .d file and determines that the environment 46*6777b538SAndroid Build Coastguard Worker# dependency means that the target always needs to be rebuilt. 47*6777b538SAndroid Build Coastguard Worker# 48*6777b538SAndroid Build Coastguard Worker# That's all correct, but _we_ know that some of these environment 49*6777b538SAndroid Build Coastguard Worker# variables (typically, all of them) are set by .gn files which ninja 50*6777b538SAndroid Build Coastguard Worker# tracks independently. So we remove them from the .d file. 51*6777b538SAndroid Build Coastguard Worker# 52*6777b538SAndroid Build Coastguard Worker# RSP files: 53*6777b538SAndroid Build Coastguard Worker# 54*6777b538SAndroid Build Coastguard Worker# We want to put the ninja/gn variables {{rustdeps}} and {{externs}} 55*6777b538SAndroid Build Coastguard Worker# in an RSP file. Unfortunately, they are space-separated variables 56*6777b538SAndroid Build Coastguard Worker# but Rust requires a newline-separated input. This script duly makes 57*6777b538SAndroid Build Coastguard Worker# the adjustment. This works around a gn issue: 58*6777b538SAndroid Build Coastguard Worker# TODO(https://bugs.chromium.org/p/gn/issues/detail?id=249): fix this 59*6777b538SAndroid Build Coastguard Worker# 60*6777b538SAndroid Build Coastguard Worker# WORKAROUND WINDOWS BUGS: 61*6777b538SAndroid Build Coastguard Worker# 62*6777b538SAndroid Build Coastguard Worker# On Windows platforms, this temporarily works around some issues in gn. 63*6777b538SAndroid Build Coastguard Worker# See comments inline, linking to the relevant gn fixes. 64*6777b538SAndroid Build Coastguard Worker# 65*6777b538SAndroid Build Coastguard Worker# Usage: 66*6777b538SAndroid Build Coastguard Worker# rustc_wrapper.py --rustc <path to rustc> --depfile <path to .d file> 67*6777b538SAndroid Build Coastguard Worker# -- <normal rustc args> LDFLAGS {{ldflags}} RUSTENV {{rustenv}} 68*6777b538SAndroid Build Coastguard Worker# The LDFLAGS token is discarded, and everything after that is converted 69*6777b538SAndroid Build Coastguard Worker# to being a series of -Clink-arg=X arguments, until or unless RUSTENV 70*6777b538SAndroid Build Coastguard Worker# is encountered, after which those are interpreted as environment 71*6777b538SAndroid Build Coastguard Worker# variables to pass to rustc (and which will be removed from the .d file). 72*6777b538SAndroid Build Coastguard Worker# 73*6777b538SAndroid Build Coastguard Worker# Both LDFLAGS and RUSTENV **MUST** be specified, in that order, even if 74*6777b538SAndroid Build Coastguard Worker# the list following them is empty. 75*6777b538SAndroid Build Coastguard Worker# 76*6777b538SAndroid Build Coastguard Worker# TODO(https://github.com/rust-lang/rust/issues/73632): avoid using rustc 77*6777b538SAndroid Build Coastguard Worker# for linking in the first place. Most of our binaries are linked using 78*6777b538SAndroid Build Coastguard Worker# clang directly, but there are some types of Rust build product which 79*6777b538SAndroid Build Coastguard Worker# must currently be created by rustc (e.g. unit test executables). As 80*6777b538SAndroid Build Coastguard Worker# part of support for using non-rustc linkers, we should arrange to extract 81*6777b538SAndroid Build Coastguard Worker# such functionality from rustc so that we can make all types of binary 82*6777b538SAndroid Build Coastguard Worker# using our clang toolchain. That will remove the need for most of this 83*6777b538SAndroid Build Coastguard Worker# script. 84*6777b538SAndroid Build Coastguard Worker 85*6777b538SAndroid Build Coastguard WorkerFILE_RE = re.compile("[^:]+: (.+)") 86*6777b538SAndroid Build Coastguard Worker 87*6777b538SAndroid Build Coastguard Worker 88*6777b538SAndroid Build Coastguard Worker# Equivalent of python3.9 built-in 89*6777b538SAndroid Build Coastguard Workerdef remove_lib_suffix_from_l_args(text): 90*6777b538SAndroid Build Coastguard Worker if text.startswith("-l") and text.endswith(".lib"): 91*6777b538SAndroid Build Coastguard Worker return text[:-len(".lib")] 92*6777b538SAndroid Build Coastguard Worker return text 93*6777b538SAndroid Build Coastguard Worker 94*6777b538SAndroid Build Coastguard Worker 95*6777b538SAndroid Build Coastguard Workerdef verify_inputs(depline, sources, abs_build_root): 96*6777b538SAndroid Build Coastguard Worker """Verify everything used by rustc (found in `depline`) was specified in the 97*6777b538SAndroid Build Coastguard Worker GN build rule (found in `sources` or `inputs`). 98*6777b538SAndroid Build Coastguard Worker 99*6777b538SAndroid Build Coastguard Worker TODO(danakj): This allows things in `sources` that were not actually used by 100*6777b538SAndroid Build Coastguard Worker rustc since third-party packages sources need to be a union of all build 101*6777b538SAndroid Build Coastguard Worker configs/platforms for simplicity in generating build rules. For first-party 102*6777b538SAndroid Build Coastguard Worker code we could be more strict and reject things in `sources` that were not 103*6777b538SAndroid Build Coastguard Worker consumed. 104*6777b538SAndroid Build Coastguard Worker """ 105*6777b538SAndroid Build Coastguard Worker 106*6777b538SAndroid Build Coastguard Worker # str.removeprefix() does not exist before python 3.9. 107*6777b538SAndroid Build Coastguard Worker def remove_prefix(text, prefix): 108*6777b538SAndroid Build Coastguard Worker if text.startswith(prefix): 109*6777b538SAndroid Build Coastguard Worker return text[len(prefix):] 110*6777b538SAndroid Build Coastguard Worker return text 111*6777b538SAndroid Build Coastguard Worker 112*6777b538SAndroid Build Coastguard Worker def normalize_path(p): 113*6777b538SAndroid Build Coastguard Worker return os.path.relpath(os.path.normpath(remove_prefix( 114*6777b538SAndroid Build Coastguard Worker p, abs_build_root))).replace('\\', '/') 115*6777b538SAndroid Build Coastguard Worker 116*6777b538SAndroid Build Coastguard Worker # Collect the files that rustc says are needed. 117*6777b538SAndroid Build Coastguard Worker found_files = {} 118*6777b538SAndroid Build Coastguard Worker m = FILE_RE.match(depline) 119*6777b538SAndroid Build Coastguard Worker if m: 120*6777b538SAndroid Build Coastguard Worker files = m.group(1) 121*6777b538SAndroid Build Coastguard Worker found_files = {normalize_path(f): f for f in files.split()} 122*6777b538SAndroid Build Coastguard Worker # Get which ones are not listed in GN. 123*6777b538SAndroid Build Coastguard Worker missing_files = found_files.keys() - sources 124*6777b538SAndroid Build Coastguard Worker 125*6777b538SAndroid Build Coastguard Worker if not missing_files: 126*6777b538SAndroid Build Coastguard Worker return True 127*6777b538SAndroid Build Coastguard Worker 128*6777b538SAndroid Build Coastguard Worker # The matching did a bunch of path manipulation to get paths relative to the 129*6777b538SAndroid Build Coastguard Worker # build dir such that they would match GN. In errors, we will print out the 130*6777b538SAndroid Build Coastguard Worker # exact path that rustc produces for easier debugging and writing of stdlib 131*6777b538SAndroid Build Coastguard Worker # config rules. 132*6777b538SAndroid Build Coastguard Worker for file_files_key in missing_files: 133*6777b538SAndroid Build Coastguard Worker gn_type = "sources" if file_files_key.endswith(".rs") else "inputs" 134*6777b538SAndroid Build Coastguard Worker print(f'ERROR: file not in GN {gn_type}: {found_files[file_files_key]}', 135*6777b538SAndroid Build Coastguard Worker file=sys.stderr) 136*6777b538SAndroid Build Coastguard Worker return False 137*6777b538SAndroid Build Coastguard Worker 138*6777b538SAndroid Build Coastguard Worker 139*6777b538SAndroid Build Coastguard Workerdef main(): 140*6777b538SAndroid Build Coastguard Worker parser = argparse.ArgumentParser() 141*6777b538SAndroid Build Coastguard Worker parser.add_argument('--rustc', required=True, type=pathlib.Path) 142*6777b538SAndroid Build Coastguard Worker parser.add_argument('--depfile', required=True, type=pathlib.Path) 143*6777b538SAndroid Build Coastguard Worker parser.add_argument('--rsp', type=pathlib.Path, required=True) 144*6777b538SAndroid Build Coastguard Worker parser.add_argument('--target-windows', action='store_true') 145*6777b538SAndroid Build Coastguard Worker parser.add_argument('-v', action='store_true') 146*6777b538SAndroid Build Coastguard Worker parser.add_argument('args', metavar='ARG', nargs='+') 147*6777b538SAndroid Build Coastguard Worker 148*6777b538SAndroid Build Coastguard Worker args = parser.parse_args() 149*6777b538SAndroid Build Coastguard Worker 150*6777b538SAndroid Build Coastguard Worker remaining_args = args.args 151*6777b538SAndroid Build Coastguard Worker 152*6777b538SAndroid Build Coastguard Worker ldflags_separator = remaining_args.index("LDFLAGS") 153*6777b538SAndroid Build Coastguard Worker rustenv_separator = remaining_args.index("RUSTENV", ldflags_separator) 154*6777b538SAndroid Build Coastguard Worker # Sometimes we duplicate the SOURCES list into the command line for debugging 155*6777b538SAndroid Build Coastguard Worker # issues on the bots. 156*6777b538SAndroid Build Coastguard Worker try: 157*6777b538SAndroid Build Coastguard Worker sources_separator = remaining_args.index("SOURCES", rustenv_separator) 158*6777b538SAndroid Build Coastguard Worker except: 159*6777b538SAndroid Build Coastguard Worker sources_separator = None 160*6777b538SAndroid Build Coastguard Worker rustc_args = remaining_args[:ldflags_separator] 161*6777b538SAndroid Build Coastguard Worker ldflags = remaining_args[ldflags_separator + 1:rustenv_separator] 162*6777b538SAndroid Build Coastguard Worker rustenv = remaining_args[rustenv_separator + 1:sources_separator] 163*6777b538SAndroid Build Coastguard Worker 164*6777b538SAndroid Build Coastguard Worker abs_build_root = os.getcwd().replace('\\', '/') + '/' 165*6777b538SAndroid Build Coastguard Worker is_windows = sys.platform == 'win32' or args.target_windows 166*6777b538SAndroid Build Coastguard Worker 167*6777b538SAndroid Build Coastguard Worker rustc_args.extend(["-Clink-arg=%s" % arg for arg in ldflags]) 168*6777b538SAndroid Build Coastguard Worker 169*6777b538SAndroid Build Coastguard Worker with open(args.rsp) as rspfile: 170*6777b538SAndroid Build Coastguard Worker rsp_args = [l.rstrip() for l in rspfile.read().split(' ') if l.rstrip()] 171*6777b538SAndroid Build Coastguard Worker 172*6777b538SAndroid Build Coastguard Worker sources_separator = rsp_args.index("SOURCES") 173*6777b538SAndroid Build Coastguard Worker sources = set(rsp_args[sources_separator + 1:]) 174*6777b538SAndroid Build Coastguard Worker rsp_args = rsp_args[:sources_separator] 175*6777b538SAndroid Build Coastguard Worker 176*6777b538SAndroid Build Coastguard Worker if is_windows: 177*6777b538SAndroid Build Coastguard Worker # Work around for "-l<foo>.lib", where ".lib" suffix is undesirable. 178*6777b538SAndroid Build Coastguard Worker # Full fix will come from https://gn-review.googlesource.com/c/gn/+/12480 179*6777b538SAndroid Build Coastguard Worker rsp_args = [remove_lib_suffix_from_l_args(arg) for arg in rsp_args] 180*6777b538SAndroid Build Coastguard Worker out_rsp = str(args.rsp) + ".rsp" 181*6777b538SAndroid Build Coastguard Worker with open(out_rsp, 'w') as rspfile: 182*6777b538SAndroid Build Coastguard Worker # rustc needs the rsp file to be separated by newlines. Note that GN 183*6777b538SAndroid Build Coastguard Worker # generates the file separated by spaces: 184*6777b538SAndroid Build Coastguard Worker # https://bugs.chromium.org/p/gn/issues/detail?id=249, 185*6777b538SAndroid Build Coastguard Worker rspfile.write("\n".join(rsp_args)) 186*6777b538SAndroid Build Coastguard Worker rustc_args.append(f'@{out_rsp}') 187*6777b538SAndroid Build Coastguard Worker 188*6777b538SAndroid Build Coastguard Worker env = os.environ.copy() 189*6777b538SAndroid Build Coastguard Worker fixed_env_vars = [] 190*6777b538SAndroid Build Coastguard Worker for item in rustenv: 191*6777b538SAndroid Build Coastguard Worker (k, v) = item.split("=", 1) 192*6777b538SAndroid Build Coastguard Worker env[k] = v 193*6777b538SAndroid Build Coastguard Worker fixed_env_vars.append(k) 194*6777b538SAndroid Build Coastguard Worker 195*6777b538SAndroid Build Coastguard Worker try: 196*6777b538SAndroid Build Coastguard Worker if args.v: 197*6777b538SAndroid Build Coastguard Worker print(' '.join(f'{k}={shlex.quote(v)}' for k, v in env.items()), 198*6777b538SAndroid Build Coastguard Worker args.rustc, shlex.join(rustc_args)) 199*6777b538SAndroid Build Coastguard Worker r = subprocess.run([args.rustc, *rustc_args], env=env, check=False) 200*6777b538SAndroid Build Coastguard Worker finally: 201*6777b538SAndroid Build Coastguard Worker if not args.v: 202*6777b538SAndroid Build Coastguard Worker os.remove(out_rsp) 203*6777b538SAndroid Build Coastguard Worker if r.returncode != 0: 204*6777b538SAndroid Build Coastguard Worker sys.exit(r.returncode) 205*6777b538SAndroid Build Coastguard Worker 206*6777b538SAndroid Build Coastguard Worker final_depfile_lines = [] 207*6777b538SAndroid Build Coastguard Worker dirty = False 208*6777b538SAndroid Build Coastguard Worker with open(args.depfile, encoding="utf-8") as d: 209*6777b538SAndroid Build Coastguard Worker # Figure out which lines we want to keep in the depfile. If it's not the 210*6777b538SAndroid Build Coastguard Worker # whole file, we will rewrite the file. 211*6777b538SAndroid Build Coastguard Worker env_dep_re = re.compile("# env-dep:(.*)=.*") 212*6777b538SAndroid Build Coastguard Worker for line in d: 213*6777b538SAndroid Build Coastguard Worker m = env_dep_re.match(line) 214*6777b538SAndroid Build Coastguard Worker if m and m.group(1) in fixed_env_vars: 215*6777b538SAndroid Build Coastguard Worker dirty = True # We want to skip this line. 216*6777b538SAndroid Build Coastguard Worker else: 217*6777b538SAndroid Build Coastguard Worker final_depfile_lines.append(line) 218*6777b538SAndroid Build Coastguard Worker 219*6777b538SAndroid Build Coastguard Worker # Verify each dependent file is listed in sources/inputs. 220*6777b538SAndroid Build Coastguard Worker for line in final_depfile_lines: 221*6777b538SAndroid Build Coastguard Worker if not verify_inputs(line, sources, abs_build_root): 222*6777b538SAndroid Build Coastguard Worker return 1 223*6777b538SAndroid Build Coastguard Worker 224*6777b538SAndroid Build Coastguard Worker if dirty: # we made a change, let's write out the file 225*6777b538SAndroid Build Coastguard Worker with action_helpers.atomic_output(args.depfile) as output: 226*6777b538SAndroid Build Coastguard Worker output.write("\n".join(final_depfile_lines).encode("utf-8")) 227*6777b538SAndroid Build Coastguard Worker 228*6777b538SAndroid Build Coastguard Worker 229*6777b538SAndroid Build Coastguard Workerif __name__ == '__main__': 230*6777b538SAndroid Build Coastguard Worker sys.exit(main()) 231