[M120 Migration] Implement ewk_view_is_video_playing api
[platform/framework/web/chromium-efl.git] / build / rust / run_build_script.py
1 #!/usr/bin/env vpython3
2
3 # Copyright 2021 The Chromium Authors
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
27 import argparse
28 import io
29 import os
30 import platform
31 import re
32 import subprocess
33 import sys
34 import tempfile
35
36 # Set up path to be able to import action_helpers
37 sys.path.append(
38     os.path.join(os.path.dirname(os.path.abspath(__file__)), os.pardir,
39                  os.pardir, 'build'))
40 import action_helpers
41
42
43 RUSTC_VERSION_LINE = re.compile(r"(\w+): (.*)")
44
45
46 def rustc_name():
47   if platform.system() == 'Windows':
48     return "rustc.exe"
49   else:
50     return "rustc"
51
52
53 def 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
65 RUSTC_CFG_LINE = re.compile("cargo:rustc-cfg=(.*)")
66
67
68 def 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='+')
78   parser.add_argument('--env', help='environment variable', nargs='+')
79   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')
83
84   args = parser.parse_args()
85
86   rustc_path = os.path.join(args.rust_prefix, rustc_name())
87
88   # 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
108     target_components = env["TARGET"].split("-")
109     if len(target_components) == 2:
110       env["CARGO_CFG_TARGET_ARCH"] = target_components[0]
111       env["CARGO_CFG_TARGET_VENDOR"] = ''
112       env["CARGO_CFG_TARGET_OS"] = target_components[1]
113       env["CARGO_CFG_TARGET_ENV"] = ''
114     elif len(target_components) == 3:
115       env["CARGO_CFG_TARGET_ARCH"] = target_components[0]
116       env["CARGO_CFG_TARGET_VENDOR"] = target_components[1]
117       env["CARGO_CFG_TARGET_OS"] = target_components[2]
118       env["CARGO_CFG_TARGET_ENV"] = ''
119     elif len(target_components) == 4:
120       env["CARGO_CFG_TARGET_ARCH"] = target_components[0]
121       env["CARGO_CFG_TARGET_VENDOR"] = target_components[1]
122       env["CARGO_CFG_TARGET_OS"] = target_components[2]
123       env["CARGO_CFG_TARGET_ENV"] = target_components[3]
124     else:
125       print(f'Invalid TARGET {env["TARGET"]}')
126       sys.exit(1)
127     if args.features:
128       for f in args.features:
129         feature_name = f.upper().replace("-", "_")
130         env["CARGO_FEATURE_%s" % feature_name] = "1"
131     if args.env:
132       for e in args.env:
133         (k, v) = e.split("=")
134         env[k] = v
135     # Pass through a couple which are useful for diagnostics
136     if os.environ.get("RUST_BACKTRACE"):
137       env["RUST_BACKTRACE"] = os.environ.get("RUST_BACKTRACE")
138     if os.environ.get("RUST_LOG"):
139       env["RUST_LOG"] = os.environ.get("RUST_LOG")
140
141     # In the future we should, set all the variables listed here:
142     # https://doc.rust-lang.org/cargo/reference/environment-variables.html#environment-variables-cargo-sets-for-build-scripts
143
144     proc = subprocess.run([os.path.abspath(args.build_script)],
145                           env=env,
146                           cwd=args.src_dir,
147                           encoding='utf8',
148                           stdout=subprocess.PIPE,
149                           stderr=subprocess.PIPE)
150
151     if proc.stderr.rstrip():
152       print(proc.stderr.rstrip(), file=sys.stderr)
153     proc.check_returncode()
154
155     flags = ""
156     for line in proc.stdout.split("\n"):
157       m = RUSTC_CFG_LINE.match(line.rstrip())
158       if m:
159         flags = "%s--cfg\n%s\n" % (flags, m.group(1))
160
161     # AtomicOutput will ensure we only write to the file on disk if what we
162     # give to write() is different than what's currently on disk.
163     with action_helpers.atomic_output(args.output) as output:
164       output.write(flags.encode("utf-8"))
165
166     # Copy any generated code out of the temporary directory,
167     # atomically.
168     if args.generated_files:
169       for generated_file in args.generated_files:
170         in_path = os.path.join(tempdir, generated_file)
171         out_path = os.path.join(args.out_dir, generated_file)
172         out_dir = os.path.dirname(out_path)
173         if not os.path.exists(out_dir):
174           os.makedirs(out_dir)
175         with open(in_path, 'rb') as input:
176           with action_helpers.atomic_output(out_path) as output:
177             content = input.read()
178             output.write(content)
179
180
181 if __name__ == '__main__':
182   sys.exit(main())