aboutsummaryrefslogtreecommitdiff
path: root/rules/cc/cc_library_static.bzl
blob: 526db156dca4c7d6ad33a7285eaacf63b53fb8e8 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
"""
Copyright (C) 2021 The Android Open Source Project

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
"""

load(
    ":cc_library_common.bzl",
    "create_ccinfo_for_includes",
    "is_external_directory",
    "system_dynamic_deps_defaults",
    "parse_sdk_version")
load(":stl.bzl", "static_stl_deps")
load("@bazel_skylib//lib:collections.bzl", "collections")
load("@rules_cc//cc:find_cc_toolchain.bzl", "find_cpp_toolchain")
load("@rules_cc//examples:experimental_cc_shared_library.bzl", "CcSharedLibraryInfo")
load("//build/bazel/product_variables:constants.bzl", "constants")

CcStaticLibraryInfo = provider(fields = ["root_static_archive", "objects"])

def cc_library_static(
        name,
        deps = [],
        implementation_deps = [],
        dynamic_deps = [],
        implementation_dynamic_deps = [],
        whole_archive_deps = [],
        implementation_whole_archive_deps = [],
        system_dynamic_deps = None,
        export_absolute_includes = [],
        export_includes = [],
        export_system_includes = [],
        local_includes = [],
        absolute_includes = [],
        hdrs = [],
        native_bridge_supported = False,  # TODO: not supported yet.
        use_libcrt = True,
        rtti = False,
        stl = "",
        cpp_std = "",
        c_std = "",
        # Flags for C and C++
        copts = [],
        # C++ attributes
        srcs = [],
        cppflags = [],
        # C attributes
        srcs_c = [],
        conlyflags = [],
        # asm attributes
        srcs_as = [],
        asflags = [],
        features = [],
        alwayslink = None,
        target_compatible_with = [],
        # TODO(b/202299295): Handle data attribute.
        data = [],
        sdk_version = "",
        min_sdk_version = "",
        use_version_lib = False):
    "Bazel macro to correspond with the cc_library_static Soong module."

    exports_name = "%s_exports" % name
    locals_name = "%s_locals" % name
    cpp_name = "%s_cpp" % name
    c_name = "%s_c" % name
    asm_name = "%s_asm" % name

    toolchain_features = []
    toolchain_features += features

    if is_external_directory(native.package_name()):
        toolchain_features += [
            "-non_external_compiler_flags",
            "external_compiler_flags",
        ]

    if use_version_lib:
        libbuildversionLabel = "//build/soong/cc/libbuildversion:libbuildversion"
        whole_archive_deps = whole_archive_deps + [libbuildversionLabel]

    if rtti:
        toolchain_features += ["rtti"]
    if not use_libcrt:
        toolchain_features += ["use_libcrt"]
    if cpp_std:
        toolchain_features += [cpp_std, "-cpp_std_default"]
    if c_std:
        toolchain_features += [c_std, "-c_std_default"]

    if min_sdk_version:
        toolchain_features += [
            "sdk_version_" + parse_sdk_version(min_sdk_version),
            "-sdk_version_default"
        ]

    if system_dynamic_deps == None:
        system_dynamic_deps = system_dynamic_deps_defaults

    _cc_includes(
        name = exports_name,
        includes = export_includes,
        absolute_includes = export_absolute_includes,
        system_includes = export_system_includes,
        # whole archive deps always re-export their includes, etc
        deps = deps + whole_archive_deps + dynamic_deps,
        target_compatible_with = target_compatible_with,
    )

    _cc_includes(
        name = locals_name,
        includes = local_includes,
        absolute_includes = absolute_includes,
        deps = implementation_deps + implementation_dynamic_deps + system_dynamic_deps + static_stl_deps(stl) + implementation_whole_archive_deps,
        target_compatible_with = target_compatible_with,
    )

    # Silently drop these attributes for now:
    # - native_bridge_supported
    common_attrs = dict(
        [
            # TODO(b/199917423): This may be superfluous. Investigate and possibly remove.
            ("linkstatic", True),
            ("hdrs", hdrs),
            # Add dynamic_deps to implementation_deps, as the include paths from the
            # dynamic_deps are also needed.
            ("implementation_deps", [locals_name]),
            ("deps", [exports_name]),
            ("features", toolchain_features),
            ("toolchains", ["//build/bazel/platforms:android_target_product_vars"]),
            ("alwayslink", alwayslink),
            ("target_compatible_with", target_compatible_with),
        ],
    )

    native.cc_library(
        name = cpp_name,
        srcs = srcs,
        copts = copts + cppflags,
        **common_attrs
    )
    native.cc_library(
        name = c_name,
        srcs = srcs_c,
        copts = copts + conlyflags,
        **common_attrs
    )
    native.cc_library(
        name = asm_name,
        srcs = srcs_as,
        copts = asflags,
        **common_attrs
    )

    # Root target to handle combining of the providers of the language-specific targets.
    _cc_library_combiner(
        name = name,
        deps = [cpp_name, c_name, asm_name] + whole_archive_deps + implementation_whole_archive_deps,
        target_compatible_with = target_compatible_with,
    )

# Returns a CcInfo object which combines one or more CcInfo objects, except that all
# linker inputs owned by  owners in `old_owner_labels` are relinked and owned by the current target.
#
# This is useful in the "macro with proxy rule" pattern, as some rules upstream
# may expect they are depending directly on a target which generates linker inputs,
# as opposed to a proxy target which is a level of indirection to such a target.
def _cc_library_combiner_impl(ctx):
    old_owner_labels = []
    cc_infos = []
    for dep in ctx.attr.deps:
        old_owner_labels.append(dep.label)
        cc_infos.append(dep[CcInfo])
    combined_info = cc_common.merge_cc_infos(cc_infos = cc_infos)

    objects_to_link = []

    # This is not ideal, as it flattens a depset.
    for old_linker_input in combined_info.linking_context.linker_inputs.to_list():
        if old_linker_input.owner in old_owner_labels:
            for lib in old_linker_input.libraries:
                # These objects will be recombined into the root archive.
                objects_to_link.extend(lib.objects)
        else:
            # Android macros don't handle transitive linker dependencies because
            # it's unsupported in legacy. We may want to change this going forward,
            # but for now it's good to validate that this invariant remains.
            fail("cc_static_library %s given transitive linker dependency from %s" % (ctx.label, old_linker_input.owner))

    cc_toolchain = find_cpp_toolchain(ctx)
    CPP_LINK_STATIC_LIBRARY_ACTION_NAME = "c++-link-static-library"
    feature_configuration = cc_common.configure_features(
        ctx = ctx,
        cc_toolchain = cc_toolchain,
        requested_features = ctx.features,
        unsupported_features = ctx.disabled_features + ["linker_flags"],
    )

    output_file = ctx.actions.declare_file("lib" + ctx.label.name + ".a")
    linker_input = cc_common.create_linker_input(
        owner = ctx.label,
        libraries = depset(direct = [
            cc_common.create_library_to_link(
                actions = ctx.actions,
                feature_configuration = feature_configuration,
                cc_toolchain = cc_toolchain,
                static_library = output_file,
                objects = objects_to_link,
            ),
        ]),
    )

    linking_context = cc_common.create_linking_context(linker_inputs = depset(direct = [linker_input]))

    archiver_path = cc_common.get_tool_for_action(
        feature_configuration = feature_configuration,
        action_name = CPP_LINK_STATIC_LIBRARY_ACTION_NAME,
    )
    archiver_variables = cc_common.create_link_variables(
        feature_configuration = feature_configuration,
        cc_toolchain = cc_toolchain,
        output_file = output_file.path,
        is_using_linker = False,
    )
    command_line = cc_common.get_memory_inefficient_command_line(
        feature_configuration = feature_configuration,
        action_name = CPP_LINK_STATIC_LIBRARY_ACTION_NAME,
        variables = archiver_variables,
    )
    args = ctx.actions.args()
    args.add_all(command_line)
    args.add_all(objects_to_link)

    ctx.actions.run(
        executable = archiver_path,
        arguments = [args],
        inputs = depset(
            direct = objects_to_link,
            transitive = [
                cc_toolchain.all_files,
            ],
        ),
        outputs = [output_file],
    )
    return [
        DefaultInfo(files = depset(direct = [output_file]), data_runfiles = ctx.runfiles(files = [output_file])),
        CcInfo(compilation_context = combined_info.compilation_context, linking_context = linking_context),
        CcStaticLibraryInfo(root_static_archive = output_file, objects = objects_to_link),
    ]

# A rule which combines objects of oen or more cc_library targets into a single
# static linker input. This outputs a single archive file combining the objects
# of its direct deps, and propagates Cc providers describing that these objects
# should be linked for linking rules upstream.
# This rule is useful for maintaining the illusion that the target's deps are
# comprised by a single consistent rule:
#   - A single archive file is always output by this rule.
#   - A single linker input struct is always output by this rule, and it is 'owned'
#       by this rule.
_cc_library_combiner = rule(
    implementation = _cc_library_combiner_impl,
    attrs = {
        "deps": attr.label_list(providers = [CcInfo]),
        "_cc_toolchain": attr.label(
            default = Label("@local_config_cc//:toolchain"),
            providers = [cc_common.CcToolchainInfo],
        ),
    },
    toolchains = ["@bazel_tools//tools/cpp:toolchain_type"],
    provides = [CcInfo],
    fragments = ["cpp"],
)

def _cc_includes_impl(ctx):
    return [create_ccinfo_for_includes(
        ctx,
        includes = ctx.attr.includes,
        absolute_includes = ctx.attr.absolute_includes,
        system_includes = ctx.attr.system_includes,
        deps = ctx.attr.deps,
    )]

# Bazel's native cc_library rule supports specifying include paths two ways:
# 1. non-exported includes can be specified via copts attribute
# 2. exported -isystem includes can be specified via includes attribute
#
# In order to guarantee a correct inclusion search order, we need to export
# includes paths for both -I and -isystem; however, there is no native Bazel
# support to export both of these, this rule provides a CcInfo to propagate the
# given package-relative include/system include paths as exec root relative
# include/system include paths.
_cc_includes = rule(
    implementation = _cc_includes_impl,
    attrs = {
        "absolute_includes": attr.string_list(doc = "List of exec-root relative or absolute search paths for headers, usually passed with -I"),
        "includes": attr.string_list(doc = "Package-relative list of search paths for headers, usually passed with -I"),
        "system_includes": attr.string_list(doc = "Package-relative list of search paths for headers, usually passed with -isystem"),
        "deps": attr.label_list(doc = "Re-propagates the includes obtained from these dependencies.", providers = [CcInfo]),
    },
    toolchains = ["@bazel_tools//tools/cpp:toolchain_type"],
    fragments = ["cpp"],
    provides = [CcInfo],
)