xref: /aosp_15_r20/external/angle/build/rust/run_build_script.py (revision 8975f5c5ed3d1c378011245431ada316dfb6f244)
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