1#!/usr/bin/env python3 2 3# Copyright 2022 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 7import argparse 8import json 9import os 10import subprocess 11import sys 12 13THIS_DIR = os.path.dirname(os.path.abspath(__file__)) 14CHROMIUM_SRC_DIR = os.path.relpath(os.path.join(THIS_DIR, os.pardir, os.pardir)) 15sys.path.append(THIS_DIR) 16from run_bindgen import filter_clang_args 17 18RUST_TOOLCHAIN_DIR = os.path.join(CHROMIUM_SRC_DIR, "third_party", 19 "rust-toolchain") 20RUSTFMT_EXE_PATH = os.path.join(RUST_TOOLCHAIN_DIR, "bin", "rustfmt") 21RUSTFMT_CONFIG_PATH = os.path.join(CHROMIUM_SRC_DIR, ".rustfmt.toml") 22RS_BINDINGS_FROM_CC_EXE_PATH = os.path.join(RUST_TOOLCHAIN_DIR, "bin", 23 "rs_bindings_from_cc") 24 25 26def format_cmdline(args): 27 def quote_arg(x): 28 if ' ' not in x: return x 29 x = x.replace('"', '\\"') 30 return f"\"{x}\"" 31 32 return " ".join([quote_arg(x) for x in args]) 33 34 35def main(): 36 parser = argparse.ArgumentParser() 37 parser.add_argument("--targets_and_args_from_gn", 38 metavar="FILE", 39 help="File parsed into --targets_and_args Crubit arg", 40 required=True), 41 parser.add_argument("--public_headers", 42 metavar="FILE", 43 help="Passed through to Crubit", 44 required=True), 45 parser.add_argument("--rs_out", 46 metavar="FILE", 47 help="Passed through to Crubit", 48 required=True), 49 parser.add_argument("--cc_out", 50 metavar="FILE", 51 help="Passed through to Crubit", 52 required=True), 53 parser.add_argument("clang_args", 54 metavar="CLANGARGS", 55 help="Arguments to forward to clang libraries", 56 nargs=argparse.REMAINDER) 57 args = parser.parse_args() 58 59 # Output paths 60 generator_args = [] 61 generator_args.append("--rs_out={0}".format(os.path.relpath(args.rs_out))) 62 generator_args.append("--cc_out={0}".format(os.path.relpath(args.cc_out))) 63 if "CRUBIT_DEBUG" in os.environ: 64 generator_args.append("--ir_out={0}".format( 65 os.path.relpath(args.rs_out).replace(".rs", ".ir"))) 66 67 # Public headers. 68 generator_args.append("--public_headers={0}".format(",".join( 69 [os.path.relpath(hdr) for hdr in args.public_headers.split(",")]))) 70 71 # Targets to headers map. 72 with open(args.targets_and_args_from_gn, "r") as f: 73 targets_and_args = json.load(f) 74 for entry in targets_and_args: 75 entry["f"] = ["supported"] 76 hdrs = entry["h"] 77 for i in range(len(hdrs)): 78 hdrs[i] = os.path.relpath(hdrs[i]) 79 generator_args.append("--targets_and_args={0}".format( 80 json.dumps(targets_and_args))) 81 82 # All Crubit invocations in Chromium share the following cmdline args. 83 generator_args.append(f"--rustfmt_exe_path={RUSTFMT_EXE_PATH}") 84 generator_args.append(f"--rustfmt_config_path={RUSTFMT_CONFIG_PATH}") 85 generator_args.append( 86 "--crubit_support_path=third_party/crubit/src/rs_bindings_from_cc/support" 87 ) 88 89 # Long cmdlines may not work - work around that by using Abseil's `--flagfile` 90 # https://abseil.io/docs/python/guides/flags#a-note-about---flagfile 91 # 92 # Note that `clang_args` are not written to the flag file, because Abseil's 93 # flag parsing code is only aware of `ABSL_FLAG`-declared flags and doesn't 94 # know about Clang args (e.g. `-W...` or `-I...`). 95 params_file_path = os.path.relpath(args.rs_out).replace(".rs", ".params") 96 with open(params_file_path, "w") as f: 97 for line in generator_args: 98 print(line, file=f) 99 100 # Clang arguments. 101 # 102 # The call to `filter_clang_args` is needed to avoid the following error: 103 # error: unable to find plugin 'find-bad-constructs' 104 clang_args = [] 105 clang_args.extend(filter_clang_args(args.clang_args)) 106 # TODO(crbug.com/40226863): This warning needs to be suppressed, because 107 # otherwise Crubit/Clang complains as follows: 108 # error: .../third_party/rust-toolchain/bin/rs_bindings_from_cc: 109 # 'linker' input unused [-Werror,-Wunused-command-line-argument] 110 # Maybe `build/rust/rs_bindings_from_cc.gni` gives too much in `args`? But 111 # then `{{cflags}}` seems perfectly reasonable... 112 clang_args += ["-Wno-unused-command-line-argument"] 113 114 # Print a copy&pastable final cmdline when asked for debugging help. 115 cmdline = [RS_BINDINGS_FROM_CC_EXE_PATH, f"--flagfile={params_file_path}"] 116 cmdline.extend(clang_args) 117 if "CRUBIT_DEBUG" in os.environ: 118 pretty_cmdline = format_cmdline(cmdline) 119 print(f"CRUBIT_DEBUG: CMDLINE: {pretty_cmdline}", file=sys.stderr) 120 121 # TODO(crbug.com/40226863): run_bindgen.py removes the outputs when the tool 122 # fails. Maybe we need to do something similar here? OTOH in most failure 123 # modes Crubit will fail *before* generating its outputs... 124 return subprocess.run(cmdline).returncode 125 126 127if __name__ == '__main__': 128 sys.exit(main()) 129