"""Provides a rule that outputs a monolithic static library.""" load("@bazel_tools//tools/cpp:toolchain_utils.bzl", "find_cpp_toolchain") TOOLS_CPP_REPO = "@bazel_tools" def _cc_static_library_impl(ctx): output_lib = ctx.actions.declare_file("{}.a".format(ctx.attr.name)) output_flags = ctx.actions.declare_file("{}.link".format(ctx.attr.name)) cc_toolchain = find_cpp_toolchain(ctx) # Populate the feature configuration. This is used by the various cc library # functions to generate contexts. feature_configuration = cc_common.configure_features( ctx = ctx, cc_toolchain = cc_toolchain, requested_features = ctx.features, unsupported_features = ctx.disabled_features ) lib_sets = [] lib_inputs = [] unique_flags = {} merged_compilation_context = cc_common.merge_compilation_contexts( compilation_contexts = [dep[CcInfo].compilation_context for dep in ctx.attr.deps] ) for dep in ctx.attr.deps: if hasattr(dep[CcInfo].linking_context.linker_inputs, "to_list"): lib_inputs.append(dep[CcInfo].linking_context.linker_inputs.to_list()) else: lib_inputs.append(dep[CcInfo].linking_context.linker_inputs) for lib in dep[CcInfo].linking_context.linker_inputs.to_list(): if hasattr(lib.libraries, "to_list"): lib_sets.append(lib.libraries) else: lib_sets.append(depset(direct = lib.libraries)) unique_flags.update({ flag: None for flag in lib.user_link_flags }) libraries_to_link = depset(transitive = lib_sets) link_flags = unique_flags.keys() libs = [] libs.extend([lib.pic_static_library for lib in libraries_to_link.to_list() if lib.pic_static_library]) libs.extend([ lib.static_library for lib in libraries_to_link.to_list() if lib.static_library and not lib.pic_static_library ]) script_file = ctx.actions.declare_file("{}.mri".format(ctx.attr.name)) commands = ["create {}".format(output_lib.path)] for lib in libs: commands.append("addlib {}".format(lib.path)) commands.append("save") commands.append("end") ctx.actions.write( output = script_file, content = "\n".join(commands) + "\n", ) ar_tool = cc_common.get_tool_for_action( feature_configuration = feature_configuration, action_name = "ar", ) ctx.actions.run_shell( command = "{} -M < {}".format(ar_tool, script_file.path), inputs = [script_file] + libs + cc_toolchain.all_files.to_list(), outputs = [output_lib], mnemonic = "ArMerge", progress_message = "Merging static library {}".format(output_lib.path), ) ctx.actions.write( output = output_flags, content = "\n".join(link_flags) + "\n", ) # With the library in hand, the next step is to set up information for # Bazel's C/C++ library to utilize the newly-created library. library_to_link = cc_common.create_library_to_link( actions = ctx.actions, feature_configuration = feature_configuration, static_library = output_lib, #user_link_flags = ['fooxbar'] ) linker_input = cc_common.create_linker_input( libraries = depset([library_to_link]), owner = ctx.label, ) linking_context = cc_common.create_linking_context( linker_inputs = depset([linker_input]) ) # CcInfo is what is actually passed to future targets. We may need to merge # this with the dependency's original info in order for builds to complete. # Not sure yet. this_cc_info = CcInfo(compilation_context = merged_compilation_context, linking_context = linking_context) cc_infos = [this_cc_info] # print(this_cc_info.linking_context) # print(ctx.attr.library[CcInfo].compilation_context) # print(ctx.attr.library[CcInfo].linking_context) # cc_infos.append(ctx.attr.library[CcInfo]) merged_cc_info = cc_common.merge_cc_infos(direct_cc_infos = [this_cc_info], cc_infos = cc_infos) # print(merged_cc_info.linking_context) return [ DefaultInfo(files = depset([output_lib])), merged_cc_info ] cc_static_library = rule( implementation = _cc_static_library_impl, attrs = { "deps": attr.label_list(), "_cc_toolchain": attr.label( default = TOOLS_CPP_REPO + "//tools/cpp:current_cc_toolchain", ), }, toolchains = [TOOLS_CPP_REPO + "//tools/cpp:toolchain_type"], fragments = ["cpp"], incompatible_use_toolchain_transition = True, )