Skip to content

Commit f9008f6

Browse files
oquenchilcopybara-github
authored andcommitted
Fix cc_shared_library to take into account indirect top level deps
The cc_shared_library implementation wraps its top level direct dependencies in whole archive because otherwise since there aren't any symbols in the shared library depending on them they would be dropped by the linker. There are instances where the target placed in the direct dep of a cc_shared_library doesn't have a linker_input or if it has it, the linker_input may not have any code to be linked statically into the shared library. When that happens, the actual code to be linked is depended on indirectly, e.g. cc_shared_library -> cc_proto_library -> proto_library. Here it is the code generated by the aspect applied on proto_library that should be linked statically. Since this dependency was indirect, that code wasn't whole-archived. Before this fix, we only tried to whole-archive cc_proto_library but the cc_proto_library target itself did not produce any linker_inputs, for this reason the code from the proto_library was dropped by the linker. This CL also adds more test coverage for the dynamic_only_roots failure triggered explicitly by the cc_shared_library implementation. RELNOTES:none PiperOrigin-RevId: 515017649 Change-Id: I022e1cb4f70fa4584893d3c34172236fdf2e5f73
1 parent 56a9efb commit f9008f6

File tree

7 files changed

+190
-18
lines changed

7 files changed

+190
-18
lines changed

src/main/starlark/builtins_bzl/common/cc/experimental_cc_shared_library.bzl

Lines changed: 60 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -193,6 +193,47 @@ def _check_if_target_should_be_exported_with_filter(target, current_label, expor
193193

194194
return False
195195

196+
# Checks if the linker_input has code to link statically, i.e. either
197+
# archives or object files, ignores library.dynamic_library.
198+
def _contains_code_to_link(linker_input):
199+
for library in linker_input.libraries:
200+
if (library.static_library != None or
201+
library.pic_static_library != None or
202+
len(library.objects) or len(library.pic_objects)):
203+
return True
204+
205+
return False
206+
207+
def _find_top_level_linker_input_labels(
208+
nodes,
209+
linker_inputs_to_be_linked_statically_map,
210+
targets_to_be_linked_dynamically_set):
211+
top_level_linker_input_labels_set = {}
212+
nodes_to_check = list(nodes)
213+
214+
for i in range(2147483647):
215+
if i == len(nodes_to_check):
216+
break
217+
218+
node = nodes_to_check[i]
219+
node_label = str(node.label)
220+
if node_label in linker_inputs_to_be_linked_statically_map:
221+
has_code_to_link = False
222+
for linker_input in linker_inputs_to_be_linked_statically_map[node_label]:
223+
if _contains_code_to_link(linker_input):
224+
top_level_linker_input_labels_set[node_label] = True
225+
has_code_to_link = True
226+
break
227+
228+
if not has_code_to_link:
229+
nodes_to_check.extend(node.children)
230+
elif node_label not in targets_to_be_linked_dynamically_set:
231+
# This can happen when there was a target in the graph that exported other libraries'
232+
# linker_inputs but didn't contribute any linker_input of its own.
233+
nodes_to_check.extend(node.children)
234+
235+
return top_level_linker_input_labels_set
236+
196237
def _filter_inputs(
197238
ctx,
198239
feature_configuration,
@@ -205,11 +246,11 @@ def _filter_inputs(
205246

206247
graph_structure_aspect_nodes = []
207248
dependency_linker_inputs = []
208-
direct_exports = {}
209-
for export in deps:
210-
direct_exports[str(export.label)] = True
211-
dependency_linker_inputs.extend(export[CcInfo].linking_context.linker_inputs.to_list())
212-
graph_structure_aspect_nodes.append(export[GraphNodeInfo])
249+
direct_deps_set = {}
250+
for dep in deps:
251+
direct_deps_set[str(dep.label)] = True
252+
dependency_linker_inputs.extend(dep[CcInfo].linking_context.linker_inputs.to_list())
253+
graph_structure_aspect_nodes.append(dep[GraphNodeInfo])
213254

214255
can_be_linked_dynamically = {}
215256
for linker_input in dependency_linker_inputs:
@@ -224,6 +265,18 @@ def _filter_inputs(
224265
can_be_linked_dynamically,
225266
)
226267

268+
linker_inputs_to_be_linked_statically_map = {}
269+
for linker_input in dependency_linker_inputs:
270+
owner = str(linker_input.owner)
271+
if owner in targets_to_be_linked_statically_map:
272+
linker_inputs_to_be_linked_statically_map.setdefault(owner, []).append(linker_input)
273+
274+
top_level_linker_input_labels_set = _find_top_level_linker_input_labels(
275+
graph_structure_aspect_nodes,
276+
linker_inputs_to_be_linked_statically_map,
277+
targets_to_be_linked_dynamically_set,
278+
)
279+
227280
# We keep track of precompiled_only_dynamic_libraries, so that we can add
228281
# them to runfiles.
229282
precompiled_only_dynamic_libraries = []
@@ -260,7 +313,6 @@ def _filter_inputs(
260313
# link_once_static_libs_map[owner] but is not being exported
261314
linked_statically_but_not_exported.setdefault(link_once_static_libs_map[owner], []).append(owner)
262315

263-
is_direct_export = owner in direct_exports
264316
dynamic_only_libraries = []
265317
static_libraries = []
266318
for library in linker_input.libraries:
@@ -272,15 +324,15 @@ def _filter_inputs(
272324
if len(dynamic_only_libraries):
273325
precompiled_only_dynamic_libraries.extend(dynamic_only_libraries)
274326
if not len(static_libraries):
275-
if is_direct_export:
327+
if owner in direct_deps_set:
276328
dynamic_only_roots[owner] = True
277329
linker_inputs.append(linker_input)
278330
continue
279331
if len(static_libraries) and owner in dynamic_only_roots:
280332
dynamic_only_roots.pop(owner)
281333

282334
linker_input_to_be_linked_statically = linker_input
283-
if is_direct_export:
335+
if owner in top_level_linker_input_labels_set:
284336
linker_input_to_be_linked_statically = _wrap_static_library_with_alwayslink(
285337
ctx,
286338
feature_configuration,

src/main/starlark/tests/builtins_bzl/cc/cc_shared_library/test_cc_shared_library/BUILD.builtin_test

Lines changed: 40 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,9 @@ load(
77
"linking_suffix_test",
88
"paths_test",
99
"runfiles_test",
10-
"check_already_linked_inputs_are_not_passed_to_linking_action_test",
10+
"check_linking_action_lib_parameters_test",
11+
"forwarding_cc_lib",
12+
"nocode_cc_lib",
1113
)
1214

1315
LINKABLE_MORE_THAN_ONCE = "LINKABLE_MORE_THAN_ONCE"
@@ -117,6 +119,8 @@ cc_shared_library(
117119
deps = [
118120
"baz",
119121
"foo",
122+
"cc_lib_with_no_srcs",
123+
"nocode_cc_lib",
120124
"a_suffix",
121125
],
122126
user_link_flags = select({
@@ -156,6 +160,33 @@ cc_library(
156160
}),
157161
)
158162

163+
forwarding_cc_lib(
164+
name = "cc_lib_with_no_srcs",
165+
deps = [
166+
"indirect_dep",
167+
],
168+
)
169+
170+
nocode_cc_lib(
171+
name = "nocode_cc_lib",
172+
additional_inputs = [
173+
":additional_script.txt",
174+
],
175+
deps = [
176+
"indirect_dep2",
177+
],
178+
)
179+
180+
cc_library(
181+
name = "indirect_dep",
182+
srcs = ["indirect_dep.cc"],
183+
)
184+
185+
cc_library(
186+
name = "indirect_dep2",
187+
srcs = ["indirect_dep2.cc"],
188+
)
189+
159190
cc_library(
160191
name = "a_suffix",
161192
srcs = ["a_suffix.cc"],
@@ -420,13 +451,13 @@ runfiles_test(
420451
target_under_test = ":python_test",
421452
)
422453

423-
check_already_linked_inputs_are_not_passed_to_linking_action_test(
454+
check_linking_action_lib_parameters_test(
424455
name = "check_binary_doesnt_take_already_linked_in_libs",
425456
target_under_test = ":binary",
426457
libs_that_shouldnt_be_present = ["foo", "bar"],
427458
)
428459

429-
check_already_linked_inputs_are_not_passed_to_linking_action_test(
460+
check_linking_action_lib_parameters_test(
430461
name = "check_shared_lib_doesnt_take_already_linked_in_libs",
431462
target_under_test = ":foo_so",
432463
libs_that_shouldnt_be_present = ["bar"],
@@ -437,3 +468,9 @@ build_failure_test(
437468
message = "'cc_shared_library' must have at least one dependency in 'deps' (or 'roots')",
438469
target_under_test = "//src/main/starlark/tests/builtins_bzl/cc/cc_shared_library/test_cc_shared_library/failing_targets:failing_with_no_deps_so",
439470
)
471+
472+
build_failure_test(
473+
name = "direct_dep_with_only_shared_lib_file",
474+
message = "Do not place libraries which only contain a precompiled dynamic library",
475+
target_under_test = "//src/main/starlark/tests/builtins_bzl/cc/cc_shared_library/test_cc_shared_library/failing_targets:failing_only_dynamic_lib",
476+
)

src/main/starlark/tests/builtins_bzl/cc/cc_shared_library/test_cc_shared_library/cc_shared_library_integration_test.sh

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -38,14 +38,15 @@ function check_symbol_absent() {
3838

3939
function test_shared_library_symbols() {
4040
foo_so=$(find . -name libfoo_so.so)
41-
symbols=$(nm -D $foo_so)
41+
symbols=$(nm $foo_so)
4242
check_symbol_present "$symbols" "U _Z3barv"
4343
check_symbol_present "$symbols" "T _Z3bazv"
4444
check_symbol_present "$symbols" "T _Z3foov"
45-
46-
check_symbol_absent "$symbols" "_Z3quxv"
45+
check_symbol_present "$symbols" "T _Z3foov"
46+
check_symbol_present "$symbols" "t _Z3quxv"
47+
check_symbol_present "$symbols" "t _Z12indirect_depv"
48+
check_symbol_present "$symbols" "t _Z13indirect_dep2v"
4749
check_symbol_absent "$symbols" "_Z4bar3v"
48-
check_symbol_absent "$symbols" "_Z4bar4v"
4950
}
5051

5152
function test_shared_library_user_link_flags() {

src/main/starlark/tests/builtins_bzl/cc/cc_shared_library/test_cc_shared_library/failing_targets/BUILD.builtin_test

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,3 +89,27 @@ cc_shared_library(
8989
name = "failing_with_no_deps_so",
9090
tags = TAGS,
9191
)
92+
93+
94+
cc_shared_library(
95+
name = "failing_only_dynamic_lib",
96+
deps = [
97+
"dynamic_only",
98+
],
99+
tags = TAGS,
100+
)
101+
102+
cc_library(
103+
name = "dynamic_only",
104+
srcs = [
105+
"libabc.so",
106+
],
107+
)
108+
109+
genrule(
110+
name = "abc_lib",
111+
srcs = [],
112+
outs = ["libabc.so"],
113+
cmd = "touch \"$@\"",
114+
)
115+
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
// Copyright 2023 The Bazel Authors. All rights reserved.
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
int indirect_dep() { return 0; }
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
// Copyright 2023 The Bazel Authors. All rights reserved.
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
int indirect_dep2() { return 0; }

src/main/starlark/tests/builtins_bzl/cc/cc_shared_library/test_cc_shared_library/starlark_tests.bzl

Lines changed: 31 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -223,7 +223,7 @@ interface_library_output_group_test = analysistest.make(
223223
},
224224
)
225225

226-
def _check_already_linked_inputs_are_not_passed_to_linking_action_test_impl(ctx):
226+
def _check_linking_action_lib_parameters_test_impl(ctx):
227227
env = analysistest.begin(ctx)
228228

229229
actions = analysistest.target_actions(env)
@@ -240,9 +240,37 @@ def _check_already_linked_inputs_are_not_passed_to_linking_action_test_impl(ctx)
240240

241241
return analysistest.end(env)
242242

243-
check_already_linked_inputs_are_not_passed_to_linking_action_test = analysistest.make(
244-
_check_already_linked_inputs_are_not_passed_to_linking_action_test_impl,
243+
check_linking_action_lib_parameters_test = analysistest.make(
244+
_check_linking_action_lib_parameters_test_impl,
245245
attrs = {
246246
"libs_that_shouldnt_be_present": attr.string_list(),
247247
},
248248
)
249+
250+
def _forwarding_cc_lib_impl(ctx):
251+
return [ctx.attr.deps[0][CcInfo]]
252+
253+
forwarding_cc_lib = rule(
254+
implementation = _forwarding_cc_lib_impl,
255+
attrs = {
256+
"deps": attr.label_list(),
257+
},
258+
provides = [CcInfo],
259+
)
260+
261+
def _nocode_cc_lib_impl(ctx):
262+
linker_input = cc_common.create_linker_input(
263+
owner = ctx.label,
264+
additional_inputs = depset([ctx.files.additional_inputs[0]]),
265+
)
266+
cc_info = CcInfo(linking_context = cc_common.create_linking_context(linker_inputs = depset([linker_input])))
267+
return [cc_common.merge_cc_infos(cc_infos = [cc_info, ctx.attr.deps[0][CcInfo]])]
268+
269+
nocode_cc_lib = rule(
270+
implementation = _nocode_cc_lib_impl,
271+
attrs = {
272+
"additional_inputs": attr.label_list(allow_files = True),
273+
"deps": attr.label_list(),
274+
},
275+
provides = [CcInfo],
276+
)

0 commit comments

Comments
 (0)