Allows libraries to be linked more than once.
Libraries are tagged with LINKABLE_MORE_THAN_ONCE can be linked into more than
one shared library and not give an error.
RELNOTES:none
PiperOrigin-RevId: 297352891
Change-Id: Id32b5c341bfd9d5906d67216773e82b0d8b63faf
diff --git a/examples/experimental_cc_shared_library.bzl b/examples/experimental_cc_shared_library.bzl
index 07ea515..fedb4ca 100644
--- a/examples/experimental_cc_shared_library.bzl
+++ b/examples/experimental_cc_shared_library.bzl
@@ -9,22 +9,32 @@
# TODO(#5200): Add export_define to library_to_link and cc_library
+# Add this as a tag to any target that can be linked by more than one
+# cc_shared_library because it doesn't have static initializers or anything
+# else that may cause issues when being linked more than once. This should be
+# used sparingly after making sure it's safe to use.
+LINKABLE_MORE_THAN_ONCE = "LINKABLE_MORE_THAN_ONCE"
+
GraphNodeInfo = provider(
fields = {
"children": "Other GraphNodeInfo from dependencies of this target",
"exported_by": "Labels of targets that can export the library of this node",
"label": "Label of the target visited",
+ "linkable_more_than_once": "Linkable into more than a single cc_shared_library",
},
)
CcSharedLibraryInfo = provider(
fields = {
"dynamic_deps": "All shared libraries depended on transitively",
"exports": "cc_libraries that are linked statically and exported",
+ "link_once_static_libs": "All libraries linked statically into this library that should " +
+ "only be linked once, e.g. because they have static " +
+ "initializers. If we try to link them more than once, " +
+ "we will throw an error",
"linker_input": "the resulting linker input artifact for the shared library",
"preloaded_deps": "cc_libraries needed by this cc_shared_library that should" +
" be linked the binary. If this is set, this cc_shared_library has to " +
" be a direct dependency of the cc_binary",
- "static_libs": "All libraries linked statically into this library",
},
)
@@ -47,7 +57,7 @@
if node_label in can_be_linked_dynamically:
link_dynamically_labels[node_label] = True
elif node_label not in preloaded_deps_direct_labels:
- link_statically_labels[node_label] = True
+ link_statically_labels[node_label] = node.linkable_more_than_once
all_children.extend(node.children)
return (link_statically_labels, link_dynamically_labels)
@@ -67,7 +77,7 @@
dynamic_dep_entry = (
dep[CcSharedLibraryInfo].exports,
dep[CcSharedLibraryInfo].linker_input,
- dep[CcSharedLibraryInfo].static_libs,
+ dep[CcSharedLibraryInfo].link_once_static_libs,
)
dynamic_deps.append(dynamic_dep_entry)
transitive_dynamic_deps.append(dep[CcSharedLibraryInfo].dynamic_deps)
@@ -88,19 +98,19 @@
exports_map[export] = linker_input
return exports_map
-def _build_static_libs_map(merged_shared_library_infos):
- static_libs_map = {}
+def _build_link_once_static_libs_map(merged_shared_library_infos):
+ link_once_static_libs_map = {}
for entry in merged_shared_library_infos.to_list():
- static_libs = entry[2]
+ link_once_static_libs = entry[2]
linker_input = entry[1]
- for static_lib in static_libs:
- if static_lib in static_libs_map:
+ for static_lib in link_once_static_libs:
+ if static_lib in link_once_static_libs_map:
fail("Two shared libraries in dependencies link the same " +
- " library statically. Both " + static_libs_map[static_lib] +
+ " library statically. Both " + link_once_static_libs_map[static_lib] +
" and " + str(linker_input.owner) +
" link statically" + static_lib)
- static_libs_map[static_lib] = str(linker_input.owner)
- return static_libs_map
+ link_once_static_libs_map[static_lib] = str(linker_input.owner)
+ return link_once_static_libs_map
def _wrap_static_library_with_alwayslink(ctx, feature_configuration, cc_toolchain, linker_input):
new_libraries_to_link = []
@@ -147,9 +157,9 @@
cc_toolchain,
transitive_exports,
preloaded_deps_direct_labels,
- static_libs_map):
+ link_once_static_libs_map):
linker_inputs = []
- static_libs = []
+ link_once_static_libs = []
graph_structure_aspect_nodes = []
dependency_linker_inputs = []
@@ -181,9 +191,9 @@
dynamic_linker_input = transitive_exports[owner]
linker_inputs.append(dynamic_linker_input)
elif owner in link_statically_labels:
- if owner in static_libs_map:
+ if owner in link_once_static_libs_map:
fail(owner + " is already linked statically in " +
- static_libs_map[owner] + " but not exported")
+ link_once_static_libs_map[owner] + " but not exported")
if owner in direct_exports:
wrapped_library = _wrap_static_library_with_alwayslink(
@@ -193,7 +203,8 @@
linker_input,
)
- static_libs.append(owner)
+ if not link_statically_labels[owner]:
+ link_once_static_libs.append(owner)
linker_inputs.append(wrapped_library)
else:
can_be_linked_statically = False
@@ -206,13 +217,14 @@
can_be_linked_statically = True
break
if can_be_linked_statically:
- static_libs.append(owner)
+ if not link_statically_labels[owner]:
+ link_once_static_libs.append(owner)
linker_inputs.append(linker_input)
else:
fail("We can't link " +
str(owner) + " either statically or dynamically")
- return (linker_inputs, static_libs)
+ return (linker_inputs, link_once_static_libs)
def _same_package_or_above(label_a, label_b):
if label_a.workspace_name != label_b.workspace_name:
@@ -267,15 +279,15 @@
preloaded_dep_merged_cc_info = cc_common.merge_cc_infos(cc_infos = preloaded_deps_cc_infos)
- static_libs_map = _build_static_libs_map(merged_cc_shared_library_info)
+ link_once_static_libs_map = _build_link_once_static_libs_map(merged_cc_shared_library_info)
- (linker_inputs, static_libs) = _filter_inputs(
+ (linker_inputs, link_once_static_libs) = _filter_inputs(
ctx,
feature_configuration,
cc_toolchain,
exports_map,
preloaded_deps_direct_labels,
- static_libs_map,
+ link_once_static_libs_map,
)
linking_context = _create_linker_context(ctx, linker_inputs)
@@ -313,7 +325,7 @@
CcSharedLibraryInfo(
dynamic_deps = merged_cc_shared_library_info,
exports = exports,
- static_libs = static_libs,
+ link_once_static_libs = link_once_static_libs,
linker_input = cc_common.create_linker_input(
owner = ctx.label,
libraries = depset([linking_outputs.library_to_link]),
@@ -331,6 +343,7 @@
children.append(dep[GraphNodeInfo])
exported_by = []
+ linkable_more_than_once = False
if hasattr(ctx.rule.attr, "tags"):
for tag in ctx.rule.attr.tags:
if tag.startswith("exported_by=") and len(tag) > 12:
@@ -343,11 +356,14 @@
Label(target) # Checking synthax is ok.
exported_by.append(target)
+ elif tag == LINKABLE_MORE_THAN_ONCE:
+ linkable_more_than_once = True
return [GraphNodeInfo(
label = ctx.label,
children = children,
exported_by = exported_by,
+ linkable_more_than_once = linkable_more_than_once,
)]
graph_structure_aspect = aspect(
diff --git a/examples/test_cc_shared_library/BUILD b/examples/test_cc_shared_library/BUILD
index e860b5c..6241fad 100644
--- a/examples/test_cc_shared_library/BUILD
+++ b/examples/test_cc_shared_library/BUILD
@@ -1,5 +1,5 @@
load("//cc:defs.bzl", "cc_binary", "cc_library", "cc_test")
-load("//examples:experimental_cc_shared_library.bzl", "cc_shared_library")
+load("//examples:experimental_cc_shared_library.bzl", "LINKABLE_MORE_THAN_ONCE", "cc_shared_library")
load(":starlark_tests.bzl", "additional_inputs_test", "linking_suffix_test")
cc_test(
@@ -26,6 +26,7 @@
preloaded_deps = ["preloaded_dep"],
static_deps = [
"//examples/test_cc_shared_library:qux",
+ "//examples/test_cc_shared_library:qux2",
],
user_link_flags = [
"-Wl,-rpath,kittens",
@@ -68,6 +69,7 @@
"baz",
# Not exported.
"qux",
+ "qux2",
],
)
@@ -83,6 +85,13 @@
hdrs = ["qux.h"],
)
+cc_library(
+ name = "qux2",
+ srcs = ["qux2.cc"],
+ hdrs = ["qux2.h"],
+ tags = [LINKABLE_MORE_THAN_ONCE],
+)
+
cc_shared_library(
name = "bar_so",
additional_linker_inputs = [
@@ -91,6 +100,7 @@
static_deps = [
"//examples/test_cc_shared_library:barX",
"@test_repo//:bar",
+ "//examples/test_cc_shared_library:qux2",
],
user_link_flags = [
"-Wl,--version-script=$(location :bar.lds)",
@@ -117,6 +127,7 @@
hdrs = ["bar.h"],
deps = [
"barX",
+ "qux2",
],
)
diff --git a/examples/test_cc_shared_library/qux2.cc b/examples/test_cc_shared_library/qux2.cc
new file mode 100644
index 0000000..34a96dc
--- /dev/null
+++ b/examples/test_cc_shared_library/qux2.cc
@@ -0,0 +1,3 @@
+#include "examples/test_cc_shared_library/qux2.h"
+
+int qux2() { return 42; }
diff --git a/examples/test_cc_shared_library/qux2.h b/examples/test_cc_shared_library/qux2.h
new file mode 100644
index 0000000..1246a11
--- /dev/null
+++ b/examples/test_cc_shared_library/qux2.h
@@ -0,0 +1,6 @@
+#ifndef EXAMPLES_TEST_CC_SHARED_LIBRARY_QUX2_H_
+#define EXAMPLES_TEST_CC_SHARED_LIBRARY_QUX2_H_
+
+int qux2();
+
+#endif // EXAMPLES_TEST_CC_SHARED_LIBRARY_QUX2_H_