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