diff --git a/shared/bazel/rules/cc_rules.bzl b/shared/bazel/rules/cc_rules.bzl index c7225efd49..c364744593 100644 --- a/shared/bazel/rules/cc_rules.bzl +++ b/shared/bazel/rules/cc_rules.bzl @@ -7,6 +7,31 @@ load("@rules_cc//cc/common:cc_common.bzl", "cc_common") load("@rules_pkg//:mappings.bzl", "pkg_files") load("@rules_pkg//:pkg.bzl", "pkg_zip") +# Copied from bazel since it isn't exposed publicly that I can find. +# https://github.com/bazelbuild/bazel/blob/cc4e3b25a89cd8294406d9489ece706cfcc019bd/src/main/starlark/builtins_bzl/common/cc/cc_helper.bzl#L272 +def generate_def_file(ctx, def_parser, object_files, dll_name): + def_file = ctx.actions.declare_file(ctx.label.name + ".gen.def") + + args = ctx.actions.args() + args.add(def_file) + args.add(dll_name) + argv = ctx.actions.args() + argv.use_param_file("@%s", use_always = True) + argv.set_param_file_format("shell") + for object_file in object_files: + argv.add(object_file.path) + + ctx.actions.run( + mnemonic = "DefParser", + executable = def_parser, + toolchain = None, + arguments = [args, argv], + inputs = object_files, + outputs = [def_file], + use_default_shell_env = True, + ) + return def_file + def _folder_prefix(name): if "/" in name: last_slash = name.rfind("/") @@ -154,15 +179,23 @@ def wpilib_cc_library( def wpilib_cc_shared_library( name, auto_export_windows_symbols = True, + win_def_file = None, **kwargs): folder, lib = _folder_prefix(name) features = [] if auto_export_windows_symbols: features.append("windows_export_all_symbols") + cc_shared_library( name = name, features = features, + # Only include a .def file on windows. This makes it so we can mark + # the .def file as only compatible with windows. + win_def_file = select({ + "@platforms//os:windows": win_def_file, + "//conditions:default": None, + }), **kwargs ) @@ -388,3 +421,91 @@ def wpilib_cc_static_library( static_lib_name = static_lib_name, **kwargs ) + +def _generate_def_windows_impl(ctx): + deps = ctx.attr.deps + + cc_toolchain = find_cpp_toolchain(ctx) + + feature_configuration = cc_common.configure_features( + ctx = ctx, + cc_toolchain = cc_toolchain, + requested_features = ctx.features + ["force_no_whole_archive"], + unsupported_features = ctx.disabled_features, + ) + + def_parser = ctx.file._def_parser + win_def_file = [] + + if cc_common.is_enabled(feature_configuration = feature_configuration, feature_name = "targets_windows"): + object_files = [] + + # Now, hunt down all the linker inputs directly specified. + for dep in deps: + linker_input = dep[CcInfo].linking_context.linker_inputs + + for l in linker_input.to_list(): + # Find the linker stanza owned directly by the dependency, not transitively + if l.owner != dep.label: + continue + + # Grab all the .o's out of it. + for library in l.libraries: + if library.pic_static_library != None: + if library.pic_objects != None: + object_files.extend(library.pic_objects) + elif library.static_library != None: + if library.objects != None: + object_files.extend(library.objects) + + # Filter the list so we only generate def files for the provided dependencies. + filtered_object_files = [] + for o in object_files: + for f in ctx.attr.filters: + if f in o.path: + filtered_object_files.append(o) + break + + if def_parser != None: + generated_def_file = generate_def_file(ctx, def_parser, filtered_object_files, ctx.label.name) + + win_def_file = [generated_def_file] + + files = depset(direct = win_def_file) + return [ + DefaultInfo(files = files), + OutputGroupInfo(default = files), + ] + +_generate_def_windows = rule( + implementation = _generate_def_windows_impl, + attrs = { + "deps": attr.label_list( + providers = [CcInfo], + doc = """ +List of all static libraries to not duplicate .o files from. +""", + ), + "filters": attr.string_list(), + "_def_parser": attr.label(default = "@bazel_tools//tools/def_parser:def_parser", allow_single_file = True, cfg = "exec"), + } | CC_TOOLCHAIN_ATTRS, + toolchains = use_cc_toolchain(), + fragments = ["cpp"], +) + +def generate_def_windows(name, deps = None, **kwargs): + """Generates a .def file for linking a windows .dll for the provided cc_library and filters + + Args: + deps: A list of cc_libraries to export symbols from. + filters: All object files in the provided cc_libraries (but not their + dependencies) are checked against this list. If a string in + this list appears inside the name of the object file, it is + added to the export list. + """ + _generate_def_windows( + name = name, + deps = deps, + target_compatible_with = ["@platforms//os:windows"], + **kwargs + ) diff --git a/wpimath/BUILD.bazel b/wpimath/BUILD.bazel index 95ae371530..a4f3b22e34 100644 --- a/wpimath/BUILD.bazel +++ b/wpimath/BUILD.bazel @@ -4,7 +4,7 @@ load("@rules_cc//cc:defs.bzl", "cc_binary", "cc_library", "cc_test") load("@rules_java//java:defs.bzl", "java_binary") load("@rules_pkg//:mappings.bzl", "pkg_files") load("@rules_python//python:defs.bzl", "py_binary") -load("//shared/bazel/rules:cc_rules.bzl", "third_party_cc_lib_helper", "wpilib_cc_library", "wpilib_cc_shared_library", "wpilib_cc_static_library") +load("//shared/bazel/rules:cc_rules.bzl", "generate_def_windows", "third_party_cc_lib_helper", "wpilib_cc_library", "wpilib_cc_shared_library", "wpilib_cc_static_library") load("//shared/bazel/rules:java_rules.bzl", "wpilib_java_junit5_test") load("//shared/bazel/rules:jni_rules.bzl", "wpilib_jni_cc_library", "wpilib_jni_java_library") load("//shared/bazel/rules:packaging.bzl", "package_minimal_jni_project") @@ -154,6 +154,17 @@ wpilib_cc_library( ], ) +generate_def_windows( + name = "wpimath_def", + filters = [ + ".pb.obj", + ".npb.obj", + ], + deps = [ + ":wpimath", + ], +) + wpilib_cc_shared_library( name = "shared/wpimath", auto_export_windows_symbols = False, @@ -161,6 +172,7 @@ wpilib_cc_shared_library( "//wpiutil:shared/wpiutil", ], visibility = ["//visibility:public"], + win_def_file = ":wpimath_def", deps = [ ":wpimath", ],