blob: dc4337f8bce410b9e1e49823ccd2a6d7b46b224f [file] [log] [blame]
Adrian Taylor792467f22021-10-29 20:18:301#!/usr/bin/env vpython3
2
3# Copyright 2021 The Chromium Authors. All rights reserved.
4# Use of this source code is governed by a BSD-style license that can be
5# found in the LICENSE file.
6
7# This is a wrapper script which runs a Cargo build.rs build script
8# executable in a Cargo-like environment. Build scripts can do arbitrary
9# things and we can't support everything. Moreover, we do not WANT
10# to support everything because that means the build is not deterministic.
11# Code review processes must be applied to ensure that the build script
12# depends upon only these inputs:
13#
14# * The environment variables set by Cargo here:
15# https://doc.rust-lang.org/cargo/reference/environment-variables.html#environment-variables-cargo-sets-for-build-scripts
16# * Output from rustc commands, e.g. to figure out the Rust version.
17#
18# Similarly, the only allowable output from such a build script
19# is currently:
20#
21# * Generated .rs files
22# * cargo:rustc-cfg output.
23#
24# That's it. We don't even support the other standard cargo:rustc-
25# output messages.
26
27import os
28import sys
29
Adrian Taylor1f172cd2021-11-04 18:35:4430# Set up path to be able to import build_utils
Adrian Taylor792467f22021-10-29 20:18:3031sys.path.append(
32 os.path.join(os.path.dirname(os.path.abspath(__file__)), os.pardir,
Adrian Taylor1f172cd2021-11-04 18:35:4433 os.pardir, 'build', 'android', 'gyp'))
Adrian Taylor792467f22021-10-29 20:18:3034from util import build_utils
35
36import argparse
37import io
38import subprocess
39import re
40import platform
Adrian Taylor8e61d572021-12-09 23:07:1341import tempfile
Adrian Taylor792467f22021-10-29 20:18:3042
43RUSTC_VERSION_LINE = re.compile(r"(\w+): (.*)")
44
45
46def rustc_name():
47 if platform.system() == 'Windows':
48 return "rustc.exe"
49 else:
50 return "rustc"
51
52
53def host_triple(rustc_path):
54 """ Works out the host rustc target. """
55 args = [rustc_path, "-vV"]
56 known_vars = dict()
57 proc = subprocess.Popen(args, stdout=subprocess.PIPE)
58 for line in io.TextIOWrapper(proc.stdout, encoding="utf-8"):
59 m = RUSTC_VERSION_LINE.match(line.rstrip())
60 if m:
61 known_vars[m.group(1)] = m.group(2)
62 return known_vars["host"]
63
64
65RUSTC_CFG_LINE = re.compile("cargo:rustc-cfg=(.*)")
66
Adrian Taylor792467f22021-10-29 20:18:3067
Adrian Taylor8e61d572021-12-09 23:07:1368def main():
69 parser = argparse.ArgumentParser(description='Run Rust build script.')
70 parser.add_argument('--build-script',
71 required=True,
72 help='build script to run')
73 parser.add_argument('--output',
74 required=True,
75 help='where to write output rustc flags')
76 parser.add_argument('--target', help='rust target triple')
77 parser.add_argument('--features', help='features', nargs='+')
Adrian Taylor3b0577f2021-12-23 01:12:3978 parser.add_argument('--env', help='environment variable', nargs='+')
Adrian Taylor8e61d572021-12-09 23:07:1379 parser.add_argument('--rust-prefix', required=True, help='rust path prefix')
80 parser.add_argument('--generated-files', nargs='+', help='any generated file')
81 parser.add_argument('--out-dir', required=True, help='target out dir')
82 parser.add_argument('--src-dir', required=True, help='target source dir')
Adrian Taylor792467f22021-10-29 20:18:3083
Adrian Taylor8e61d572021-12-09 23:07:1384 args = parser.parse_args()
Adrian Taylore9e15d12021-11-18 19:36:2185
Adrian Taylor8e61d572021-12-09 23:07:1386 rustc_path = os.path.join(args.rust_prefix, rustc_name())
Adrian Taylor792467f22021-10-29 20:18:3087
Adrian Taylor8e61d572021-12-09 23:07:1388 # We give the build script an OUT_DIR of a temporary directory,
89 # and copy out only any files which gn directives say that it
90 # should generate. Mostly this is to ensure we can atomically
91 # create those files, but it also serves to avoid side-effects
92 # from the build script.
93 # In the future, we could consider isolating this build script
94 # into a chroot jail or similar on some platforms, but ultimately
95 # we are always going to be reliant on code review to ensure the
96 # build script is deterministic and trustworthy, so this would
97 # really just be a backup to humans.
98 with tempfile.TemporaryDirectory() as tempdir:
99 env = {} # try to avoid build scripts depending on other things
100 env["RUSTC"] = os.path.abspath(rustc_path)
101 env["OUT_DIR"] = tempdir
102 env["CARGO_MANIFEST_DIR"] = os.path.abspath(args.src_dir)
103 env["HOST"] = host_triple(rustc_path)
104 if args.target is None:
105 env["TARGET"] = env["HOST"]
106 else:
107 env["TARGET"] = args.target
Adrian Taylora516cf082022-02-15 22:27:01108 target_components = env["TARGET"].split("-")
109 env["CARGO_CFG_TARGET_ARCH"] = target_components[0]
Adrian Taylor8e61d572021-12-09 23:07:13110 if args.features:
111 for f in args.features:
112 feature_name = f.upper().replace("-", "_")
113 env["CARGO_FEATURE_%s" % feature_name] = "1"
Adrian Taylor3b0577f2021-12-23 01:12:39114 if args.env:
115 for e in args.env:
116 (k, v) = e.split("=")
117 env[k] = v
118 # Pass through a couple which are useful for diagnostics
119 if os.environ.get("RUST_BACKTRACE"):
120 env["RUST_BACKTRACE"] = os.environ.get("RUST_BACKTRACE")
121 if os.environ.get("RUST_LOG"):
122 env["RUST_LOG"] = os.environ.get("RUST_LOG")
Adrian Taylor792467f22021-10-29 20:18:30123
Adrian Taylor8e61d572021-12-09 23:07:13124 # In the future we should, set all the variables listed here:
125 # https://doc.rust-lang.org/cargo/reference/environment-variables.html#environment-variables-cargo-sets-for-build-scripts
Adrian Taylorb238a972021-12-01 15:02:23126
Adrian Taylor8e61d572021-12-09 23:07:13127 proc = subprocess.run([os.path.abspath(args.build_script)],
128 env=env,
129 cwd=args.src_dir,
130 encoding='utf8',
131 capture_output=True)
Adrian Taylor792467f22021-10-29 20:18:30132
Adrian Taylor8e61d572021-12-09 23:07:13133 if proc.stderr.rstrip():
134 print(proc.stderr.rstrip(), file=sys.stderr)
135 proc.check_returncode()
136
137 flags = ""
138 for line in proc.stdout.split("\n"):
139 m = RUSTC_CFG_LINE.match(line.rstrip())
140 if m:
141 flags = "%s--cfg\n%s\n" % (flags, m.group(1))
142
143 # AtomicOutput will ensure we only write to the file on disk if what we
144 # give to write() is different than what's currently on disk.
145 with build_utils.AtomicOutput(args.output) as output:
146 output.write(flags.encode("utf-8"))
147
148 # Copy any generated code out of the temporary directory,
149 # atomically.
150 if args.generated_files:
151 for generated_file in args.generated_files:
152 in_path = os.path.join(tempdir, generated_file)
153 out_path = os.path.join(args.out_dir, generated_file)
154 out_dir = os.path.dirname(out_path)
155 if not os.path.exists(out_dir):
156 os.makedirs(out_dir)
157 with open(in_path, 'rb') as input:
158 with build_utils.AtomicOutput(out_path) as output:
159 content = input.read()
160 output.write(content)
161
162
163if __name__ == '__main__':
164 sys.exit(main())