| // Copyright 2015 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. |
| // Copyright 2006 Google Inc. All rights reserved. |
| |
| package com.google.devtools.build.lib.rules.cpp; |
| |
| import static com.google.common.truth.Truth.assertThat; |
| |
| import com.google.common.collect.ImmutableList; |
| import com.google.devtools.build.lib.actions.Artifact; |
| import com.google.devtools.build.lib.analysis.ConfiguredTarget; |
| import com.google.devtools.build.lib.analysis.util.AnalysisMock; |
| import com.google.devtools.build.lib.analysis.util.BuildViewTestCase; |
| import com.google.devtools.build.lib.collect.nestedset.NestedSet; |
| import com.google.devtools.build.lib.packages.util.Crosstool.CcToolchainConfig; |
| import java.util.List; |
| import org.junit.Before; |
| import org.junit.Test; |
| import org.junit.runner.RunWith; |
| import org.junit.runners.JUnit4; |
| |
| /** "White-box" unit test of cc_import rule. */ |
| @RunWith(JUnit4.class) |
| public abstract class CcImportBaseConfiguredTargetTest extends BuildViewTestCase { |
| protected String starlarkImplementationLoadStatement = ""; |
| |
| @Before |
| public void setStarlarkImplementationLoadStatement() throws Exception { |
| invalidatePackages(); |
| setIsStarlarkImplementation(); |
| } |
| |
| protected abstract void setIsStarlarkImplementation(); |
| |
| @Test |
| public void testCcImportRule() throws Exception { |
| scratch.file( |
| "third_party/BUILD", |
| starlarkImplementationLoadStatement, |
| "cc_import(", |
| " name = 'a_import',", |
| " static_library = 'A.a',", |
| " shared_library = 'A.so',", |
| " interface_library = 'A.ifso',", |
| " hdrs = ['a.h'],", |
| " alwayslink = 1,", |
| " system_provided = 0,", |
| ")"); |
| getConfiguredTarget("//third_party:a_import"); |
| } |
| |
| @Test |
| public void testWrongCcImportDefinitions() throws Exception { |
| checkError( |
| "a", |
| "foo", |
| "does not produce any cc_import static_library files " + "(expected", |
| starlarkImplementationLoadStatement, |
| "cc_import(", |
| " name = 'foo',", |
| " static_library = 'libfoo.so',", |
| ")"); |
| checkError( |
| "b", |
| "foo", |
| "does not produce any cc_import shared_library files (expected", |
| starlarkImplementationLoadStatement, |
| "cc_import(", |
| " name = 'foo',", |
| " shared_library = 'libfoo.a',", |
| ")"); |
| checkError( |
| "c", |
| "foo", |
| "does not produce any cc_import interface_library files " + "(expected", |
| starlarkImplementationLoadStatement, |
| "cc_import(", |
| " name = 'foo',", |
| " shared_library = 'libfoo.dll',", |
| " interface_library = 'libfoo.a',", |
| ")"); |
| checkError( |
| "d", |
| "foo", |
| "'shared_library' shouldn't be specified when 'system_provided' is true", |
| starlarkImplementationLoadStatement, |
| "cc_import(", |
| " name = 'foo',", |
| " shared_library = 'libfoo.so',", |
| " system_provided = 1,", |
| ")"); |
| checkError( |
| "e", |
| "foo", |
| "'shared_library' should be specified when 'system_provided' is false", |
| starlarkImplementationLoadStatement, |
| "cc_import(", |
| " name = 'foo',", |
| " interface_library = 'libfoo.ifso',", |
| " system_provided = 0,", |
| ")"); |
| } |
| |
| @Test |
| public void testRuntimeOnlyCcImportDefinitionsOnWindows() throws Exception { |
| AnalysisMock.get() |
| .ccSupport() |
| .setupCcToolchainConfig( |
| mockToolsConfig, |
| CcToolchainConfig.builder() |
| .withFeatures( |
| CppRuleClasses.COPY_DYNAMIC_LIBRARIES_TO_BINARY, |
| CppRuleClasses.TARGETS_WINDOWS)); |
| useConfiguration(); |
| ConfiguredTarget target = |
| scratchConfiguredTarget( |
| "a", |
| "foo", |
| starlarkImplementationLoadStatement, |
| "cc_import(name = 'foo', shared_library = 'libfoo.dll')"); |
| Artifact dynamicLibrary = |
| target |
| .get(CcInfo.PROVIDER) |
| .getCcLinkingContext() |
| .getLibraries() |
| .getSingleton() |
| .getResolvedSymlinkDynamicLibrary(); |
| Iterable<Artifact> dynamicLibrariesForRuntime = |
| target |
| .get(CcInfo.PROVIDER) |
| .getCcLinkingContext() |
| .getDynamicLibrariesForRuntime(/* linkingStatically= */ false); |
| assertThat(dynamicLibrary).isEqualTo(null); |
| assertThat(artifactsToStrings(dynamicLibrariesForRuntime)).containsExactly("src a/libfoo.dll"); |
| } |
| |
| @Test |
| public void testCcImportWithStaticLibrary() throws Exception { |
| ConfiguredTarget target = |
| scratchConfiguredTarget( |
| "a", |
| "foo", |
| starlarkImplementationLoadStatement, |
| "cc_import(name = 'foo', static_library = 'libfoo.a')"); |
| Artifact library = |
| target |
| .get(CcInfo.PROVIDER) |
| .getCcLinkingContext() |
| .getLibraries() |
| .getSingleton() |
| .getStaticLibrary(); |
| assertThat(artifactsToStrings(ImmutableList.of(library))).containsExactly("src a/libfoo.a"); |
| } |
| |
| @Test |
| public void testCcImportWithSharedLibrary() throws Exception { |
| useConfiguration("--cpu=k8", "--noincompatible_enable_cc_toolchain_resolution"); |
| ConfiguredTarget target = |
| scratchConfiguredTarget( |
| "a", |
| "foo", |
| starlarkImplementationLoadStatement, |
| "cc_import(name = 'foo', shared_library = 'libfoo.so')"); |
| Artifact dynamicLibrary = |
| target |
| .get(CcInfo.PROVIDER) |
| .getCcLinkingContext() |
| .getLibraries() |
| .getSingleton() |
| .getResolvedSymlinkDynamicLibrary(); |
| Iterable<Artifact> dynamicLibrariesForRuntime = |
| target |
| .get(CcInfo.PROVIDER) |
| .getCcLinkingContext() |
| .getDynamicLibrariesForRuntime(/* linkingStatically= */ false); |
| assertThat(artifactsToStrings(ImmutableList.of(dynamicLibrary))) |
| .containsExactly("src a/libfoo.so"); |
| assertThat(artifactsToStrings(dynamicLibrariesForRuntime)) |
| .containsExactly("bin _solib_k8/_U_S_Sa_Cfoo___Ua/libfoo.so"); |
| } |
| |
| @Test |
| public void testCcImportWithVersionedSharedLibrary() throws Exception { |
| useConfiguration("--cpu=k8", "--noincompatible_enable_cc_toolchain_resolution"); |
| ConfiguredTarget target = |
| scratchConfiguredTarget( |
| "a", |
| "foo", |
| starlarkImplementationLoadStatement, |
| "cc_import(name = 'foo', shared_library = 'libfoo.so.1ab2.1_a2')"); |
| Artifact dynamicLibrary = |
| target |
| .get(CcInfo.PROVIDER) |
| .getCcLinkingContext() |
| .getLibraries() |
| .getSingleton() |
| .getResolvedSymlinkDynamicLibrary(); |
| Iterable<Artifact> dynamicLibrariesForRuntime = |
| target |
| .get(CcInfo.PROVIDER) |
| .getCcLinkingContext() |
| .getDynamicLibrariesForRuntime(/* linkingStatically= */ false); |
| assertThat(artifactsToStrings(ImmutableList.of(dynamicLibrary))) |
| .containsExactly("src a/libfoo.so.1ab2.1_a2"); |
| assertThat(artifactsToStrings(dynamicLibrariesForRuntime)) |
| .containsExactly("bin _solib_k8/_U_S_Sa_Cfoo___Ua/libfoo.so.1ab2.1_a2"); |
| } |
| |
| @Test |
| public void testCcImportWithVersionedSharedLibraryWithDotInTheName() throws Exception { |
| useConfiguration("--cpu=k8", "--noincompatible_enable_cc_toolchain_resolution"); |
| |
| ConfiguredTarget target = |
| scratchConfiguredTarget( |
| "a", |
| "foo", |
| starlarkImplementationLoadStatement, |
| "cc_import(name = 'foo', shared_library = 'libfoo.qux.so.1ab2.1_a2')"); |
| |
| Artifact dynamicLibrary = |
| target |
| .get(CcInfo.PROVIDER) |
| .getCcLinkingContext() |
| .getLibraries() |
| .getSingleton() |
| .getResolvedSymlinkDynamicLibrary(); |
| Iterable<Artifact> dynamicLibrariesForRuntime = |
| target |
| .get(CcInfo.PROVIDER) |
| .getCcLinkingContext() |
| .getDynamicLibrariesForRuntime(/* linkingStatically= */ false); |
| assertThat(artifactsToStrings(ImmutableList.of(dynamicLibrary))) |
| .containsExactly("src a/libfoo.qux.so.1ab2.1_a2"); |
| assertThat(artifactsToStrings(dynamicLibrariesForRuntime)) |
| .containsExactly("bin _solib_k8/_U_S_Sa_Cfoo___Ua/libfoo.qux.so.1ab2.1_a2"); |
| } |
| |
| @Test |
| public void testCcImportWithInvalidVersionedSharedLibrary() throws Exception { |
| checkError( |
| "a", |
| "foo", |
| "does not produce any cc_import shared_library files " + "(expected", |
| starlarkImplementationLoadStatement, |
| "cc_import(", |
| " name = 'foo',", |
| " shared_library = 'libfoo.so.1ab2.ab',", |
| ")"); |
| } |
| |
| @Test |
| public void testCcImportWithInvalidSharedLibraryNoExtension() throws Exception { |
| checkError( |
| "a", |
| "foo", |
| "does not produce any cc_import shared_library files " + "(expected", |
| starlarkImplementationLoadStatement, |
| "cc_import(", |
| " name = 'foo',", |
| " shared_library = 'libfoo',", |
| ")"); |
| } |
| |
| @Test |
| public void testCcImportWithInterfaceSharedLibrary() throws Exception { |
| useConfiguration("--cpu=k8", "--noincompatible_enable_cc_toolchain_resolution"); |
| ConfiguredTarget target = |
| scratchConfiguredTarget( |
| "b", |
| "foo", |
| starlarkImplementationLoadStatement, |
| "cc_import(name = 'foo', shared_library = 'libfoo.so'," |
| + " interface_library = 'libfoo.ifso')"); |
| ; |
| Artifact library = |
| target |
| .get(CcInfo.PROVIDER) |
| .getCcLinkingContext() |
| .getLibraries() |
| .getSingleton() |
| .getResolvedSymlinkInterfaceLibrary(); |
| assertThat(artifactsToStrings(ImmutableList.of(library))).containsExactly("src b/libfoo.ifso"); |
| Iterable<Artifact> dynamicLibrariesForRuntime = |
| target |
| .get(CcInfo.PROVIDER) |
| .getCcLinkingContext() |
| .getDynamicLibrariesForRuntime(/* linkingStatically= */ false); |
| assertThat(artifactsToStrings(dynamicLibrariesForRuntime)) |
| .containsExactly("bin _solib_k8/_U_S_Sb_Cfoo___Ub/libfoo.so"); |
| } |
| |
| @Test |
| public void testCcImportWithBothStaticAndSharedLibraries() throws Exception { |
| useConfiguration("--cpu=k8", "--noincompatible_enable_cc_toolchain_resolution"); |
| ConfiguredTarget target = |
| scratchConfiguredTarget( |
| "a", |
| "foo", |
| starlarkImplementationLoadStatement, |
| "cc_import(name = 'foo', static_library = 'libfoo.a', shared_library = 'libfoo.so')"); |
| |
| Artifact library = |
| target |
| .get(CcInfo.PROVIDER) |
| .getCcLinkingContext() |
| .getLibraries() |
| .getSingleton() |
| .getStaticLibrary(); |
| assertThat(artifactsToStrings(ImmutableList.of(library))).containsExactly("src a/libfoo.a"); |
| |
| Artifact dynamicLibrary = |
| target |
| .get(CcInfo.PROVIDER) |
| .getCcLinkingContext() |
| .getLibraries() |
| .getSingleton() |
| .getResolvedSymlinkDynamicLibrary(); |
| Iterable<Artifact> dynamicLibrariesForRuntime = |
| target |
| .get(CcInfo.PROVIDER) |
| .getCcLinkingContext() |
| .getDynamicLibrariesForRuntime(/* linkingStatically= */ false); |
| assertThat(artifactsToStrings(ImmutableList.of(dynamicLibrary))) |
| .containsExactly("src a/libfoo.so"); |
| assertThat(artifactsToStrings(dynamicLibrariesForRuntime)) |
| .containsExactly("bin _solib_k8/_U_S_Sa_Cfoo___Ua/libfoo.so"); |
| } |
| |
| @Test |
| public void testCcImportWithAlwaysLinkStaticLibrary() throws Exception { |
| ConfiguredTarget target = |
| scratchConfiguredTarget( |
| "a", |
| "foo", |
| starlarkImplementationLoadStatement, |
| "cc_import(name = 'foo', static_library = 'libfoo.a', alwayslink = 1)"); |
| boolean alwayslink = |
| target |
| .get(CcInfo.PROVIDER) |
| .getCcLinkingContext() |
| .getLibraries() |
| .getSingleton() |
| .getAlwayslink(); |
| assertThat(alwayslink).isTrue(); |
| } |
| |
| @Test |
| public void testCcImportSystemProvidedIsTrue() throws Exception { |
| ConfiguredTarget target = |
| scratchConfiguredTarget( |
| "a", |
| "foo", |
| starlarkImplementationLoadStatement, |
| "cc_import(name = 'foo', interface_library = 'libfoo.ifso', system_provided = 1)"); |
| Artifact library = |
| target |
| .get(CcInfo.PROVIDER) |
| .getCcLinkingContext() |
| .getLibraries() |
| .getSingleton() |
| .getResolvedSymlinkInterfaceLibrary(); |
| assertThat(artifactsToStrings(ImmutableList.of(library))).containsExactly("src a/libfoo.ifso"); |
| Iterable<Artifact> dynamicLibrariesForRuntime = |
| target |
| .get(CcInfo.PROVIDER) |
| .getCcLinkingContext() |
| .getDynamicLibrariesForRuntime(/* linkingStatically= */ false); |
| assertThat(artifactsToStrings(dynamicLibrariesForRuntime)).isEmpty(); |
| } |
| |
| @Test |
| public void testCcImportProvideHeaderFiles() throws Exception { |
| NestedSet<Artifact> headers = |
| scratchConfiguredTarget( |
| "a", |
| "foo", |
| starlarkImplementationLoadStatement, |
| "cc_import(name = 'foo', static_library = 'libfoo.a', hdrs = ['foo.h'])") |
| .get(CcInfo.PROVIDER) |
| .getCcCompilationContext() |
| .getDeclaredIncludeSrcs(); |
| assertThat(artifactsToStrings(headers)).containsExactly("src a/foo.h"); |
| } |
| |
| @Test |
| public void testCcImportLoadedThroughMacro() throws Exception { |
| setupTestCcImportLoadedThroughMacro(/* loadMacro= */ true); |
| assertThat(getConfiguredTarget("//a:a")).isNotNull(); |
| assertNoEvents(); |
| } |
| |
| private void setupTestCcImportLoadedThroughMacro(boolean loadMacro) throws Exception { |
| scratch.file( |
| "a/BUILD", |
| getAnalysisMock().ccSupport().getMacroLoadStatement(loadMacro, "cc_import"), |
| "cc_import(name='a', static_library='a.a')"); |
| } |
| |
| @Test |
| public void testCcImportWithSharedLibraryAddsRpathEntry() throws Exception { |
| AnalysisMock.get() |
| .ccSupport() |
| .setupCcToolchainConfig( |
| mockToolsConfig, |
| CcToolchainConfig.builder().withFeatures(CppRuleClasses.SUPPORTS_DYNAMIC_LINKER)); |
| useConfiguration("--cpu=k8"); |
| ConfiguredTarget target = |
| scratchConfiguredTarget( |
| "a", |
| "foo", |
| starlarkImplementationLoadStatement, |
| "cc_import(name = 'foo', shared_library = 'libfoo.so')"); |
| scratch.file("bin/BUILD", "cc_binary(name='bin', deps=['//a:foo'])"); |
| |
| Artifact dynamicLibrary = |
| target |
| .get(CcInfo.PROVIDER) |
| .getCcLinkingContext() |
| .getLibraries() |
| .getSingleton() |
| .getResolvedSymlinkDynamicLibrary(); |
| Iterable<Artifact> dynamicLibrariesForRuntime = |
| target |
| .get(CcInfo.PROVIDER) |
| .getCcLinkingContext() |
| .getDynamicLibrariesForRuntime(/* linkingStatically= */ false); |
| assertThat(artifactsToStrings(ImmutableList.of(dynamicLibrary))) |
| .containsExactly("src a/libfoo.so"); |
| assertThat(artifactsToStrings(dynamicLibrariesForRuntime)) |
| .containsExactly("bin _solib_k8/_U_S_Sa_Cfoo___Ua/libfoo.so"); |
| |
| ConfiguredTarget main = getConfiguredTarget("//bin:bin"); |
| Artifact mainBin = getBinArtifact("bin", main); |
| CppLinkAction action = (CppLinkAction) getGeneratingAction(mainBin); |
| List<String> linkArgv = action.getLinkCommandLineForTesting().arguments(); |
| assertThat(linkArgv) |
| .containsAtLeast("-Xlinker", "-rpath", "-Xlinker", "$ORIGIN/../_solib_k8/_U_S_Sa_Cfoo___Ua") |
| .inOrder(); |
| } |
| |
| @Test |
| public void testCcImportWithSharedLibraryWithTransitionAddsRpathEntry() throws Exception { |
| AnalysisMock.get() |
| .ccSupport() |
| .setupCcToolchainConfig( |
| mockToolsConfig, |
| CcToolchainConfig.builder().withFeatures(CppRuleClasses.SUPPORTS_DYNAMIC_LINKER)); |
| useConfiguration("--cpu=k8"); |
| ConfiguredTarget target = |
| scratchConfiguredTarget( |
| "a", |
| "foo", |
| starlarkImplementationLoadStatement, |
| "cc_import(name = 'foo', shared_library = 'libfoo.so')"); |
| |
| scratch.file( |
| "bin/custom_transition.bzl", |
| "def _custom_transition_impl(settings, attr):", |
| " _ignore = settings, attr", |
| "", |
| " return {'//command_line_option:copt': ['-DFLAG']}", |
| "", |
| "custom_transition = transition(", |
| " implementation = _custom_transition_impl,", |
| " inputs = [],", |
| " outputs = ['//command_line_option:copt'],", |
| ")", |
| "", |
| "def _apply_custom_transition_impl(ctx):", |
| " cc_infos = []", |
| " for dep in ctx.attr.deps:", |
| " cc_infos.append(dep[CcInfo])", |
| " merged_cc_info = cc_common.merge_cc_infos(cc_infos = cc_infos)", |
| " return merged_cc_info", |
| "", |
| "apply_custom_transition = rule(", |
| " implementation = _apply_custom_transition_impl,", |
| " attrs = {", |
| " '_whitelist_function_transition': attr.label(", |
| " default = '//tools/allowlists/function_transition_allowlist',", |
| " ),", |
| " 'deps': attr.label_list(cfg = custom_transition),", |
| " },", |
| ")"); |
| scratch.overwriteFile( |
| "tools/allowlists/function_transition_allowlist/BUILD", |
| "package_group(", |
| " name = 'function_transition_allowlist',", |
| " packages = ['//...'],", |
| ")"); |
| scratch.file( |
| "bin/BUILD", |
| "load(':custom_transition.bzl', 'apply_custom_transition')", |
| "cc_library(name='lib', deps=['//a:foo'])", |
| "apply_custom_transition(name='transitioned_lib', deps=[':lib'])", |
| "cc_binary(name='bin', deps=[':transitioned_lib'])"); |
| |
| Artifact dynamicLibrary = |
| target |
| .get(CcInfo.PROVIDER) |
| .getCcLinkingContext() |
| .getLibraries() |
| .getSingleton() |
| .getResolvedSymlinkDynamicLibrary(); |
| Iterable<Artifact> dynamicLibrariesForRuntime = |
| target |
| .get(CcInfo.PROVIDER) |
| .getCcLinkingContext() |
| .getDynamicLibrariesForRuntime(/* linkingStatically= */ false); |
| assertThat(artifactsToStrings(ImmutableList.of(dynamicLibrary))) |
| .containsExactly("src a/libfoo.so"); |
| assertThat(artifactsToStrings(dynamicLibrariesForRuntime)) |
| .containsExactly("bin _solib_k8/_U_S_Sa_Cfoo___Ua/libfoo.so"); |
| |
| ConfiguredTarget main = getConfiguredTarget("//bin:bin"); |
| Artifact mainBin = getBinArtifact("bin", main); |
| CppLinkAction action = (CppLinkAction) getGeneratingAction(mainBin); |
| List<String> linkArgv = action.getLinkCommandLineForTesting().arguments(); |
| assertThat(linkArgv) |
| .containsAtLeast("-Xlinker", "-rpath", "-Xlinker", "$ORIGIN/../_solib_k8/_U_S_Sa_Cfoo___Ua") |
| .inOrder(); |
| } |
| } |