Open source CcLibraryConfiguredTargetTest.
--
MOS_MIGRATED_REVID=110153755
diff --git a/src/test/java/com/google/devtools/build/lib/BUILD b/src/test/java/com/google/devtools/build/lib/BUILD
index 9ae6869..3f371a9 100644
--- a/src/test/java/com/google/devtools/build/lib/BUILD
+++ b/src/test/java/com/google/devtools/build/lib/BUILD
@@ -937,6 +937,7 @@
"//src/main/java/com/google/devtools/build/lib/rules/cpp",
"//src/main/java/com/google/devtools/common/options",
"//src/main/protobuf:crosstool_config_proto",
+ "//src/main/protobuf:extra_actions_base_proto",
"//src/test/java/com/google/devtools/build/lib:packages_testutil",
"//third_party:guava",
"//third_party:guava-testlib",
diff --git a/src/test/java/com/google/devtools/build/lib/MOCK_CROSSTOOL b/src/test/java/com/google/devtools/build/lib/MOCK_CROSSTOOL
index a85c089..05e61fb 100644
--- a/src/test/java/com/google/devtools/build/lib/MOCK_CROSSTOOL
+++ b/src/test/java/com/google/devtools/build/lib/MOCK_CROSSTOOL
@@ -55,6 +55,7 @@
cxx_builtin_include_directory: "/usr/include"
objcopy_embed_flag: "-I"
objcopy_embed_flag: "binary"
+ supports_interface_shared_objects: true
}
toolchain {
@@ -91,6 +92,7 @@
cxx_builtin_include_directory: "/usr/include"
objcopy_embed_flag: "-I"
objcopy_embed_flag: "binary"
+ supports_interface_shared_objects: true
}
toolchain {
@@ -117,7 +119,7 @@
tool_path { name: "objdump" path: "/usr/bin/objdump" }
tool_path { name: "strip" path: "/usr/bin/strip" }
- needsPic: true
+ needsPic: false
builtin_sysroot: ""
cxx_flag: "-std=c++0x"
@@ -132,6 +134,7 @@
cxx_builtin_include_directory: "/Library/Developer/CommandLineTools"
objcopy_embed_flag: "-I"
objcopy_embed_flag: "binary"
+ supports_interface_shared_objects: true
}
toolchain {
@@ -164,6 +167,7 @@
cxx_builtin_include_directory: "C:/mingw/lib/gcc"
objcopy_embed_flag: "-I"
objcopy_embed_flag: "binary"
+ supports_interface_shared_objects: true
}
toolchain {
@@ -195,6 +199,7 @@
cxx_builtin_include_directory: "C:/msys64/mingw64/x86_64-w64-mingw32/include"
objcopy_embed_flag: "-I"
objcopy_embed_flag: "binary"
+ supports_interface_shared_objects: true
}
toolchain {
@@ -228,6 +233,8 @@
objcopy_embed_flag: "-I"
objcopy_embed_flag: "binary"
builtin_sysroot: ""
+
+ supports_interface_shared_objects: true
}
# For Android rules where no Android NDK is specified
@@ -268,4 +275,6 @@
tool_path { name: "objcopy" path: "/bin/false" }
tool_path { name: "objdump" path: "/bin/false" }
tool_path { name: "strip" path: "/bin/false" }
+
+ supports_interface_shared_objects: true
}
diff --git a/src/test/java/com/google/devtools/build/lib/rules/cpp/CcLibraryConfiguredTargetTest.java b/src/test/java/com/google/devtools/build/lib/rules/cpp/CcLibraryConfiguredTargetTest.java
new file mode 100644
index 0000000..1dad94f
--- /dev/null
+++ b/src/test/java/com/google/devtools/build/lib/rules/cpp/CcLibraryConfiguredTargetTest.java
@@ -0,0 +1,818 @@
+// 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.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+
+import com.google.common.base.Predicate;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
+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.ConfiguredTarget;
+import com.google.devtools.build.lib.analysis.OutputGroupProvider;
+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.util.MockCcSupport;
+import com.google.devtools.build.lib.testutil.TestConstants;
+import com.google.devtools.build.lib.util.FileType;
+import com.google.devtools.build.lib.vfs.PathFragment;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * "White-box" unit test of cc_library rule.
+ */
+@RunWith(JUnit4.class)
+public class CcLibraryConfiguredTargetTest extends BuildViewTestCase {
+ private static final PathFragment STL_CPPMAP = new PathFragment("stl.cppmap");
+ private static final PathFragment CROSSTOOL_CPPMAP = new PathFragment("crosstool.cppmap");
+
+
+ @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.getProvider(CppCompilationContext.class).getCppModuleMap();
+ return (CppModuleMapAction) getGeneratingAction(cppModuleMap.getArtifact());
+ }
+
+ private void assertNoCppModuleMapAction(String label) throws Exception {
+ ConfiguredTarget target = getConfiguredTarget(label);
+ assertNull(target.getProvider(CppCompilationContext.class).getCppModuleMap());
+ }
+
+
+ @Test
+ public void testFilesToBuild() throws Exception {
+ ConfiguredTarget hello = getConfiguredTarget("//hello:hello");
+ Artifact archive = getBinArtifact("libhello.a", hello);
+ Artifact implSharedObject = getBinArtifact("libhello.so", hello);
+ Artifact implInterfaceSharedObject = getBinArtifact("libhello.ifso", hello);
+ Artifact implSharedObjectLink = getSharedArtifact("_solib_k8/libhello_Slibhello.so", hello);
+ Artifact implInterfaceSharedObjectLink =
+ getSharedArtifact("_solib_k8/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 testFilesToBuildWithInterfaceSharedObjects() throws Exception {
+ useConfiguration("--interface_shared_objects");
+ ConfiguredTarget hello = getConfiguredTarget("//hello:hello");
+ Artifact archive = getBinArtifact("libhello.a", hello);
+ Artifact sharedObject = getBinArtifact("libhello.ifso", hello);
+ Artifact implSharedObject = getBinArtifact("libhello.so", hello);
+ Artifact sharedObjectLink = getSharedArtifact("_solib_k8/libhello_Slibhello.ifso", hello);
+ Artifact implSharedObjectLink = getSharedArtifact("_solib_k8/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");
+ assertTrue(hello.getProvider(CcLinkParamsProvider.class)
+ .getCcLinkParams(false, false).getLinkopts().isEmpty());
+ }
+
+ @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");
+ 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();
+ ExtraActionInfo info = builder.build();
+ assertEquals("CppLink", info.getMnemonic());
+
+ CppLinkInfo cppLinkInfo = info.getExtension(CppLinkInfo.cppLinkInfo);
+ assertNotNull(cppLinkInfo);
+
+ Iterable<String> inputs = Artifact.asExecPaths(
+ LinkerInputs.toLibraryArtifacts(action.getLinkCommandLine().getLinkerInputs()));
+ assertThat(cppLinkInfo.getInputFileList()).containsExactlyElementsIn(inputs);
+ assertEquals(action.getPrimaryOutput().getExecPathString(), cppLinkInfo.getOutputFile());
+ assertFalse(cppLinkInfo.hasInterfaceOutputFile());
+ assertEquals(action.getLinkCommandLine().getLinkTargetType().name(),
+ cppLinkInfo.getLinkTargetType());
+ assertEquals(action.getLinkCommandLine().getLinkStaticness().name(),
+ cppLinkInfo.getLinkStaticness());
+ Iterable<String> linkstamps = Artifact.asExecPaths(
+ action.getLinkCommandLine().getLinkstamps().values());
+ assertThat(cppLinkInfo.getLinkStampList()).containsExactlyElementsIn(linkstamps);
+ Iterable<String> buildInfoHeaderArtifacts = Artifact.asExecPaths(
+ action.getLinkCommandLine().getBuildInfoHeaderArtifacts());
+ assertThat(cppLinkInfo.getBuildInfoHeaderArtifactList())
+ .containsExactlyElementsIn(buildInfoHeaderArtifacts);
+ assertThat(cppLinkInfo.getLinkOptList())
+ .containsExactlyElementsIn(action.getLinkCommandLine().getLinkopts());
+ }
+
+ @Test
+ public void testCppLinkActionExtraActionInfoWithSharedLibraries() throws Exception {
+ 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();
+ ExtraActionInfo info = builder.build();
+ assertEquals("CppLink", info.getMnemonic());
+
+ CppLinkInfo cppLinkInfo = info.getExtension(CppLinkInfo.cppLinkInfo);
+ assertNotNull(cppLinkInfo);
+
+ Iterable<String> inputs = Artifact.asExecPaths(
+ LinkerInputs.toLibraryArtifacts(action.getLinkCommandLine().getLinkerInputs()));
+ assertThat(cppLinkInfo.getInputFileList()).containsExactlyElementsIn(inputs);
+ assertEquals(action.getPrimaryOutput().getExecPathString(), cppLinkInfo.getOutputFile());
+ Artifact interfaceOutput = action.getLinkCommandLine().getInterfaceOutput();
+ assertEquals(interfaceOutput.getExecPathString(), cppLinkInfo.getInterfaceOutputFile());
+ assertEquals(action.getLinkCommandLine().getLinkTargetType().name(),
+ cppLinkInfo.getLinkTargetType());
+ assertEquals(action.getLinkCommandLine().getLinkStaticness().name(),
+ cppLinkInfo.getLinkStaticness());
+ Iterable<String> linkstamps = Artifact.asExecPaths(
+ action.getLinkCommandLine().getLinkstamps().values());
+ assertThat(cppLinkInfo.getLinkStampList()).containsExactlyElementsIn(linkstamps);
+ Iterable<String> buildInfoHeaderArtifacts = Artifact.asExecPaths(
+ action.getLinkCommandLine().getBuildInfoHeaderArtifacts());
+ assertThat(cppLinkInfo.getBuildInfoHeaderArtifactList())
+ .containsExactlyElementsIn(buildInfoHeaderArtifacts);
+ assertThat(cppLinkInfo.getLinkOptList())
+ .containsExactlyElementsIn(action.getLinkCommandLine().getLinkopts());
+ }
+
+ @Test
+ public void testArtifactsToAlwaysBuild() throws Exception {
+ // ArtifactsToAlwaysBuild should apply both for static libraries.
+ ConfiguredTarget helloStatic = getConfiguredTarget("//hello:hello_static");
+ assertEquals(ImmutableSet.of("bin hello/_objs/hello_static/hello/hello.pic.o"),
+ artifactsToStrings(getOutputGroup(helloStatic, OutputGroupProvider.HIDDEN_TOP_LEVEL)));
+ Artifact implSharedObject = getBinArtifact("libhello_static.so", helloStatic);
+ assertThat(getFilesToBuild(helloStatic)).doesNotContain(implSharedObject);
+
+ // And for shared libraries.
+ ConfiguredTarget hello = getConfiguredTarget("//hello:hello");
+ assertEquals(ImmutableSet.of("bin hello/_objs/hello_static/hello/hello.pic.o"),
+ artifactsToStrings(getOutputGroup(helloStatic, OutputGroupProvider.HIDDEN_TOP_LEVEL)));
+ implSharedObject = getBinArtifact("libhello.so", hello);
+ assertThat(getFilesToBuild(hello)).contains(implSharedObject);
+ }
+
+ @Test
+ public void testTransitiveArtifactsToAlwaysBuildStatic() throws Exception {
+ 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'])");
+ assertEquals(
+ ImmutableSet.of(
+ "bin foo/_objs/x/foo/x.pic.o",
+ "bin foo/_objs/y/foo/y.pic.o",
+ "bin foo/_objs/z/foo/z.pic.o"),
+ artifactsToStrings(getOutputGroup(x, OutputGroupProvider.HIDDEN_TOP_LEVEL)));
+ }
+
+ @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(new PathFragment(flag).getBaseName());
+ }
+ }
+ return names;
+ }
+
+ @Test
+ public void testCompileHeaderModules() throws Exception {
+ AnalysisMock.get()
+ .ccSupport()
+ .setupCrosstool(
+ mockToolsConfig,
+ "" + "feature { name: 'header_modules' }" + "feature { name: 'module_maps' }");
+ useConfiguration();
+ 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"),
+ getSourceArtifact("module/b.h"),
+ getSourceArtifact("module/t.h"));
+ assertThat(aObjectAction.getInputs()).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();
+ 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())).containsExactly(
+ getBinArtifact("_objs/b/module/b.pic.pcm", "//module:b"));
+ // 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"),
+ getSourceArtifact("module/b.h"));
+ 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())).containsExactly(
+ getBinArtifact("_objs/b/module/b.pic.pcm", "//module:b"));
+ // All headers of transitive dependencies that are built as modules are needed as entry points
+ // for include scanning.
+ assertThat(dObjectAction.getIncludeScannerSources()).containsExactly(
+ getSourceArtifact("module/b.h"),
+ 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.getInputs())).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"),
+ getSourceArtifact("module/b.h"),
+ getSourceArtifact("module/g.h"));
+ assertThat(jObjectAction.getMainIncludeScannerSource()).isEqualTo(
+ getSourceArtifact("module/j.cc"));
+ assertThat(getHeaderModuleFlags(jObjectAction.getCompilerOptions()))
+ .containsExactly("b.pic.pcm", "g.pic.pcm");
+ }
+
+ @Test
+ public void testCompileUsingHeaderModulesTransitivelyWithTranstiveModuleMaps() throws Exception {
+ AnalysisMock.get()
+ .ccSupport()
+ .setupCrosstool(
+ mockToolsConfig,
+ MockCcSupport.HEADER_MODULES_FEATURE_CONFIGURATION
+ + "feature { name: 'transitive_module_maps' }");
+ useConfiguration("--features=transitive_module_maps");
+ 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("y.cppmap", "//nomodule:y"),
+ getGenfilesArtifact("z.cppmap", "//nomodule:z"),
+ getGenfilesArtifact("a.cppmap", "//nomodule:a"),
+ 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("y.cppmap", "//nomodule:y"),
+ getGenfilesArtifact("z.cppmap", "//nomodule:z"),
+ getGenfilesArtifact("a.cppmap", "//nomodule:a"),
+ getGenfilesArtifact("b.cppmap", "//module:b"),
+ getGenfilesArtifact("c.cppmap", "//nomodule:e"));
+ assertThat(getHeaderModuleFlags(cObjectAction.getCompilerOptions()))
+ .containsExactly("b.pic.pcm");
+
+ 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("y.cppmap", "//nomodule:y"),
+ getGenfilesArtifact("z.cppmap", "//nomodule:z"),
+ getGenfilesArtifact("a.cppmap", "//nomodule:a"),
+ getGenfilesArtifact("b.cppmap", "//module:b"),
+ getGenfilesArtifact("c.cppmap", "//nomodule:c"),
+ getGenfilesArtifact("d.cppmap", "//nomodule:d"));
+ assertThat(getHeaderModuleFlags(dObjectAction.getCompilerOptions()))
+ .containsExactly("b.pic.pcm");
+ }
+
+ @Test
+ public void testCompileUsingHeaderModulesTransitively() throws Exception {
+ AnalysisMock.get()
+ .ccSupport()
+ .setupCrosstool(mockToolsConfig, MockCcSupport.HEADER_MODULES_FEATURE_CONFIGURATION);
+ useConfiguration();
+ 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(getHeaderModuleFlags(cObjectAction.getCompilerOptions()))
+ .containsExactly("b.pic.pcm");
+
+ 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(getHeaderModuleFlags(dObjectAction.getCompilerOptions()))
+ .containsExactly("b.pic.pcm");
+ }
+
+ @Test
+ public void testTopLevelHeaderModules() throws Exception {
+ AnalysisMock.get()
+ .ccSupport()
+ .setupCrosstool(
+ mockToolsConfig,
+ MockCcSupport.HEADER_MODULES_FEATURE_CONFIGURATION
+ + "feature { name: 'header_module_includes_dependencies' }");
+ useConfiguration();
+ setupPackagesForModuleTests(/*useHeaderModules=*/false);
+ getConfiguredTarget("//module:j");
+ Artifact jObjectArtifact = getBinArtifact("_objs/j/module/j.pic.o", "//module:j");
+ CppCompileAction jObjectAction = (CppCompileAction) getGeneratingAction(jObjectArtifact);
+ assertThat(getHeaderModuleFlags(jObjectAction.getCompilerOptions()))
+ .containsExactly("g.pic.pcm");
+ }
+
+ private void writeSimpleCcLibrary() throws Exception {
+ scratch.file("module/BUILD",
+ "cc_library(",
+ " name = 'map',",
+ " srcs = ['a.cc', 'a.h'],",
+ ")");
+ }
+
+ @Test
+ public void testNoCppModuleMap() throws Exception {
+ AnalysisMock.get()
+ .ccSupport()
+ .setupCrosstool(mockToolsConfig, "feature { name: 'no_legacy_features' }");
+ 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");
+ assertEquals(ImmutableSet.of("src module/a.h"),
+ artifactsToStrings(action.getPrivateHeaders()));
+ 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 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",
+ "-isystem", TestConstants.GCC_INCLUDE_PATH));
+ }
+
+ @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();
+ assertThat(flags).contains("-fastbuild");
+ assertThat(flags).containsNoneOf("-opt", "-dbg");
+
+ flags = getCompilationModeFlags("-c", "fastbuild");
+ assertThat(flags).contains("-fastbuild");
+ assertThat(flags).containsNoneOf("-opt", "-dbg");
+
+ flags = getCompilationModeFlags("-c", "opt");
+ assertThat(flags).contains("-opt");
+ assertThat(flags).containsNoneOf("-fastbuild", "-dbg");
+
+ flags = getCompilationModeFlags("-c", "dbg");
+ assertThat(flags).contains("-dbg");
+ assertThat(flags).containsNoneOf("-fastbuild", "-opt");
+ }
+
+ @Test
+ public void testIncludePathsOutsideExecutionRoot() throws Exception {
+ checkError("root", "a",
+ "The include path 'd/../../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);
+ }
+}