blob: d249fb1f84fb8d3e6b006a34e7f9e0c8211bebeb [file] [log] [blame]
// 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.collect.Iterables.getOnlyElement;
import static com.google.common.truth.Truth.assertThat;
import static org.junit.Assert.fail;
import com.google.common.base.Predicate;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Iterables;
import com.google.devtools.build.lib.actions.Artifact;
import com.google.devtools.build.lib.actions.FailAction;
import com.google.devtools.build.lib.actions.extra.CppLinkInfo;
import com.google.devtools.build.lib.actions.extra.ExtraActionInfo;
import com.google.devtools.build.lib.actions.util.ActionsTestUtil;
import com.google.devtools.build.lib.analysis.ConfiguredRuleClassProvider;
import com.google.devtools.build.lib.analysis.ConfiguredTarget;
import com.google.devtools.build.lib.analysis.OutputGroupInfo;
import com.google.devtools.build.lib.analysis.test.InstrumentedFilesProvider;
import com.google.devtools.build.lib.analysis.util.AnalysisMock;
import com.google.devtools.build.lib.analysis.util.BuildViewTestCase;
import com.google.devtools.build.lib.packages.ImplicitOutputsFunction;
import com.google.devtools.build.lib.packages.util.MockCcSupport;
import com.google.devtools.build.lib.skyframe.ConfiguredTargetAndData;
import com.google.devtools.build.lib.testutil.TestRuleClassProvider;
import com.google.devtools.build.lib.util.FileType;
import com.google.devtools.build.lib.vfs.PathFragment;
import com.google.devtools.build.lib.view.config.crosstool.CrosstoolConfig;
import com.google.protobuf.TextFormat;
import java.util.ArrayList;
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_library rule.
*/
@RunWith(JUnit4.class)
public class CcLibraryConfiguredTargetTest extends BuildViewTestCase {
private static final PathFragment STL_CPPMAP = PathFragment.create("stl.cppmap");
private static final PathFragment CROSSTOOL_CPPMAP = PathFragment.create("crosstool.cppmap");
@Override
protected ConfiguredRuleClassProvider getRuleClassProvider() {
ConfiguredRuleClassProvider.Builder builder = new ConfiguredRuleClassProvider.Builder();
TestRuleClassProvider.addStandardRules(builder);
return builder.addRuleDefinition(new TestRuleClassProvider.MakeVariableTesterRule()).build();
}
@Before
public final void createFiles() throws Exception {
scratch.file("hello/BUILD",
"cc_library(name = 'hello',",
" srcs = ['hello.cc'])",
"cc_library(name = 'hello_static',",
" srcs = ['hello.cc'],",
" linkstatic = 1)");
scratch.file("hello/hello.cc",
"#include <stdio.h>",
"int hello_world() { printf(\"Hello, world!\\n\"); }");
}
private CppCompileAction getCppCompileAction(String label) throws Exception {
return getCppCompileAction(getConfiguredTarget(label));
}
private CppCompileAction getCppCompileAction(ConfiguredTarget target) throws Exception {
List<CppCompileAction> compilationSteps =
actionsTestUtil().findTransitivePrerequisitesOf(
ActionsTestUtil.getFirstArtifactEndingWith(getFilesToBuild(target), ".a"),
CppCompileAction.class);
return compilationSteps.get(0);
}
private CppModuleMapAction getCppModuleMapAction(String label) throws Exception {
ConfiguredTarget target = getConfiguredTarget(label);
CppModuleMap cppModuleMap = target.get(CcCompilationInfo.PROVIDER).getCppModuleMap();
return (CppModuleMapAction) getGeneratingAction(cppModuleMap.getArtifact());
}
private void assertNoCppModuleMapAction(String label) throws Exception {
ConfiguredTarget target = getConfiguredTarget(label);
assertThat(target.get(CcCompilationInfo.PROVIDER).getCppModuleMap()).isNull();
}
@Test
public void testDefinesAndMakeVariables() throws Exception {
ConfiguredTarget l = scratchConfiguredTarget("a", "l",
"cc_library(name='l', srcs=['l.cc'], defines=['V=$(FOO)'], toolchains=[':v'])",
"make_variable_tester(name='v', variables={'FOO': 'BAR'})");
assertThat(l.get(CcCompilationInfo.PROVIDER).getDefines()).contains("V=BAR");
}
@Test
public void testMisconfiguredCrosstoolRaisesErrorWhenLinking() throws Exception {
AnalysisMock.get()
.ccSupport()
.setupCrosstool(
mockToolsConfig,
MockCcSupport.NO_LEGACY_FEATURES_FEATURE,
MockCcSupport.EMPTY_COMPILE_ACTION_CONFIG);
useConfiguration();
checkError(
"test",
"test",
"Expected action_config for 'c++-link-static-library' to be configured",
"cc_library(name = 'test', srcs = ['test.cc'])");
}
@Test
public void testMisconfiguredCrosstoolRaisesErrorWhenCompiling() throws Exception {
AnalysisMock.get()
.ccSupport()
.setupCrosstool(
mockToolsConfig,
MockCcSupport.NO_LEGACY_FEATURES_FEATURE,
MockCcSupport.EMPTY_STATIC_LIBRARY_ACTION_CONFIG);
useConfiguration();
checkError(
"test",
"test",
"Expected action_config for 'c++-compile' to be configured",
"cc_library(name = 'test', srcs = ['test.cc'])");
}
@Test
public void testFilesToBuild() throws Exception {
useConfiguration("--cpu=k8");
ConfiguredTarget hello = getConfiguredTarget("//hello:hello");
String cpu = getTargetConfiguration().getCpu();
Artifact archive = getBinArtifact("libhello.a", hello);
Artifact implSharedObject = getBinArtifact("libhello.so", hello);
Artifact implInterfaceSharedObject = getBinArtifact("libhello.ifso", hello);
Artifact implSharedObjectLink =
getSharedArtifact("_solib_" + cpu + "/libhello_Slibhello.so", hello);
Artifact implInterfaceSharedObjectLink =
getSharedArtifact("_solib_" + cpu + "/libhello_Slibhello.ifso", hello);
assertThat(getFilesToBuild(hello)).containsExactly(archive, implSharedObject,
implInterfaceSharedObject);
assertThat(LinkerInputs.toLibraryArtifacts(
hello.getProvider(CcNativeLibraryProvider.class).getTransitiveCcNativeLibraries()))
.containsExactly(implInterfaceSharedObjectLink);
assertThat(hello.getProvider(CcExecutionDynamicLibrariesProvider.class)
.getExecutionDynamicLibraryArtifacts()).containsExactly(implSharedObjectLink);
}
@Test
public void testFilesToBuildWithoutDSO() throws Exception {
CrosstoolConfig.CrosstoolRelease.Builder release = CrosstoolConfig.CrosstoolRelease.newBuilder()
.mergeFrom(CrosstoolConfigurationHelper.simpleCompleteToolchainProto());
release.getToolchainBuilder(0)
.setTargetCpu("k8")
.setCompiler("compiler")
.clearLinkingModeFlags();
scratch.file("crosstool/BUILD",
"cc_toolchain_suite(",
" name = 'crosstool',",
" toolchains = {'k8|compiler': ':cc-compiler-k8'})",
"filegroup(name = 'empty')",
"cc_toolchain(",
" name = 'cc-compiler-k8',",
" output_licenses = ['unencumbered'],",
" cpu = 'k8',",
" compiler_files = ':empty',",
" dwp_files = ':empty',",
" coverage_files = ':empty',",
" linker_files = ':empty',",
" strip_files = ':empty',",
" objcopy_files = ':empty',",
" static_runtime_libs = [':empty'],",
" dynamic_runtime_libs = [':empty'],",
" all_files = ':empty',",
" licenses = ['unencumbered'])");
scratch.file("crosstool/CROSSTOOL", TextFormat.printToString(release));
// This is like the preceding test, but with a toolchain that can't build '.so' files
useConfiguration("--crosstool_top=//crosstool:crosstool", "--compiler=compiler",
"--cpu=k8", "--host_cpu=k8");
ConfiguredTarget hello = getConfiguredTarget("//hello:hello");
Artifact archive = getBinArtifact("libhello.a", hello);
assertThat(getFilesToBuild(hello)).containsExactly(archive);
}
@Test
public void testFilesToBuildWithInterfaceSharedObjects() throws Exception {
useConfiguration("--interface_shared_objects");
useConfiguration("--cpu=k8");
ConfiguredTarget hello = getConfiguredTarget("//hello:hello");
String cpu = getTargetConfiguration().getCpu();
Artifact archive = getBinArtifact("libhello.a", hello);
Artifact sharedObject = getBinArtifact("libhello.ifso", hello);
Artifact implSharedObject = getBinArtifact("libhello.so", hello);
Artifact sharedObjectLink =
getSharedArtifact("_solib_" + cpu + "/libhello_Slibhello.ifso", hello);
Artifact implSharedObjectLink =
getSharedArtifact("_solib_" + cpu + "/libhello_Slibhello.so", hello);
assertThat(getFilesToBuild(hello)).containsExactly(archive, sharedObject, implSharedObject);
assertThat(LinkerInputs.toLibraryArtifacts(
hello.getProvider(CcNativeLibraryProvider.class).getTransitiveCcNativeLibraries()))
.containsExactly(sharedObjectLink);
assertThat(hello.getProvider(CcExecutionDynamicLibrariesProvider.class)
.getExecutionDynamicLibraryArtifacts()).containsExactly(implSharedObjectLink);
}
@Test
public void testEmptyLinkopts() throws Exception {
ConfiguredTarget hello = getConfiguredTarget("//hello:hello");
assertThat(
hello
.get(CcLinkParamsInfo.PROVIDER)
.getCcLinkParams(false, false)
.getLinkopts()
.isEmpty())
.isTrue();
}
@Test
public void testSoName() throws Exception {
// Without interface shared libraries.
useConfiguration("--nointerface_shared_objects");
ConfiguredTarget hello = getConfiguredTarget("//hello:hello");
Artifact sharedObject = getOnlyElement(FileType.filter(getFilesToBuild(hello),
CppFileTypes.SHARED_LIBRARY));
CppLinkAction action = (CppLinkAction) getGeneratingAction(sharedObject);
for (String option : action.getLinkCommandLine().getLinkopts()) {
assertThat(option).doesNotContain("-Wl,-soname");
}
// With interface shared libraries.
useConfiguration("--interface_shared_objects");
useConfiguration("--cpu=k8");
hello = getConfiguredTarget("//hello:hello");
sharedObject =
FileType.filter(getFilesToBuild(hello), CppFileTypes.SHARED_LIBRARY).iterator().next();
action = (CppLinkAction) getGeneratingAction(sharedObject);
assertThat(action.getLinkCommandLine().getLinkopts())
.contains("-Wl,-soname=libhello_Slibhello.so");
}
@Test
public void testCppLinkActionExtraActionInfoWithoutSharedLibraries() throws Exception {
useConfiguration("--nointerface_shared_objects");
ConfiguredTarget hello = getConfiguredTarget("//hello:hello");
Artifact sharedObject = getOnlyElement(FileType.filter(getFilesToBuild(hello),
CppFileTypes.SHARED_LIBRARY));
CppLinkAction action = (CppLinkAction) getGeneratingAction(sharedObject);
ExtraActionInfo.Builder builder = action.getExtraActionInfo(actionKeyContext);
ExtraActionInfo info = builder.build();
assertThat(info.getMnemonic()).isEqualTo("CppLink");
CppLinkInfo cppLinkInfo = info.getExtension(CppLinkInfo.cppLinkInfo);
assertThat(cppLinkInfo).isNotNull();
Iterable<String> inputs = Artifact.asExecPaths(
LinkerInputs.toLibraryArtifacts(action.getLinkCommandLine().getLinkerInputs()));
assertThat(cppLinkInfo.getInputFileList()).containsExactlyElementsIn(inputs);
assertThat(cppLinkInfo.getOutputFile())
.isEqualTo(action.getPrimaryOutput().getExecPathString());
assertThat(cppLinkInfo.hasInterfaceOutputFile()).isFalse();
assertThat(cppLinkInfo.getLinkTargetType())
.isEqualTo(action.getLinkCommandLine().getLinkTargetType().name());
assertThat(cppLinkInfo.getLinkStaticness())
.isEqualTo(action.getLinkCommandLine().getLinkStaticness().name());
Iterable<String> linkstamps = Artifact.asExecPaths(action.getLinkstampObjects());
assertThat(cppLinkInfo.getLinkStampList()).containsExactlyElementsIn(linkstamps);
Iterable<String> buildInfoHeaderArtifacts =
Artifact.asExecPaths(action.getBuildInfoHeaderArtifacts());
assertThat(cppLinkInfo.getBuildInfoHeaderArtifactList())
.containsExactlyElementsIn(buildInfoHeaderArtifacts);
assertThat(cppLinkInfo.getLinkOptList()).containsExactlyElementsIn(action.getArguments());
}
@Test
public void testCppLinkActionExtraActionInfoWithSharedLibraries() throws Exception {
useConfiguration("--cpu=k8");
ConfiguredTarget hello = getConfiguredTarget("//hello:hello");
Artifact sharedObject =
FileType.filter(getFilesToBuild(hello), CppFileTypes.SHARED_LIBRARY).iterator().next();
CppLinkAction action = (CppLinkAction) getGeneratingAction(sharedObject);
ExtraActionInfo.Builder builder = action.getExtraActionInfo(actionKeyContext);
ExtraActionInfo info = builder.build();
assertThat(info.getMnemonic()).isEqualTo("CppLink");
CppLinkInfo cppLinkInfo = info.getExtension(CppLinkInfo.cppLinkInfo);
assertThat(cppLinkInfo).isNotNull();
Iterable<String> inputs = Artifact.asExecPaths(
LinkerInputs.toLibraryArtifacts(action.getLinkCommandLine().getLinkerInputs()));
assertThat(cppLinkInfo.getInputFileList()).containsExactlyElementsIn(inputs);
assertThat(cppLinkInfo.getOutputFile())
.isEqualTo(action.getPrimaryOutput().getExecPathString());
assertThat(cppLinkInfo.getLinkTargetType())
.isEqualTo(action.getLinkCommandLine().getLinkTargetType().name());
assertThat(cppLinkInfo.getLinkStaticness())
.isEqualTo(action.getLinkCommandLine().getLinkStaticness().name());
Iterable<String> linkstamps = Artifact.asExecPaths(action.getLinkstampObjects());
assertThat(cppLinkInfo.getLinkStampList()).containsExactlyElementsIn(linkstamps);
Iterable<String> buildInfoHeaderArtifacts =
Artifact.asExecPaths(action.getBuildInfoHeaderArtifacts());
assertThat(cppLinkInfo.getBuildInfoHeaderArtifactList())
.containsExactlyElementsIn(buildInfoHeaderArtifacts);
assertThat(cppLinkInfo.getLinkOptList()).containsExactlyElementsIn(action.getArguments());
}
@Test
public void testLinkActionCanConsumeArtifactExtensions() throws Exception {
AnalysisMock.get()
.ccSupport()
.setupCrosstool(mockToolsConfig, MockCcSupport.STATIC_LINK_TWEAKED_CONFIGURATION);
useConfiguration("--features=" + Link.LinkTargetType.STATIC_LIBRARY.getActionName());
ConfiguredTarget hello = getConfiguredTarget("//hello:hello");
Artifact archive =
FileType.filter(getFilesToBuild(hello), FileType.of(".tweaked.a")).iterator().next();
CppLinkAction action = (CppLinkAction) getGeneratingAction(archive);
assertThat(action.getArguments()).contains(archive.getExecPathString());
}
@Test
public void testObjectFileNamesCanBeSpecifiedInToolchain() throws Exception {
AnalysisMock.get()
.ccSupport()
.setupCrosstool(mockToolsConfig,
"artifact_name_pattern {"
+ " category_name: 'object_file'"
+ " pattern: '%{output_name}.test.o'"
+ "}");
useConfiguration();
ConfiguredTarget hello = getConfiguredTarget("//hello:hello");
assertThat(artifactByPath(getFilesToBuild(hello), ".a", ".test.o")).isNotNull();
}
@Test
public void testArtifactSelectionBaseNameTemplating() throws Exception {
AnalysisMock.get()
.ccSupport()
.setupCrosstool(mockToolsConfig, MockCcSupport.STATIC_LINK_AS_DOT_A_CONFIGURATION);
useConfiguration("--features=" + Link.LinkTargetType.STATIC_LIBRARY.getActionName());
ConfiguredTarget hello = getConfiguredTarget("//hello:hello");
Artifact archive =
FileType.filter(getFilesToBuild(hello), CppFileTypes.ARCHIVE).iterator().next();
assertThat(archive.getExecPathString()).endsWith("libhello.a");
}
@Test
public void testArtifactSelectionErrorOnBadTemplateVariable() throws Exception {
AnalysisMock.get()
.ccSupport()
.setupCrosstool(mockToolsConfig, MockCcSupport.STATIC_LINK_BAD_TEMPLATE_CONFIGURATION);
useConfiguration("--features=" + Link.LinkTargetType.STATIC_LIBRARY.getActionName());
try {
getConfiguredTarget("//hello:hello");
fail("Should fail");
} catch (AssertionError e) {
assertThat(e)
.hasMessageThat()
.contains("Invalid toolchain configuration: Cannot find variable named 'bad_variable'");
}
}
@Test
public void testArtifactsToAlwaysBuild() throws Exception {
useConfiguration("--cpu=k8");
// ArtifactsToAlwaysBuild should apply both for static libraries.
ConfiguredTarget helloStatic = getConfiguredTarget("//hello:hello_static");
assertThat(
artifactsToStrings(getOutputGroup(helloStatic, OutputGroupInfo.HIDDEN_TOP_LEVEL)))
.containsExactly("bin hello/_objs/hello_static/hello/hello.pic.o");
Artifact implSharedObject = getBinArtifact("libhello_static.so", helloStatic);
assertThat(getFilesToBuild(helloStatic)).doesNotContain(implSharedObject);
// And for shared libraries.
ConfiguredTarget hello = getConfiguredTarget("//hello:hello");
assertThat(
artifactsToStrings(getOutputGroup(helloStatic, OutputGroupInfo.HIDDEN_TOP_LEVEL)))
.containsExactly("bin hello/_objs/hello_static/hello/hello.pic.o");
implSharedObject = getBinArtifact("libhello.so", hello);
assertThat(getFilesToBuild(hello)).contains(implSharedObject);
}
@Test
public void testTransitiveArtifactsToAlwaysBuildStatic() throws Exception {
useConfiguration("--cpu=k8");
ConfiguredTarget x = scratchConfiguredTarget(
"foo", "x",
"cc_library(name = 'x', srcs = ['x.cc'], deps = [':y'], linkstatic = 1)",
"cc_library(name = 'y', srcs = ['y.cc'], deps = [':z'])",
"cc_library(name = 'z', srcs = ['z.cc'])");
assertThat(artifactsToStrings(getOutputGroup(x, OutputGroupInfo.HIDDEN_TOP_LEVEL)))
.containsExactly(
"bin foo/_objs/x/foo/x.pic.o",
"bin foo/_objs/y/foo/y.pic.o",
"bin foo/_objs/z/foo/z.pic.o");
}
@Test
public void testBuildHeaderModulesAsPrerequisites() throws Exception {
AnalysisMock.get()
.ccSupport()
.setupCrosstool(mockToolsConfig, MockCcSupport.HEADER_MODULES_FEATURE_CONFIGURATION);
useConfiguration("--cpu=k8");
ConfiguredTarget x =
scratchConfiguredTarget(
"foo",
"x",
"package(features = ['header_modules'])",
"cc_library(name = 'x', srcs = ['x.cc'], deps = [':y'])",
"cc_library(name = 'y', hdrs = ['y.h'])");
assertThat(
ActionsTestUtil.baseNamesOf(
getOutputGroup(x, OutputGroupInfo.COMPILATION_PREREQUISITES)))
.isEqualTo("y.h y.cppmap stl.cppmap crosstool.cppmap x.cppmap y.pic.pcm x.cc");
}
@Test
public void testCodeCoverage() throws Exception {
AnalysisMock.get()
.ccSupport()
.setupCrosstool(mockToolsConfig, MockCcSupport.HEADER_MODULES_FEATURE_CONFIGURATION);
useConfiguration("--cpu=k8", "--collect_code_coverage");
ConfiguredTarget x =
scratchConfiguredTarget(
"foo",
"x",
"package(features = ['header_modules'])",
"cc_library(name = 'x', srcs = ['x.cc'])");
assertThat(
ActionsTestUtil.baseArtifactNames(
x.getProvider(InstrumentedFilesProvider.class).getInstrumentationMetadataFiles()))
.containsExactly("x.pic.gcno");
}
@Test
public void testDisablingHeaderModulesWhenDependingOnModuleBuildTransitively() throws Exception {
AnalysisMock.get()
.ccSupport()
.setupCrosstool(mockToolsConfig, MockCcSupport.HEADER_MODULES_FEATURE_CONFIGURATION);
useConfiguration();
scratch.file("module/BUILD",
"package(features = ['header_modules'])",
"cc_library(",
" name = 'module',",
" srcs = ['a.cc', 'a.h'],",
")");
scratch.file("nomodule/BUILD",
"package(features = ['-header_modules'])",
"cc_library(",
" name = 'nomodule',",
" srcs = ['a.cc', 'a.h'],",
" deps = ['//module']",
")");
CppCompileAction moduleAction = getCppCompileAction("//module:module");
assertThat(moduleAction.getCompilerOptions()).contains("module_name://module:module");
CppCompileAction noModuleAction = getCppCompileAction("//nomodule:nomodule");
assertThat(noModuleAction.getCompilerOptions()).doesNotContain("module_name://module:module");
}
/**
* Returns the non-system module maps in {@code input}.
*/
private Iterable<Artifact> getNonSystemModuleMaps(Iterable<Artifact> input) {
return Iterables.filter(input, new Predicate<Artifact>() {
@Override
public boolean apply(Artifact input) {
PathFragment path = input.getExecPath();
return CppFileTypes.CPP_MODULE_MAP.matches(path)
&& !path.endsWith(STL_CPPMAP)
&& !path.endsWith(CROSSTOOL_CPPMAP);
}
});
}
/**
* Returns the header module artifacts in {@code input}.
*/
private Iterable<Artifact> getHeaderModules(Iterable<Artifact> input) {
return Iterables.filter(input, new Predicate<Artifact>() {
@Override
public boolean apply(Artifact input) {
return CppFileTypes.CPP_MODULE.matches(input.getExecPath());
}
});
}
/**
* Returns the flags in {@code input} that reference a header module.
*/
private Iterable<String> getHeaderModuleFlags(Iterable<String> input) {
List<String> names = new ArrayList<>();
for (String flag : input) {
if (CppFileTypes.CPP_MODULE.matches(flag)) {
names.add(PathFragment.create(flag).getBaseName());
}
}
return names;
}
@Test
public void testCompileHeaderModules() throws Exception {
AnalysisMock.get()
.ccSupport()
.setupCrosstool(
mockToolsConfig,
""
+ "feature { name: 'header_modules' implies: 'use_header_modules' }"
+ "feature { name: 'module_maps' }"
+ "feature { name: 'use_header_modules' }");
useConfiguration("--cpu=k8");
scratch.file("module/BUILD",
"package(features = ['header_modules'])",
"cc_library(",
" name = 'a',",
" srcs = ['a.h', 'a.cc'],",
" deps = ['b']",
")",
"cc_library(",
" name = 'b',",
" srcs = ['b.h'],",
" textual_hdrs = ['t.h'],",
")");
getConfiguredTarget("//module:b");
Artifact bModuleArtifact = getBinArtifact("_objs/b/module/b.pic.pcm", "//module:b");
CppCompileAction bModuleAction = (CppCompileAction) getGeneratingAction(bModuleArtifact);
assertThat(bModuleAction.getIncludeScannerSources()).containsExactly(
getSourceArtifact("module/b.h"), getSourceArtifact("module/t.h"));
assertThat(bModuleAction.getInputs()).contains(
getGenfilesArtifactWithNoOwner("module/b.cppmap"));
getConfiguredTarget("//module:a");
Artifact aObjectArtifact = getBinArtifact("_objs/a/module/a.pic.o", "//module:a");
CppCompileAction aObjectAction = (CppCompileAction) getGeneratingAction(aObjectArtifact);
assertThat(aObjectAction.getIncludeScannerSources()).containsExactly(
getSourceArtifact("module/a.cc"));
assertThat(aObjectAction.getCcCompilationInfo().getTransitiveModules(true))
.contains(getBinArtifact("_objs/b/module/b.pic.pcm", "//module:b"));
assertThat(aObjectAction.getInputs()).contains(
getGenfilesArtifactWithNoOwner("module/b.cppmap"));
assertNoEvents();
}
private void setupPackagesForModuleTests(boolean useHeaderModules) throws Exception {
scratch.file("module/BUILD",
"package(features = ['header_modules'])",
"cc_library(",
" name = 'b',",
" srcs = ['b.h'],",
" deps = ['//nomodule:a'],",
")",
"cc_library(",
" name = 'g',",
" srcs = ['g.h', 'g.cc'],",
" deps = ['//nomodule:c'],",
")",
"cc_library(",
" name = 'j',",
" srcs = ['j.h', 'j.cc'],",
" deps = ['//nomodule:c', '//nomodule:i'],",
")");
scratch.file("nomodule/BUILD",
"package(features = ['-header_modules'"
+ (useHeaderModules ? ", 'use_header_modules'" : "") + "])",
"cc_library(",
" name = 'y',",
" srcs = ['y.h'],",
")",
"cc_library(",
" name = 'z',",
" srcs = ['z.h'],",
" deps = [':y'],",
")",
"cc_library(",
" name = 'a',",
" srcs = ['a.h'],",
" deps = [':z'],",
")",
"cc_library(",
" name = 'c',",
" srcs = ['c.h', 'c.cc'],",
" deps = ['//module:b'],",
")",
"cc_library(",
" name = 'd',",
" srcs = ['d.h', 'd.cc'],",
" deps = [':c'],",
")",
"cc_library(",
" name = 'e',",
" srcs = ['e.h'],",
" deps = [':a'],",
")",
"cc_library(",
" name = 'f',",
" srcs = ['f.h', 'f.cc'],",
" deps = [':e'],",
")",
"cc_library(",
" name = 'h',",
" srcs = ['h.h', 'h.cc'],",
" deps = ['//module:g'],",
")",
"cc_library(",
" name = 'i',",
" srcs = ['i.h', 'i.cc'],",
" deps = [':h'],",
")");
}
@Test
public void testCompileHeaderModulesTransitively() throws Exception {
AnalysisMock.get()
.ccSupport()
.setupCrosstool(mockToolsConfig, MockCcSupport.HEADER_MODULES_FEATURE_CONFIGURATION);
useConfiguration("--cpu=k8");
setupPackagesForModuleTests(/*useHeaderModules=*/false);
// The //nomodule:f target only depends on non-module targets, thus it should be module-free.
getConfiguredTarget("//nomodule:f");
assertThat(getGeneratingAction(getBinArtifact("_objs/f/nomodule/f.pic.pcm", "//nomodule:f")))
.isNull();
Artifact fObjectArtifact = getBinArtifact("_objs/f/nomodule/f.pic.o", "//nomodule:f");
CppCompileAction fObjectAction = (CppCompileAction) getGeneratingAction(fObjectArtifact);
// Only the module map of f itself itself and the direct dependencies are needed.
assertThat(getNonSystemModuleMaps(fObjectAction.getInputs())).containsExactly(
getGenfilesArtifact("f.cppmap", "//nomodule:f"),
getGenfilesArtifact("e.cppmap", "//nomodule:e"));
assertThat(getHeaderModules(fObjectAction.getInputs())).isEmpty();
assertThat(fObjectAction.getIncludeScannerSources()).containsExactly(
getSourceArtifact("nomodule/f.cc"));
assertThat(getHeaderModuleFlags(fObjectAction.getCompilerOptions())).isEmpty();
// The //nomodule:c target will get the header module for //module:b, which is a direct
// dependency.
getConfiguredTarget("//nomodule:c");
assertThat(getGeneratingAction(getBinArtifact("_objs/c/nomodule/c.pic.pcm", "//nomodule:c")))
.isNull();
Artifact cObjectArtifact = getBinArtifact("_objs/c/nomodule/c.pic.o", "//nomodule:c");
CppCompileAction cObjectAction = (CppCompileAction) getGeneratingAction(cObjectArtifact);
assertThat(getNonSystemModuleMaps(cObjectAction.getInputs())).containsExactly(
getGenfilesArtifact("b.cppmap", "//module:b"),
getGenfilesArtifact("c.cppmap", "//nomodule:e"));
assertThat(getHeaderModules(cObjectAction.getInputs())).isEmpty();
// All headers of transitive dependencies that are built as modules are needed as entry points
// for include scanning.
assertThat(cObjectAction.getIncludeScannerSources()).containsExactly(
getSourceArtifact("nomodule/c.cc"));
assertThat(cObjectAction.getMainIncludeScannerSource()).isEqualTo(
getSourceArtifact("nomodule/c.cc"));
assertThat(getHeaderModuleFlags(cObjectAction.getCompilerOptions())).isEmpty();
// The //nomodule:d target depends on //module:b via one indirection (//nomodule:c).
getConfiguredTarget("//nomodule:d");
assertThat(getGeneratingAction(getBinArtifact("_objs/d/nomodule/d.pic.pcm", "//nomodule:d")))
.isNull();
Artifact dObjectArtifact = getBinArtifact("_objs/d/nomodule/d.pic.o", "//nomodule:d");
CppCompileAction dObjectAction = (CppCompileAction) getGeneratingAction(dObjectArtifact);
// Module map 'c.cppmap' is needed because it is a direct dependency.
assertThat(getNonSystemModuleMaps(dObjectAction.getInputs())).containsExactly(
getGenfilesArtifact("c.cppmap", "//nomodule:c"),
getGenfilesArtifact("d.cppmap", "//nomodule:d"));
assertThat(getHeaderModules(dObjectAction.getInputs())).isEmpty();
assertThat(dObjectAction.getIncludeScannerSources()).containsExactly(
getSourceArtifact("nomodule/d.cc"));
assertThat(getHeaderModuleFlags(dObjectAction.getCompilerOptions())).isEmpty();
// The //module:j target depends on //module:g via //nomodule:h and on //module:b via
// both //module:g and //nomodule:c.
getConfiguredTarget("//module:j");
Artifact jObjectArtifact = getBinArtifact("_objs/j/module/j.pic.o", "//module:j");
CppCompileAction jObjectAction = (CppCompileAction) getGeneratingAction(jObjectArtifact);
assertThat(getHeaderModules(jObjectAction.getCcCompilationInfo().getTransitiveModules(true)))
.containsExactly(
getBinArtifact("_objs/b/module/b.pic.pcm", "//module:b"),
getBinArtifact("_objs/g/module/g.pic.pcm", "//module:g"));
assertThat(jObjectAction.getIncludeScannerSources()).containsExactly(
getSourceArtifact("module/j.cc"));
assertThat(jObjectAction.getMainIncludeScannerSource()).isEqualTo(
getSourceArtifact("module/j.cc"));
assertThat(getHeaderModules(jObjectAction.getCcCompilationInfo().getTransitiveModules(true)))
.containsExactly(
getBinArtifact("_objs/b/module/b.pic.pcm", "//module:b"),
getBinArtifact("_objs/g/module/g.pic.pcm", "//module:g"));
}
@Test
public void testCompileUsingHeaderModulesTransitively() throws Exception {
AnalysisMock.get()
.ccSupport()
.setupCrosstool(mockToolsConfig, MockCcSupport.HEADER_MODULES_FEATURE_CONFIGURATION);
useConfiguration("--cpu=k8");
setupPackagesForModuleTests( /*useHeaderModules=*/true);
getConfiguredTarget("//nomodule:f");
Artifact fObjectArtifact = getBinArtifact("_objs/f/nomodule/f.pic.o", "//nomodule:f");
CppCompileAction fObjectAction = (CppCompileAction) getGeneratingAction(fObjectArtifact);
// Only the module map of f itself itself and the direct dependencies are needed.
assertThat(getNonSystemModuleMaps(fObjectAction.getInputs()))
.containsExactly(
getGenfilesArtifact("f.cppmap", "//nomodule:f"),
getGenfilesArtifact("e.cppmap", "//nomodule:e"));
getConfiguredTarget("//nomodule:c");
Artifact cObjectArtifact = getBinArtifact("_objs/c/nomodule/c.pic.o", "//nomodule:c");
CppCompileAction cObjectAction = (CppCompileAction) getGeneratingAction(cObjectArtifact);
assertThat(getNonSystemModuleMaps(cObjectAction.getInputs()))
.containsExactly(
getGenfilesArtifact("b.cppmap", "//module:b"),
getGenfilesArtifact("c.cppmap", "//nomodule:e"));
assertThat(getHeaderModules(cObjectAction.getCcCompilationInfo().getTransitiveModules(true)))
.containsExactly(getBinArtifact("_objs/b/module/b.pic.pcm", "//module:b"));
getConfiguredTarget("//nomodule:d");
Artifact dObjectArtifact = getBinArtifact("_objs/d/nomodule/d.pic.o", "//nomodule:d");
CppCompileAction dObjectAction = (CppCompileAction) getGeneratingAction(dObjectArtifact);
assertThat(getNonSystemModuleMaps(dObjectAction.getInputs()))
.containsExactly(
getGenfilesArtifact("c.cppmap", "//nomodule:c"),
getGenfilesArtifact("d.cppmap", "//nomodule:d"));
assertThat(getHeaderModules(dObjectAction.getCcCompilationInfo().getTransitiveModules(true)))
.containsExactly(getBinArtifact("_objs/b/module/b.pic.pcm", "//module:b"));
}
private void writeSimpleCcLibrary() throws Exception {
scratch.file("module/BUILD",
"cc_library(",
" name = 'map',",
" srcs = ['a.cc', 'a.h'],",
")");
}
@Test
public void testPicNotAvailableError() throws Exception {
AnalysisMock.get()
.ccSupport()
.setupCrosstool(
mockToolsConfig,
MockCcSupport.EMPTY_STATIC_LIBRARY_ACTION_CONFIG,
MockCcSupport.EMPTY_COMPILE_ACTION_CONFIG,
MockCcSupport.NO_LEGACY_FEATURES_FEATURE);
useConfiguration("--cpu=k8");
writeSimpleCcLibrary();
reporter.removeHandler(failFastHandler);
getConfiguredTarget("//module:map");
assertContainsEvent("PIC compilation is requested but the toolchain does not support it");
}
@Test
public void testToolchainWithoutPicForNoPicCompilation() throws Exception {
AnalysisMock.get()
.ccSupport()
.setupCrosstool(
mockToolsConfig,
"needsPic: false",
MockCcSupport.EMPTY_COMPILE_ACTION_CONFIG,
MockCcSupport.EMPTY_EXECUTABLE_ACTION_CONFIG,
MockCcSupport.EMPTY_DYNAMIC_LIBRARY_ACTION_CONFIG,
MockCcSupport.EMPTY_TRANSITIVE_DYNAMIC_LIBRARY_ACTION_CONFIG,
MockCcSupport.EMPTY_STATIC_LIBRARY_ACTION_CONFIG,
MockCcSupport.EMPTY_STRIP_ACTION_CONFIG,
MockCcSupport.NO_LEGACY_FEATURES_FEATURE);
useConfiguration();
scratchConfiguredTarget("a", "a",
"cc_binary(name='a', srcs=['a.cc'], deps=[':b'])",
"cc_library(name='b', srcs=['b.cc'])");
}
@Test
public void testNoCppModuleMap() throws Exception {
AnalysisMock.get()
.ccSupport()
.setupCrosstool(
mockToolsConfig,
MockCcSupport.EMPTY_COMPILE_ACTION_CONFIG,
MockCcSupport.EMPTY_EXECUTABLE_ACTION_CONFIG,
MockCcSupport.EMPTY_STATIC_LIBRARY_ACTION_CONFIG,
MockCcSupport.EMPTY_DYNAMIC_LIBRARY_ACTION_CONFIG,
MockCcSupport.EMPTY_TRANSITIVE_DYNAMIC_LIBRARY_ACTION_CONFIG,
MockCcSupport.NO_LEGACY_FEATURES_FEATURE,
MockCcSupport.PIC_FEATURE);
useConfiguration();
writeSimpleCcLibrary();
assertNoCppModuleMapAction("//module:map");
}
@Test
public void testCppModuleMap() throws Exception {
AnalysisMock.get()
.ccSupport()
.setupCrosstool(mockToolsConfig, "feature { name: 'module_maps' }");
useConfiguration();
writeSimpleCcLibrary();
CppModuleMapAction action = getCppModuleMapAction("//module:map");
assertThat(ActionsTestUtil.baseArtifactNames(action.getDependencyArtifacts())).containsExactly(
"stl.cppmap",
"crosstool.cppmap");
assertThat(artifactsToStrings(action.getPrivateHeaders()))
.containsExactly("src module/a.h");
assertThat(action.getPublicHeaders()).isEmpty();
}
/**
* Historically, blaze hasn't added the pre-compiled libraries from srcs to the files to build.
* This test ensures that we do not accidentally break that - we may do so intentionally.
*/
@Test
public void testFilesToBuildWithPrecompiledStaticLibrary() throws Exception {
ConfiguredTarget hello = scratchConfiguredTarget("precompiled", "library",
"cc_library(name = 'library', ",
" srcs = ['missing.a'])");
assertThat(artifactsToStrings(getFilesToBuild(hello)))
.doesNotContain("src precompiled/missing.a");
}
@Test
public void testAllowDuplicateNonCompiledSources() throws Exception {
ConfiguredTarget x =
scratchConfiguredTarget(
"x",
"x",
"filegroup(name = 'xso', srcs = ['x.so'])",
"cc_library(name = 'x', srcs = ['x.so', ':xso'])");
assertThat(x).isNotNull();
}
@Test
public void testDoNotCompileSourceFilesInHeaders() throws Exception {
AnalysisMock.get()
.ccSupport()
.setupCrosstool(mockToolsConfig, MockCcSupport.HEADER_PROCESSING_FEATURE_CONFIGURATION);
useConfiguration("--features=parse_headers");
ConfiguredTarget x =
scratchConfiguredTarget("x", "x", "cc_library(name = 'x', hdrs = ['x.cc'])");
assertThat(getGeneratingAction(getBinArtifact("_objs/x/x/x.pic.o", x))).isNull();
}
@Test
public void testProcessHeadersInDependencies() throws Exception {
AnalysisMock.get()
.ccSupport()
.setupCrosstool(mockToolsConfig, MockCcSupport.HEADER_PROCESSING_FEATURE_CONFIGURATION);
useConfiguration("--features=parse_headers", "--process_headers_in_dependencies");
ConfiguredTarget x =
scratchConfiguredTarget(
"foo",
"x",
"cc_library(name = 'x', deps = [':y'])",
"cc_library(name = 'y', hdrs = ['y.h'])");
assertThat(ActionsTestUtil.baseNamesOf(getOutputGroup(x, OutputGroupInfo.HIDDEN_TOP_LEVEL)))
.isEqualTo("y.h.processed");
}
@Test
public void testProcessHeadersInDependenciesOfBinaries() throws Exception {
AnalysisMock.get()
.ccSupport()
.setupCrosstool(mockToolsConfig, MockCcSupport.HEADER_PROCESSING_FEATURE_CONFIGURATION);
useConfiguration("--features=parse_headers", "--process_headers_in_dependencies");
ConfiguredTarget x =
scratchConfiguredTarget(
"foo",
"x",
"cc_binary(name = 'x', deps = [':y', ':z'])",
"cc_library(name = 'y', hdrs = ['y.h'])",
"cc_library(name = 'z', srcs = ['z.cc'])");
String hiddenTopLevel =
ActionsTestUtil.baseNamesOf(getOutputGroup(x, OutputGroupInfo.HIDDEN_TOP_LEVEL));
assertThat(hiddenTopLevel).contains("y.h.processed");
assertThat(hiddenTopLevel).doesNotContain("z.pic.o");
}
@Test
public void testDoNotProcessHeadersInDependencies() throws Exception {
AnalysisMock.get()
.ccSupport()
.setupCrosstool(mockToolsConfig, MockCcSupport.HEADER_PROCESSING_FEATURE_CONFIGURATION);
useConfiguration("--features=parse_headers");
ConfiguredTarget x =
scratchConfiguredTarget(
"foo",
"x",
"cc_library(name = 'x', deps = [':y'])",
"cc_library(name = 'y', hdrs = ['y.h'])");
assertThat(ActionsTestUtil.baseNamesOf(getOutputGroup(x, OutputGroupInfo.HIDDEN_TOP_LEVEL)))
.isEmpty();
}
@Test
public void testProcessHeadersInCompileOnlyMode() throws Exception {
AnalysisMock.get()
.ccSupport()
.setupCrosstool(mockToolsConfig, MockCcSupport.HEADER_PROCESSING_FEATURE_CONFIGURATION);
useConfiguration("--features=parse_headers", "--process_headers_in_dependencies");
ConfiguredTarget y =
scratchConfiguredTarget(
"foo",
"y",
"cc_library(name = 'x', deps = [':y'])",
"cc_library(name = 'y', hdrs = ['y.h'])");
assertThat(ActionsTestUtil.baseNamesOf(getOutputGroup(y, OutputGroupInfo.FILES_TO_COMPILE)))
.isEqualTo("y.h.processed");
}
@Test
public void testIncludePathOrder() throws Exception {
scratch.file("foo/BUILD",
"cc_library(",
" name = 'bar',",
" includes = ['bar'],",
")",
"cc_library(",
" name = 'foo',",
" srcs = ['foo.cc'],",
" includes = ['foo'],",
" deps = [':bar'],",
")");
ConfiguredTarget target = getConfiguredTarget("//foo");
CppCompileAction action = getCppCompileAction(target);
String genfilesDir = target.getConfiguration().getGenfilesFragment().toString();
// Local include paths come first.
assertContainsSublist(
action.getCompilerOptions(),
ImmutableList.of(
"-isystem",
"foo/foo",
"-isystem",
genfilesDir + "/foo/foo",
"-isystem",
"foo/bar",
"-isystem",
genfilesDir + "/foo/bar"));
}
@Test
public void testDefinesOrder() throws Exception {
scratch.file("foo/BUILD",
"cc_library(",
" name = 'bar',",
" defines = ['BAR'],",
")",
"cc_library(",
" name = 'foo',",
" srcs = ['foo.cc'],",
" defines = ['FOO'],",
" deps = [':bar'],",
")");
CppCompileAction action = getCppCompileAction("//foo");
// Inherited defines come first.
assertContainsSublist(action.getCompilerOptions(), ImmutableList.of("-DBAR", "-DFOO"));
}
// Regression test - setting "-shared" caused an exception when computing the link command.
@Test
public void testLinkOptsNotPassedToStaticLink() throws Exception {
scratchConfiguredTarget("foo", "foo",
"cc_library(",
" name = 'foo',",
" srcs = ['foo.cc'],",
" linkopts = ['-shared'],",
")");
}
private static final String COMPILATION_MODE_FEATURES = ""
+ "feature {"
+ " name: 'dbg'"
+ " flag_set {"
+ " action: 'c++-compile'"
+ " flag_group { flag: '-dbg' }"
+ " }"
+ "}"
+ "feature {"
+ " name: 'fastbuild'"
+ " flag_set {"
+ " action: 'c++-compile'"
+ " flag_group { flag: '-fastbuild' }"
+ " }"
+ "}"
+ "feature {"
+ " name: 'opt'"
+ " flag_set {"
+ " action: 'c++-compile'"
+ " flag_group { flag: '-opt' }"
+ " }"
+ "}";
private List<String> getCompilationModeFlags(String... flags) throws Exception {
AnalysisMock.get().ccSupport().setupCrosstool(mockToolsConfig, COMPILATION_MODE_FEATURES);
useConfiguration(flags);
scratch.overwriteFile("mode/BUILD", "cc_library(name = 'a', srcs = ['a.cc'])");
getConfiguredTarget("//mode:a");
Artifact objectArtifact = getBinArtifact("_objs/a/mode/a.pic.o", "//mode:a");
CppCompileAction action = (CppCompileAction) getGeneratingAction(objectArtifact);
return action.getCompilerOptions();
}
@Test
public void testCompilationModeFeatures() throws Exception {
List<String> flags;
flags = getCompilationModeFlags("--cpu=k8");
assertThat(flags).contains("-fastbuild");
assertThat(flags).containsNoneOf("-opt", "-dbg");
flags = getCompilationModeFlags("--cpu=k8", "--compilation_mode=fastbuild");
assertThat(flags).contains("-fastbuild");
assertThat(flags).containsNoneOf("-opt", "-dbg");
flags = getCompilationModeFlags("--cpu=k8", "--compilation_mode=opt");
assertThat(flags).contains("-opt");
assertThat(flags).containsNoneOf("-fastbuild", "-dbg");
flags = getCompilationModeFlags("--cpu=k8", "--compilation_mode=dbg");
assertThat(flags).contains("-dbg");
assertThat(flags).containsNoneOf("-fastbuild", "-opt");
}
private List<String> getHostAndTargetFlags(boolean useHost) throws Exception {
AnalysisMock.get()
.ccSupport()
.setupCrosstool(mockToolsConfig, MockCcSupport.HOST_AND_NONHOST_CONFIGURATION);
scratch.overwriteFile("mode/BUILD", "cc_library(name = 'a', srcs = ['a.cc'])");
useConfiguration("--cpu=k8");
ConfiguredTarget target;
String objectPath;
if (useHost) {
target = getHostConfiguredTarget("//mode:a");
objectPath = "_objs/a/mode/a.o";
} else {
target = getConfiguredTarget("//mode:a");
objectPath = "_objs/a/mode/a.pic.o";
}
Artifact objectArtifact = getBinArtifact(objectPath, target);
CppCompileAction action = (CppCompileAction) getGeneratingAction(objectArtifact);
assertThat(action).isNotNull();
return action.getCompilerOptions();
}
@Test
public void testHostAndNonHostFeatures() throws Exception {
List<String> flags;
flags = getHostAndTargetFlags(true);
assertThat(flags).contains("-host");
assertThat(flags).doesNotContain("-nonhost");
flags = getHostAndTargetFlags(false);
assertThat(flags).contains("-nonhost");
assertThat(flags).doesNotContain("-host");
}
@Test
public void testIncludePathsOutsideExecutionRoot() throws Exception {
checkError(
"root",
"a",
"The include path '../somewhere' references a path outside of the execution root.",
"cc_library(name='a', srcs=['a.cc'], copts=['-Id/../../somewhere'])");
}
@Test
public void testAbsoluteIncludePathsOutsideExecutionRoot() throws Exception {
checkError(
"root",
"a",
"The include path '/somewhere' references a path outside of the execution root.",
"cc_library(name='a', srcs=['a.cc'], copts=['-I/somewhere'])");
}
@Test
public void testSystemIncludePathsOutsideExecutionRoot() throws Exception {
checkError(
"root",
"a",
"The include path '../system' references a path outside of the execution root.",
"cc_library(name='a', srcs=['a.cc'], copts=['-isystem../system'])");
}
@Test
public void testAbsoluteSystemIncludePathsOutsideExecutionRoot() throws Exception {
checkError(
"root",
"a",
"The include path '/system' references a path outside of the execution root.",
"cc_library(name='a', srcs=['a.cc'], copts=['-isystem/system'])");
}
/**
* Tests that configurable "srcs" doesn't crash because of orphaned implicit .so outputs.
* (see {@link CcLibrary#appearsToHaveObjectFiles}).
*/
@Test
public void testConfigurableSrcs() throws Exception {
scratch.file("foo/BUILD",
"cc_library(",
" name = 'foo',",
" srcs = select({'//conditions:default': []}),",
")");
ConfiguredTarget target = getConfiguredTarget("//foo:foo");
Artifact soOutput = getBinArtifact("libfoo.so", target);
assertThat(getGeneratingAction(soOutput)).isInstanceOf(FailAction.class);
}
@Test
public void alwaysAddStaticAndDynamicLibraryToFilesToBuildWhenBuilding() throws Exception {
useConfiguration("--cpu=k8");
ConfiguredTarget target =
scratchConfiguredTarget("a", "b", "cc_library(name = 'b', srcs = ['source.cc'])");
assertThat(artifactsToStrings(getFilesToBuild(target)))
.containsExactly("bin a/libb.a", "bin a/libb.ifso", "bin a/libb.so");
}
@Test
public void addOnlyStaticLibraryToFilesToBuildWhenWrappingIffImplicitOutput() throws Exception {
// This shared library has the same name as the archive generated by this rule, so it should
// override said archive. However, said archive should still be put in files to build.
ConfiguredTargetAndData target =
scratchConfiguredTargetAndTarget("a", "b", "cc_library(name = 'b', srcs = ['libb.so'])");
if (target.getTarget().getAssociatedRule().getImplicitOutputsFunction()
!= ImplicitOutputsFunction.NONE) {
assertThat(artifactsToStrings(getFilesToBuild(target.getConfiguredTarget())))
.containsExactly("bin a/libb.a");
} else {
assertThat(artifactsToStrings(getFilesToBuild(target.getConfiguredTarget()))).isEmpty();
}
}
@Test
public void addStaticLibraryToStaticSharedLinkParamsWhenBuilding() throws Exception {
ConfiguredTarget target =
scratchConfiguredTarget("a", "foo", "cc_library(name = 'foo', srcs = ['foo.cc'])");
Iterable<Artifact> libraries =
LinkerInputs.toNonSolibArtifacts(
target
.get(CcLinkParamsInfo.PROVIDER)
.getCcLinkParams(true, true)
.getLibraries());
assertThat(artifactsToStrings(libraries)).contains("bin a/libfoo.a");
}
@Test
public void dontAddStaticLibraryToStaticSharedLinkParamsWhenWrappingSameLibraryIdentifier()
throws Exception {
ConfiguredTarget target =
scratchConfiguredTarget("a", "foo", "cc_library(name = 'foo', srcs = ['libfoo.so'])");
Iterable<Artifact> libraries =
LinkerInputs.toNonSolibArtifacts(
target
.get(CcLinkParamsInfo.PROVIDER)
.getCcLinkParams(true, true)
.getLibraries());
assertThat(artifactsToStrings(libraries)).doesNotContain("bin a/libfoo.a");
assertThat(artifactsToStrings(libraries)).contains("src a/libfoo.so");
}
@Test
public void onlyAddOneWrappedLibraryWithSameLibraryIdentifierToLinkParams() throws Exception {
ConfiguredTarget target =
scratchConfiguredTarget(
"a", "foo", "cc_library(name = 'foo', srcs = ['libfoo.lo', 'libfoo.so'])");
Iterable<Artifact> libraries =
LinkerInputs.toNonSolibArtifacts(
target
.get(CcLinkParamsInfo.PROVIDER)
.getCcLinkParams(true, true)
.getLibraries());
assertThat(artifactsToStrings(libraries)).doesNotContain("src a/libfoo.so");
assertThat(artifactsToStrings(libraries)).contains("src a/libfoo.lo");
}
@Test
public void testCcLinkParamsHasExecutionDynamicLibraries() throws Exception {
AnalysisMock.get()
.ccSupport()
.setupCrosstool(
mockToolsConfig, MockCcSupport.COPY_DYNAMIC_LIBRARIES_TO_BINARY_CONFIGURATION);
useConfiguration("--cpu=k8", "--features=copy_dynamic_libraries_to_binary");
ConfiguredTarget target =
scratchConfiguredTarget("a", "foo", "cc_library(name = 'foo', srcs = ['foo.cc'])");
Iterable<Artifact> libraries =
target
.get(CcLinkParamsInfo.PROVIDER)
.getCcLinkParams(false, true)
.getExecutionDynamicLibraries();
assertThat(artifactsToStrings(libraries)).doesNotContain("bin a/libfoo.ifso");
assertThat(artifactsToStrings(libraries)).contains("bin a/libfoo.so");
}
@Test
public void testCcLinkParamsHasExecutionDynamicLibrariesWithoutCopyFeature() throws Exception {
useConfiguration("--cpu=k8");
ConfiguredTarget target =
scratchConfiguredTarget("a", "foo", "cc_library(name = 'foo', srcs = ['foo.cc'])");
Iterable<Artifact> libraries =
target
.get(CcLinkParamsInfo.PROVIDER)
.getCcLinkParams(false, true)
.getExecutionDynamicLibraries();
assertThat(artifactsToStrings(libraries)).doesNotContain("bin _solib_k8/liba_Slibfoo.ifso");
assertThat(artifactsToStrings(libraries)).contains("bin _solib_k8/liba_Slibfoo.so");
}
@Test
public void testCcLinkParamsDoNotHasExecutionDynamicLibraries() throws Exception {
useConfiguration("--cpu=k8");
ConfiguredTarget target =
scratchConfiguredTarget(
"a", "foo", "cc_library(name = 'foo', srcs = ['foo.cc'], linkstatic=1)");
Iterable<Artifact> libraries =
target
.get(CcLinkParamsInfo.PROVIDER)
.getCcLinkParams(false, true)
.getExecutionDynamicLibraries();
assertThat(artifactsToStrings(libraries)).isEmpty();
}
@Test
public void forbidBuildingAndWrappingSameLibraryIdentifier() throws Exception {
useConfiguration("--cpu=k8");
checkError(
"a",
"foo",
"in cc_library rule //a:foo: Can't put libfoo.lo into the srcs of a cc_library with the "
+ "same name (foo) which also contains other code or objects to link; it shares a name "
+ "with libfoo.a, libfoo.ifso, libfoo.so (output compiled and linked from the "
+ "non-library sources of this rule), which could cause confusion",
"cc_library(name = 'foo', srcs = ['foo.cc', 'libfoo.lo'])");
}
@Test
public void testProcessedHeadersWithPicSharedLibsAndNoPicBinaries() throws Exception {
AnalysisMock.get()
.ccSupport()
.setupCrosstool(mockToolsConfig,
MockCcSupport.HEADER_PROCESSING_FEATURE_CONFIGURATION);
useConfiguration("--features=parse_headers", "-c", "opt");
// Should not crash
scratchConfiguredTarget("a", "a", "cc_library(name='a', hdrs=['a.h'])");
}
@Test
public void testStlWithAlias() throws Exception {
scratch.file("a/BUILD",
"cc_library(name='a')",
"alias(name='stl', actual=':realstl')",
"cc_library(name='realstl')");
useConfiguration("--experimental_stl=//a:stl");
getConfiguredTarget("//a:a");
}
}