• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# Copyright (c) 2024, Google Inc.
2#
3# Permission to use, copy, modify, and/or distribute this software for any
4# purpose with or without fee is hereby granted, provided that the above
5# copyright notice and this permission notice appear in all copies.
6#
7# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
8# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
9# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
10# SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
11# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
12# OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
13# CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
14
15load("@rules_cc//cc:defs.bzl", "cc_binary", "cc_library", "cc_test")
16
17# Configure C, C++, and common flags for GCC-compatible toolchains.
18#
19# TODO(davidben): Can we remove some of these? In Bazel, are warnings the
20# toolchain or project's responsibility? -fno-common did not become default
21# until https://gcc.gnu.org/bugzilla/show_bug.cgi?id=85678.
22gcc_copts = [
23    # This list of warnings should match those in the top-level CMakeLists.txt.
24    "-Wall",
25    "-Werror",
26    "-Wformat=2",
27    "-Wsign-compare",
28    "-Wmissing-field-initializers",
29    "-Wwrite-strings",
30    "-Wshadow",
31    "-fno-common",
32]
33
34gcc_copts_cxx = [
35    "-Wmissing-declarations",
36]
37
38gcc_copts_c = [
39    "-Wmissing-prototypes",
40    "-Wold-style-definition",
41    "-Wstrict-prototypes",
42]
43
44boringssl_copts_common = select({
45    # This condition and the asm_srcs_used one below must be kept in sync.
46    "@platforms//os:windows": ["-DOPENSSL_NO_ASM"],
47    "//conditions:default": [],
48}) + select({
49    # We assume that non-Windows builds use a GCC-compatible toolchain and that
50    # Windows builds do not.
51    #
52    # TODO(davidben): Should these be querying something in @bazel_tools?
53    # Unfortunately, @bazel_tools is undocumented. See
54    # https://github.com/bazelbuild/bazel/issues/14914
55    "@platforms//os:windows": [],
56    "//conditions:default": gcc_copts,
57}) + select({
58    # This is needed on glibc systems to get rwlock in pthreads, but it should
59    # not be set on Apple platforms or FreeBSD, where it instead disables APIs
60    # we use.
61    # See compat(5), sys/cdefs.h, and https://crbug.com/boringssl/471
62    "@platforms//os:linux": ["-D_XOPEN_SOURCE=700"],
63    # Without WIN32_LEAN_AND_MEAN, <windows.h> pulls in wincrypt.h, which
64    # conflicts with our <openssl/x509.h>.
65    "@platforms//os:windows": ["-DWIN32_LEAN_AND_MEAN", "-utf-8"],
66    "//conditions:default": [],
67})
68
69# We do not specify the C++ version here because Bazel expects C++ version
70# to come from the top-level toolchain. The concern is that different C++
71# versions may cause ABIs, notably Abseil's, to change.
72boringssl_copts_cxx = boringssl_copts_common + select({
73    "@platforms//os:windows": [],
74    "//conditions:default": gcc_copts_cxx,
75})
76
77# We specify the C version because Bazel projects often do not remember to
78# specify the C version. We do not expect ABIs to vary by C versions, at least
79# for our code or the headers we include, so configure the C version inside the
80# library. If Bazel's C/C++ version handling improves, we may reconsider this.
81boringssl_copts_c = boringssl_copts_common + select({
82    "@platforms//os:windows": ["/std:c11"],
83    "//conditions:default": ["-std=c11"] + gcc_copts_c,
84})
85
86def linkstatic_kwargs(linkstatic):
87    # Although Bazel's documentation says linkstatic defaults to True or False
88    # for the various target types, this is not true. The defaults differ by
89    # platform non-Windows and True on Windows. There is now way to request the
90    # default except to omit the parameter, so we must use kwargs.
91    kwargs = {}
92    if linkstatic != None:
93        kwargs["linkstatic"] = linkstatic
94    return kwargs
95
96def handle_mixed_c_cxx(
97        name,
98        copts,
99        deps,
100        internal_hdrs,
101        includes,
102        linkopts,
103        linkstatic,
104        srcs,
105        testonly,
106        alwayslink):
107    """
108    Works around https://github.com/bazelbuild/bazel/issues/22041. Determines
109    whether a target contains C, C++, or both. If the target is multi-language,
110    the C sources are split into a separate library. Returns a tuple of updated
111    (copts, deps, srcs) to apply.
112    """
113    has_c, has_cxx = False, False
114    for src in srcs:
115        if src.endswith(".c"):
116            has_c = True
117        elif src.endswith(".cc"):
118            has_cxx = True
119
120    # If a target has both C and C++, we need to split it in two.
121    if has_c and has_cxx:
122        # Pull the C++ files out.
123        srcs_cxx = [src for src in srcs if src.endswith(".cc") or src.endswith(".h")]
124        name_cxx = name + "_cxx"
125        cc_library(
126            name = name_cxx,
127            srcs = srcs_cxx + internal_hdrs,
128            copts = copts + boringssl_copts_cxx,
129            includes = includes,
130            linkopts = linkopts,
131            deps = deps,
132            testonly = testonly,
133            alwayslink = alwayslink,
134            **linkstatic_kwargs(linkstatic),
135        )
136
137        # Build the remainder as a C-only target.
138        deps = deps + [":" + name_cxx]
139        srcs = [src for src in srcs if not src.endswith(".cc")]
140        has_cxx = False
141
142    if has_c:
143        copts = copts + boringssl_copts_c
144    else:
145        copts = copts + boringssl_copts_cxx
146
147    return copts, deps, srcs
148
149def handle_asm_srcs(asm_srcs):
150    if not asm_srcs:
151        return []
152
153    # By default, the C files will expect assembly files, if any, to be linked
154    # in with the build. This default can be flipped with -DOPENSSL_NO_ASM. If
155    # building in a configuration where we have no assembly optimizations,
156    # -DOPENSSL_NO_ASM has no effect, and either value is fine.
157    #
158    # Like C files, assembly files are wrapped in #ifdef (or NASM equivalent),
159    # so it is safe to include a file for the wrong platform in the build. It
160    # will just output an empty object file. However, we need some platform
161    # selectors to distinguish between gas or NASM syntax.
162    #
163    # For all non-Windows platforms, we use gas assembly syntax and can assume
164    # any GCC-compatible toolchain includes a gas-compatible assembler.
165    #
166    # For Windows, we use NASM on x86 and x86_64 and gas, specifically
167    # clang-assembler, on aarch64. We have not yet added NASM support to this
168    # build, and would need to detect MSVC vs clang-cl for aarch64 so, for now,
169    # we just disable assembly on Windows across the board.
170    #
171    # This select and the corresponding one in boringssl_copts_common must be
172    # kept in sync.
173    #
174    # TODO(https://crbug.com/boringssl/531): Enable assembly for Windows.
175    return select({
176        "@platforms//os:windows": [],
177        "//conditions:default": asm_srcs,
178    })
179
180def bssl_cc_library(
181        name,
182        asm_srcs = [],
183        copts = [],
184        deps = [],
185        hdrs = [],
186        includes = [],
187        internal_hdrs = [],
188        linkopts = [],
189        linkstatic = None,
190        srcs = [],
191        testonly = False,
192        alwayslink = False,
193        visibility = []):
194    copts, deps, srcs = handle_mixed_c_cxx(
195        name = name,
196        copts = copts,
197        deps = deps,
198        internal_hdrs = hdrs + internal_hdrs,
199        includes = includes,
200        linkopts = linkopts,
201        # Ideally we would set linkstatic = True to statically link the helper
202        # library into main cc_library. But Bazel interprets linkstatic such
203        # that, if A(test, linkshared) -> B(library) -> C(library, linkstatic),
204        # C will be statically linked into A, not B. This is probably to avoid
205        # diamond dependency problems but means linkstatic does not help us make
206        # this function transparent. Instead, just pass along the linkstatic
207        # nature of the main library.
208        linkstatic = linkstatic,
209        srcs = srcs,
210        testonly = testonly,
211        alwayslink = alwayslink,
212    )
213
214    # BoringSSL's notion of internal headers are slightly different from
215    # Bazel's. libcrypto's internal headers may be used by libssl, but they
216    # cannot be used outside the library. To express this, we make separate
217    # internal and external targets. This impact's Bazel's layering check.
218    name_internal = name
219    if visibility:
220        name_internal = name + "_internal"
221
222    cc_library(
223        name = name_internal,
224        srcs = srcs + handle_asm_srcs(asm_srcs),
225        hdrs = hdrs + internal_hdrs,
226        copts = copts,
227        includes = includes,
228        linkopts = linkopts,
229        deps = deps,
230        testonly = testonly,
231        alwayslink = alwayslink,
232        **linkstatic_kwargs(linkstatic)
233    )
234
235    if visibility:
236        cc_library(
237            name = name,
238            hdrs = hdrs,
239            deps = [":" + name_internal],
240            visibility = visibility,
241        )
242
243def bssl_cc_binary(
244        name,
245        srcs = [],
246        asm_srcs = [],
247        copts = [],
248        includes = [],
249        linkstatic = None,
250        linkopts = [],
251        deps = [],
252        testonly = False,
253        visibility = []):
254    copts, deps, srcs = handle_mixed_c_cxx(
255        name = name,
256        copts = copts,
257        deps = deps,
258        internal_hdrs = [],
259        includes = includes,
260        # If it weren't for https://github.com/bazelbuild/bazel/issues/22041,
261        # the split library be part of `srcs` and linked statically. Set
262        # linkstatic to match.
263        linkstatic = True,
264        linkopts = linkopts,
265        srcs = srcs,
266        testonly = testonly,
267        # TODO(davidben): Should this be alwayslink = True? How does Bazel treat
268        # the real cc_binary.srcs?
269        alwayslink = False,
270    )
271
272    cc_binary(
273        name = name,
274        srcs = srcs + handle_asm_srcs(asm_srcs),
275        copts = copts,
276        includes = includes,
277        linkopts = linkopts,
278        deps = deps,
279        testonly = testonly,
280        visibility = visibility,
281        **linkstatic_kwargs(linkstatic)
282    )
283
284def bssl_cc_test(
285        name,
286        srcs = [],
287        asm_srcs = [],
288        data = [],
289        size = "medium",
290        copts = [],
291        includes = [],
292        linkopts = [],
293        linkstatic = None,
294        deps = [],
295        shard_count = None):
296    copts, deps, srcs = handle_mixed_c_cxx(
297        name = name,
298        copts = copts,
299        deps = deps,
300        internal_hdrs = [],
301        includes = includes,
302        # If it weren't for https://github.com/bazelbuild/bazel/issues/22041,
303        # the split library be part of `srcs` and linked statically. Set
304        # linkstatic to match.
305        linkstatic = True,
306        linkopts = linkopts,
307        srcs = srcs,
308        testonly = True,
309        # If any sources get extracted, they must always be linked, otherwise
310        # tests will be dropped.
311        alwayslink = True,
312    )
313
314    cc_test(
315        name = name,
316        data = data,
317        deps = deps,
318        srcs = srcs + handle_asm_srcs(asm_srcs),
319        copts = copts,
320        includes = includes,
321        linkopts = linkopts,
322        shard_count = shard_count,
323        size = size,
324        **linkstatic_kwargs(linkstatic)
325    )
326