| #!/usr/bin/env fuchsia-vendored-python |
| # Copyright 2023 The Fuchsia Authors. All rights reserved. |
| # Use of this source code is governed by a BSD-style license that can be |
| # found in the LICENSE file. |
| |
| import os |
| import re |
| import subprocess |
| import sys |
| import datetime |
| |
| # All other paths are relative to here (main changes to this directory on startup). |
| ROOT_PATH = os.path.join(os.path.dirname(__file__), "..", "..", "..") |
| |
| RUST_DIR = "prebuilt/third_party/rust/linux-x64/bin" |
| BINDGEN_PATH = "prebuilt/third_party/rust_bindgen/linux-x64/bindgen" |
| FX_PATH = "scripts/fx" |
| |
| GENERATED_FILE_HEADER = ( |
| """// Copyright %d The Fuchsia Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #![allow(dead_code)] |
| #![allow(non_camel_case_types)] |
| #![allow(non_snake_case)] |
| #![allow(non_upper_case_globals)] |
| """ |
| % datetime.datetime.now().year |
| ) |
| |
| |
| class Bindgen: |
| def __init__(self): |
| self.clang_target = "x86_64-pc-linux-gnu" |
| self.c_types_prefix = "" |
| self.raw_lines = "" |
| self.opaque_types = [] |
| self.include_dirs = [] |
| self.auto_derive_traits = [] |
| self.replacements = [] |
| self.ignore_functions = False |
| self.function_allowlist = [] |
| self.var_allowlist = [] |
| self.type_allowlist = [] |
| self.no_debug_types = [] |
| self.no_copy_types = [] |
| |
| def set_auto_derive_traits(self, traits_map): |
| self.auto_derive_traits = [(re.compile(x[0]), x[1]) for x in traits_map] |
| |
| def set_replacements(self, replacements): |
| self.replacements = [(re.compile(x[0]), x[1]) for x in replacements] |
| |
| def run_bindgen(self, input_file, output_file): |
| # Bindgen arguments. |
| args = [ |
| BINDGEN_PATH, |
| "--no-layout-tests", |
| "--with-derive-default", |
| "--explicit-padding", |
| "--raw-line", |
| GENERATED_FILE_HEADER + self.raw_lines, |
| "-o", |
| output_file, |
| ] |
| |
| if self.ignore_functions: |
| args.append("--ignore-functions") |
| |
| if self.c_types_prefix: |
| args.append("--ctypes-prefix=" + self.c_types_prefix) |
| |
| args += ["--allowlist-function=" + x for x in self.function_allowlist] |
| args += ["--allowlist-var=" + x for x in self.var_allowlist] |
| args += ["--allowlist-type=" + x for x in self.type_allowlist] |
| args += ["--opaque-type=" + x for x in self.opaque_types] |
| args += ["--no-debug=" + x for x in self.no_debug_types] |
| args += ["--no-copy=" + x for x in self.no_copy_types] |
| |
| args += [input_file] |
| |
| # Clang arguments (after the "--"). |
| args += [ |
| "--", |
| "-target", |
| self.clang_target, |
| "-nostdlibinc", |
| "-D__Fuchsia_API_level__=4294967295", |
| "-DIS_BINDGEN=1", |
| ] |
| for i in self.include_dirs: |
| args += ["-I", i] |
| args += ["-I", "."] |
| |
| # Need to set the PATH to the prebuilt binary location for it to find rustfmt. |
| env = os.environ.copy() |
| env["PATH"] = "%s:%s" % (os.path.abspath(RUST_DIR), env["PATH"]) |
| subprocess.check_call(args, env=env) |
| |
| def get_auto_derive_traits(self, line): |
| """Returns true if the given line defines a Rust structure with a name |
| matching any of the types we need to add FromBytes.""" |
| if not ( |
| line.startswith("pub struct ") or line.startswith("pub union ") |
| ): |
| return None |
| |
| # The third word (after the "pub struct") is the type name. |
| split = re.split("[ <\(]", line) |
| if len(split) < 3: |
| return None |
| type_name = split[2] |
| |
| for t, traits in self.auto_derive_traits: |
| if t.match(type_name): |
| return traits |
| return None |
| |
| def post_process_rust_file(self, rust_file_name): |
| with open(rust_file_name, "r+") as source_file: |
| input_lines = source_file.readlines() |
| output_lines = [] |
| for line in input_lines: |
| extra_traits = self.get_auto_derive_traits(line) |
| if extra_traits: |
| # Parse existing traits, if any. |
| if len(output_lines) > 0 and output_lines[-1].startswith( |
| "#[derive(" |
| ): |
| traits = output_lines[-1][9:-3].split(", ") |
| traits.extend( |
| x for x in extra_traits if x not in traits |
| ) |
| output_lines.pop() |
| else: |
| traits = extra_traits |
| output_lines.append( |
| "#[derive(" + ", ".join(traits) + ")]\n" |
| ) |
| output_lines.append(line) |
| |
| text = "".join(output_lines) |
| for regexp, replacement in self.replacements: |
| text = regexp.sub(replacement, text) |
| |
| source_file.seek(0) |
| source_file.write(text) |
| |
| def run(self, input_file, rust_file): |
| os.chdir(ROOT_PATH) |
| |
| self.run_bindgen(input_file, rust_file) |
| self.post_process_rust_file(rust_file) |
| |
| subprocess.check_call([FX_PATH, "format-code", "--files=" + rust_file]) |