From e80ce8ddeca1240505d4174023128d1fea1c65e7 Mon Sep 17 00:00:00 2001 From: Keith Smiley Date: Wed, 21 Jan 2026 21:06:37 -0800 Subject: [PATCH] Make cc_library(alwayslink=True) apply to .a in srcs Previously if you had an archive in the srcs of a cc_library and set alwayslink=True that was ignored. You had to have named the library .lo (arguably an implementation detail) to get the desired behavior. --- cc/private/rules_impl/cc_library.bzl | 8 ++-- tests/alwayslink_srcs/BUILD | 62 +++++++++++++++++++++++++ tests/alwayslink_srcs/alwayslink_test.c | 29 ++++++++++++ tests/alwayslink_srcs/registered.c | 21 +++++++++ tests/alwayslink_srcs/registered.h | 24 ++++++++++ tests/alwayslink_srcs/registerer.c | 20 ++++++++ 6 files changed, 161 insertions(+), 3 deletions(-) create mode 100644 tests/alwayslink_srcs/BUILD create mode 100644 tests/alwayslink_srcs/alwayslink_test.c create mode 100644 tests/alwayslink_srcs/registered.c create mode 100644 tests/alwayslink_srcs/registered.h create mode 100644 tests/alwayslink_srcs/registerer.c diff --git a/cc/private/rules_impl/cc_library.bzl b/cc/private/rules_impl/cc_library.bzl index a66995944..a03e0e2c0 100755 --- a/cc/private/rules_impl/cc_library.bzl +++ b/cc/private/rules_impl/cc_library.bzl @@ -194,6 +194,7 @@ def _cc_library_impl(ctx): feature_configuration, ctx.fragments.cpp.force_pic(), precompiled_files, + ctx.attr.alwayslink, ) if not cc_helper.is_compilation_outputs_empty(compilation_outputs): @@ -373,7 +374,8 @@ def _convert_precompiled_libraries_to_library_to_link( cc_toolchain, feature_configuration, force_pic, - precompiled_files): + precompiled_files, + alwayslink): static_libraries = _build_map_identifier_to_artifact(precompiled_files[2]) pic_static_libraries = _build_map_identifier_to_artifact(precompiled_files[3]) alwayslink_static_libraries = _build_map_identifier_to_artifact(precompiled_files[4]) @@ -413,7 +415,7 @@ def _convert_precompiled_libraries_to_library_to_link( static_library = static_library, pic_static_library = pic_static_library, dynamic_library = dynamic_library, - alwayslink = identifier in alwayslink_static_libraries, + alwayslink = alwayslink or identifier in alwayslink_static_libraries, ) libraries.append(library) @@ -435,7 +437,7 @@ def _convert_precompiled_libraries_to_library_to_link( feature_configuration = feature_configuration, cc_toolchain = cc_toolchain, pic_static_library = pic_static_library, - alwayslink = identifier in alwayslink_static_libraries, + alwayslink = alwayslink or identifier in alwayslink_static_libraries, ) libraries.append(library) diff --git a/tests/alwayslink_srcs/BUILD b/tests/alwayslink_srcs/BUILD new file mode 100644 index 000000000..b1461b4cf --- /dev/null +++ b/tests/alwayslink_srcs/BUILD @@ -0,0 +1,62 @@ +# Copyright 2026 The Bazel Authors. All rights reserved. +# +# 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:cc_library.bzl", "cc_library") +load("//cc:cc_test.bzl", "cc_test") + +licenses(["notice"]) + +# Library that provides the registration state (was_registered/set_registered). +cc_library( + name = "registered", + srcs = ["registered.c"], + hdrs = ["registered.h"], +) + +# Library that registers itself via a static constructor. +cc_library( + name = "registerer_lib", + srcs = ["registerer.c"], + deps = [":registered"], +) + +# Copy the .a file produced by registerer_lib, simulates having a prebuilt .a file. +genrule( + name = "copy_registerer_archive", + srcs = [":registerer_lib"], + outs = ["libregisterer_copied.a"], + cmd = "for f in $(locations :registerer_lib); do if [[ $$f == *.a ]]; then cp $$f $@; fi; done", +) + +# Library that uses the copied .a file in srcs with alwayslink=True. +# Without alwayslink, the registerer would be dropped since nothing +# directly references its symbols. +cc_library( + name = "registerer_alwayslink", + srcs = [":copy_registerer_archive"], + deps = [":registered"], + alwayslink = True, +) + +# Test that verifies the symbol was loaded at runtime. +# The test does not directly reference any symbol from registerer, +# so without alwayslink the static constructor would not run. +cc_test( + name = "alwayslink_test", + srcs = ["alwayslink_test.c"], + deps = [ + ":registered", + ":registerer_alwayslink", + ], +) diff --git a/tests/alwayslink_srcs/alwayslink_test.c b/tests/alwayslink_srcs/alwayslink_test.c new file mode 100644 index 000000000..47faec577 --- /dev/null +++ b/tests/alwayslink_srcs/alwayslink_test.c @@ -0,0 +1,29 @@ +// Copyright 2026 The Bazel Authors. All rights reserved. +// +// 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. + +#include + +#include "tests/alwayslink_srcs/registered.h" + +int main(void) { + // If alwayslink worked correctly, the registerer library should have been + // linked even though nothing directly references its symbols. The static + // constructor should have called set_registered(). + if (!was_registered()) { + fprintf(stderr, "FAILED: alwayslink did not work - static constructor was not executed\n"); + return 1; + } + printf("PASSED: alwayslink worked - static constructor was executed\n"); + return 0; +} diff --git a/tests/alwayslink_srcs/registered.c b/tests/alwayslink_srcs/registered.c new file mode 100644 index 000000000..dd1de99e9 --- /dev/null +++ b/tests/alwayslink_srcs/registered.c @@ -0,0 +1,21 @@ +// Copyright 2026 The Bazel Authors. All rights reserved. +// +// 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. + +#include "tests/alwayslink_srcs/registered.h" + +static int g_registered = 0; + +int was_registered(void) { return g_registered; } + +void set_registered(void) { g_registered = 1; } diff --git a/tests/alwayslink_srcs/registered.h b/tests/alwayslink_srcs/registered.h new file mode 100644 index 000000000..f570452b7 --- /dev/null +++ b/tests/alwayslink_srcs/registered.h @@ -0,0 +1,24 @@ +// Copyright 2026 The Bazel Authors. All rights reserved. +// +// 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. + +#ifndef TESTS_ALWAYSLINK_SRCS_REGISTERED_H_ +#define TESTS_ALWAYSLINK_SRCS_REGISTERED_H_ + +// Returns 1 if the registerer constructor was executed. +int was_registered(void); + +// Called by the registerer's constructor. +void set_registered(void); + +#endif // TESTS_ALWAYSLINK_SRCS_REGISTERED_H_ diff --git a/tests/alwayslink_srcs/registerer.c b/tests/alwayslink_srcs/registerer.c new file mode 100644 index 000000000..d80ba8060 --- /dev/null +++ b/tests/alwayslink_srcs/registerer.c @@ -0,0 +1,20 @@ +// Copyright 2026 The Bazel Authors. All rights reserved. +// +// 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. + +#include "tests/alwayslink_srcs/registered.h" + +// Static constructor that registers on load. +__attribute__((constructor)) static void registerer_init(void) { + set_registered(); +}