blob: 5c66ad0cf4eff2c3faf4b2a092f4b7de856dc466 [file] [log] [blame]
# Copyright 2021 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.
"""Starlark tests for cc_shared_library"""
load("@bazel_skylib//lib:unittest.bzl", "analysistest", "asserts", "unittest")
def _same_package_or_above(label_a, label_b):
if label_a.workspace_name != label_b.workspace_name:
return False
package_a_tokenized = label_a.package.split("/")
package_b_tokenized = label_b.package.split("/")
if len(package_b_tokenized) < len(package_a_tokenized):
return False
if package_a_tokenized[0] != "":
for i in range(len(package_a_tokenized)):
if package_a_tokenized[i] != package_b_tokenized[i]:
return False
return True
def _check_if_target_under_path(value, pattern):
if pattern.workspace_name != value.workspace_name:
return False
if pattern.name == "__pkg__":
return pattern.package == value.package
if pattern.name == "__subpackages__":
return _same_package_or_above(pattern, value)
return pattern.package == value.package and pattern.name == value.name
def _linking_order_test_impl(ctx):
env = analysistest.begin(ctx)
def _filename(path):
filename = None
for i in range(len(path) - 1, -1, -1):
if path[i] == "/":
filename = path[i + 1:]
break
asserts.true(env, filename != None, "the test assumes the argument is a file path")
return filename
if ctx.attr.is_linux:
target_under_test = analysistest.target_under_test(env)
actions = analysistest.target_actions(env)
target_action = None
for action in actions:
if action.mnemonic == "FileWrite":
target_action = action
break
args = target_action.content.split("\n")
user_libs = []
for arg in args:
if arg.endswith(".o"):
user_libs.append(_filename(arg))
asserts.true(env, user_libs.index("foo.pic.o") < user_libs.index("baz.pic.o"), "'foo' should appear before 'bar' in command line")
asserts.true(env, user_libs[-1] == "a_suffix.pic.o", "liba_suffix.pic.o should be the last user library linked")
return analysistest.end(env)
linking_order_test = analysistest.make(
_linking_order_test_impl,
attrs = {
"is_linux": attr.bool(),
},
)
def _additional_inputs_test_impl(ctx):
env = analysistest.begin(ctx)
if ctx.attr.is_linux:
target_under_test = analysistest.target_under_test(env)
actions = analysistest.target_actions(env)
found = False
target_action = None
for action in actions:
if action.mnemonic == "CppLink":
target_action = action
break
for arg in target_action.argv:
if arg.find("-Wl,--script=") != -1:
asserts.equals(env, "src/main/starlark/tests/builtins_bzl/cc/cc_shared_library/test_cc_shared_library/additional_script.txt", arg[13:])
found = True
break
asserts.true(env, found, "Should have seen option --script=")
return analysistest.end(env)
additional_inputs_test = analysistest.make(
_additional_inputs_test_impl,
attrs = {
"is_linux": attr.bool(),
},
)
def _build_failure_test_impl(ctx):
env = analysistest.begin(ctx)
if ctx.attr.message:
asserts.expect_failure(env, ctx.attr.message)
if ctx.attr.messages:
for message in ctx.attr.messages:
asserts.expect_failure(env, message)
return analysistest.end(env)
build_failure_test = analysistest.make(
_build_failure_test_impl,
expect_failure = True,
attrs = {
"message": attr.string(),
"messages": attr.string_list(),
},
)
def _paths_test_impl(ctx):
env = unittest.begin(ctx)
asserts.false(env, _check_if_target_under_path(Label("//foo"), Label("//bar")))
asserts.false(env, _check_if_target_under_path(Label("@foo//foo"), Label("@bar//bar")))
asserts.false(env, _check_if_target_under_path(Label("//bar"), Label("@foo//bar")))
asserts.true(env, _check_if_target_under_path(Label("@foo//bar"), Label("@foo//bar")))
asserts.true(env, _check_if_target_under_path(Label("@foo//bar:bar"), Label("@foo//bar")))
asserts.true(env, _check_if_target_under_path(Label("//bar:bar"), Label("//bar")))
asserts.false(env, _check_if_target_under_path(Label("@foo//bar/baz"), Label("@foo//bar")))
asserts.false(env, _check_if_target_under_path(Label("@foo//bar/baz"), Label("@foo//bar:__pkg__")))
asserts.true(env, _check_if_target_under_path(Label("@foo//bar/baz"), Label("@foo//bar:__subpackages__")))
asserts.true(env, _check_if_target_under_path(Label("@foo//bar:qux"), Label("@foo//bar:__pkg__")))
asserts.false(env, _check_if_target_under_path(Label("@foo//bar"), Label("@foo//bar/baz:__subpackages__")))
asserts.false(env, _check_if_target_under_path(Label("//bar"), Label("//bar/baz:__pkg__")))
asserts.true(env, _check_if_target_under_path(Label("//foo/bar:baz"), Label("//:__subpackages__")))
return unittest.end(env)
paths_test = unittest.make(_paths_test_impl)
def _runfiles_test_impl(ctx):
env = analysistest.begin(ctx)
if not ctx.attr.is_linux:
return analysistest.end(env)
target_under_test = analysistest.target_under_test(env)
expected_suffixes = [
"libfoo_so.so",
"libbar_so.so",
"libdiff_pkg_so.so",
"libprivate_lib_so.so",
"renamed_so_file_2.so",
"Smain_Sstarlark_Stests_Sbuiltins_Ubzl_Scc_Scc_Ushared_Ulibrary_Stest_Ucc_Ushared_Ulibrary_Slibfoo_Uso.so",
"Smain_Sstarlark_Stests_Sbuiltins_Ubzl_Scc_Scc_Ushared_Ulibrary_Stest_Ucc_Ushared_Ulibrary_Slibbar_Uso.so",
"Smain_Sstarlark_Stests_Sbuiltins_Ubzl_Scc_Scc_Ushared_Ulibrary_Stest_Ucc_Ushared_Ulibrary3_Slibdiff_Upkg_Uso.so",
"Smain_Sstarlark_Stests_Sbuiltins_Ubzl_Scc_Scc_Ushared_Ulibrary_Stest_Ucc_Ushared_Ulibrary/renamed_so_file_copy.so",
"Smain_Sstarlark_Stests_Sbuiltins_Ubzl_Scc_Scc_Ushared_Ulibrary_Stest_Ucc_Ushared_Ulibrary/libdirect_so_file.so",
]
for runfile in target_under_test[DefaultInfo].default_runfiles.files.to_list():
# Ignore Python runfiles
if "python" in runfile.path:
continue
found_suffix = False
for expected_suffix in expected_suffixes:
if runfile.path.endswith(expected_suffix):
found_suffix = True
break
asserts.true(env, found_suffix, runfile.path + " not found in expected suffixes:\n" + "\n".join(expected_suffixes))
return analysistest.end(env)
runfiles_test = analysistest.make(
_runfiles_test_impl,
attrs = {
"is_linux": attr.bool(),
},
)
def _interface_library_output_group_test_impl(ctx):
env = analysistest.begin(ctx)
if not ctx.attr.is_windows:
return analysistest.end(env)
target_under_test = analysistest.target_under_test(env)
actual_files = []
for interface_library in target_under_test[OutputGroupInfo].interface_library.to_list():
actual_files.append(interface_library.basename)
expected_files = ["foo_so.if.lib"]
asserts.equals(env, expected_files, actual_files)
return analysistest.end(env)
interface_library_output_group_test = analysistest.make(
_interface_library_output_group_test_impl,
attrs = {
"is_windows": attr.bool(),
},
)
def _check_linking_action_lib_parameters_test_impl(ctx):
env = analysistest.begin(ctx)
actions = analysistest.target_actions(env)
target_action = None
for action in actions:
if action.mnemonic == "FileWrite":
target_action = action
break
args = target_action.content.split("\n")
for arg in args:
for bad_lib_entry in ctx.attr.libs_that_shouldnt_be_present:
asserts.true(env, arg.find("{}.".format(bad_lib_entry)) == -1, "Should not have seen library `{}` in command line".format(arg))
return analysistest.end(env)
check_linking_action_lib_parameters_test = analysistest.make(
_check_linking_action_lib_parameters_test_impl,
attrs = {
"libs_that_shouldnt_be_present": attr.string_list(),
},
)
AspectCcInfo = provider("Takes a cc_info.", fields = {"cc_info": "cc_info"})
WrappedCcInfo = provider("Takes a cc_info.", fields = {"cc_info": "cc_info"})
def _forwarding_cc_lib_aspect_impl(target, ctx):
cc_info = target[WrappedCcInfo].cc_info
linker_inputs = []
owner = ctx.label.relative(ctx.label.name + ".custom")
for linker_input in cc_info.linking_context.linker_inputs.to_list():
if linker_input.owner == ctx.label.relative("indirect_dep"):
linker_inputs.append(cc_common.create_linker_input(
owner = owner,
libraries = depset(linker_input.libraries),
))
else:
linker_inputs.append(linker_input)
cc_info = CcInfo(
compilation_context = cc_info.compilation_context,
linking_context = cc_common.create_linking_context(
linker_inputs = depset(linker_inputs),
),
)
return [
AspectCcInfo(cc_info = cc_info),
cc_common.CcSharedLibraryHintInfo_6_X_constructor_do_not_use(
owners = [owner],
),
]
forwarding_cc_lib_aspect = aspect(
implementation = _forwarding_cc_lib_aspect_impl,
required_providers = [WrappedCcInfo],
provides = [AspectCcInfo, cc_common.CcSharedLibraryHintInfo_6_X_getter_do_not_use],
)
def _wrapped_cc_lib_impl(ctx):
return [WrappedCcInfo(cc_info = ctx.attr.deps[0][CcInfo]), CcInfo()]
wrapped_cc_lib = rule(
implementation = _wrapped_cc_lib_impl,
attrs = {
"deps": attr.label_list(providers = [CcInfo]),
},
provides = [WrappedCcInfo, CcInfo],
)
def _forwarding_cc_lib_impl(ctx):
hints = cc_common.CcSharedLibraryHintInfo_6_X_constructor_do_not_use(attributes = ["deps"])
if ctx.attr.deps:
return [ctx.attr.deps[0][AspectCcInfo].cc_info, hints]
else:
return [ctx.attr.do_not_follow_deps[0][AspectCcInfo].cc_info, hints]
forwarding_cc_lib = rule(
implementation = _forwarding_cc_lib_impl,
attrs = {
"deps": attr.label_list(providers = [WrappedCcInfo], aspects = [forwarding_cc_lib_aspect]),
"do_not_follow_deps": attr.label_list(providers = [WrappedCcInfo], aspects = [forwarding_cc_lib_aspect]),
},
provides = [CcInfo, cc_common.CcSharedLibraryHintInfo_6_X_getter_do_not_use],
)
def _nocode_cc_lib_impl(ctx):
linker_input = cc_common.create_linker_input(
owner = ctx.label,
additional_inputs = depset([ctx.files.additional_inputs[0]]),
)
cc_info = CcInfo(linking_context = cc_common.create_linking_context(linker_inputs = depset([linker_input])))
return [cc_common.merge_cc_infos(cc_infos = [cc_info, ctx.attr.deps[0][CcInfo]])]
nocode_cc_lib = rule(
implementation = _nocode_cc_lib_impl,
attrs = {
"additional_inputs": attr.label_list(allow_files = True),
"deps": attr.label_list(),
},
provides = [CcInfo],
)