1load("@rules_cc//cc:find_cc_toolchain.bzl", "find_cpp_toolchain") 2 3def cc_library_static( 4 name, 5 srcs = [], 6 deps = [], 7 hdrs = [], 8 copts = [], 9 includes = [], 10 native_bridge_supported = False, # TODO: not supported yet. 11 whole_archive_deps = [], 12 **kwargs): 13 "Bazel macro to correspond with the cc_library_static Soong module." 14 mainlib_name = "%s_mainlib" % name 15 16 # Silently drop these attributes for now: 17 # - native_bridge_supported 18 native.cc_library( 19 name = mainlib_name, 20 srcs = srcs, 21 hdrs = hdrs, 22 # TODO(b/187533117): Handle whole_archive_deps differently from regular static deps. 23 deps = deps + whole_archive_deps, 24 copts = copts, 25 includes = includes, 26 **kwargs 27 ) 28 29 # Safeguard target to handle the no-srcs no-deps case. 30 # With no-srcs no-deps, this returns a stub. Otherwise, it's a passthrough no-op. 31 _empty_library_safeguard( 32 name = name, 33 deps = [mainlib_name], 34 ) 35 36# Returns a cloned copy of the given CcInfo object, except that all linker inputs 37# with owner `old_owner_label` are recreated and owned by the current target. 38# 39# This is useful in the "macro with proxy rule" pattern, as some rules upstream 40# may expect they are depending directly on a target which generates linker inputs, 41# as opposed to a proxy target which is a level of indirection to such a target. 42def _claim_ownership(ctx, old_owner_label, ccinfo): 43 linker_inputs = [] 44 # This is not ideal, as it flattens a depset. 45 for old_linker_input in ccinfo.linking_context.linker_inputs.to_list(): 46 if old_linker_input.owner == old_owner_label: 47 new_linker_input = cc_common.create_linker_input( 48 owner = ctx.label, 49 libraries = depset(direct = old_linker_input.libraries)) 50 linker_inputs.append(new_linker_input) 51 else: 52 linker_inputs.append(old_linker_input) 53 54 linking_context = cc_common.create_linking_context(linker_inputs = depset(direct = linker_inputs)) 55 return CcInfo(compilation_context = ccinfo.compilation_context, linking_context = linking_context) 56 57def _empty_library_safeguard_impl(ctx): 58 if len(ctx.attr.deps) != 1: 59 fail("the deps attribute should always contain exactly one label") 60 61 main_target = ctx.attr.deps[0] 62 if len(ctx.files.deps) > 0: 63 # This safeguard is a no-op, as a library was generated by the main target. 64 new_cc_info = _claim_ownership(ctx, main_target.label, main_target[CcInfo]) 65 return [new_cc_info, main_target[DefaultInfo]] 66 67 # The main library is empty; link a stub and propagate it to match Soong behavior. 68 cc_toolchain = find_cpp_toolchain(ctx) 69 CPP_LINK_STATIC_LIBRARY_ACTION_NAME = "c++-link-static-library" 70 feature_configuration = cc_common.configure_features( 71 ctx = ctx, 72 cc_toolchain = cc_toolchain, 73 requested_features = ctx.features, 74 unsupported_features = ctx.disabled_features + ["linker_flags"], 75 ) 76 77 output_file = ctx.actions.declare_file(ctx.label.name + ".a") 78 linker_input = cc_common.create_linker_input( 79 owner = ctx.label, 80 libraries = depset(direct = [ 81 cc_common.create_library_to_link( 82 actions = ctx.actions, 83 feature_configuration = feature_configuration, 84 cc_toolchain = cc_toolchain, 85 static_library = output_file, 86 ), 87 ]), 88 ) 89 compilation_context = cc_common.create_compilation_context() 90 linking_context = cc_common.create_linking_context(linker_inputs = depset(direct = [linker_input])) 91 92 archiver_path = cc_common.get_tool_for_action( 93 feature_configuration = feature_configuration, 94 action_name = CPP_LINK_STATIC_LIBRARY_ACTION_NAME, 95 ) 96 archiver_variables = cc_common.create_link_variables( 97 feature_configuration = feature_configuration, 98 cc_toolchain = cc_toolchain, 99 output_file = output_file.path, 100 is_using_linker = False, 101 ) 102 command_line = cc_common.get_memory_inefficient_command_line( 103 feature_configuration = feature_configuration, 104 action_name = CPP_LINK_STATIC_LIBRARY_ACTION_NAME, 105 variables = archiver_variables, 106 ) 107 args = ctx.actions.args() 108 args.add_all(command_line) 109 110 ctx.actions.run( 111 executable = archiver_path, 112 arguments = [args], 113 inputs = depset( 114 transitive = [ 115 cc_toolchain.all_files, 116 ], 117 ), 118 outputs = [output_file], 119 ) 120 121 cc_info = cc_common.merge_cc_infos(cc_infos = [ 122 main_target[CcInfo], 123 CcInfo(compilation_context = compilation_context, linking_context = linking_context), 124 ]) 125 return [ 126 DefaultInfo(files = depset([output_file])), 127 cc_info, 128 ] 129 130# A rule which depends on a single cc_library target. If the cc_library target 131# has no outputs (indicating that it has no srcs or deps), then this safeguard 132# rule creates a single stub .a file using llvm-ar. This mimics Soong's behavior 133# in this regard. Otherwise, this safeguard is a simple passthrough for the providers 134# of the cc_library. 135_empty_library_safeguard = rule( 136 implementation = _empty_library_safeguard_impl, 137 attrs = { 138 # This should really be a label attribute since it always contains a 139 # single dependency, but cc_shared_library requires that C++ rules 140 # depend on each other through the "deps" attribute. 141 "deps": attr.label_list(providers = [CcInfo]), 142 "_cc_toolchain": attr.label( 143 default = Label("@local_config_cc//:toolchain"), 144 providers = [cc_common.CcToolchainInfo], 145 ), 146 }, 147 toolchains = ["@bazel_tools//tools/cpp:toolchain_type"], 148 fragments = ["cpp"], 149) 150