Austin Schuh | 8f99c82 | 2024-05-05 22:43:40 -0700 | [diff] [blame] | 1 | load("@bazel_tools//tools/build_defs/cc:action_names.bzl", "ACTION_NAMES") |
Adam Snaider | 0967b81 | 2023-11-02 15:29:43 -0700 | [diff] [blame] | 2 | load("@org_frc971//tools/rust:defs.bzl", "rust_library") |
Brian Silverman | c270a4d | 2022-07-23 16:08:06 -0700 | [diff] [blame] | 3 | load("@rules_cc//cc:find_cc_toolchain.bzl", "find_cc_toolchain") |
Brian Silverman | c270a4d | 2022-07-23 16:08:06 -0700 | [diff] [blame] | 4 | |
| 5 | def _cc_toolchain_flags(ctx, cc_toolchain): |
| 6 | feature_configuration = cc_common.configure_features( |
| 7 | ctx = ctx, |
| 8 | cc_toolchain = cc_toolchain, |
| 9 | requested_features = ctx.features, |
| 10 | unsupported_features = ctx.disabled_features, |
| 11 | ) |
James Kuszmaul | 126dcff | 2022-08-12 16:30:05 -0700 | [diff] [blame] | 12 | preprocessor_defines = [] |
| 13 | for lib in ctx.attr.libs: |
| 14 | preprocessor_defines.append(lib[CcInfo].compilation_context.defines) |
Brian Silverman | c270a4d | 2022-07-23 16:08:06 -0700 | [diff] [blame] | 15 | compile_variables = cc_common.create_compile_variables( |
| 16 | feature_configuration = feature_configuration, |
| 17 | cc_toolchain = cc_toolchain, |
| 18 | user_compile_flags = ctx.fragments.cpp.copts + ctx.fragments.cpp.cxxopts, |
James Kuszmaul | 126dcff | 2022-08-12 16:30:05 -0700 | [diff] [blame] | 19 | preprocessor_defines = depset(transitive = preprocessor_defines), |
Brian Silverman | c270a4d | 2022-07-23 16:08:06 -0700 | [diff] [blame] | 20 | ) |
| 21 | command_line = cc_common.get_memory_inefficient_command_line( |
| 22 | feature_configuration = feature_configuration, |
| 23 | action_name = ACTION_NAMES.cpp_compile, |
| 24 | variables = compile_variables, |
| 25 | ) |
| 26 | env = cc_common.get_environment_variables( |
| 27 | feature_configuration = feature_configuration, |
| 28 | action_name = ACTION_NAMES.cpp_compile, |
| 29 | variables = compile_variables, |
| 30 | ) |
| 31 | return command_line, env |
| 32 | |
| 33 | # All the stuff about AUTOCXX_RS_FILE and--fix-rs-include-name only applies when |
| 34 | # using --gen-rs-include. We use --gen-rs-archive so none of that matters. |
| 35 | # |
| 36 | # The generated .rs file uses `include!` on the top-level C++ headers imported |
| 37 | # via `#include` in `include_cpp!`. This always operates relative to the source |
| 38 | # file (I don't see any way to change it), nor does autocxx have a way to change |
| 39 | # the path. There are headers involved which use `#pragma once`, so just copying |
| 40 | # them is a bad idea. Instead, we generate forwarding headers. |
| 41 | def _autocxx_library_gen_impl(ctx): |
Brian Silverman | c270a4d | 2022-07-23 16:08:06 -0700 | [diff] [blame] | 42 | # TODO(Brian): Provide some way to override this globally in WORKSPACE? Need |
| 43 | # a real strategy for coordinating toolchains and flags, see the TODO below |
| 44 | # where cc_command_line is used for details. |
| 45 | if ctx.attr.override_cc_toolchain: |
| 46 | cc_toolchain = ctx.attr.override_cc_toolchain[cc_common.CcToolchainInfo] |
| 47 | else: |
| 48 | cc_toolchain = find_cc_toolchain(ctx) |
| 49 | |
| 50 | # The directory where we generate files. Needs to be unique and in our package. |
| 51 | gendir = ctx.label.name + "__dir" |
| 52 | |
| 53 | cc_command_line, cc_env = _cc_toolchain_flags(ctx, cc_toolchain) |
| 54 | |
| 55 | includes = [] |
| 56 | in_headers = [] |
| 57 | forwarding_headers = [] |
| 58 | for lib in ctx.attr.libs: |
| 59 | compilation = lib[CcInfo].compilation_context |
| 60 | |
| 61 | # TODO(Brian): How should we be juggling includes, quote_includes, and system_includes? |
| 62 | includes.append(compilation.includes) |
| 63 | includes.append(compilation.quote_includes) |
| 64 | includes.append(compilation.system_includes) |
| 65 | in_headers.append(compilation.headers) |
| 66 | for header in compilation.direct_public_headers: |
| 67 | # TODO(Brian): This doesn't work if it's being `#include`ed (via |
| 68 | # `include_cpp!`) using one of the includes paths. Maybe we should |
| 69 | # add each `includes` prefixed with `gendir` to solve that? |
| 70 | forwarding = ctx.actions.declare_file("%s/%s" % (gendir, header.short_path)) |
| 71 | forwarding_headers.append(forwarding) |
| 72 | ctx.actions.write(forwarding, '#include "%s"' % header.short_path) |
| 73 | includes = depset(transitive = includes) |
| 74 | action_inputs = depset( |
| 75 | direct = ctx.files.srcs + ctx.files.cxxbridge_srcs, |
| 76 | transitive = in_headers + [cc_toolchain.all_files], |
| 77 | ) |
| 78 | |
| 79 | # This is always the name with --gen-rs-archive, regardless of other flags. |
| 80 | out_rs_json = ctx.actions.declare_file("%s/gen.rs.json" % gendir) |
| 81 | out_env_file = ctx.actions.declare_file("%s/rustc_env" % gendir) |
| 82 | ctx.actions.write( |
| 83 | output = out_env_file, |
Brian Silverman | 9809c5f | 2022-07-23 16:12:23 -0700 | [diff] [blame] | 84 | # The first path is valid for rust_library/rust_binary/rust_test/etc, the second one |
| 85 | # is valid for rust_doc_test due to working directory differences. |
| 86 | content = "AUTOCXX_RS_JSON_ARCHIVE=%s:%s" % (out_rs_json.path, out_rs_json.short_path), |
Brian Silverman | c270a4d | 2022-07-23 16:08:06 -0700 | [diff] [blame] | 87 | ) |
| 88 | |
| 89 | out_h = ctx.actions.declare_file("%s_cxxgen.h" % ctx.label.name.rstrip("__gen")) |
| 90 | out_h_guard = out_h.short_path.replace("/", "_").replace(".", "_") |
| 91 | out_h_contents = [ |
| 92 | "#ifndef %s" % out_h_guard, |
| 93 | "#define %s" % out_h_guard, |
| 94 | "// GENERATED FILE, DO NOT EDIT", |
| 95 | "//", |
| 96 | "// #includes all of the declarations exported to C++ from %s" % ctx.label, |
| 97 | ] |
| 98 | out_cc = [] |
| 99 | |
| 100 | # See `gen --help` for details on the naming of these outputs. |
| 101 | for cc_index in range(ctx.attr.sections_to_generate): |
| 102 | out_cc.append(ctx.actions.declare_file("%s/gen%d.cc" % (gendir, cc_index))) |
| 103 | gen_h = ctx.actions.declare_file("%s/gen%d.h" % (gendir, cc_index)) |
| 104 | out_cc.append(gen_h) |
| 105 | out_h_contents.append("#include \"%s\"" % gen_h.short_path) |
| 106 | autocxxgen_h = ctx.actions.declare_file("%s/autocxxgen%d.h" % (gendir, cc_index)) |
| 107 | out_cc.append(autocxxgen_h) |
| 108 | out_h_contents.append("#include \"%s\"" % autocxxgen_h.short_path) |
| 109 | |
| 110 | cxxbridge_cc_srcs = [] |
| 111 | for src in ctx.files.cxxbridge_srcs: |
| 112 | cxxbridge_cc = ctx.actions.declare_file("%s/cxxbridge.cc" % gendir) |
| 113 | cxxbridge_cc_srcs.append(cxxbridge_cc) |
| 114 | cxxbridge_h = ctx.actions.declare_file("%s/cxxbridge.h" % gendir) |
| 115 | cxxbridge_cc_srcs.append(cxxbridge_h) |
| 116 | out_h_contents.append("#include \"%s\"" % cxxbridge_h.short_path) |
| 117 | ctx.actions.run( |
| 118 | mnemonic = "CxxCodegen", |
| 119 | executable = ctx.executable._cxx_codegen, |
| 120 | inputs = [src], |
| 121 | outputs = [cxxbridge_cc, cxxbridge_h], |
| 122 | arguments = [src.path, "--output", cxxbridge_h.path, "--output", cxxbridge_cc.path], |
| 123 | ) |
| 124 | |
| 125 | out_h_contents.append("#endif // %s" % out_h_guard) |
| 126 | ctx.actions.write( |
| 127 | output = out_h, |
| 128 | content = "\n".join(out_h_contents), |
| 129 | ) |
| 130 | |
| 131 | gen_rs = ctx.actions.args() |
| 132 | gen_rs.add_all(["--outdir", out_rs_json.dirname]) |
| 133 | gen_rs.add("--gen-rs-archive") |
| 134 | gen_rs.add("--gen-cpp") |
Austin Schuh | 50e3dca | 2023-07-23 14:34:27 -0700 | [diff] [blame] | 135 | #gen_rs.add("--auto-allowlist") |
Brian Silverman | c270a4d | 2022-07-23 16:08:06 -0700 | [diff] [blame] | 136 | |
| 137 | gen_rs.add_all(["--generate-exact", ctx.attr.sections_to_generate]) |
| 138 | |
| 139 | gen_rs.add_all(ctx.files.srcs) |
| 140 | gen_rs.add_all(ctx.files.cxxbridge_srcs) |
| 141 | |
| 142 | # TODO: Do these go before or after the --? They're partially redundant with |
| 143 | # cc_command_line too. |
| 144 | gen_rs.add_all(includes, before_each = "-I") |
| 145 | gen_rs.add("--") |
| 146 | |
| 147 | # TODO: These are flags for the cc_toolchain, not the libclang they're being passed to. |
| 148 | # Figure out how to handle that nicely. Maybe just require they're compatible, and direct |
| 149 | # people to overriding the toolchain in use instead? |
| 150 | gen_rs.add_all(cc_command_line) |
| 151 | |
| 152 | gen_rs.add("-Wno-unused-private-field") |
| 153 | env = dict(cc_env) |
| 154 | env.update( |
| 155 | LIBCLANG_PATH = ctx.file._libclang.path, |
| 156 | ) |
| 157 | if ctx.attr.gen_debug: |
| 158 | env.update( |
| 159 | RUST_BACKTRACE = "full", |
| 160 | RUST_LOG = "autocxx_engine=info", |
| 161 | ) |
| 162 | ctx.actions.run( |
| 163 | arguments = [gen_rs], |
| 164 | outputs = [out_rs_json] + out_cc, |
| 165 | tools = [ctx.file._libclang], |
| 166 | inputs = action_inputs, |
| 167 | env = env, |
| 168 | executable = ctx.executable._autocxx_gen, |
| 169 | mnemonic = "AutocxxGen", |
| 170 | ) |
| 171 | |
| 172 | return [ |
| 173 | OutputGroupInfo( |
| 174 | cc_srcs = out_cc + cxxbridge_cc_srcs, |
| 175 | hdr_srcs = [out_h], |
| 176 | compile_data = forwarding_headers + [out_rs_json], |
| 177 | env_files = [out_env_file], |
| 178 | ), |
| 179 | ] |
| 180 | |
| 181 | _autocxx_library_gen = rule( |
| 182 | implementation = _autocxx_library_gen_impl, |
| 183 | attrs = { |
| 184 | "libs": attr.label_list( |
| 185 | mandatory = True, |
| 186 | providers = [CcInfo], |
| 187 | doc = "C++ libraries to let Rust use headers from", |
| 188 | ), |
| 189 | "srcs": attr.label_list( |
| 190 | allow_files = [".rs"], |
| 191 | mandatory = False, |
| 192 | doc = "Rust sources with `include_cpp!` macros", |
| 193 | default = [], |
| 194 | ), |
| 195 | # TODO(Brian): Do we need to support this? Or just throw them in srcs? |
| 196 | "cxxbridge_srcs": attr.label_list( |
| 197 | allow_files = [".rs"], |
| 198 | mandatory = False, |
| 199 | doc = "Rust sources with only [cxx::bridge] annotations", |
| 200 | default = [], |
| 201 | ), |
| 202 | "sections_to_generate": attr.int( |
| 203 | default = 20, |
| 204 | doc = ( |
| 205 | "The number of `cxx::bridge` sections to support," + |
| 206 | " including ones created by `autocxx::include_cpp!`." + |
| 207 | " The default is sufficient for most use cases." + |
| 208 | " Setting this too large has a small performance impact, setting it" + |
| 209 | " too low will result in a build failure." |
| 210 | ), |
| 211 | ), |
| 212 | "gen_debug": attr.bool( |
| 213 | default = False, |
| 214 | doc = "Print (lots of) debug info about autocxx's codegen at build time.", |
| 215 | ), |
| 216 | "_autocxx_gen": attr.label( |
| 217 | executable = True, |
| 218 | default = Label("@//third_party/autocxx/gen/cmd:gen"), |
| 219 | cfg = "exec", |
| 220 | ), |
| 221 | "_cxx_codegen": attr.label( |
| 222 | executable = True, |
Adam Snaider | 770b97b | 2023-08-04 21:07:48 -0700 | [diff] [blame] | 223 | default = Label("@cxxbridge-cmd//:cxxbridge-cmd"), |
Brian Silverman | c270a4d | 2022-07-23 16:08:06 -0700 | [diff] [blame] | 224 | cfg = "exec", |
| 225 | ), |
| 226 | "_libclang": attr.label( |
| 227 | cfg = "exec", |
| 228 | default = Label("@llvm_k8//:libclang"), |
| 229 | allow_single_file = True, |
| 230 | ), |
| 231 | "override_cc_toolchain": attr.label(mandatory = False, providers = [cc_common.CcToolchainInfo]), |
| 232 | "_cc_toolchain": attr.label(default = Label("@bazel_tools//tools/cpp:current_cc_toolchain")), |
| 233 | }, |
| 234 | toolchains = [ |
| 235 | "@rules_rust//rust:toolchain", |
| 236 | "@bazel_tools//tools/cpp:toolchain_type", |
| 237 | ], |
| 238 | fragments = ["cpp"], |
| 239 | ) |
| 240 | |
| 241 | def autocxx_library( |
| 242 | name, |
| 243 | visibility = None, |
Adam Snaider | 0967b81 | 2023-11-02 15:29:43 -0700 | [diff] [blame] | 244 | target_compatible_with = ["//tools/platforms/rust:has_support"], |
Brian Silverman | c270a4d | 2022-07-23 16:08:06 -0700 | [diff] [blame] | 245 | libs = [], |
| 246 | srcs = [], |
| 247 | cxxbridge_srcs = [], |
| 248 | override_cc_toolchain = None, |
| 249 | deps = [], |
| 250 | rs_deps = [], |
| 251 | testonly = None, |
| 252 | crate_features = None, |
| 253 | crate_name = None, |
Adam Snaider | 5f00067 | 2023-11-02 16:04:30 -0700 | [diff] [blame] | 254 | gen_debug = None, |
Adam Snaider | f560ae9 | 2023-11-07 17:06:21 -0800 | [diff] [blame] | 255 | **kwargs): |
Brian Silverman | c270a4d | 2022-07-23 16:08:06 -0700 | [diff] [blame] | 256 | """A macro to generate Rust <-> C++ interop code with autocxx. |
| 257 | |
| 258 | Creates the following rules: |
| 259 | * A rust_library with the given name, which includes the given srcs. Note that it will not |
| 260 | include them directly due to how autocxx works, instead they will be copied into a |
| 261 | generated file along with changes from autocxx. |
| 262 | * A cc_library with the given name + `_cc`. This is for C++ code that wants to use APIs |
| 263 | from the given Rust code. Rust dependencies should _not_ depend on this. The header for C++ |
| 264 | to #include will be named by the given name + `_cxxgen.h`. |
| 265 | |
| 266 | `deps` is for other `autocxx_library` rules. `rs_deps` is for dependencies of the Rust code. |
| 267 | """ |
| 268 | library_gen_name = "%s__gen" % name |
| 269 | _autocxx_library_gen( |
| 270 | name = library_gen_name, |
| 271 | visibility = ["//visibility:private"], |
| 272 | target_compatible_with = target_compatible_with, |
| 273 | testonly = testonly, |
| 274 | libs = libs, |
| 275 | srcs = srcs, |
| 276 | cxxbridge_srcs = cxxbridge_srcs, |
| 277 | override_cc_toolchain = override_cc_toolchain, |
| 278 | gen_debug = gen_debug, |
| 279 | ) |
| 280 | gen_cc_srcs_name = "%s__cc_srcs" % name |
| 281 | native.filegroup( |
| 282 | name = gen_cc_srcs_name, |
| 283 | visibility = ["//visibility:private"], |
| 284 | target_compatible_with = target_compatible_with, |
| 285 | testonly = testonly, |
| 286 | srcs = [library_gen_name], |
| 287 | output_group = "cc_srcs", |
| 288 | ) |
| 289 | gen_hdr_srcs_name = "%s__hdr_srcs" % name |
| 290 | native.filegroup( |
| 291 | name = gen_hdr_srcs_name, |
| 292 | visibility = ["//visibility:private"], |
| 293 | target_compatible_with = target_compatible_with, |
| 294 | testonly = testonly, |
| 295 | srcs = [library_gen_name], |
| 296 | output_group = "hdr_srcs", |
| 297 | ) |
| 298 | gen_compile_data_name = "%s__compile_data" % name |
| 299 | native.filegroup( |
| 300 | name = gen_compile_data_name, |
| 301 | visibility = ["//visibility:private"], |
| 302 | target_compatible_with = target_compatible_with, |
| 303 | testonly = testonly, |
| 304 | srcs = [library_gen_name], |
| 305 | output_group = "compile_data", |
| 306 | ) |
| 307 | gen_env_files_name = "%s__env_files" % name |
| 308 | native.filegroup( |
| 309 | name = gen_env_files_name, |
| 310 | visibility = ["//visibility:private"], |
| 311 | target_compatible_with = target_compatible_with, |
| 312 | testonly = testonly, |
| 313 | srcs = [library_gen_name], |
| 314 | output_group = "env_files", |
| 315 | ) |
| 316 | cc_library_name = "%s__cc" % name |
| 317 | native.cc_library( |
| 318 | name = cc_library_name, |
| 319 | visibility = visibility, |
| 320 | target_compatible_with = target_compatible_with, |
| 321 | testonly = testonly, |
| 322 | deps = deps + libs + [ |
Adam Snaider | 770b97b | 2023-08-04 21:07:48 -0700 | [diff] [blame] | 323 | "@crate_index//:cxx_cc", |
Brian Silverman | c270a4d | 2022-07-23 16:08:06 -0700 | [diff] [blame] | 324 | ], |
| 325 | srcs = [gen_cc_srcs_name], |
| 326 | hdrs = [gen_hdr_srcs_name], |
| 327 | ) |
| 328 | |
| 329 | rust_library( |
| 330 | name = name, |
| 331 | visibility = visibility, |
| 332 | target_compatible_with = target_compatible_with, |
| 333 | testonly = testonly, |
| 334 | srcs = srcs + cxxbridge_srcs, |
| 335 | proc_macro_deps = [ |
Adam Snaider | 770b97b | 2023-08-04 21:07:48 -0700 | [diff] [blame] | 336 | "@crate_index//:cxxbridge-macro", |
Brian Silverman | c270a4d | 2022-07-23 16:08:06 -0700 | [diff] [blame] | 337 | ], |
| 338 | crate_features = crate_features, |
| 339 | crate_name = crate_name, |
| 340 | deps = deps + rs_deps + [ |
| 341 | cc_library_name, |
Adam Snaider | 770b97b | 2023-08-04 21:07:48 -0700 | [diff] [blame] | 342 | "@crate_index//:cxx", |
Brian Silverman | c270a4d | 2022-07-23 16:08:06 -0700 | [diff] [blame] | 343 | "//third_party/autocxx", |
| 344 | ], |
| 345 | compile_data = [gen_compile_data_name], |
| 346 | rustc_env_files = [gen_env_files_name], |
Adam Snaider | f560ae9 | 2023-11-07 17:06:21 -0800 | [diff] [blame] | 347 | **kwargs |
Brian Silverman | c270a4d | 2022-07-23 16:08:06 -0700 | [diff] [blame] | 348 | ) |