Open-source cpp tests
Open-source:
- lib/rules/cpp/CcBadDependenciesTest.java
- lib/rules/cpp/CcBinaryThinLtoTest.java
- lib/rules/cpp/CcCompilationHelperTest.java
- lib/rules/cpp/CppConfigurationSkylarkTest.java
- lib/rules/cpp/CppSysrootTest.java
- lib/rules/cpp/LinkCommandLineTest.java
- lib/rules/cpp/SpawnGccStrategyTest.java
Some test cases of CcBinaryThinLtoTest don't work
on Windows. Those have been moved into
NonWindowsCcBinaryThinLtoTest.
RELNOTES: none
PiperOrigin-RevId: 293759606
diff --git a/src/test/java/com/google/devtools/build/lib/rules/cpp/BUILD b/src/test/java/com/google/devtools/build/lib/rules/cpp/BUILD
index 397ec4c..d445555 100644
--- a/src/test/java/com/google/devtools/build/lib/rules/cpp/BUILD
+++ b/src/test/java/com/google/devtools/build/lib/rules/cpp/BUILD
@@ -19,8 +19,12 @@
exclude = [
"CcImportBaseConfiguredTargetTest.java",
"SkylarkCcCommonTestHelper.java",
+ "NonWindowsCcBinaryThinLtoTest.java",
],
- ) + ["proto/CcProtoLibraryTest.java"],
+ ) + ["proto/CcProtoLibraryTest.java"] + select({
+ "//src/conditions:windows": [],
+ "//conditions:default": ["NonWindowsCcBinaryThinLtoTest.java"],
+ }),
resources = [
"@rules_cc//cc:srcs",
],
@@ -46,6 +50,7 @@
"//src/main/java/com/google/devtools/build/lib/cmdline",
"//src/main/java/com/google/devtools/build/lib/collect/nestedset",
"//src/main/java/com/google/devtools/build/lib/concurrent",
+ "//src/main/java/com/google/devtools/build/lib/rules/apple",
"//src/main/java/com/google/devtools/build/lib/rules/cpp",
"//src/main/java/com/google/devtools/build/lib/rules/platform",
"//src/main/java/com/google/devtools/build/lib/skyframe/serialization",
diff --git a/src/test/java/com/google/devtools/build/lib/rules/cpp/CcBadDependenciesTest.java b/src/test/java/com/google/devtools/build/lib/rules/cpp/CcBadDependenciesTest.java
new file mode 100644
index 0000000..9f03e68
--- /dev/null
+++ b/src/test/java/com/google/devtools/build/lib/rules/cpp/CcBadDependenciesTest.java
@@ -0,0 +1,67 @@
+// Copyright 2020 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.
+
+package com.google.devtools.build.lib.rules.cpp;
+
+import com.google.devtools.build.lib.analysis.ConfiguredTarget;
+import com.google.devtools.build.lib.analysis.util.BuildViewTestCase;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+/** A test for dependencies between C++ libraries. */
+@RunWith(JUnit4.class)
+public final class CcBadDependenciesTest extends BuildViewTestCase {
+ private ConfiguredTarget configure(String targetLabel) throws Exception {
+ return getConfiguredTarget(targetLabel);
+ }
+
+ @Test
+ public void testRejectsSingleUnknownSourceFile() throws Exception {
+ reporter.removeHandler(failFastHandler);
+ scratch.file("foo/BUILD",
+ "cc_library(name = 'foo', srcs = ['unknown.oops'])");
+ scratch.file("foo/unknown.oops", "foo");
+ configure("//foo:foo");
+ assertContainsEvent(getErrorMsgMisplacedFiles(
+ "srcs", "cc_library", "//foo:foo", "//foo:unknown.oops"));
+ }
+
+ @Test
+ public void testAcceptsDependencyWithAtLeastOneGoodSource() throws Exception {
+ scratch.file("dependency/BUILD",
+ "genrule(name = 'goodandbad_gen', ",
+ " cmd = '/bin/true',",
+ " outs = ['good.cc', 'bad.oops'])");
+ scratch.file("foo/BUILD",
+ "cc_library(name = 'foo',",
+ " srcs = ['//dependency:goodandbad_gen'])");
+ configure("//foo:foo");
+ }
+
+ @Test
+ public void testRejectsBadGeneratedFile() throws Exception {
+ reporter.removeHandler(failFastHandler);
+ scratch.file("dependency/BUILD",
+ "genrule(name = 'generated', ",
+ " cmd = '/bin/true',",
+ " outs = ['bad.oops'])");
+ scratch.file("foo/BUILD",
+ "cc_library(name = 'foo',",
+ " srcs = ['//dependency:generated'])");
+ configure("//foo:foo");
+ assertContainsEvent(
+ getErrorMsgNoGoodFiles("srcs", "cc_library", "//foo:foo", "//dependency:generated"));
+ }
+}
diff --git a/src/test/java/com/google/devtools/build/lib/rules/cpp/CcBinaryThinLtoTest.java b/src/test/java/com/google/devtools/build/lib/rules/cpp/CcBinaryThinLtoTest.java
new file mode 100644
index 0000000..0a8ab0d
--- /dev/null
+++ b/src/test/java/com/google/devtools/build/lib/rules/cpp/CcBinaryThinLtoTest.java
@@ -0,0 +1,1934 @@
+// Copyright 2020 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.
+package com.google.devtools.build.lib.rules.cpp;
+
+import static com.google.common.truth.Truth.assertThat;
+import static com.google.common.truth.Truth.assertWithMessage;
+
+import com.google.common.base.Joiner;
+import com.google.common.collect.ImmutableList;
+import com.google.devtools.build.lib.actions.Action;
+import com.google.devtools.build.lib.actions.ActionAnalysisMetadata;
+import com.google.devtools.build.lib.actions.Artifact;
+import com.google.devtools.build.lib.actions.util.ActionsTestUtil;
+import com.google.devtools.build.lib.analysis.ConfiguredTarget;
+import com.google.devtools.build.lib.analysis.RuleContext;
+import com.google.devtools.build.lib.analysis.actions.SpawnAction;
+import com.google.devtools.build.lib.analysis.util.AnalysisMock;
+import com.google.devtools.build.lib.analysis.util.BuildViewTestCase;
+import com.google.devtools.build.lib.cmdline.RepositoryName;
+import com.google.devtools.build.lib.packages.util.Crosstool.CcToolchainConfig;
+import com.google.devtools.build.lib.packages.util.MockCcSupport;
+import com.google.devtools.build.lib.rules.cpp.CppConfiguration.Tool;
+import com.google.devtools.build.lib.skyframe.ConfiguredTargetKey;
+import com.google.devtools.build.lib.skyframe.ConfiguredTargetValue;
+import com.google.devtools.build.lib.vfs.PathFragment;
+import java.io.IOException;
+import java.util.List;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+/** Tests for cc_binary with ThinLTO. */
+@RunWith(JUnit4.class)
+public class CcBinaryThinLtoTest extends BuildViewTestCase {
+
+ @Before
+ public void createBasePkg() throws IOException {
+ scratch.overwriteFile(
+ "base/BUILD", "cc_library(name = 'system_malloc', visibility = ['//visibility:public'])");
+ }
+
+ public void createBuildFiles(String targetName, String... extraCcBinaryParameters)
+ throws Exception {
+ scratch.file(
+ "pkg/BUILD",
+ "package(features = ['thin_lto'])",
+ "",
+ "cc_binary(name = '" + targetName + "',",
+ " srcs = ['binfile.cc', ],",
+ " deps = [ ':lib' ], ",
+ String.join("", extraCcBinaryParameters),
+ " malloc = '//base:system_malloc')",
+ "cc_library(name = 'lib',",
+ " srcs = ['libfile.cc'],",
+ " hdrs = ['libfile.h'],",
+ " linkstamp = 'linkstamp.cc',",
+ " )");
+
+ scratch.file("pkg/binfile.cc", "#include \"pkg/libfile.h\"", "int main() { return pkg(); }");
+ scratch.file("pkg/libfile.cc", "int pkg() { return 42; }");
+ scratch.file("pkg/libfile.h", "int pkg();");
+ scratch.file("pkg/linkstamp.cc");
+ }
+
+ public void createTestFiles(String extraTestParameters, String extraLibraryParameters)
+ throws Exception {
+ scratch.file(
+ "pkg/BUILD",
+ "package(features = ['thin_lto'])",
+ "cc_test(",
+ " name = 'bin_test',",
+ " srcs = ['bin_test.cc', ],",
+ " deps = [ ':lib' ], ",
+ extraTestParameters,
+ " malloc = '//base:system_malloc'",
+ ")",
+ "cc_test(",
+ " name = 'bin_test2',",
+ " srcs = ['bin_test2.cc', ],",
+ " deps = [ ':lib' ], ",
+ extraTestParameters,
+ " malloc = '//base:system_malloc'",
+ ")",
+ "cc_library(",
+ " name = 'lib',",
+ " srcs = ['libfile.cc'],",
+ " hdrs = ['libfile.h'],",
+ extraLibraryParameters,
+ " linkstamp = 'linkstamp.cc',",
+ ")");
+
+ scratch.file("pkg/bin_test.cc", "#include \"pkg/libfile.h\"", "int main() { return pkg(); }");
+ scratch.file("pkg/bin_test2.cc", "#include \"pkg/libfile.h\"", "int main() { return pkg(); }");
+ scratch.file("pkg/libfile.cc", "int pkg() { return 42; }");
+ scratch.file("pkg/libfile.h", "int pkg();");
+ scratch.file("pkg/linkstamp.cc");
+ }
+
+ @Test
+ public void testActionGraph() throws Exception {
+ createBuildFiles("bin");
+
+ AnalysisMock.get()
+ .ccSupport()
+ .setupCcToolchainConfig(
+ mockToolsConfig,
+ CcToolchainConfig.builder()
+ .withFeatures(
+ CppRuleClasses.THIN_LTO,
+ MockCcSupport.HOST_AND_NONHOST_CONFIGURATION_FEATURES,
+ CppRuleClasses.SUPPORTS_PIC,
+ CppRuleClasses.SUPPORTS_START_END_LIB));
+ useConfiguration("--noincompatible_make_thinlto_command_lines_standalone");
+
+ /*
+ We follow the chain from the final product backwards.
+
+ binary <=[Link]=
+ .lto/...o <=[LTOBackend]=
+ {.o.thinlto.bc,.o.imports} <=[LTOIndexing]=
+ .o <= [CppCompile] .cc
+ */
+ ConfiguredTarget pkg = getConfiguredTarget("//pkg:bin");
+
+ Artifact pkgArtifact = getFilesToBuild(pkg).getSingleton();
+
+ CppLinkAction linkAction = (CppLinkAction) getGeneratingAction(pkgArtifact);
+ assertThat(linkAction.getOutputs()).containsExactly(pkgArtifact);
+ assertThat(ActionsTestUtil.getFirstArtifactEndingWith(linkAction.getInputs(), "linkstamp.o"))
+ .isNotNull();
+
+ List<String> commandLine = linkAction.getLinkCommandLine().getRawLinkArgv();
+ String prefix = getTargetConfiguration().getOutputDirectory(RepositoryName.MAIN)
+ .getExecPathString();
+ assertThat(commandLine)
+ .containsAtLeast(
+ prefix + "/bin/pkg/bin.lto.merged.o",
+ "thinlto_param_file=" + prefix + "/bin/pkg/bin-lto-final.params")
+ .inOrder();
+
+ // We have no bitcode files: all files have pkg/bin.lto/
+ for (String arg : commandLine) {
+ if (arg.contains("_objs") && !arg.contains("linkstamp.o")) {
+ assertThat(arg).contains("pkg/bin.lto");
+ }
+ }
+
+ assertThat(artifactsToStrings(linkAction.getInputs()))
+ .containsAtLeast(
+ "bin pkg/bin.lto/pkg/_objs/bin/binfile.pic.o",
+ "bin pkg/bin.lto/pkg/_objs/lib/libfile.pic.o",
+ "bin pkg/bin-2.params",
+ "bin pkg/bin-lto-final.params");
+
+ LtoBackendAction backendAction =
+ (LtoBackendAction)
+ getPredecessorByInputName(linkAction, "pkg/bin.lto/pkg/_objs/bin/binfile.pic.o");
+ assertThat(backendAction.getMnemonic()).isEqualTo("CcLtoBackendCompile");
+
+ assertThat(artifactsToStrings(backendAction.getInputs()))
+ .containsAtLeast(
+ "bin pkg/bin.lto/pkg/_objs/bin/binfile.pic.o.thinlto.bc",
+ "bin pkg/bin.lto/pkg/_objs/bin/binfile.pic.o.imports");
+
+ assertThat(backendAction.getArguments())
+ .containsAtLeast(
+ "thinlto_index=" + prefix + "/bin/pkg/bin.lto/pkg/_objs/bin/binfile.pic.o.thinlto.bc",
+ "thinlto_output_object_file=" + prefix + "/bin/pkg/bin.lto/pkg/_objs/bin/binfile.pic.o",
+ "thinlto_input_bitcode_file=" + prefix + "/bin/pkg/_objs/bin/binfile.pic.o");
+
+ CppLinkAction indexAction =
+ (CppLinkAction)
+ getPredecessorByInputName(
+ backendAction, "pkg/bin.lto/pkg/_objs/bin/binfile.pic.o.thinlto.bc");
+
+ ConfiguredTargetValue configuredTargetValue =
+ (ConfiguredTargetValue)
+ getSkyframeExecutor()
+ .getEvaluatorForTesting()
+ .getExistingEntryAtLatestVersion(
+ ConfiguredTargetKey.of(pkg.getLabel(), getConfiguration(pkg)))
+ .getValue();
+ ImmutableList<ActionAnalysisMetadata> linkstampCompileActions =
+ configuredTargetValue
+ .getActions()
+ .stream()
+ .filter(a -> a.getMnemonic().equals("CppLinkstampCompile"))
+ .collect(ImmutableList.toImmutableList());
+ assertThat(linkstampCompileActions).hasSize(1);
+ ActionAnalysisMetadata linkstampCompileAction = linkstampCompileActions.get(0);
+ assertThat(indexAction.getInputs().toList())
+ .doesNotContain(linkstampCompileAction.getOutputs());
+
+ assertThat(indexAction.getArguments())
+ .containsAtLeast(
+ "param_file=" + prefix + "/bin/pkg/bin-lto-final.params",
+ "prefix_replace=" + prefix + "/bin;" + prefix + "/bin/pkg/bin.lto",
+ "thinlto_merged_object_file=" + prefix + "/bin/pkg/bin.lto.merged.o",
+ "object_suffix_replace=.indexing.o;.o");
+ assertThat(indexAction.getArguments())
+ .doesNotContain("thinlto_param_file=" + prefix + "/bin/pkg/bin-lto-final.params");
+
+ assertThat(artifactsToStrings(indexAction.getOutputs()))
+ .containsAtLeast(
+ "bin pkg/bin.lto/pkg/_objs/bin/binfile.pic.o.imports",
+ "bin pkg/bin.lto/pkg/_objs/bin/binfile.pic.o.thinlto.bc",
+ "bin pkg/bin.lto/pkg/_objs/lib/libfile.pic.o.imports",
+ "bin pkg/bin.lto/pkg/_objs/lib/libfile.pic.o.thinlto.bc",
+ "bin pkg/bin-lto-final.params");
+
+ assertThat(indexAction.getMnemonic()).isEqualTo("CppLTOIndexing");
+
+ assertThat(artifactsToStrings(indexAction.getInputs()))
+ .containsAtLeast(
+ "bin pkg/_objs/bin/binfile.pic.indexing.o", "bin pkg/_objs/lib/libfile.pic.indexing.o");
+
+ CppCompileAction bitcodeAction =
+ (CppCompileAction)
+ getPredecessorByInputName(indexAction, "pkg/_objs/bin/binfile.pic.indexing.o");
+ assertThat(bitcodeAction.getMnemonic()).isEqualTo("CppCompile");
+ assertThat(bitcodeAction.getArguments())
+ .contains("lto_indexing_bitcode=" + prefix + "/bin/pkg/_objs/bin/binfile.pic.indexing.o");
+ }
+
+ @Test
+ public void testLinkshared() throws Exception {
+ createBuildFiles("bin.so", "linkshared = 1,");
+
+ AnalysisMock.get()
+ .ccSupport()
+ .setupCcToolchainConfig(
+ mockToolsConfig,
+ CcToolchainConfig.builder()
+ .withFeatures(
+ CppRuleClasses.THIN_LTO,
+ CppRuleClasses.SUPPORTS_PIC,
+ MockCcSupport.HOST_AND_NONHOST_CONFIGURATION_FEATURES,
+ CppRuleClasses.SUPPORTS_START_END_LIB));
+ useConfiguration();
+
+ ConfiguredTarget pkg = getConfiguredTarget("//pkg:bin.so");
+
+ Artifact pkgArtifact = getFilesToBuild(pkg).getSingleton();
+
+ CppLinkAction linkAction = (CppLinkAction) getGeneratingAction(pkgArtifact);
+ assertThat(linkAction.getOutputs()).containsExactly(pkgArtifact);
+
+ Action backendAction =
+ getPredecessorByInputName(linkAction, "pkg/bin.so.lto/pkg/_objs/bin.so/binfile.pic.o");
+ assertThat(backendAction.getMnemonic()).isEqualTo("CcLtoBackendCompile");
+ }
+
+ @Test
+ public void testNoLinkstatic() throws Exception {
+ createBuildFiles("bin", "linkstatic = 0,");
+
+ AnalysisMock.get()
+ .ccSupport()
+ .setupCcToolchainConfig(
+ mockToolsConfig,
+ CcToolchainConfig.builder()
+ .withFeatures(
+ CppRuleClasses.THIN_LTO,
+ MockCcSupport.HOST_AND_NONHOST_CONFIGURATION_FEATURES,
+ CppRuleClasses.SUPPORTS_DYNAMIC_LINKER,
+ CppRuleClasses.SUPPORTS_START_END_LIB,
+ CppRuleClasses.SUPPORTS_PIC,
+ CppRuleClasses.SUPPORTS_INTERFACE_SHARED_LIBRARIES));
+ useConfiguration("--noincompatible_make_thinlto_command_lines_standalone");
+
+ /*
+ We follow the chain from the final product backwards to verify intermediate actions.
+
+ binary <=[Link]=
+ .ifso <=[SolibSymlink]=
+ _S...ifso <=[SolibSymlink]=
+ .ifso <=[Link]=
+ .lto/...o <=[LTOBackend]=
+ {.o.thinlto.bc,.o.imports} <=[LTOIndexing]=
+ .o <= [CppCompile] .cc
+ */
+ ConfiguredTarget pkg = getConfiguredTarget("//pkg:bin");
+
+ Artifact pkgArtifact = getFilesToBuild(pkg).getSingleton();
+
+ CppLinkAction linkAction = (CppLinkAction) getGeneratingAction(pkgArtifact);
+ assertThat(linkAction.getOutputs()).containsExactly(pkgArtifact);
+
+ List<String> commandLine = linkAction.getLinkCommandLine().getRawLinkArgv();
+ String prefix = getTargetConfiguration().getOutputDirectory(RepositoryName.MAIN)
+ .getExecPathString();
+
+ assertThat(commandLine).contains("-Wl,@" + prefix + "/bin/pkg/bin-lto-final.params");
+
+ // We have no bitcode files: all files have pkg/bin.lto/
+ for (String arg : commandLine) {
+ if (arg.contains("_objs") && !arg.contains("linkstamp.o")) {
+ assertThat(arg).contains("pkg/bin.lto");
+ }
+ }
+
+ assertThat(artifactsToStrings(linkAction.getInputs()))
+ .containsAtLeast(
+ "bin pkg/bin.lto/pkg/_objs/bin/binfile.pic.o",
+ "bin _solib_k8/libpkg_Sliblib.ifso",
+ "bin pkg/bin-2.params",
+ "bin pkg/bin-lto-final.params");
+
+ SolibSymlinkAction solibSymlinkAction =
+ (SolibSymlinkAction) getPredecessorByInputName(linkAction, "_solib_k8/libpkg_Sliblib.ifso");
+ assertThat(solibSymlinkAction.getMnemonic()).isEqualTo("SolibSymlink");
+
+ CppLinkAction libLinkAction =
+ (CppLinkAction) getPredecessorByInputName(solibSymlinkAction, "bin/pkg/liblib.ifso");
+ assertThat(libLinkAction.getMnemonic()).isEqualTo("CppLink");
+
+ LtoBackendAction backendAction =
+ (LtoBackendAction)
+ getPredecessorByInputName(
+ libLinkAction, "pkg/liblib.so.lto/pkg/_objs/lib/libfile.pic.o");
+ assertThat(backendAction.getMnemonic()).isEqualTo("CcLtoBackendCompile");
+
+ assertThat(artifactsToStrings(backendAction.getInputs()))
+ .contains("bin pkg/liblib.so.lto/pkg/_objs/lib/libfile.pic.o.thinlto.bc");
+
+ assertThat(backendAction.getArguments())
+ .containsAtLeast(
+ "thinlto_index="
+ + prefix
+ + "/bin/pkg/liblib.so.lto/pkg/_objs/lib/libfile.pic.o.thinlto.bc",
+ "thinlto_output_object_file="
+ + prefix
+ + "/bin/pkg/liblib.so.lto/pkg/_objs/lib/libfile.pic.o",
+ "thinlto_input_bitcode_file=" + prefix + "/bin/pkg/_objs/lib/libfile.pic.o");
+
+ CppLinkAction indexAction =
+ (CppLinkAction)
+ getPredecessorByInputName(
+ backendAction, "pkg/liblib.so.lto/pkg/_objs/lib/libfile.pic.o.thinlto.bc");
+
+ assertThat(indexAction.getArguments())
+ .containsAtLeast(
+ "param_file=" + prefix + "/bin/pkg/liblib.so-lto-final.params",
+ "prefix_replace=" + prefix + "/bin;" + prefix + "/bin/pkg/liblib.so.lto",
+ "object_suffix_replace=.indexing.o;.o");
+
+ assertThat(artifactsToStrings(indexAction.getOutputs()))
+ .containsAtLeast(
+ "bin pkg/liblib.so.lto/pkg/_objs/lib/libfile.pic.o.imports",
+ "bin pkg/liblib.so.lto/pkg/_objs/lib/libfile.pic.o.thinlto.bc",
+ "bin pkg/liblib.so-lto-final.params");
+
+ assertThat(indexAction.getMnemonic()).isEqualTo("CppLTOIndexing");
+
+ assertThat(artifactsToStrings(indexAction.getInputs()))
+ .contains("bin pkg/_objs/lib/libfile.pic.indexing.o");
+
+ CppCompileAction bitcodeAction =
+ (CppCompileAction)
+ getPredecessorByInputName(indexAction, "pkg/_objs/lib/libfile.pic.indexing.o");
+ assertThat(bitcodeAction.getMnemonic()).isEqualTo("CppCompile");
+ assertThat(bitcodeAction.getArguments())
+ .contains("lto_indexing_bitcode=" + prefix + "/bin/pkg/_objs/lib/libfile.pic.indexing.o");
+ }
+
+ /** Helper method to get the root prefix from the given dwpFile. */
+ private static PathFragment dwpRootPrefix(Artifact dwpFile) throws Exception {
+ return dwpFile
+ .getExecPath()
+ .subFragment(
+ 0, dwpFile.getExecPath().segmentCount() - dwpFile.getRootRelativePath().segmentCount());
+ }
+
+ /** Helper method that checks that a .dwp has the expected generating action structure. */
+ private void validateDwp(
+ RuleContext ruleContext,
+ Artifact dwpFile,
+ CcToolchainProvider toolchain,
+ List<String> expectedInputs)
+ throws Exception {
+ SpawnAction dwpAction = (SpawnAction) getGeneratingAction(dwpFile);
+ String dwpToolPath = toolchain.getToolPathFragment(Tool.DWP, ruleContext).getPathString();
+ assertThat(dwpAction.getMnemonic()).isEqualTo("CcGenerateDwp");
+ assertThat(dwpToolPath).isEqualTo(dwpAction.getCommandFilename());
+ List<String> commandArgs = dwpAction.getArguments();
+ // The first argument should be the command being executed.
+ assertThat(dwpToolPath).isEqualTo(commandArgs.get(0));
+ // The final two arguments should be "-o dwpOutputFile".
+ assertThat(commandArgs.subList(commandArgs.size() - 2, commandArgs.size()))
+ .containsExactly("-o", dwpFile.getExecPathString())
+ .inOrder();
+ // The remaining arguments should be the set of .dwo inputs (in any order).
+ assertThat(commandArgs.subList(1, commandArgs.size() - 2))
+ .containsExactlyElementsIn(expectedInputs);
+ }
+
+ @Test
+ public void testFission() throws Exception {
+ createBuildFiles("bin");
+
+ AnalysisMock.get()
+ .ccSupport()
+ .setupCcToolchainConfig(
+ mockToolsConfig,
+ CcToolchainConfig.builder()
+ .withFeatures(
+ CppRuleClasses.THIN_LTO,
+ CppRuleClasses.SUPPORTS_PIC,
+ MockCcSupport.HOST_AND_NONHOST_CONFIGURATION_FEATURES,
+ CppRuleClasses.SUPPORTS_START_END_LIB,
+ CppRuleClasses.PER_OBJECT_DEBUG_INFO));
+ useConfiguration("--fission=yes", "--copt=-g0");
+
+ ConfiguredTarget pkg = getConfiguredTarget("//pkg:bin");
+ Artifact pkgArtifact = getFilesToBuild(pkg).getSingleton();
+ CppLinkAction linkAction = (CppLinkAction) getGeneratingAction(pkgArtifact);
+
+ LtoBackendAction backendAction =
+ (LtoBackendAction)
+ getPredecessorByInputName(linkAction, "pkg/bin.lto/pkg/_objs/bin/binfile.pic.o");
+ assertThat(backendAction.getMnemonic()).isEqualTo("CcLtoBackendCompile");
+ assertThat(artifactsToStrings(backendAction.getOutputs()))
+ .containsExactly(
+ "bin pkg/bin.lto/pkg/_objs/bin/binfile.pic.o",
+ "bin pkg/bin.lto/pkg/_objs/bin/binfile.pic.dwo");
+
+ assertThat(backendAction.getArguments()).containsAtLeast("-g0", "per_object_debug_info_option");
+
+ backendAction =
+ (LtoBackendAction)
+ getPredecessorByInputName(linkAction, "pkg/bin.lto/pkg/_objs/lib/libfile.pic.o");
+ assertThat(backendAction.getMnemonic()).isEqualTo("CcLtoBackendCompile");
+ assertThat(artifactsToStrings(backendAction.getOutputs()))
+ .containsExactly(
+ "bin pkg/bin.lto/pkg/_objs/lib/libfile.pic.o",
+ "bin pkg/bin.lto/pkg/_objs/lib/libfile.pic.dwo");
+
+ assertThat(backendAction.getArguments()).contains("per_object_debug_info_option");
+
+ // Now check the dwp action.
+ Artifact dwpFile = getFileConfiguredTarget(pkg.getLabel() + ".dwp").getArtifact();
+ PathFragment rootPrefix = dwpRootPrefix(dwpFile);
+ RuleContext ruleContext = getRuleContext(pkg);
+ CcToolchainProvider toolchain =
+ CppHelper.getToolchainUsingDefaultCcToolchainAttribute(ruleContext);
+ validateDwp(
+ ruleContext,
+ dwpFile,
+ toolchain,
+ ImmutableList.of(
+ rootPrefix + "/pkg/bin.lto/pkg/_objs/lib/libfile.pic.dwo",
+ rootPrefix + "/pkg/bin.lto/pkg/_objs/bin/binfile.pic.dwo"));
+ }
+
+ @Test
+ public void testNoLinkstaticFission() throws Exception {
+ createBuildFiles("bin", "linkstatic = 0,");
+
+ AnalysisMock.get()
+ .ccSupport()
+ .setupCcToolchainConfig(
+ mockToolsConfig,
+ CcToolchainConfig.builder()
+ .withFeatures(
+ CppRuleClasses.THIN_LTO,
+ MockCcSupport.HOST_AND_NONHOST_CONFIGURATION_FEATURES,
+ CppRuleClasses.SUPPORTS_PIC,
+ CppRuleClasses.SUPPORTS_START_END_LIB,
+ CppRuleClasses.SUPPORTS_INTERFACE_SHARED_LIBRARIES,
+ CppRuleClasses.SUPPORTS_DYNAMIC_LINKER,
+ CppRuleClasses.PER_OBJECT_DEBUG_INFO));
+ useConfiguration("--fission=yes");
+
+ ConfiguredTarget pkg = getConfiguredTarget("//pkg:bin");
+ Artifact pkgArtifact = getFilesToBuild(pkg).getSingleton();
+ CppLinkAction linkAction = (CppLinkAction) getGeneratingAction(pkgArtifact);
+
+ SolibSymlinkAction solibSymlinkAction =
+ (SolibSymlinkAction) getPredecessorByInputName(linkAction, "_solib_k8/libpkg_Sliblib.ifso");
+ assertThat(solibSymlinkAction.getMnemonic()).isEqualTo("SolibSymlink");
+
+ CppLinkAction libLinkAction =
+ (CppLinkAction) getPredecessorByInputName(solibSymlinkAction, "bin/pkg/liblib.ifso");
+ assertThat(libLinkAction.getMnemonic()).isEqualTo("CppLink");
+
+ LtoBackendAction backendAction =
+ (LtoBackendAction)
+ getPredecessorByInputName(
+ libLinkAction, "pkg/liblib.so.lto/pkg/_objs/lib/libfile.pic.o");
+ assertThat(backendAction.getMnemonic()).isEqualTo("CcLtoBackendCompile");
+ assertThat(artifactsToStrings(backendAction.getOutputs()))
+ .containsExactly(
+ "bin pkg/liblib.so.lto/pkg/_objs/lib/libfile.pic.o",
+ "bin pkg/liblib.so.lto/pkg/_objs/lib/libfile.pic.dwo");
+
+ assertThat(backendAction.getArguments()).contains("per_object_debug_info_option");
+
+ // Check the dwp action.
+ Artifact dwpFile = getFileConfiguredTarget(pkg.getLabel() + ".dwp").getArtifact();
+ PathFragment rootPrefix = dwpRootPrefix(dwpFile);
+ RuleContext ruleContext = getRuleContext(pkg);
+ CcToolchainProvider toolchain =
+ CppHelper.getToolchainUsingDefaultCcToolchainAttribute(ruleContext);
+ validateDwp(
+ ruleContext,
+ dwpFile,
+ toolchain,
+ ImmutableList.of(rootPrefix + "/pkg/bin.lto/pkg/_objs/bin/binfile.pic.dwo"));
+ }
+
+ @Test
+ public void testLinkstaticCcTestFission() throws Exception {
+ createTestFiles("linkstatic = 1,", "");
+
+ AnalysisMock.get()
+ .ccSupport()
+ .setupCcToolchainConfig(
+ mockToolsConfig,
+ CcToolchainConfig.builder()
+ .withFeatures(
+ CppRuleClasses.THIN_LTO,
+ CppRuleClasses.SUPPORTS_PIC,
+ CppRuleClasses.SUPPORTS_START_END_LIB,
+ CppRuleClasses.THIN_LTO_LINKSTATIC_TESTS_USE_SHARED_NONLTO_BACKENDS,
+ MockCcSupport.HOST_AND_NONHOST_CONFIGURATION_FEATURES,
+ CppRuleClasses.PER_OBJECT_DEBUG_INFO));
+ useConfiguration(
+ "--fission=yes", "--features=thin_lto_linkstatic_tests_use_shared_nonlto_backends");
+
+ ConfiguredTarget pkg = getConfiguredTarget("//pkg:bin_test");
+ Artifact pkgArtifact = getFilesToBuild(pkg).getSingleton();
+ CppLinkAction linkAction = (CppLinkAction) getGeneratingAction(pkgArtifact);
+
+ // All backends should be shared non-LTO in this case
+ LtoBackendAction backendAction =
+ (LtoBackendAction)
+ getPredecessorByInputName(
+ linkAction, "shared.nonlto/pkg/_objs/bin_test/bin_test.pic.o");
+ assertThat(backendAction.getMnemonic()).isEqualTo("CcLtoBackendCompile");
+ assertThat(artifactsToStrings(backendAction.getOutputs()))
+ .containsExactly(
+ "bin shared.nonlto/pkg/_objs/bin_test/bin_test.pic.o",
+ "bin shared.nonlto/pkg/_objs/bin_test/bin_test.pic.dwo");
+
+ assertThat(backendAction.getArguments()).contains("per_object_debug_info_option");
+
+ backendAction =
+ (LtoBackendAction)
+ getPredecessorByInputName(linkAction, "shared.nonlto/pkg/_objs/lib/libfile.pic.o");
+ assertThat(backendAction.getMnemonic()).isEqualTo("CcLtoBackendCompile");
+ assertThat(backendAction.getArguments()).contains("-fPIC");
+ assertThat(artifactsToStrings(backendAction.getOutputs()))
+ .containsExactly(
+ "bin shared.nonlto/pkg/_objs/lib/libfile.pic.o",
+ "bin shared.nonlto/pkg/_objs/lib/libfile.pic.dwo");
+
+ assertThat(backendAction.getArguments()).contains("per_object_debug_info_option");
+
+ // Now check the dwp action.
+ Artifact dwpFile = getFileConfiguredTarget(pkg.getLabel() + ".dwp").getArtifact();
+ PathFragment rootPrefix = dwpRootPrefix(dwpFile);
+ RuleContext ruleContext = getRuleContext(pkg);
+ CcToolchainProvider toolchain =
+ CppHelper.getToolchainUsingDefaultCcToolchainAttribute(ruleContext);
+ validateDwp(
+ ruleContext,
+ dwpFile,
+ toolchain,
+ ImmutableList.of(
+ rootPrefix + "/shared.nonlto/pkg/_objs/lib/libfile.pic.dwo",
+ rootPrefix + "/shared.nonlto/pkg/_objs/bin_test/bin_test.pic.dwo"));
+ }
+
+ @Test
+ public void testLinkstaticCcTest() throws Exception {
+ createTestFiles("linkstatic = 1,", "");
+
+ AnalysisMock.get()
+ .ccSupport()
+ .setupCcToolchainConfig(
+ mockToolsConfig,
+ CcToolchainConfig.builder()
+ .withFeatures(
+ CppRuleClasses.THIN_LTO,
+ CppRuleClasses.SUPPORTS_PIC,
+ CppRuleClasses.SUPPORTS_START_END_LIB,
+ CppRuleClasses.THIN_LTO_LINKSTATIC_TESTS_USE_SHARED_NONLTO_BACKENDS,
+ MockCcSupport.HOST_AND_NONHOST_CONFIGURATION_FEATURES,
+ CppRuleClasses.PER_OBJECT_DEBUG_INFO));
+ useConfiguration("--features=thin_lto_linkstatic_tests_use_shared_nonlto_backends");
+
+ ConfiguredTarget pkg = getConfiguredTarget("//pkg:bin_test");
+ Artifact pkgArtifact = getFilesToBuild(pkg).getSingleton();
+ CppLinkAction linkAction = (CppLinkAction) getGeneratingAction(pkgArtifact);
+
+ ConfiguredTarget pkg2 = getConfiguredTarget("//pkg:bin_test2");
+ Artifact pkgArtifact2 = getFilesToBuild(pkg2).getSingleton();
+ CppLinkAction linkAction2 = (CppLinkAction) getGeneratingAction(pkgArtifact2);
+
+ // All backends should be shared non-LTO in this case
+ LtoBackendAction backendAction =
+ (LtoBackendAction)
+ getPredecessorByInputName(
+ linkAction, "shared.nonlto/pkg/_objs/bin_test/bin_test.pic.o");
+ assertThat(backendAction.getMnemonic()).isEqualTo("CcLtoBackendCompile");
+
+ backendAction =
+ (LtoBackendAction)
+ getPredecessorByInputName(linkAction, "shared.nonlto/pkg/_objs/lib/libfile.pic.o");
+ assertThat(backendAction.getMnemonic()).isEqualTo("CcLtoBackendCompile");
+ assertThat(backendAction.getArguments()).contains("-fPIC");
+
+ LtoBackendAction backendAction2 =
+ (LtoBackendAction)
+ getPredecessorByInputName(linkAction2, "shared.nonlto/pkg/_objs/lib/libfile.pic.o");
+ assertThat(backendAction2.getMnemonic()).isEqualTo("CcLtoBackendCompile");
+
+ assertThat(backendAction).isEqualTo(backendAction2);
+ }
+
+ @Test
+ public void testTestOnlyTarget() throws Exception {
+ createBuildFiles("bin", "testonly = 1,");
+
+ AnalysisMock.get()
+ .ccSupport()
+ .setupCcToolchainConfig(
+ mockToolsConfig,
+ CcToolchainConfig.builder()
+ .withFeatures(
+ CppRuleClasses.SUPPORTS_PIC,
+ CppRuleClasses.THIN_LTO,
+ CppRuleClasses.SUPPORTS_START_END_LIB,
+ CppRuleClasses.THIN_LTO_LINKSTATIC_TESTS_USE_SHARED_NONLTO_BACKENDS,
+ MockCcSupport.HOST_AND_NONHOST_CONFIGURATION_FEATURES));
+ useConfiguration("--features=thin_lto_linkstatic_tests_use_shared_nonlto_backends");
+
+ ConfiguredTarget pkg = getConfiguredTarget("//pkg:bin");
+ Artifact pkgArtifact = getFilesToBuild(pkg).getSingleton();
+ CppLinkAction linkAction = (CppLinkAction) getGeneratingAction(pkgArtifact);
+
+ LtoBackendAction backendAction =
+ (LtoBackendAction)
+ getPredecessorByInputName(linkAction, "shared.nonlto/pkg/_objs/bin/binfile.pic.o");
+ assertThat(backendAction.getMnemonic()).isEqualTo("CcLtoBackendCompile");
+ }
+
+ @Test
+ public void testUseSharedAllLinkstatic() throws Exception {
+ createBuildFiles("bin");
+
+ AnalysisMock.get()
+ .ccSupport()
+ .setupCcToolchainConfig(
+ mockToolsConfig,
+ CcToolchainConfig.builder()
+ .withFeatures(
+ CppRuleClasses.THIN_LTO,
+ CppRuleClasses.THIN_LTO_ALL_LINKSTATIC_USE_SHARED_NONLTO_BACKENDS,
+ CppRuleClasses.SUPPORTS_START_END_LIB,
+ CppRuleClasses.SUPPORTS_PIC,
+ MockCcSupport.HOST_AND_NONHOST_CONFIGURATION_FEATURES));
+ useConfiguration("--features=thin_lto_all_linkstatic_use_shared_nonlto_backends");
+
+ ConfiguredTarget pkg = getConfiguredTarget("//pkg:bin");
+ Artifact pkgArtifact = getFilesToBuild(pkg).getSingleton();
+ CppLinkAction linkAction = (CppLinkAction) getGeneratingAction(pkgArtifact);
+
+ LtoBackendAction backendAction =
+ (LtoBackendAction)
+ getPredecessorByInputName(linkAction, "shared.nonlto/pkg/_objs/bin/binfile.pic.o");
+ assertThat(backendAction.getMnemonic()).isEqualTo("CcLtoBackendCompile");
+ }
+
+ private Action getPredecessorByInputName(Action action, String str) {
+ for (Artifact a : action.getInputs().toList()) {
+ if (a.getExecPathString().contains(str)) {
+ return getGeneratingAction(a);
+ }
+ }
+ return null;
+ }
+
+ @Test
+ public void testAssemblerSource() throws Exception {
+ scratch.file(
+ "pkg/BUILD",
+ "package(features = ['thin_lto'])",
+ "",
+ "cc_binary(name = 'bin',",
+ " srcs = ['binfile.cc', ],",
+ " deps = [ ':lib' ], ",
+ " malloc = '//base:system_malloc')",
+ "cc_library(name = 'lib',",
+ " srcs = ['tracing.cc', 'tracing_x86-64.S'],",
+ " )");
+
+ scratch.file("pkg/binfile.cc", "int main() { return pkg(); }");
+ scratch.file("pkg/tracing.cc", "// hello");
+ scratch.file("pkg/tracing_x86-64.S", "NOP");
+
+ AnalysisMock.get()
+ .ccSupport()
+ .setupCcToolchainConfig(
+ mockToolsConfig,
+ CcToolchainConfig.builder()
+ .withFeatures(
+ CppRuleClasses.THIN_LTO,
+ MockCcSupport.HOST_AND_NONHOST_CONFIGURATION_FEATURES,
+ CppRuleClasses.SUPPORTS_START_END_LIB));
+ useConfiguration();
+
+ ConfiguredTarget bin = getConfiguredTarget("//pkg:bin");
+
+ Artifact binArtifact = getFilesToBuild(bin).getSingleton();
+
+ CppLinkAction linkAction = (CppLinkAction) getGeneratingAction(binArtifact);
+ assertThat(linkAction.getOutputs()).containsExactly(binArtifact);
+
+ Action dataGen = getPredecessorByInputName(linkAction, "tracing_x86-64");
+ assertWithMessage(linkAction.getInputs().toString()).that(dataGen).isNotNull();
+ assertThat(dataGen.getMnemonic()).isEqualTo("CppCompile");
+ }
+
+ // Make sure we don't choke on a cc_library without sources and therefore, without bitcode files.
+ @Test
+ public void testNoSourceFiles() throws Exception {
+ scratch.file(
+ "pkg/BUILD",
+ "package(features = ['thin_lto'])",
+ "",
+ "cc_binary(name = 'bin',",
+ " srcs = ['binfile.cc', ],",
+ " deps = [ ':lib' ], ",
+ " malloc = '//base:system_malloc')",
+ "cc_library(name = 'lib',",
+ " srcs = ['static.a'],",
+ " )");
+
+ scratch.file("pkg/binfile.cc", "int main() { return 1; }");
+ scratch.file("pkg/static.a", "xyz");
+ AnalysisMock.get()
+ .ccSupport()
+ .setupCcToolchainConfig(
+ mockToolsConfig,
+ CcToolchainConfig.builder()
+ .withFeatures(
+ CppRuleClasses.THIN_LTO,
+ MockCcSupport.HOST_AND_NONHOST_CONFIGURATION_FEATURES,
+ CppRuleClasses.SUPPORTS_START_END_LIB));
+ useConfiguration();
+
+ getConfiguredTarget("//pkg:bin");
+ }
+
+ @Test
+ public void testFdoInstrument() throws Exception {
+ scratch.file(
+ "pkg/BUILD",
+ "package(features = ['thin_lto'])",
+ "",
+ "cc_binary(name = 'bin',",
+ " srcs = ['binfile.cc', ],",
+ " malloc = '//base:system_malloc')");
+
+ scratch.file("pkg/binfile.cc", "int main() {}");
+
+ AnalysisMock.get()
+ .ccSupport()
+ .setupCcToolchainConfig(
+ mockToolsConfig,
+ CcToolchainConfig.builder()
+ .withFeatures(
+ CppRuleClasses.THIN_LTO,
+ CppRuleClasses.SUPPORTS_START_END_LIB,
+ MockCcSupport.HOST_AND_NONHOST_CONFIGURATION_FEATURES,
+ CppRuleClasses.SUPPORTS_PIC,
+ CppRuleClasses.FDO_INSTRUMENT));
+ useConfiguration("--fdo_instrument=profiles");
+
+ ConfiguredTarget pkg = getConfiguredTarget("//pkg:bin");
+
+ Artifact pkgArtifact = getFilesToBuild(pkg).getSingleton();
+
+ CppLinkAction linkAction = (CppLinkAction) getGeneratingAction(pkgArtifact);
+ assertThat(linkAction.getOutputs()).containsExactly(pkgArtifact);
+
+ LtoBackendAction backendAction =
+ (LtoBackendAction)
+ getPredecessorByInputName(linkAction, "pkg/bin.lto/pkg/_objs/bin/binfile.pic.o");
+ // If the LtoBackendAction incorrectly tries to add the fdo_instrument
+ // feature, we will fail with an "unknown variable 'fdo_instrument_path'"
+ // error. But let's also explicitly confirm that the fdo_instrument
+ // option didn't end up here.
+ assertThat(backendAction.getArguments()).doesNotContain("fdo_instrument_option");
+ }
+
+ @Test
+ public void testLtoIndexOpt() throws Exception {
+ createBuildFiles("bin");
+
+ AnalysisMock.get()
+ .ccSupport()
+ .setupCcToolchainConfig(
+ mockToolsConfig,
+ CcToolchainConfig.builder()
+ .withFeatures(
+ CppRuleClasses.THIN_LTO,
+ MockCcSupport.HOST_AND_NONHOST_CONFIGURATION_FEATURES,
+ CppRuleClasses.SUPPORTS_PIC,
+ CppRuleClasses.SUPPORTS_START_END_LIB));
+ useConfiguration(
+ "--ltoindexopt=anltoindexopt", "--noincompatible_make_thinlto_command_lines_standalone");
+
+ /*
+ We follow the chain from the final product backwards.
+
+ binary <=[Link]=
+ .lto/...o <=[LTOBackend]=
+ {.o.thinlto.bc,.o.imports} <=[LTOIndexing]=
+ .o <= [CppCompile] .cc
+ */
+ ConfiguredTarget pkg = getConfiguredTarget("//pkg:bin");
+
+ Artifact pkgArtifact = getFilesToBuild(pkg).getSingleton();
+
+ CppLinkAction linkAction = (CppLinkAction) getGeneratingAction(pkgArtifact);
+ assertThat(linkAction.getOutputs()).containsExactly(pkgArtifact);
+
+ LtoBackendAction backendAction =
+ (LtoBackendAction)
+ getPredecessorByInputName(linkAction, "pkg/bin.lto/pkg/_objs/bin/binfile.pic.o");
+ assertThat(backendAction.getMnemonic()).isEqualTo("CcLtoBackendCompile");
+
+ CppLinkAction indexAction =
+ (CppLinkAction)
+ getPredecessorByInputName(
+ backendAction, "pkg/bin.lto/pkg/_objs/bin/binfile.pic.o.thinlto.bc");
+
+ assertThat(indexAction.getArguments()).contains("anltoindexopt");
+ }
+
+ @Test
+ public void testLtoStandaloneCommandLines() throws Exception {
+ createBuildFiles("bin");
+
+ AnalysisMock.get()
+ .ccSupport()
+ .setupCcToolchainConfig(
+ mockToolsConfig,
+ CcToolchainConfig.builder()
+ .withFeatures(
+ CppRuleClasses.THIN_LTO,
+ MockCcSupport.HOST_AND_NONHOST_CONFIGURATION_FEATURES,
+ CppRuleClasses.SUPPORTS_PIC,
+ CppRuleClasses.SUPPORTS_START_END_LIB));
+ useConfiguration(
+ "--ltoindexopt=anltoindexopt",
+ "--incompatible_make_thinlto_command_lines_standalone",
+ "--features=thin_lto");
+
+ /*
+ We follow the chain from the final product backwards.
+
+ binary <=[Link]=
+ .lto/...o <=[LTOBackend]=
+ {.o.thinlto.bc,.o.imports} <=[LTOIndexing]=
+ .o <= [CppCompile] .cc
+ */
+ ConfiguredTarget pkg = getConfiguredTarget("//pkg:bin");
+
+ Artifact pkgArtifact = getFilesToBuild(pkg).getSingleton();
+
+ CppLinkAction linkAction = (CppLinkAction) getGeneratingAction(pkgArtifact);
+ assertThat(linkAction.getOutputs()).containsExactly(pkgArtifact);
+
+ LtoBackendAction backendAction =
+ (LtoBackendAction)
+ getPredecessorByInputName(linkAction, "pkg/bin.lto/pkg/_objs/bin/binfile.pic.o");
+ assertThat(backendAction.getMnemonic()).isEqualTo("CcLtoBackendCompile");
+
+ CppLinkAction indexAction =
+ (CppLinkAction)
+ getPredecessorByInputName(
+ backendAction, "pkg/bin.lto/pkg/_objs/bin/binfile.pic.o.thinlto.bc");
+
+ assertThat(indexAction.getArguments())
+ .contains("--i_come_from_standalone_lto_index=anltoindexopt");
+ }
+
+ @Test
+ public void testCopt() throws Exception {
+ createBuildFiles("bin");
+
+ AnalysisMock.get()
+ .ccSupport()
+ .setupCcToolchainConfig(
+ mockToolsConfig,
+ CcToolchainConfig.builder()
+ .withFeatures(
+ CppRuleClasses.THIN_LTO,
+ MockCcSupport.HOST_AND_NONHOST_CONFIGURATION_FEATURES,
+ CppRuleClasses.SUPPORTS_START_END_LIB,
+ CppRuleClasses.SUPPORTS_PIC));
+ useConfiguration("--copt=acopt");
+
+ /*
+ We follow the chain from the final product backwards.
+
+ binary <=[Link]=
+ .lto/...o <=[LTOBackend]=
+ */
+ ConfiguredTarget pkg = getConfiguredTarget("//pkg:bin");
+
+ Artifact pkgArtifact = getFilesToBuild(pkg).getSingleton();
+
+ CppLinkAction linkAction = (CppLinkAction) getGeneratingAction(pkgArtifact);
+ assertThat(linkAction.getOutputs()).containsExactly(pkgArtifact);
+
+ LtoBackendAction backendAction =
+ (LtoBackendAction)
+ getPredecessorByInputName(linkAction, "pkg/bin.lto/pkg/_objs/bin/binfile.pic.o");
+ assertThat(backendAction.getMnemonic()).isEqualTo("CcLtoBackendCompile");
+ assertThat(backendAction.getArguments()).contains("acopt");
+ }
+
+ @Test
+ public void testPerFileCopt() throws Exception {
+ createBuildFiles("bin");
+
+ AnalysisMock.get()
+ .ccSupport()
+ .setupCcToolchainConfig(
+ mockToolsConfig,
+ CcToolchainConfig.builder()
+ .withFeatures(
+ CppRuleClasses.THIN_LTO,
+ MockCcSupport.HOST_AND_NONHOST_CONFIGURATION_FEATURES,
+ CppRuleClasses.SUPPORTS_PIC,
+ CppRuleClasses.SUPPORTS_START_END_LIB));
+ useConfiguration(
+ "--per_file_copt=binfile\\.cc@copt1",
+ "--per_file_copt=libfile\\.cc@copt2",
+ "--per_file_copt=.*\\.cc,-binfile\\.cc@copt2");
+
+ /*
+ We follow the chain from the final product backwards.
+
+ binary <=[Link]=
+ .lto/...o <=[LTOBackend]=
+ */
+ ConfiguredTarget pkg = getConfiguredTarget("//pkg:bin");
+ Artifact pkgArtifact = getFilesToBuild(pkg).getSingleton();
+
+ CppLinkAction linkAction = (CppLinkAction) getGeneratingAction(pkgArtifact);
+ assertThat(linkAction.getOutputs()).containsExactly(pkgArtifact);
+
+ LtoBackendAction backendAction =
+ (LtoBackendAction)
+ getPredecessorByInputName(linkAction, "pkg/bin.lto/pkg/_objs/bin/binfile.pic.o");
+ assertThat(backendAction.getArguments()).contains("copt1");
+ assertThat(backendAction.getArguments()).doesNotContain("copt2");
+
+ backendAction =
+ (LtoBackendAction)
+ getPredecessorByInputName(linkAction, "pkg/bin.lto/pkg/_objs/lib/libfile.pic.o");
+ assertThat(backendAction.getArguments()).doesNotContain("copt1");
+ assertThat(backendAction.getArguments()).contains("copt2");
+ }
+
+ @Test
+ public void testCoptNoCoptAttributes() throws Exception {
+ createBuildFiles("bin", "copts = ['acopt', 'nocopt1'], nocopts = 'nocopt1|nocopt2',");
+
+ AnalysisMock.get()
+ .ccSupport()
+ .setupCcToolchainConfig(
+ mockToolsConfig,
+ CcToolchainConfig.builder()
+ .withFeatures(
+ CppRuleClasses.THIN_LTO,
+ MockCcSupport.HOST_AND_NONHOST_CONFIGURATION_FEATURES,
+ CppRuleClasses.SUPPORTS_PIC,
+ CppRuleClasses.SUPPORTS_START_END_LIB));
+ useConfiguration("--copt=nocopt2", "--noincompatible_disable_nocopts");
+
+ /*
+ We follow the chain from the final product backwards.
+
+ binary <=[Link]=
+ .lto/...o <=[LTOBackend]=
+ */
+ ConfiguredTarget pkg = getConfiguredTarget("//pkg:bin");
+
+ Artifact pkgArtifact = getFilesToBuild(pkg).getSingleton();
+
+ CppLinkAction linkAction = (CppLinkAction) getGeneratingAction(pkgArtifact);
+ assertThat(linkAction.getOutputs()).containsExactly(pkgArtifact);
+
+ LtoBackendAction backendAction =
+ (LtoBackendAction)
+ getPredecessorByInputName(linkAction, "pkg/bin.lto/pkg/_objs/bin/binfile.pic.o");
+ assertThat(backendAction.getMnemonic()).isEqualTo("CcLtoBackendCompile");
+ assertThat(backendAction.getArguments()).contains("acopt");
+ // TODO(b/122303926): Remove when nocopts are removed, or uncomment and fix if not removing.
+ // assertThat(backendAction.getArguments()).doesNotContain("nocopt1");
+ // assertThat(backendAction.getArguments()).doesNotContain("nocopt2");
+ }
+
+ @Test
+ public void testLtoBackendOpt() throws Exception {
+ createBuildFiles("bin");
+
+ AnalysisMock.get()
+ .ccSupport()
+ .setupCcToolchainConfig(
+ mockToolsConfig,
+ CcToolchainConfig.builder()
+ .withFeatures(
+ CppRuleClasses.THIN_LTO,
+ CppRuleClasses.SUPPORTS_PIC,
+ MockCcSupport.HOST_AND_NONHOST_CONFIGURATION_FEATURES,
+ CppRuleClasses.SUPPORTS_START_END_LIB,
+ MockCcSupport.USER_COMPILE_FLAGS));
+ useConfiguration("--ltobackendopt=anltobackendopt");
+
+ /*
+ We follow the chain from the final product backwards.
+
+ binary <=[Link]=
+ .lto/...o <=[LTOBackend]=
+ */
+ ConfiguredTarget pkg = getConfiguredTarget("//pkg:bin");
+
+ Artifact pkgArtifact = getFilesToBuild(pkg).getSingleton();
+
+ CppLinkAction linkAction = (CppLinkAction) getGeneratingAction(pkgArtifact);
+ assertThat(linkAction.getOutputs()).containsExactly(pkgArtifact);
+
+ LtoBackendAction backendAction =
+ (LtoBackendAction)
+ getPredecessorByInputName(linkAction, "pkg/bin.lto/pkg/_objs/bin/binfile.pic.o");
+ assertThat(backendAction.getMnemonic()).isEqualTo("CcLtoBackendCompile");
+ assertThat(backendAction.getArguments())
+ .containsAtLeast("--default-compile-flag", "anltobackendopt");
+ }
+
+ @Test
+ public void testPerFileLtoBackendOpt() throws Exception {
+ createBuildFiles("bin");
+
+ AnalysisMock.get()
+ .ccSupport()
+ .setupCcToolchainConfig(
+ mockToolsConfig,
+ CcToolchainConfig.builder()
+ .withFeatures(
+ CppRuleClasses.THIN_LTO,
+ MockCcSupport.HOST_AND_NONHOST_CONFIGURATION_FEATURES,
+ CppRuleClasses.SUPPORTS_PIC,
+ CppRuleClasses.SUPPORTS_START_END_LIB));
+ useConfiguration(
+ "--per_file_ltobackendopt=binfile\\.pic\\.o@ltobackendopt1",
+ "--per_file_ltobackendopt=.*\\.o,-binfile\\.pic\\.o@ltobackendopt2");
+
+ /*
+ We follow the chain from the final product backwards.
+
+ binary <=[Link]=
+ .lto/...o <=[LTOBackend]=
+ */
+ ConfiguredTarget pkg = getConfiguredTarget("//pkg:bin");
+ Artifact pkgArtifact = getFilesToBuild(pkg).getSingleton();
+
+ CppLinkAction linkAction = (CppLinkAction) getGeneratingAction(pkgArtifact);
+ assertThat(linkAction.getOutputs()).containsExactly(pkgArtifact);
+
+ LtoBackendAction backendAction =
+ (LtoBackendAction)
+ getPredecessorByInputName(linkAction, "pkg/bin.lto/pkg/_objs/bin/binfile.pic.o");
+ assertThat(backendAction.getArguments()).contains("ltobackendopt1");
+ assertThat(backendAction.getArguments()).doesNotContain("ltobackendopt2");
+
+ backendAction =
+ (LtoBackendAction)
+ getPredecessorByInputName(linkAction, "pkg/bin.lto/pkg/_objs/lib/libfile.pic.o");
+ assertThat(backendAction.getArguments()).doesNotContain("ltobackendopt1");
+ assertThat(backendAction.getArguments()).contains("ltobackendopt2");
+ }
+
+ @Test
+ public void testNoUseLtoIndexingBitcodeFile() throws Exception {
+ createBuildFiles("bin");
+
+ AnalysisMock.get()
+ .ccSupport()
+ .setupCcToolchainConfig(
+ mockToolsConfig,
+ CcToolchainConfig.builder()
+ .withFeatures(
+ CppRuleClasses.THIN_LTO,
+ CppRuleClasses.NO_USE_LTO_INDEXING_BITCODE_FILE,
+ MockCcSupport.HOST_AND_NONHOST_CONFIGURATION_FEATURES,
+ CppRuleClasses.SUPPORTS_PIC,
+ CppRuleClasses.SUPPORTS_START_END_LIB));
+ useConfiguration("--features=no_use_lto_indexing_bitcode_file");
+
+ /*
+ We follow the chain from the final product backwards.
+
+ binary <=[Link]=
+ .lto/...o <=[LTOBackend]=
+ {.o.thinlto.bc,.o.imports} <=[LTOIndexing]=
+ .o <= [CppCompile] .cc
+ */
+ ConfiguredTarget pkg = getConfiguredTarget("//pkg:bin");
+
+ Artifact pkgArtifact = getFilesToBuild(pkg).getSingleton();
+
+ CppLinkAction linkAction = (CppLinkAction) getGeneratingAction(pkgArtifact);
+
+ LtoBackendAction backendAction =
+ (LtoBackendAction)
+ getPredecessorByInputName(linkAction, "pkg/bin.lto/pkg/_objs/bin/binfile.pic.o");
+
+ CppLinkAction indexAction =
+ (CppLinkAction)
+ getPredecessorByInputName(
+ backendAction, "pkg/bin.lto/pkg/_objs/bin/binfile.pic.o.thinlto.bc");
+
+ assertThat(indexAction.getArguments()).doesNotContain("object_suffix_replace");
+
+ assertThat(artifactsToStrings(indexAction.getInputs()))
+ .containsAtLeast("bin pkg/_objs/bin/binfile.pic.o", "bin pkg/_objs/lib/libfile.pic.o");
+
+ CppCompileAction bitcodeAction =
+ (CppCompileAction) getPredecessorByInputName(indexAction, "pkg/_objs/bin/binfile.pic.o");
+ assertThat(bitcodeAction.getArguments()).doesNotContain("lto_indexing_bitcode=");
+ }
+
+ @Test
+ public void testAutoFdo() throws Exception {
+ scratch.file(
+ "pkg/BUILD",
+ "package(features = ['thin_lto'])",
+ "",
+ "cc_binary(name = 'bin',",
+ " srcs = ['binfile.cc', ],",
+ " malloc = '//base:system_malloc')");
+
+ scratch.file("pkg/binfile.cc", "int main() {}");
+ scratch.file("pkg/profile.afdo", "");
+
+ AnalysisMock.get()
+ .ccSupport()
+ .setupCcToolchainConfig(
+ mockToolsConfig,
+ CcToolchainConfig.builder()
+ .withFeatures(
+ CppRuleClasses.THIN_LTO,
+ CppRuleClasses.SUPPORTS_START_END_LIB,
+ MockCcSupport.HOST_AND_NONHOST_CONFIGURATION_FEATURES,
+ CppRuleClasses.AUTOFDO));
+ useConfiguration("--fdo_optimize=pkg/profile.afdo", "--compilation_mode=opt");
+
+ Artifact binArtifact = getFilesToBuild(getConfiguredTarget("//pkg:bin")).getSingleton();
+
+ CppLinkAction linkAction = (CppLinkAction) getGeneratingAction(binArtifact);
+ assertThat(linkAction.getOutputs()).containsExactly(binArtifact);
+
+ LtoBackendAction backendAction =
+ (LtoBackendAction)
+ getPredecessorByInputName(linkAction, "pkg/bin.lto/pkg/_objs/bin/binfile.o");
+
+ // Checks that -fauto-profile is added to the LtoBackendAction.
+ assertThat(Joiner.on(" ").join(backendAction.getArguments())).containsMatch(
+ "-fauto-profile=[^ ]*/profile.afdo");
+ assertThat(ActionsTestUtil.baseArtifactNames(backendAction.getInputs())).contains(
+ "profile.afdo");
+ }
+
+ private void setupAutoFdoThinLtoCrosstool() throws Exception {
+ AnalysisMock.get()
+ .ccSupport()
+ .setupCcToolchainConfig(
+ mockToolsConfig,
+ CcToolchainConfig.builder()
+ .withFeatures(
+ CppRuleClasses.THIN_LTO,
+ CppRuleClasses.SUPPORTS_START_END_LIB,
+ MockCcSupport.HOST_AND_NONHOST_CONFIGURATION_FEATURES,
+ CppRuleClasses.AUTOFDO,
+ CppRuleClasses.ENABLE_AFDO_THINLTO,
+ CppRuleClasses.AUTOFDO_IMPLICIT_THINLTO));
+ }
+
+ /**
+ * Tests that ThinLTO is not enabled for AFDO with LLVM without
+ * --features=autofdo_implicit_thinlto.
+ */
+ @Test
+ public void testAutoFdoNoImplicitThinLto() throws Exception {
+ scratch.file(
+ "pkg/BUILD",
+ "",
+ "cc_binary(name = 'bin',",
+ " srcs = ['binfile.cc', ],",
+ " malloc = '//base:system_malloc')");
+
+ scratch.file("pkg/binfile.cc", "int main() {}");
+ scratch.file("pkg/profile.afdo", "");
+
+ setupAutoFdoThinLtoCrosstool();
+ useConfiguration("--fdo_optimize=pkg/profile.afdo", "--compilation_mode=opt");
+
+ Artifact binArtifact = getFilesToBuild(getConfiguredTarget("//pkg:bin")).getSingleton();
+
+ CppLinkAction linkAction = (CppLinkAction) getGeneratingAction(binArtifact);
+ assertThat(linkAction.getOutputs()).containsExactly(binArtifact);
+
+ LtoBackendAction backendAction =
+ (LtoBackendAction)
+ getPredecessorByInputName(linkAction, "pkg/bin.lto/pkg/_objs/bin/binfile.o");
+ // We should not have a ThinLTO backend action
+ assertThat(backendAction).isNull();
+ }
+
+ /** Tests that --features=autofdo_implicit_thinlto enables ThinLTO for AFDO with LLVM. */
+ @Test
+ public void testAutoFdoImplicitThinLto() throws Exception {
+ scratch.file(
+ "pkg/BUILD",
+ "",
+ "cc_binary(name = 'bin',",
+ " srcs = ['binfile.cc', ],",
+ " malloc = '//base:system_malloc')");
+
+ scratch.file("pkg/binfile.cc", "int main() {}");
+ scratch.file("pkg/profile.afdo", "");
+
+ setupAutoFdoThinLtoCrosstool();
+ useConfiguration(
+ "--fdo_optimize=pkg/profile.afdo",
+ "--compilation_mode=opt",
+ "--features=autofdo_implicit_thinlto");
+
+ Artifact binArtifact = getFilesToBuild(getConfiguredTarget("//pkg:bin")).getSingleton();
+
+ CppLinkAction linkAction = (CppLinkAction) getGeneratingAction(binArtifact);
+ assertThat(linkAction.getOutputs()).containsExactly(binArtifact);
+
+ LtoBackendAction backendAction =
+ (LtoBackendAction)
+ getPredecessorByInputName(linkAction, "pkg/bin.lto/pkg/_objs/bin/binfile.o");
+ // For ThinLTO compilation we should have a non-null backend action
+ assertThat(backendAction).isNotNull();
+ }
+
+ /**
+ * Tests that --features=-thin_lto overrides --features=autofdo_implicit_thinlto and prevents
+ * enabling ThinLTO for AFDO with LLVM.
+ */
+ @Test
+ public void testAutoFdoImplicitThinLtoDisabledOption() throws Exception {
+ scratch.file(
+ "pkg/BUILD",
+ "",
+ "cc_binary(name = 'bin',",
+ " srcs = ['binfile.cc', ],",
+ " malloc = '//base:system_malloc')");
+
+ scratch.file("pkg/binfile.cc", "int main() {}");
+ scratch.file("pkg/profile.afdo", "");
+
+ setupAutoFdoThinLtoCrosstool();
+ useConfiguration(
+ "--fdo_optimize=pkg/profile.afdo",
+ "--compilation_mode=opt",
+ "--features=autofdo_implicit_thinlto",
+ "--features=-thin_lto");
+
+ Artifact binArtifact = getFilesToBuild(getConfiguredTarget("//pkg:bin")).getSingleton();
+
+ CppLinkAction linkAction = (CppLinkAction) getGeneratingAction(binArtifact);
+ assertThat(linkAction.getOutputs()).containsExactly(binArtifact);
+
+ LtoBackendAction backendAction =
+ (LtoBackendAction)
+ getPredecessorByInputName(linkAction, "pkg/bin.lto/pkg/_objs/bin/binfile.o");
+ // We should not have a ThinLTO backend action
+ assertThat(backendAction).isNull();
+ }
+
+ /**
+ * Tests that features=[-thin_lto] in the build rule overrides --features=autofdo_implicit_thinlto
+ * and prevents enabling ThinLTO for AFDO with LLVM.
+ */
+ @Test
+ public void testAutoFdoImplicitThinLtoDisabledRule() throws Exception {
+ scratch.file(
+ "pkg/BUILD",
+ "",
+ "cc_binary(name = 'bin',",
+ " srcs = ['binfile.cc', ],",
+ " features = ['-thin_lto'],",
+ " malloc = '//base:system_malloc')");
+
+ scratch.file("pkg/binfile.cc", "int main() {}");
+ scratch.file("pkg/profile.afdo", "");
+
+ setupAutoFdoThinLtoCrosstool();
+ useConfiguration(
+ "--fdo_optimize=pkg/profile.afdo",
+ "--compilation_mode=opt",
+ "--features=autofdo_implicit_thinlto");
+
+ Artifact binArtifact = getFilesToBuild(getConfiguredTarget("//pkg:bin")).getSingleton();
+
+ CppLinkAction linkAction = (CppLinkAction) getGeneratingAction(binArtifact);
+ assertThat(linkAction.getOutputs()).containsExactly(binArtifact);
+
+ LtoBackendAction backendAction =
+ (LtoBackendAction)
+ getPredecessorByInputName(linkAction, "pkg/bin.lto/pkg/_objs/bin/binfile.o");
+ // We should not have a ThinLTO backend action
+ assertThat(backendAction).isNull();
+ }
+
+ /**
+ * Tests that features=[-thin_lto] in the package overrides --features=autofdo_implicit_thinlto
+ * and prevents enabling ThinLTO for AFDO with LLVM.
+ */
+ @Test
+ public void testAutoFdoImplicitThinLtoDisabledPackage() throws Exception {
+ scratch.file(
+ "pkg/BUILD",
+ "package(features = ['-thin_lto'])",
+ "",
+ "cc_binary(name = 'bin',",
+ " srcs = ['binfile.cc', ],",
+ " malloc = '//base:system_malloc')");
+
+ scratch.file("pkg/binfile.cc", "int main() {}");
+ scratch.file("pkg/profile.afdo", "");
+
+ setupAutoFdoThinLtoCrosstool();
+ useConfiguration(
+ "--fdo_optimize=pkg/profile.afdo",
+ "--compilation_mode=opt",
+ "--features=autofdo_implicit_thinlto");
+
+ Artifact binArtifact = getFilesToBuild(getConfiguredTarget("//pkg:bin")).getSingleton();
+
+ CppLinkAction linkAction = (CppLinkAction) getGeneratingAction(binArtifact);
+ assertThat(linkAction.getOutputs()).containsExactly(binArtifact);
+
+ LtoBackendAction backendAction =
+ (LtoBackendAction)
+ getPredecessorByInputName(linkAction, "pkg/bin.lto/pkg/_objs/bin/binfile.o");
+ // We should not have a ThinLTO backend action
+ assertThat(backendAction).isNull();
+ }
+
+ private void setupFdoThinLtoCrosstool() throws Exception {
+ AnalysisMock.get()
+ .ccSupport()
+ .setupCcToolchainConfig(
+ mockToolsConfig,
+ CcToolchainConfig.builder()
+ .withFeatures(
+ CppRuleClasses.THIN_LTO,
+ MockCcSupport.HOST_AND_NONHOST_CONFIGURATION_FEATURES,
+ CppRuleClasses.FDO_OPTIMIZE,
+ CppRuleClasses.ENABLE_FDO_THINLTO,
+ CppRuleClasses.SUPPORTS_START_END_LIB,
+ MockCcSupport.FDO_IMPLICIT_THINLTO));
+ }
+
+ /**
+ * Tests that ThinLTO is not enabled for FDO with LLVM without --features=fdo_implicit_thinlto.
+ */
+ @Test
+ public void testFdoNoImplicitThinLto() throws Exception {
+ AnalysisMock.get()
+ .ccSupport()
+ .setupCcToolchainConfig(
+ mockToolsConfig,
+ CcToolchainConfig.builder()
+ .withFeatures(CppRuleClasses.THIN_LTO, CppRuleClasses.SUPPORTS_START_END_LIB));
+ scratch.file(
+ "pkg/BUILD",
+ "",
+ "cc_binary(name = 'bin',",
+ " srcs = ['binfile.cc', ],",
+ " malloc = '//base:system_malloc')");
+
+ scratch.file("pkg/binfile.cc", "int main() {}");
+ scratch.file("pkg/profile.zip", "");
+
+ setupFdoThinLtoCrosstool();
+ useConfiguration("--fdo_optimize=pkg/profile.zip", "--compilation_mode=opt");
+
+ Artifact binArtifact = getFilesToBuild(getConfiguredTarget("//pkg:bin")).getSingleton();
+
+ CppLinkAction linkAction = (CppLinkAction) getGeneratingAction(binArtifact);
+ assertThat(linkAction.getOutputs()).containsExactly(binArtifact);
+
+ LtoBackendAction backendAction =
+ (LtoBackendAction)
+ getPredecessorByInputName(linkAction, "pkg/bin.lto/pkg/_objs/bin/binfile.o");
+ // We should not have a ThinLTO backend action
+ assertThat(backendAction).isNull();
+ }
+
+ /** Tests that --features=fdo_implicit_thinlto enables ThinLTO for FDO with LLVM. */
+ @Test
+ public void testFdoImplicitThinLto() throws Exception {
+ scratch.file(
+ "pkg/BUILD",
+ "",
+ "cc_binary(name = 'bin',",
+ " srcs = ['binfile.cc', ],",
+ " malloc = '//base:system_malloc')");
+
+ scratch.file("pkg/binfile.cc", "int main() {}");
+ scratch.file("pkg/profile.zip", "");
+
+ setupFdoThinLtoCrosstool();
+ useConfiguration(
+ "--fdo_optimize=pkg/profile.zip",
+ "--compilation_mode=opt",
+ "--features=fdo_implicit_thinlto");
+
+ Artifact binArtifact = getFilesToBuild(getConfiguredTarget("//pkg:bin")).getSingleton();
+
+ CppLinkAction linkAction = (CppLinkAction) getGeneratingAction(binArtifact);
+ assertThat(linkAction.getOutputs()).containsExactly(binArtifact);
+
+ LtoBackendAction backendAction =
+ (LtoBackendAction)
+ getPredecessorByInputName(linkAction, "pkg/bin.lto/pkg/_objs/bin/binfile.o");
+ // For ThinLTO compilation we should have a non-null backend action
+ assertThat(backendAction).isNotNull();
+ }
+
+ /**
+ * Tests that --features=-thin_lto overrides --features=fdo_implicit_thinlto and prevents enabling
+ * ThinLTO for FDO with LLVM.
+ */
+ @Test
+ public void testFdoImplicitThinLtoDisabledOption() throws Exception {
+ scratch.file(
+ "pkg/BUILD",
+ "",
+ "cc_binary(name = 'bin',",
+ " srcs = ['binfile.cc', ],",
+ " malloc = '//base:system_malloc')");
+
+ scratch.file("pkg/binfile.cc", "int main() {}");
+ scratch.file("pkg/profile.zip", "");
+
+ setupFdoThinLtoCrosstool();
+ useConfiguration(
+ "--fdo_optimize=pkg/profile.zip",
+ "--compilation_mode=opt",
+ "--features=fdo_implicit_thinlto",
+ "--features=-thin_lto");
+
+ Artifact binArtifact = getFilesToBuild(getConfiguredTarget("//pkg:bin")).getSingleton();
+
+ CppLinkAction linkAction = (CppLinkAction) getGeneratingAction(binArtifact);
+ assertThat(linkAction.getOutputs()).containsExactly(binArtifact);
+
+ LtoBackendAction backendAction =
+ (LtoBackendAction)
+ getPredecessorByInputName(linkAction, "pkg/bin.lto/pkg/_objs/bin/binfile.o");
+ // We should not have a ThinLTO backend action
+ assertThat(backendAction).isNull();
+ }
+
+ /**
+ * Tests that features=[-thin_lto] in the build rule overrides --features=fdo_implicit_thinlto and
+ * prevents enabling ThinLTO for FDO with LLVM.
+ */
+ @Test
+ public void testFdoImplicitThinLtoDisabledRule() throws Exception {
+ scratch.file(
+ "pkg/BUILD",
+ "",
+ "cc_binary(name = 'bin',",
+ " srcs = ['binfile.cc', ],",
+ " features = ['-thin_lto'],",
+ " malloc = '//base:system_malloc')");
+
+ scratch.file("pkg/binfile.cc", "int main() {}");
+ scratch.file("pkg/profile.zip", "");
+
+ setupFdoThinLtoCrosstool();
+ useConfiguration(
+ "--fdo_optimize=pkg/profile.zip",
+ "--compilation_mode=opt",
+ "--features=fdo_implicit_thinlto");
+
+ Artifact binArtifact = getFilesToBuild(getConfiguredTarget("//pkg:bin")).getSingleton();
+
+ CppLinkAction linkAction = (CppLinkAction) getGeneratingAction(binArtifact);
+ assertThat(linkAction.getOutputs()).containsExactly(binArtifact);
+
+ LtoBackendAction backendAction =
+ (LtoBackendAction)
+ getPredecessorByInputName(linkAction, "pkg/bin.lto/pkg/_objs/bin/binfile.o");
+ // We should not have a ThinLTO backend action
+ assertThat(backendAction).isNull();
+ }
+
+ /**
+ * Tests that features=[-thin_lto] in the package overrides --features=fdo_implicit_thinlto and
+ * prevents enabling ThinLTO for FDO with LLVM.
+ */
+ @Test
+ public void testFdoImplicitThinLtoDisabledPackage() throws Exception {
+ AnalysisMock.get()
+ .ccSupport()
+ .setupCcToolchainConfig(
+ mockToolsConfig,
+ CcToolchainConfig.builder()
+ .withFeatures(CppRuleClasses.THIN_LTO, CppRuleClasses.SUPPORTS_START_END_LIB));
+ scratch.file(
+ "pkg/BUILD",
+ "package(features = ['-thin_lto'])",
+ "",
+ "cc_binary(name = 'bin',",
+ " srcs = ['binfile.cc', ],",
+ " malloc = '//base:system_malloc')");
+
+ scratch.file("pkg/binfile.cc", "int main() {}");
+ scratch.file("pkg/profile.zip", "");
+
+ setupFdoThinLtoCrosstool();
+ useConfiguration(
+ "--fdo_optimize=pkg/profile.zip",
+ "--compilation_mode=opt",
+ "--features=fdo_implicit_thinlto");
+
+ Artifact binArtifact = getFilesToBuild(getConfiguredTarget("//pkg:bin")).getSingleton();
+
+ CppLinkAction linkAction = (CppLinkAction) getGeneratingAction(binArtifact);
+ assertThat(linkAction.getOutputs()).containsExactly(binArtifact);
+
+ LtoBackendAction backendAction =
+ (LtoBackendAction)
+ getPredecessorByInputName(linkAction, "pkg/bin.lto/pkg/_objs/bin/binfile.o");
+ // We should not have a ThinLTO backend action
+ assertThat(backendAction).isNull();
+ }
+
+ private void setupXBinaryFdoThinLtoCrosstool() throws Exception {
+ AnalysisMock.get()
+ .ccSupport()
+ .setupCcToolchainConfig(
+ mockToolsConfig,
+ CcToolchainConfig.builder()
+ .withFeatures(
+ CppRuleClasses.THIN_LTO,
+ CppRuleClasses.SUPPORTS_START_END_LIB,
+ MockCcSupport.HOST_AND_NONHOST_CONFIGURATION_FEATURES,
+ CppRuleClasses.XBINARYFDO,
+ CppRuleClasses.ENABLE_XFDO_THINLTO,
+ MockCcSupport.XFDO_IMPLICIT_THINLTO));
+ }
+
+ /**
+ * Tests that ThinLTO is not enabled for XFDO with LLVM without
+ * --features=xbinaryfdo_implicit_thinlto.
+ */
+ @Test
+ public void testXBinaryFdoNoImplicitThinLto() throws Exception {
+ scratch.file(
+ "pkg/BUILD",
+ "",
+ "cc_binary(name = 'bin',",
+ " srcs = ['binfile.cc', ])",
+ "fdo_profile(name='out.xfdo', profile='profiles.xfdo')");
+
+ scratch.file("pkg/binfile.cc", "int main() {}");
+
+ setupXBinaryFdoThinLtoCrosstool();
+ useConfiguration("--xbinary_fdo=//pkg:out.xfdo", "--compilation_mode=opt");
+
+ Artifact binArtifact = getFilesToBuild(getConfiguredTarget("//pkg:bin")).getSingleton();
+
+ CppLinkAction linkAction = (CppLinkAction) getGeneratingAction(binArtifact);
+ assertThat(linkAction.getOutputs()).containsExactly(binArtifact);
+
+ LtoBackendAction backendAction =
+ (LtoBackendAction)
+ getPredecessorByInputName(linkAction, "pkg/bin.lto/pkg/_objs/bin/binfile.o");
+ // We should not have a ThinLTO backend action
+ assertThat(backendAction).isNull();
+ }
+
+ /** Tests that --features=xbinaryfdo_implicit_thinlto enables ThinLTO for XFDO with LLVM. */
+ @Test
+ public void testXBinaryFdoImplicitThinLto() throws Exception {
+ scratch.file(
+ "pkg/BUILD",
+ "",
+ "cc_binary(name = 'bin',",
+ " srcs = ['binfile.cc', ])",
+ "fdo_profile(name='out.xfdo', profile='profiles.xfdo')");
+
+ scratch.file("pkg/binfile.cc", "int main() {}");
+
+ setupXBinaryFdoThinLtoCrosstool();
+ useConfiguration(
+ "--xbinary_fdo=//pkg:out.xfdo",
+ "--compilation_mode=opt",
+ "--features=xbinaryfdo_implicit_thinlto");
+
+ Artifact binArtifact = getFilesToBuild(getConfiguredTarget("//pkg:bin")).getSingleton();
+
+ CppLinkAction linkAction = (CppLinkAction) getGeneratingAction(binArtifact);
+ assertThat(linkAction.getOutputs()).containsExactly(binArtifact);
+
+ LtoBackendAction backendAction =
+ (LtoBackendAction)
+ getPredecessorByInputName(linkAction, "pkg/bin.lto/pkg/_objs/bin/binfile.o");
+ // For ThinLTO compilation we should have a non-null backend action
+ assertThat(backendAction).isNotNull();
+ }
+
+ /**
+ * Tests that --features=-thin_lto overrides --features=xbinaryfdo_implicit_thinlto and prevents
+ * enabling ThinLTO for XFDO with LLVM.
+ */
+ @Test
+ public void testXBinaryFdoImplicitThinLtoDisabledOption() throws Exception {
+ scratch.file(
+ "pkg/BUILD",
+ "",
+ "cc_binary(name = 'bin',",
+ " srcs = ['binfile.cc', ])",
+ "fdo_profile(name='out.xfdo', profile='profiles.xfdo')");
+
+ scratch.file("pkg/binfile.cc", "int main() {}");
+
+ setupXBinaryFdoThinLtoCrosstool();
+ useConfiguration(
+ "--xbinary_fdo=//pkg:out.xfdo",
+ "--compilation_mode=opt",
+ "--features=xbinaryfdo_implicit_thinlto",
+ "--features=-thin_lto");
+
+ Artifact binArtifact = getFilesToBuild(getConfiguredTarget("//pkg:bin")).getSingleton();
+
+ CppLinkAction linkAction = (CppLinkAction) getGeneratingAction(binArtifact);
+ assertThat(linkAction.getOutputs()).containsExactly(binArtifact);
+
+ LtoBackendAction backendAction =
+ (LtoBackendAction)
+ getPredecessorByInputName(linkAction, "pkg/bin.lto/pkg/_objs/bin/binfile.o");
+ // We should not have a ThinLTO backend action
+ assertThat(backendAction).isNull();
+ }
+
+ /**
+ * Tests that features=[-thin_lto] in the build rule overrides
+ * --features=xbinaryfdo_implicit_thinlto and prevents enabling ThinLTO for XFDO with LLVM.
+ */
+ @Test
+ public void testXBinaryFdoImplicitThinLtoDisabledRule() throws Exception {
+ scratch.file(
+ "pkg/BUILD",
+ "",
+ "cc_binary(name = 'bin',",
+ " srcs = ['binfile.cc', ],",
+ " features = ['-thin_lto'])",
+ "fdo_profile(name='out.xfdo', profile='profiles.xfdo')");
+
+ scratch.file("pkg/binfile.cc", "int main() {}");
+
+ setupXBinaryFdoThinLtoCrosstool();
+ useConfiguration(
+ "--xbinary_fdo=//pkg:out.xfdo",
+ "--compilation_mode=opt",
+ "--features=xbinaryfdo_implicit_thinlto");
+
+ Artifact binArtifact = getFilesToBuild(getConfiguredTarget("//pkg:bin")).getSingleton();
+
+ CppLinkAction linkAction = (CppLinkAction) getGeneratingAction(binArtifact);
+ assertThat(linkAction.getOutputs()).containsExactly(binArtifact);
+
+ LtoBackendAction backendAction =
+ (LtoBackendAction)
+ getPredecessorByInputName(linkAction, "pkg/bin.lto/pkg/_objs/bin/binfile.o");
+ // We should not have a ThinLTO backend action
+ assertThat(backendAction).isNull();
+ }
+
+ /**
+ * Tests that features=[-thin_lto] in the package overrides --features=fdo_implicit_thinlto and
+ * prevents enabling ThinLTO for XFDO with LLVM.
+ */
+ @Test
+ public void testXBinaryFdoImplicitThinLtoDisabledPackage() throws Exception {
+ scratch.file(
+ "pkg/BUILD",
+ "package(features = ['-thin_lto'])",
+ "",
+ "cc_binary(name = 'bin',",
+ " srcs = ['binfile.cc', ])",
+ "fdo_profile(name='out.xfdo', profile='profiles.xfdo')");
+
+ scratch.file("pkg/binfile.cc", "int main() {}");
+
+ setupXBinaryFdoThinLtoCrosstool();
+ useConfiguration(
+ "--xbinary_fdo=//pkg:out.xfdo",
+ "--compilation_mode=opt",
+ "--features=xbinaryfdo_implicit_thinlto");
+
+ Artifact binArtifact = getFilesToBuild(getConfiguredTarget("//pkg:bin")).getSingleton();
+
+ CppLinkAction linkAction = (CppLinkAction) getGeneratingAction(binArtifact);
+ assertThat(linkAction.getOutputs()).containsExactly(binArtifact);
+
+ LtoBackendAction backendAction =
+ (LtoBackendAction)
+ getPredecessorByInputName(linkAction, "pkg/bin.lto/pkg/_objs/bin/binfile.o");
+ // We should not have a ThinLTO backend action
+ assertThat(backendAction).isNull();
+ }
+
+ @Test
+ public void testXBinaryFdo() throws Exception {
+ scratch.file(
+ "pkg/BUILD",
+ "package(features = ['thin_lto'])",
+ "",
+ "cc_binary(name = 'bin',",
+ " srcs = ['binfile.cc', ],",
+ " malloc = '//base:system_malloc')",
+ "fdo_profile(name='out.xfdo', profile='profiles.xfdo')");
+
+ scratch.file("pkg/binfile.cc", "int main() {}");
+
+ AnalysisMock.get()
+ .ccSupport()
+ .setupCcToolchainConfig(
+ mockToolsConfig,
+ CcToolchainConfig.builder()
+ .withFeatures(
+ CppRuleClasses.THIN_LTO,
+ CppRuleClasses.SUPPORTS_START_END_LIB,
+ MockCcSupport.HOST_AND_NONHOST_CONFIGURATION_FEATURES,
+ CppRuleClasses.XBINARYFDO));
+ useConfiguration("--xbinary_fdo=//pkg:out.xfdo", "--compilation_mode=opt");
+
+ Artifact binArtifact = getFilesToBuild(getConfiguredTarget("//pkg:bin")).getSingleton();
+
+ CppLinkAction linkAction = (CppLinkAction) getGeneratingAction(binArtifact);
+ assertThat(linkAction.getOutputs()).containsExactly(binArtifact);
+
+ LtoBackendAction backendAction =
+ (LtoBackendAction)
+ getPredecessorByInputName(linkAction, "pkg/bin.lto/pkg/_objs/bin/binfile.o");
+
+ // Checks that -fauto-profile is added to the LtoBackendAction.
+ assertThat(Joiner.on(" ").join(backendAction.getArguments()))
+ .containsMatch("-fauto-profile=[^ ]*/profiles.xfdo");
+ assertThat(ActionsTestUtil.baseArtifactNames(backendAction.getInputs()))
+ .contains("profiles.xfdo");
+ }
+
+ /**
+ * Tests that ThinLTO is not enabled for XBINARYFDO with --features=autofdo_implicit_thinlto and
+ * --features=fdo_implicit_thinlto.
+ */
+ @Test
+ public void testXBinaryFdoNoAutoFdoOrFdoImplicitThinLto() throws Exception {
+ scratch.file(
+ "pkg/BUILD",
+ "",
+ "cc_binary(name = 'bin',",
+ " srcs = ['binfile.cc', ],",
+ " malloc = '//base:system_malloc')",
+ "fdo_profile(name='out.xfdo', profile='profiles.xfdo')");
+
+ scratch.file("pkg/binfile.cc", "int main() {}");
+
+ AnalysisMock.get()
+ .ccSupport()
+ .setupCcToolchainConfig(
+ mockToolsConfig,
+ CcToolchainConfig.builder()
+ .withFeatures(
+ CppRuleClasses.THIN_LTO,
+ MockCcSupport.HOST_AND_NONHOST_CONFIGURATION_FEATURES,
+ CppRuleClasses.ENABLE_FDO_THINLTO,
+ MockCcSupport.FDO_IMPLICIT_THINLTO,
+ CppRuleClasses.SUPPORTS_START_END_LIB,
+ CppRuleClasses.ENABLE_AFDO_THINLTO,
+ MockCcSupport.AUTOFDO_IMPLICIT_THINLTO,
+ CppRuleClasses.XBINARYFDO));
+ useConfiguration(
+ "--xbinary_fdo=//pkg:out.xfdo",
+ "--compilation_mode=opt",
+ "--features=autofdo_implicit_thinlto",
+ "--features=fdo_implicit_thinlto");
+
+ Artifact binArtifact = getFilesToBuild(getConfiguredTarget("//pkg:bin")).getSingleton();
+
+ CppLinkAction linkAction = (CppLinkAction) getGeneratingAction(binArtifact);
+ assertThat(linkAction.getOutputs()).containsExactly(binArtifact);
+
+ LtoBackendAction backendAction =
+ (LtoBackendAction)
+ getPredecessorByInputName(linkAction, "pkg/bin.lto/pkg/_objs/bin/pkg/binfile.o");
+ // We should not have a ThinLTO backend action
+ assertThat(backendAction).isNull();
+ }
+
+ @Test
+ public void testPICBackendOrder() throws Exception {
+ createBuildFiles("bin");
+
+ AnalysisMock.get()
+ .ccSupport()
+ .setupCcToolchainConfig(
+ mockToolsConfig,
+ CcToolchainConfig.builder()
+ .withFeatures(
+ CppRuleClasses.THIN_LTO,
+ MockCcSupport.HOST_AND_NONHOST_CONFIGURATION_FEATURES,
+ CppRuleClasses.SUPPORTS_PIC,
+ CppRuleClasses.SUPPORTS_START_END_LIB));
+ useConfiguration("--copt=-fno-PIE");
+
+ ConfiguredTarget pkg = getConfiguredTarget("//pkg:bin");
+ LtoBackendAction backendAction =
+ (LtoBackendAction)
+ getGeneratingAction(artifactByPath(getFilesToBuild(pkg), "bin", "binfile.pic.o"));
+ assertThat(backendAction.getMnemonic()).isEqualTo("CcLtoBackendCompile");
+ assertThat(backendAction.getArguments()).containsAtLeast("-fno-PIE", "-fPIC").inOrder();
+ }
+
+ private void testLLVMCachePrefetchBackendOption(String extraOption, boolean asLabel)
+ throws Exception {
+ scratch.file(
+ "pkg/BUILD",
+ "package(features = ['thin_lto'])",
+ "",
+ "cc_binary(name = 'bin',",
+ " srcs = ['binfile.cc', ])");
+ if (asLabel) {
+ scratch.file(
+ "fdo/BUILD",
+ "fdo_prefetch_hints(name='test_profile', profile=':prefetch.afdo')");
+ } else {
+ scratch.file(
+ "fdo/BUILD",
+ "fdo_prefetch_hints(name='test_profile', absolute_path_profile='/tmp/prefetch.afdo')");
+ }
+
+ scratch.file("pkg/binfile.cc", "int main() {}");
+
+ AnalysisMock.get()
+ .ccSupport()
+ .setupCcToolchainConfig(
+ mockToolsConfig,
+ CcToolchainConfig.builder()
+ .withFeatures(
+ CppRuleClasses.THIN_LTO,
+ CppRuleClasses.SUPPORTS_START_END_LIB,
+ CppRuleClasses.SUPPORTS_PIC,
+ MockCcSupport.HOST_AND_NONHOST_CONFIGURATION_FEATURES,
+ CppRuleClasses.AUTOFDO));
+ useConfiguration(
+ "--fdo_prefetch_hints=//fdo:test_profile",
+ "--compilation_mode=opt",
+ extraOption);
+
+ Artifact binArtifact = getFilesToBuild(getConfiguredTarget("//pkg:bin")).getSingleton();
+
+ CppLinkAction linkAction = (CppLinkAction) getGeneratingAction(binArtifact);
+ assertThat(linkAction.getOutputs()).containsExactly(binArtifact);
+
+ LtoBackendAction backendAction =
+ (LtoBackendAction)
+ getPredecessorByInputName(linkAction, "pkg/bin.lto/pkg/_objs/bin/binfile.o");
+
+ String expectedCompilerFlag =
+ "-Xclang-only=-prefetch-hints-file="
+ + (asLabel ? ".*/prefetch.afdo" : "(blaze|bazel)-out/.*/fdo/.*/prefetch.afdo");
+ assertThat(Joiner.on(" ").join(backendAction.getArguments()))
+ .containsMatch(
+ "-Xclang-only=-mllvm " + expectedCompilerFlag);
+
+ assertThat(ActionsTestUtil.baseArtifactNames(backendAction.getInputs()))
+ .contains("prefetch.afdo");
+ }
+
+ @Test
+ public void testFdoCachePrefetchLLVMOptionsToBackendFromPath() throws Exception {
+ testLLVMCachePrefetchBackendOption("", false);
+ }
+
+ @Test
+ public void testFdoCachePrefetchAndFdoLLVMOptionsToBackendFromPath() throws Exception {
+ testLLVMCachePrefetchBackendOption("--fdo_optimize=./profile.zip", false);
+ }
+
+ @Test
+ public void testFdoCachePrefetchLLVMOptionsToBackendFromLabel() throws Exception {
+ testLLVMCachePrefetchBackendOption("", true);
+ }
+
+ @Test
+ public void testFdoCachePrefetchAndFdoLLVMOptionsToBackendFromLabel() throws Exception {
+ testLLVMCachePrefetchBackendOption("--fdo_optimize=./profile.zip", true);
+ }
+}
diff --git a/src/test/java/com/google/devtools/build/lib/rules/cpp/CcCompilationHelperTest.java b/src/test/java/com/google/devtools/build/lib/rules/cpp/CcCompilationHelperTest.java
new file mode 100644
index 0000000..bda155d
--- /dev/null
+++ b/src/test/java/com/google/devtools/build/lib/rules/cpp/CcCompilationHelperTest.java
@@ -0,0 +1,142 @@
+// Copyright 2020 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.
+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.common.collect.ImmutableMap;
+import com.google.common.testing.NullPointerTester;
+import com.google.common.testing.NullPointerTester.Visibility;
+import com.google.devtools.build.lib.actions.Artifact;
+import com.google.devtools.build.lib.analysis.ConfiguredTarget;
+import com.google.devtools.build.lib.analysis.RuleContext;
+import com.google.devtools.build.lib.analysis.config.BuildConfiguration;
+import com.google.devtools.build.lib.analysis.util.BuildViewTestCase;
+import com.google.devtools.build.lib.cmdline.Label;
+import com.google.devtools.build.lib.rules.cpp.CcCompilationHelper.SourceCategory;
+import com.google.devtools.build.lib.rules.cpp.CcToolchainFeatures.FeatureConfiguration;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+/** Tests for {@link CcCompilationHelper}. */
+@RunWith(JUnit4.class)
+public final class CcCompilationHelperTest extends BuildViewTestCase {
+
+ @Test
+ public void testConstructorThrowsNPE() throws Exception {
+ ConfiguredTarget target =
+ scratchConfiguredTarget("a", "b", "cc_library(name = 'b', srcs = [],)");
+ RuleContext ruleContext = getRuleContext(target);
+ CcToolchainProvider ccToolchain =
+ CppHelper.getToolchainUsingDefaultCcToolchainAttribute(ruleContext);
+ FdoContext fdoContext = ccToolchain.getFdoContext();
+ Artifact grepIncludes = getBinArtifact("grep_includes", target);
+ NullPointerTester tester =
+ new NullPointerTester()
+ .setDefault(RuleContext.class, ruleContext)
+ .setDefault(CcCommon.class, new CcCommon(ruleContext))
+ .setDefault(CppSemantics.class, MockCppSemantics.INSTANCE)
+ .setDefault(CcToolchainProvider.class, ccToolchain)
+ .setDefault(BuildConfiguration.class, ruleContext.getConfiguration())
+ .setDefault(FdoContext.class, fdoContext)
+ .setDefault(Label.class, ruleContext.getLabel())
+ .setDefault(Artifact.class, grepIncludes)
+ .setDefault(CcCompilationOutputs.class, CcCompilationOutputs.builder().build());
+ tester.testConstructors(CcCompilationHelper.class, Visibility.PACKAGE);
+ tester.testAllPublicInstanceMethods(
+ new CcCompilationHelper(
+ ruleContext,
+ ruleContext,
+ target.getLabel(),
+ /* grepIncludes= */ null,
+ MockCppSemantics.INSTANCE,
+ FeatureConfiguration.EMPTY,
+ ccToolchain,
+ fdoContext,
+ /* executionInfo= */ ImmutableMap.of()));
+ }
+
+ @Test
+ public void testCanIgnoreObjcSource() throws Exception {
+ ConfiguredTarget target =
+ scratchConfiguredTarget("a", "b", "cc_library(name = 'b', srcs = ['cpp.cc'])");
+ Artifact objcSrc = getSourceArtifact("objc.m");
+ RuleContext ruleContext = getRuleContext(target);
+ CcToolchainProvider ccToolchain =
+ CppHelper.getToolchainUsingDefaultCcToolchainAttribute(getRuleContext(target));
+ FdoContext fdoContext = ccToolchain.getFdoContext();
+ CcCompilationHelper helper =
+ new CcCompilationHelper(
+ ruleContext,
+ ruleContext,
+ ruleContext.getLabel(),
+ /* grepIncludes= */ null,
+ MockCppSemantics.INSTANCE,
+ FeatureConfiguration.EMPTY,
+ ccToolchain,
+ fdoContext,
+ /* executionInfo= */ ImmutableMap.of())
+ .addSources(objcSrc);
+
+ ImmutableList.Builder<Artifact> helperArtifacts = ImmutableList.builder();
+ for (CppSource source : helper.getCompilationUnitSources()) {
+ helperArtifacts.add(source.getSource());
+ }
+
+ assertThat(Artifact.toRootRelativePaths(helperArtifacts.build())).isEmpty();
+ }
+
+ @Test
+ public void testCanConsumeObjcSource() throws Exception {
+ ConfiguredTarget target =
+ scratchConfiguredTarget("a", "b", "cc_library(name = 'b', srcs = ['cpp.cc'])");
+ Artifact objcSrc = getSourceArtifact("objc.m");
+ RuleContext ruleContext = getRuleContext(target);
+ CcToolchainProvider ccToolchain =
+ CppHelper.getToolchainUsingDefaultCcToolchainAttribute(getRuleContext(target));
+ FdoContext fdoContext = ccToolchain.getFdoContext();
+ CcCompilationHelper helper =
+ new CcCompilationHelper(
+ ruleContext,
+ ruleContext,
+ ruleContext.getLabel(),
+ /* grepIncludes= */ null,
+ MockCppSemantics.INSTANCE,
+ FeatureConfiguration.EMPTY,
+ SourceCategory.CC_AND_OBJC,
+ ccToolchain,
+ fdoContext,
+ ruleContext.getConfiguration(),
+ ImmutableMap.of())
+ .addSources(objcSrc);
+
+ ImmutableList.Builder<Artifact> helperArtifacts = ImmutableList.builder();
+ for (CppSource source : helper.getCompilationUnitSources()) {
+ helperArtifacts.add(source.getSource());
+ }
+
+ assertThat(Artifact.toRootRelativePaths(helperArtifacts.build()))
+ .contains(objcSrc.getRootRelativePath().toString());
+ }
+
+ @Test
+ public void testSetAllowCodeCoverage() throws Exception {
+ useConfiguration("--collect_code_coverage", "--instrumentation_filter=.");
+ ConfiguredTarget target =
+ scratchConfiguredTarget("a", "b", "cc_library(name = 'b', srcs = ['cpp.cc'])");
+ assertThat(CcCompilationHelper.isCodeCoverageEnabled(getRuleContext(target))).isTrue();
+ }
+}
diff --git a/src/test/java/com/google/devtools/build/lib/rules/cpp/CppConfigurationSkylarkTest.java b/src/test/java/com/google/devtools/build/lib/rules/cpp/CppConfigurationSkylarkTest.java
new file mode 100644
index 0000000..dbd2e63
--- /dev/null
+++ b/src/test/java/com/google/devtools/build/lib/rules/cpp/CppConfigurationSkylarkTest.java
@@ -0,0 +1,79 @@
+// Copyright 2020 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.
+package com.google.devtools.build.lib.rules.cpp;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import com.google.devtools.build.lib.analysis.util.BuildViewTestCase;
+import com.google.devtools.build.lib.syntax.Sequence;
+import java.io.IOException;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+/** Tests for C++ fragments in Starlark. */
+@RunWith(JUnit4.class)
+public final class CppConfigurationSkylarkTest extends BuildViewTestCase {
+
+ @Test
+ public void testCopts() throws Exception {
+ writeRuleReturning("ctx.fragments.cpp.copts");
+ useConfiguration("--copt=-wololoo");
+
+ @SuppressWarnings("unchecked")
+ Sequence<String> result = (Sequence<String>) getConfiguredTarget("//foo:bar").get("result");
+ assertThat(result).containsExactly("-wololoo");
+ }
+
+ @Test
+ public void testCxxopts() throws Exception {
+ writeRuleReturning("ctx.fragments.cpp.cxxopts");
+ useConfiguration("--cxxopt=-wololoo");
+
+ @SuppressWarnings("unchecked")
+ Sequence<String> result = (Sequence<String>) getConfiguredTarget("//foo:bar").get("result");
+ assertThat(result).containsExactly("-wololoo");
+ }
+
+ @Test
+ public void testConlyopts() throws Exception {
+ writeRuleReturning("ctx.fragments.cpp.conlyopts");
+ useConfiguration("--conlyopt=-wololoo");
+
+ @SuppressWarnings("unchecked")
+ Sequence<String> result = (Sequence<String>) getConfiguredTarget("//foo:bar").get("result");
+ assertThat(result).containsExactly("-wololoo");
+ }
+
+ @Test
+ public void testLinkopts() throws Exception {
+ writeRuleReturning("ctx.fragments.cpp.linkopts");
+ useConfiguration("--linkopt=-wololoo");
+
+ @SuppressWarnings("unchecked")
+ Sequence<String> result = (Sequence<String>) getConfiguredTarget("//foo:bar").get("result");
+ assertThat(result).containsExactly("-wololoo");
+ }
+
+ private void writeRuleReturning(String s) throws IOException {
+ scratch.file(
+ "foo/lib.bzl",
+ "def _impl(ctx):",
+ " return struct(",
+ " result = " + s,
+ " )",
+ "foo = rule(implementation=_impl, fragments = ['cpp'])");
+ scratch.file("foo/BUILD", "load(':lib.bzl', 'foo')", "foo(name='bar')");
+ }
+}
diff --git a/src/test/java/com/google/devtools/build/lib/rules/cpp/CppSysrootTest.java b/src/test/java/com/google/devtools/build/lib/rules/cpp/CppSysrootTest.java
new file mode 100644
index 0000000..300c1ff
--- /dev/null
+++ b/src/test/java/com/google/devtools/build/lib/rules/cpp/CppSysrootTest.java
@@ -0,0 +1,211 @@
+// Copyright 2020 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.
+package com.google.devtools.build.lib.rules.cpp;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import com.google.common.base.Preconditions;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import com.google.devtools.build.lib.analysis.ConfigurationMakeVariableContext;
+import com.google.devtools.build.lib.analysis.RuleContext;
+import com.google.devtools.build.lib.analysis.config.BuildConfiguration;
+import com.google.devtools.build.lib.analysis.platform.ToolchainInfo;
+import com.google.devtools.build.lib.analysis.util.AnalysisMock;
+import com.google.devtools.build.lib.analysis.util.BuildViewTestCase;
+import com.google.devtools.build.lib.cmdline.Label;
+import com.google.devtools.build.lib.packages.util.Crosstool.CcToolchainConfig;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+/** Tests for calculating the sysroot that require building configured targets. */
+@RunWith(JUnit4.class)
+public final class CppSysrootTest extends BuildViewTestCase {
+
+ @Before
+ public void writeDummyLibrary() throws Exception {
+ scratch.file("dummy/BUILD", "cc_library(name='library')");
+ }
+
+ void testCCFlagsContainsSysroot(BuildConfiguration config, String sysroot, boolean shouldContain)
+ throws Exception {
+
+ RuleContext ruleContext =
+ getRuleContext(
+ getConfiguredTarget(Label.parseAbsolute("//dummy:library", ImmutableMap.of()), config));
+ ConfigurationMakeVariableContext context =
+ new ConfigurationMakeVariableContext(
+ ruleContext,
+ ruleContext.getTarget().getPackage(),
+ config,
+ ImmutableList.of(new CcCommon.CcFlagsSupplier(ruleContext)));
+ if (shouldContain) {
+ assertThat(context.lookupVariable("CC_FLAGS")).contains("--sysroot=" + sysroot);
+ } else {
+ assertThat(context.lookupVariable("CC_FLAGS")).doesNotContain("--sysroot=" + sysroot);
+ }
+ }
+
+ CcToolchainProvider getCcToolchainProvider(BuildConfiguration configuration) throws Exception {
+ CppConfiguration cppConfiguration = configuration.getFragment(CppConfiguration.class);
+ return Preconditions.checkNotNull(
+ (CcToolchainProvider)
+ getConfiguredTarget(
+ cppConfiguration.getRuleProvidingCcToolchainProvider(), configuration)
+ .get(ToolchainInfo.PROVIDER));
+ }
+
+ @Test
+ public void testHostGrteTop() throws Exception {
+ scratch.file("a/grte/top/BUILD", "filegroup(name='everything')", "cc_library(name='library')");
+ useConfiguration("--host_grte_top=//a/grte/top");
+ BuildConfiguration target = getTargetConfiguration();
+ CcToolchainProvider targetCcProvider = getCcToolchainProvider(target);
+ BuildConfiguration host = getHostConfiguration();
+ CcToolchainProvider hostCcProvider = getCcToolchainProvider(host);
+
+ testCCFlagsContainsSysroot(host, "a/grte/top", true);
+ assertThat(hostCcProvider.getSysroot().equals(targetCcProvider.getSysroot())).isFalse();
+ }
+
+ @Test
+ public void testOverrideHostGrteTop() throws Exception {
+ scratch.file("a/grte/top/BUILD", "filegroup(name='everything')");
+ scratch.file("b/grte/top/BUILD", "filegroup(name='everything')");
+ useConfiguration("--grte_top=//a/grte/top", "--host_grte_top=//b/grte/top");
+ BuildConfiguration target = getTargetConfiguration();
+ CcToolchainProvider targetCcProvider = getCcToolchainProvider(target);
+ BuildConfiguration host = getHostConfiguration();
+ CcToolchainProvider hostCcProvider = getCcToolchainProvider(host);
+
+ assertThat(targetCcProvider.getSysroot()).isEqualTo("a/grte/top");
+ assertThat(hostCcProvider.getSysroot()).isEqualTo("b/grte/top");
+
+ testCCFlagsContainsSysroot(target, "a/grte/top", true);
+ testCCFlagsContainsSysroot(target, "b/grte/top", false);
+ testCCFlagsContainsSysroot(host, "b/grte/top", true);
+ testCCFlagsContainsSysroot(host, "a/grte/top", false);
+ }
+
+ @Test
+ public void testGrteTopAlias() throws Exception {
+ scratch.file("a/grte/top/BUILD", "filegroup(name='everything')");
+ scratch.file("b/grte/top/BUILD", "alias(name='everything', actual='//a/grte/top:everything')");
+ useConfiguration("--grte_top=//b/grte/top");
+ BuildConfiguration target = getTargetConfiguration();
+ CcToolchainProvider targetCcProvider = getCcToolchainProvider(target);
+
+ assertThat(targetCcProvider.getSysroot()).isEqualTo("a/grte/top");
+
+ testCCFlagsContainsSysroot(target, "a/grte/top", true);
+ testCCFlagsContainsSysroot(target, "b/grte/top", false);
+ }
+
+ @Test
+ public void testSysroot() throws Exception {
+ // BuildConfiguration shouldn't provide a sysroot option by default.
+ useConfiguration("--cpu=k8");
+ BuildConfiguration config = getTargetConfiguration();
+ testCCFlagsContainsSysroot(config, "/usr/grte/v1", true);
+
+ scratch.file("a/grte/top/BUILD", "filegroup(name='everything')");
+ // BuildConfiguration should work with label grte_top options.
+ useConfiguration("--cpu=k8", "--grte_top=//a/grte/top:everything");
+ config = getTargetConfiguration();
+ testCCFlagsContainsSysroot(config, "a/grte/top", true);
+ }
+
+ @Test
+ public void testSysrootInFeatureConfigBlocksLegacySysroot() throws Exception {
+ AnalysisMock.get()
+ .ccSupport()
+ .setupCcToolchainConfig(
+ mockToolsConfig,
+ CcToolchainConfig.builder().withActionConfigs("sysroot_in_action_config"));
+ scratch.overwriteFile("a/grte/top/BUILD", "filegroup(name='everything')");
+ useConfiguration("--grte_top=//a/grte/top:everything");
+ RuleContext ruleContext =
+ getRuleContext(
+ getConfiguredTarget(
+ Label.parseAbsolute("//dummy:library", ImmutableMap.of()), targetConfig));
+ ConfigurationMakeVariableContext context =
+ new ConfigurationMakeVariableContext(
+ ruleContext,
+ ruleContext.getTarget().getPackage(),
+ targetConfig,
+ ImmutableList.of(new CcCommon.CcFlagsSupplier(ruleContext)));
+ assertThat(context.lookupVariable("CC_FLAGS"))
+ .contains("fc-start --sysroot=a/grte/top-from-feature fc-end");
+ assertThat(context.lookupVariable("CC_FLAGS")).doesNotContain("--sysroot=a/grte/top fc");
+ }
+
+ @Test
+ public void testSysrootWithHostConfig() throws Exception {
+ // The host BuildConfiguration shouldn't provide a sysroot option by default.
+ for (String cpu : new String[] {"piii", "k8"}) {
+ useConfiguration("--cpu=" + cpu);
+ BuildConfiguration config = getHostConfiguration();
+ testCCFlagsContainsSysroot(config, "/usr/grte/v1", true);
+ }
+ // The host BuildConfiguration should work with label grte_top options.
+ scratch.file("a/grte/top/BUILD", "filegroup(name='everything')");
+ for (String cpu : new String[] {"piii", "k8"}) {
+ useConfiguration("--cpu=" + cpu, "--host_grte_top=//a/grte/top");
+ BuildConfiguration config = getHostConfiguration();
+ testCCFlagsContainsSysroot(config, "a/grte/top", true);
+
+ // "--grte_top" does *not* set the host grte_top,
+ // so we don't get "a/grte/top" here, but instead the default "/usr/grte/v1"
+ useConfiguration("--cpu=" + cpu, "--grte_top=//a/grte/top");
+ config = getHostConfiguration();
+ testCCFlagsContainsSysroot(config, "/usr/grte/v1", true);
+
+ // If a host_crosstool_top is set, we shouldn't see the grte_top option in the host config.
+ // (Assuming there was not also a --host_grte_top specified)
+ useConfiguration(
+ "--cpu=" + cpu,
+ "--grte_top=//a/grte/top",
+ "--host_crosstool_top=" + analysisMock.ccSupport().getMockCrosstoolLabel());
+ config = getHostConfiguration();
+ testCCFlagsContainsSysroot(config, "/usr/grte/v1", true);
+ }
+ }
+
+ @Test
+ public void testConfigurableSysroot() throws Exception {
+ scratch.file(
+ "test/config_setting/BUILD",
+ "config_setting(name='defines', values={'define': 'override_grte_top=1'})");
+ scratch.file("a/grte/top/BUILD", "filegroup(name='everything')");
+ scratch.file("b/grte/top/BUILD", "filegroup(name='everything')");
+ scratch.file(
+ "c/grte/top/BUILD",
+ "alias(",
+ " name = 'everything',",
+ " actual=select(",
+ " {'//test/config_setting:defines' : '//a/grte/top:everything',",
+ " '//conditions:default' : '//b/grte/top:everything'}",
+ " )",
+ ")");
+ useConfiguration("--grte_top=//c/grte/top:everything");
+ CcToolchainProvider ccProvider = getCcToolchainProvider(getTargetConfiguration());
+ assertThat(ccProvider.getSysroot()).isEqualTo("b/grte/top");
+
+ useConfiguration("--grte_top=//c/grte/top:everything", "--define=override_grte_top=1");
+ ccProvider = getCcToolchainProvider(getTargetConfiguration());
+ assertThat(ccProvider.getSysroot()).isEqualTo("a/grte/top");
+ }
+}
diff --git a/src/test/java/com/google/devtools/build/lib/rules/cpp/LinkCommandLineTest.java b/src/test/java/com/google/devtools/build/lib/rules/cpp/LinkCommandLineTest.java
new file mode 100644
index 0000000..fbe9cfb
--- /dev/null
+++ b/src/test/java/com/google/devtools/build/lib/rules/cpp/LinkCommandLineTest.java
@@ -0,0 +1,468 @@
+// Copyright 2020 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.
+
+package com.google.devtools.build.lib.rules.cpp;
+
+import static com.google.common.truth.Truth.assertThat;
+import static com.google.devtools.build.lib.testutil.MoreAsserts.assertThrows;
+
+import com.google.common.base.Joiner;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
+import com.google.devtools.build.lib.actions.Artifact;
+import com.google.devtools.build.lib.actions.Artifact.ArtifactExpander;
+import com.google.devtools.build.lib.actions.Artifact.SpecialArtifact;
+import com.google.devtools.build.lib.actions.Artifact.SpecialArtifactType;
+import com.google.devtools.build.lib.actions.Artifact.TreeFileArtifact;
+import com.google.devtools.build.lib.actions.ArtifactRoot;
+import com.google.devtools.build.lib.actions.util.ActionsTestUtil;
+import com.google.devtools.build.lib.analysis.util.BuildViewTestCase;
+import com.google.devtools.build.lib.rules.cpp.CcToolchainFeatures.FeatureConfiguration;
+import com.google.devtools.build.lib.rules.cpp.CcToolchainVariables.LibraryToLinkValue;
+import com.google.devtools.build.lib.rules.cpp.CcToolchainVariables.SequenceBuilder;
+import com.google.devtools.build.lib.rules.cpp.CppActionConfigs.CppPlatform;
+import com.google.devtools.build.lib.rules.cpp.Link.LinkTargetType;
+import com.google.devtools.build.lib.rules.cpp.Link.LinkingMode;
+import com.google.devtools.build.lib.testutil.TestUtils;
+import com.google.devtools.build.lib.util.Pair;
+import com.google.devtools.build.lib.vfs.FileSystem;
+import com.google.devtools.build.lib.vfs.Path;
+import com.google.devtools.build.lib.vfs.PathFragment;
+import com.google.devtools.build.lib.view.config.crosstool.CrosstoolConfig.CToolchain;
+import java.io.IOException;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.List;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+/**
+ * Tests for {@link LinkCommandLine}. In particular, tests command line emitted subject to the
+ * presence of certain build variables.
+ */
+@RunWith(JUnit4.class)
+public final class LinkCommandLineTest extends BuildViewTestCase {
+
+ private Artifact scratchArtifact(String s) {
+ Path execRoot = outputBase.getRelative("exec");
+ Path outputRoot = execRoot.getRelative("root");
+ ArtifactRoot root = ArtifactRoot.asDerivedRoot(execRoot, outputRoot);
+ try {
+ return ActionsTestUtil.createArtifact(
+ root, scratch.overwriteFile(outputRoot.getRelative(s).toString()));
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ private CcToolchainVariables.Builder getMockBuildVariables() {
+ return getMockBuildVariables(ImmutableList.<String>of());
+ }
+
+ private static CcToolchainVariables.Builder getMockBuildVariables(
+ ImmutableList<String> linkstampOutputs) {
+ CcToolchainVariables.Builder result = CcToolchainVariables.builder();
+
+ result.addStringVariable(LinkBuildVariables.GENERATE_INTERFACE_LIBRARY.getVariableName(), "no");
+ result.addStringVariable(
+ LinkBuildVariables.INTERFACE_LIBRARY_INPUT.getVariableName(), "ignored");
+ result.addStringVariable(
+ LinkBuildVariables.INTERFACE_LIBRARY_OUTPUT.getVariableName(), "ignored");
+ result.addStringVariable(
+ LinkBuildVariables.INTERFACE_LIBRARY_BUILDER.getVariableName(), "ignored");
+ result.addStringSequenceVariable(
+ LinkBuildVariables.LINKSTAMP_PATHS.getVariableName(), linkstampOutputs);
+
+ return result;
+ }
+
+ private FeatureConfiguration getMockFeatureConfiguration() throws Exception {
+ ImmutableList<CToolchain.Feature> features =
+ new ImmutableList.Builder<CToolchain.Feature>()
+ .addAll(
+ CppActionConfigs.getLegacyFeatures(
+ CppPlatform.LINUX,
+ ImmutableSet.of(),
+ "MOCK_LINKER_TOOL",
+ /* supportsEmbeddedRuntimes= */ true,
+ /* supportsInterfaceSharedLibraries= */ false,
+ /* doNotSplitLinkingCmdline= */ true))
+ .addAll(
+ CppActionConfigs.getFeaturesToAppearLastInFeaturesList(
+ ImmutableSet.of(), /* doNotSplitLinkingCmdline= */ true))
+ .build();
+
+ ImmutableList<CToolchain.ActionConfig> actionConfigs =
+ CppActionConfigs.getLegacyActionConfigs(
+ CppPlatform.LINUX,
+ "MOCK_GCC_TOOL",
+ "MOCK_AR_TOOL",
+ "MOCK_STRIP_TOOL",
+ /* supportsInterfaceSharedLibraries= */ false,
+ /* existingActionConfigNames= */ ImmutableSet.of());
+
+ return CcToolchainFeaturesTest.buildFeatures(features, actionConfigs)
+ .getFeatureConfiguration(
+ ImmutableSet.of(
+ Link.LinkTargetType.EXECUTABLE.getActionName(),
+ Link.LinkTargetType.NODEPS_DYNAMIC_LIBRARY.getActionName(),
+ Link.LinkTargetType.STATIC_LIBRARY.getActionName(),
+ CppActionNames.CPP_COMPILE,
+ CppActionNames.LINKSTAMP_COMPILE,
+ CppRuleClasses.INCLUDES,
+ CppRuleClasses.PREPROCESSOR_DEFINES,
+ CppRuleClasses.INCLUDE_PATHS,
+ CppRuleClasses.PIC));
+ }
+
+ private LinkCommandLine.Builder minimalConfiguration(CcToolchainVariables.Builder variables)
+ throws Exception {
+ return new LinkCommandLine.Builder()
+ .setBuildVariables(variables.build())
+ .setFeatureConfiguration(getMockFeatureConfiguration());
+ }
+
+ private LinkCommandLine.Builder minimalConfiguration() throws Exception {
+ return minimalConfiguration(getMockBuildVariables());
+ }
+
+ private void assertError(String expectedSubstring, LinkCommandLine.Builder builder) {
+ RuntimeException e = assertThrows(RuntimeException.class, () -> builder.build());
+ assertThat(e).hasMessageThat().contains(expectedSubstring);
+ }
+
+ @Test
+ public void testStaticLinkWithBuildInfoHeadersIsError() throws Exception {
+ assertError(
+ "build info headers may only be present",
+ minimalConfiguration()
+ .setLinkTargetType(LinkTargetType.STATIC_LIBRARY)
+ .setLinkingMode(LinkingMode.STATIC)
+ .setBuildInfoHeaderArtifacts(
+ ImmutableList.of(scratchArtifact("FakeBuildInfoHeaderArtifact1"))));
+ }
+
+ /**
+ * Tests that when linking without linkstamps, the exec command is the same as the link command.
+ */
+ @Test
+ public void testLinkCommandIsExecCommandWhenNoLinkstamps() throws Exception {
+ LinkCommandLine linkConfig =
+ minimalConfiguration()
+ .setActionName(LinkTargetType.EXECUTABLE.getActionName())
+ .setLinkTargetType(LinkTargetType.EXECUTABLE)
+ .build();
+ List<String> rawLinkArgv = linkConfig.getRawLinkArgv();
+ assertThat(linkConfig.arguments()).isEqualTo(rawLinkArgv);
+ }
+
+ /** Tests that symbol count output does not appear in argv when it should not. */
+ @Test
+ public void testSymbolCountsDisabled() throws Exception {
+ LinkCommandLine linkConfig =
+ minimalConfiguration()
+ .forceToolPath("foo/bar/gcc")
+ .setLinkTargetType(LinkTargetType.NODEPS_DYNAMIC_LIBRARY)
+ .setLinkingMode(LinkingMode.STATIC)
+ .build();
+ List<String> argv = linkConfig.getRawLinkArgv();
+ for (String arg : argv) {
+ assertThat(arg).doesNotContain("print-symbol-counts");
+ }
+ }
+
+ @Test
+ public void testLibrariesToLink() throws Exception {
+ CcToolchainVariables.Builder variables =
+ getMockBuildVariables()
+ .addCustomBuiltVariable(
+ LinkBuildVariables.LIBRARIES_TO_LINK.getVariableName(),
+ new SequenceBuilder()
+ .addValue(LibraryToLinkValue.forStaticLibrary("foo", false))
+ .addValue(LibraryToLinkValue.forStaticLibrary("bar", true)));
+
+ LinkCommandLine linkConfig =
+ minimalConfiguration(variables)
+ .forceToolPath("foo/bar/gcc")
+ .setActionName(LinkTargetType.NODEPS_DYNAMIC_LIBRARY.getActionName())
+ .setLinkTargetType(LinkTargetType.NODEPS_DYNAMIC_LIBRARY)
+ .setLinkingMode(LinkingMode.STATIC)
+ .build();
+ String commandLine = Joiner.on(" ").join(linkConfig.getRawLinkArgv());
+ assertThat(commandLine).matches(".*foo -Wl,-whole-archive bar -Wl,-no-whole-archive.*");
+ }
+
+ @Test
+ public void testLibrarySearchDirectories() throws Exception {
+ CcToolchainVariables.Builder variables =
+ getMockBuildVariables()
+ .addStringSequenceVariable(
+ LinkBuildVariables.LIBRARY_SEARCH_DIRECTORIES.getVariableName(),
+ ImmutableList.of("foo", "bar"));
+
+ LinkCommandLine linkConfig =
+ minimalConfiguration(variables)
+ .setActionName(LinkTargetType.NODEPS_DYNAMIC_LIBRARY.getActionName())
+ .setLinkTargetType(LinkTargetType.NODEPS_DYNAMIC_LIBRARY)
+ .setLinkingMode(LinkingMode.STATIC)
+ .build();
+ assertThat(linkConfig.getRawLinkArgv()).containsAtLeast("-Lfoo", "-Lbar").inOrder();
+ }
+
+ @Test
+ public void testLinkerParamFileForStaticLibrary() throws Exception {
+ CcToolchainVariables.Builder variables =
+ getMockBuildVariables()
+ .addStringVariable(
+ LinkBuildVariables.LINKER_PARAM_FILE.getVariableName(), "foo/bar.param");
+
+ LinkCommandLine linkConfig =
+ minimalConfiguration(variables)
+ .setActionName(LinkTargetType.STATIC_LIBRARY.getActionName())
+ .setLinkTargetType(LinkTargetType.STATIC_LIBRARY)
+ .setLinkingMode(Link.LinkingMode.STATIC)
+ .build();
+ assertThat(linkConfig.getRawLinkArgv()).contains("@foo/bar.param");
+ }
+
+ @Test
+ public void testLinkerParamFileForDynamicLibrary() throws Exception {
+ CcToolchainVariables.Builder variables =
+ getMockBuildVariables()
+ .addStringVariable(
+ LinkBuildVariables.LINKER_PARAM_FILE.getVariableName(), "foo/bar.param");
+
+ LinkCommandLine linkConfig =
+ minimalConfiguration(variables)
+ .setActionName(LinkTargetType.NODEPS_DYNAMIC_LIBRARY.getActionName())
+ .setLinkTargetType(LinkTargetType.NODEPS_DYNAMIC_LIBRARY)
+ .setLinkingMode(Link.LinkingMode.STATIC)
+ .doNotSplitLinkingCmdLine()
+ .build();
+ assertThat(linkConfig.getRawLinkArgv()).contains("@foo/bar.param");
+ }
+
+ private List<String> basicArgv(LinkTargetType targetType) throws Exception {
+ return basicArgv(targetType, getMockBuildVariables());
+ }
+
+ private List<String> basicArgv(LinkTargetType targetType, CcToolchainVariables.Builder variables)
+ throws Exception {
+ LinkCommandLine linkConfig =
+ minimalConfiguration(variables)
+ .setActionName(targetType.getActionName())
+ .setLinkTargetType(targetType)
+ .setLinkingMode(LinkingMode.STATIC)
+ .build();
+ return linkConfig.arguments();
+ }
+
+ /** Tests that a "--force_pic" configuration applies "-pie" to executable links. */
+ @Test
+ public void testPicMode() throws Exception {
+ String pieArg = "-pie";
+
+ // Disabled:
+ assertThat(basicArgv(LinkTargetType.EXECUTABLE)).doesNotContain(pieArg);
+ assertThat(basicArgv(LinkTargetType.NODEPS_DYNAMIC_LIBRARY)).doesNotContain(pieArg);
+ assertThat(basicArgv(LinkTargetType.STATIC_LIBRARY)).doesNotContain(pieArg);
+ assertThat(basicArgv(LinkTargetType.PIC_STATIC_LIBRARY)).doesNotContain(pieArg);
+ assertThat(basicArgv(LinkTargetType.ALWAYS_LINK_STATIC_LIBRARY)).doesNotContain(pieArg);
+ assertThat(basicArgv(LinkTargetType.ALWAYS_LINK_PIC_STATIC_LIBRARY)).doesNotContain(pieArg);
+
+ CcToolchainVariables.Builder picVariables =
+ getMockBuildVariables()
+ .addStringVariable(LinkBuildVariables.FORCE_PIC.getVariableName(), "");
+ // Enabled:
+ useConfiguration("--force_pic");
+ assertThat(basicArgv(LinkTargetType.EXECUTABLE, picVariables)).contains(pieArg);
+ assertThat(basicArgv(LinkTargetType.NODEPS_DYNAMIC_LIBRARY, picVariables))
+ .doesNotContain(pieArg);
+ assertThat(basicArgv(LinkTargetType.STATIC_LIBRARY, picVariables)).doesNotContain(pieArg);
+ assertThat(basicArgv(LinkTargetType.PIC_STATIC_LIBRARY, picVariables)).doesNotContain(pieArg);
+ assertThat(basicArgv(LinkTargetType.ALWAYS_LINK_STATIC_LIBRARY, picVariables))
+ .doesNotContain(pieArg);
+ assertThat(basicArgv(LinkTargetType.ALWAYS_LINK_PIC_STATIC_LIBRARY, picVariables))
+ .doesNotContain(pieArg);
+ }
+
+ @Test
+ public void testSplitStaticLinkCommand() throws Exception {
+ useConfiguration("--nostart_end_lib");
+ Artifact paramFile = scratchArtifact("some/file.params");
+ LinkCommandLine linkConfig =
+ minimalConfiguration(
+ getMockBuildVariables()
+ .addStringVariable(
+ LinkBuildVariables.OUTPUT_EXECPATH.getVariableName(), "a/FakeOutput")
+ .addStringVariable(
+ LinkBuildVariables.LINKER_PARAM_FILE.getVariableName(), "some/file.params"))
+ .setActionName(LinkTargetType.STATIC_LIBRARY.getActionName())
+ .setLinkTargetType(LinkTargetType.STATIC_LIBRARY)
+ .forceToolPath("foo/bar/ar")
+ .setParamFile(paramFile)
+ .build();
+ Pair<List<String>, List<String>> result = linkConfig.splitCommandline();
+ assertThat(result.first).isEqualTo(Arrays.asList("foo/bar/ar", "@some/file.params"));
+ assertThat(result.second).isEqualTo(Arrays.asList("rcsD", "a/FakeOutput"));
+ }
+
+ @Test
+ public void testSplitDynamicLinkCommand() throws Exception {
+ useConfiguration("--nostart_end_lib");
+ Artifact paramFile = scratchArtifact("some/file.params");
+ LinkCommandLine linkConfig =
+ minimalConfiguration(
+ getMockBuildVariables()
+ .addStringVariable(
+ LinkBuildVariables.OUTPUT_EXECPATH.getVariableName(), "a/FakeOutput")
+ .addStringVariable(
+ LinkBuildVariables.LINKER_PARAM_FILE.getVariableName(), "some/file.params")
+ .addStringSequenceVariable(
+ LinkBuildVariables.USER_LINK_FLAGS.getVariableName(), ImmutableList.of("")))
+ .setActionName(LinkTargetType.DYNAMIC_LIBRARY.getActionName())
+ .setLinkTargetType(LinkTargetType.DYNAMIC_LIBRARY)
+ .forceToolPath("foo/bar/linker")
+ .setParamFile(paramFile)
+ .doNotSplitLinkingCmdLine()
+ .build();
+ Pair<List<String>, List<String>> result = linkConfig.splitCommandline();
+ assertThat(result.first).containsExactly("foo/bar/linker", "@some/file.params").inOrder();
+ assertThat(result.second).containsExactly("-shared", "-o", "a/FakeOutput", "").inOrder();
+ }
+
+ @Test
+ public void testStaticLinkCommand() throws Exception {
+ useConfiguration("--nostart_end_lib");
+ LinkCommandLine linkConfig =
+ minimalConfiguration(
+ getMockBuildVariables()
+ .addStringVariable(
+ LinkBuildVariables.OUTPUT_EXECPATH.getVariableName(), "a/FakeOutput"))
+ .forceToolPath("foo/bar/ar")
+ .setActionName(LinkTargetType.STATIC_LIBRARY.getActionName())
+ .setLinkTargetType(LinkTargetType.STATIC_LIBRARY)
+ .build();
+ List<String> result = linkConfig.getRawLinkArgv();
+ assertThat(result).isEqualTo(Arrays.asList("foo/bar/ar", "rcsD", "a/FakeOutput"));
+ }
+
+ @Test
+ public void testSplitAlwaysLinkLinkCommand() throws Exception {
+ CcToolchainVariables.Builder variables =
+ CcToolchainVariables.builder()
+ .addStringVariable(CcCommon.SYSROOT_VARIABLE_NAME, "/usr/grte/v1")
+ .addStringVariable(LinkBuildVariables.OUTPUT_EXECPATH.getVariableName(), "a/FakeOutput")
+ .addStringVariable(
+ LinkBuildVariables.LINKER_PARAM_FILE.getVariableName(), "some/file.params")
+ .addCustomBuiltVariable(
+ LinkBuildVariables.LIBRARIES_TO_LINK.getVariableName(),
+ new CcToolchainVariables.SequenceBuilder()
+ .addValue(LibraryToLinkValue.forObjectFile("foo.o", false))
+ .addValue(LibraryToLinkValue.forObjectFile("bar.o", false)));
+
+ Artifact paramFile = scratchArtifact("some/file.params");
+ LinkCommandLine linkConfig =
+ minimalConfiguration(variables)
+ .setActionName(LinkTargetType.ALWAYS_LINK_STATIC_LIBRARY.getActionName())
+ .setLinkTargetType(LinkTargetType.ALWAYS_LINK_STATIC_LIBRARY)
+ .forceToolPath("foo/bar/ar")
+ .setParamFile(paramFile)
+ .build();
+ Pair<List<String>, List<String>> result = linkConfig.splitCommandline();
+
+ assertThat(result.first).isEqualTo(Arrays.asList("foo/bar/ar", "@some/file.params"));
+ assertThat(result.second).isEqualTo(Arrays.asList("rcsD", "a/FakeOutput", "foo.o", "bar.o"));
+ }
+
+ private SpecialArtifact createTreeArtifact(String name) {
+ FileSystem fs = scratch.getFileSystem();
+ Path execRoot = fs.getPath(TestUtils.tmpDir());
+ PathFragment execPath = PathFragment.create("out").getRelative(name);
+ return new SpecialArtifact(
+ ArtifactRoot.asDerivedRoot(execRoot, execRoot.getRelative("out")),
+ execPath,
+ ActionsTestUtil.NULL_ARTIFACT_OWNER,
+ SpecialArtifactType.TREE);
+ }
+
+ private void verifyArguments(
+ Iterable<String> arguments,
+ Iterable<String> allowedArguments,
+ Iterable<String> disallowedArguments) {
+ assertThat(arguments).containsAtLeastElementsIn(allowedArguments);
+ assertThat(arguments).containsNoneIn(disallowedArguments);
+ }
+
+ @Test
+ public void testTreeArtifactLink() throws Exception {
+ SpecialArtifact testTreeArtifact = createTreeArtifact("library_directory");
+
+ TreeFileArtifact library0 =
+ ActionsTestUtil.createTreeFileArtifactWithNoGeneratingAction(
+ testTreeArtifact, "library0.o");
+ TreeFileArtifact library1 =
+ ActionsTestUtil.createTreeFileArtifactWithNoGeneratingAction(
+ testTreeArtifact, "library1.o");
+
+ ArtifactExpander expander =
+ new ArtifactExpander() {
+ @Override
+ public void expand(Artifact artifact, Collection<? super Artifact> output) {
+ if (artifact.equals(testTreeArtifact)) {
+ output.add(library0);
+ output.add(library1);
+ }
+ };
+ };
+
+ Iterable<String> treeArtifactsPaths = ImmutableList.of(testTreeArtifact.getExecPathString());
+ Iterable<String> treeFileArtifactsPaths =
+ ImmutableList.of(library0.getExecPathString(), library1.getExecPathString());
+
+ Artifact paramFile = scratchArtifact("some/file.params");
+
+ LinkCommandLine linkConfig =
+ minimalConfiguration(
+ getMockBuildVariables()
+ .addStringVariable(
+ LinkBuildVariables.LINKER_PARAM_FILE.getVariableName(), "some/file.params")
+ .addCustomBuiltVariable(
+ LinkBuildVariables.LIBRARIES_TO_LINK.getVariableName(),
+ new CcToolchainVariables.SequenceBuilder()
+ .addValue(
+ LibraryToLinkValue.forObjectFileGroup(
+ ImmutableList.of(testTreeArtifact), false))))
+ .forceToolPath("foo/bar/gcc")
+ .setActionName(LinkTargetType.STATIC_LIBRARY.getActionName())
+ .setLinkTargetType(LinkTargetType.STATIC_LIBRARY)
+ .setLinkingMode(Link.LinkingMode.STATIC)
+ .setParamFile(paramFile)
+ .build();
+
+ // Should only reference the tree artifact.
+ verifyArguments(linkConfig.arguments(null), treeArtifactsPaths, treeFileArtifactsPaths);
+ verifyArguments(linkConfig.getRawLinkArgv(null), treeArtifactsPaths, treeFileArtifactsPaths);
+ verifyArguments(
+ linkConfig.paramCmdLine().arguments(null), treeArtifactsPaths, treeFileArtifactsPaths);
+
+ // Should only reference tree file artifacts.
+ verifyArguments(linkConfig.arguments(expander), treeFileArtifactsPaths, treeArtifactsPaths);
+ verifyArguments(
+ linkConfig.getRawLinkArgv(expander), treeFileArtifactsPaths, treeArtifactsPaths);
+ verifyArguments(
+ linkConfig.paramCmdLine().arguments(expander), treeFileArtifactsPaths, treeArtifactsPaths);
+ }
+}
diff --git a/src/test/java/com/google/devtools/build/lib/rules/cpp/NonWindowsCcBinaryThinLtoTest.java b/src/test/java/com/google/devtools/build/lib/rules/cpp/NonWindowsCcBinaryThinLtoTest.java
new file mode 100644
index 0000000..a893d6c
--- /dev/null
+++ b/src/test/java/com/google/devtools/build/lib/rules/cpp/NonWindowsCcBinaryThinLtoTest.java
@@ -0,0 +1,230 @@
+// Copyright 2020 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.
+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.Action;
+import com.google.devtools.build.lib.actions.Artifact;
+import com.google.devtools.build.lib.analysis.ConfiguredTarget;
+import com.google.devtools.build.lib.analysis.RuleContext;
+import com.google.devtools.build.lib.analysis.actions.SpawnAction;
+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.Crosstool.CcToolchainConfig;
+import com.google.devtools.build.lib.packages.util.MockCcSupport;
+import com.google.devtools.build.lib.rules.cpp.CppConfiguration.Tool;
+import com.google.devtools.build.lib.vfs.PathFragment;
+import java.util.List;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+/**
+ * Tests for cc_binary with ThinLTO.
+ *
+ * <p>As of 2020-02-06, these tests do not work on Windows, hence the "NonWindows" part of the class
+ * name.
+ */
+@RunWith(JUnit4.class)
+public final class NonWindowsCcBinaryThinLtoTest extends BuildViewTestCase {
+
+ public void createTestFiles(String extraTestParameters, String extraLibraryParameters)
+ throws Exception {
+ scratch.overwriteFile(
+ "base/BUILD", "cc_library(name = 'system_malloc', visibility = ['//visibility:public'])");
+ scratch.file(
+ "pkg/BUILD",
+ "package(features = ['thin_lto'])",
+ "cc_test(",
+ " name = 'bin_test',",
+ " srcs = ['bin_test.cc', ],",
+ " deps = [ ':lib' ], ",
+ extraTestParameters,
+ " malloc = '//base:system_malloc'",
+ ")",
+ "cc_test(",
+ " name = 'bin_test2',",
+ " srcs = ['bin_test2.cc', ],",
+ " deps = [ ':lib' ], ",
+ extraTestParameters,
+ " malloc = '//base:system_malloc'",
+ ")",
+ "cc_library(",
+ " name = 'lib',",
+ " srcs = ['libfile.cc'],",
+ " hdrs = ['libfile.h'],",
+ extraLibraryParameters,
+ " linkstamp = 'linkstamp.cc',",
+ ")");
+
+ scratch.file("pkg/bin_test.cc", "#include \"pkg/libfile.h\"", "int main() { return pkg(); }");
+ scratch.file("pkg/bin_test2.cc", "#include \"pkg/libfile.h\"", "int main() { return pkg(); }");
+ scratch.file("pkg/libfile.cc", "int pkg() { return 42; }");
+ scratch.file("pkg/libfile.h", "int pkg();");
+ scratch.file("pkg/linkstamp.cc");
+ }
+
+ /** Helper method to get the root prefix from the given dwpFile. */
+ private static PathFragment dwpRootPrefix(Artifact dwpFile) throws Exception {
+ return dwpFile
+ .getExecPath()
+ .subFragment(
+ 0, dwpFile.getExecPath().segmentCount() - dwpFile.getRootRelativePath().segmentCount());
+ }
+
+ /** Helper method that checks that a .dwp has the expected generating action structure. */
+ private void validateDwp(
+ RuleContext ruleContext,
+ Artifact dwpFile,
+ CcToolchainProvider toolchain,
+ List<String> expectedInputs)
+ throws Exception {
+ SpawnAction dwpAction = (SpawnAction) getGeneratingAction(dwpFile);
+ String dwpToolPath = toolchain.getToolPathFragment(Tool.DWP, ruleContext).getPathString();
+ assertThat(dwpAction.getMnemonic()).isEqualTo("CcGenerateDwp");
+ assertThat(dwpToolPath).isEqualTo(dwpAction.getCommandFilename());
+ List<String> commandArgs = dwpAction.getArguments();
+ // The first argument should be the command being executed.
+ assertThat(dwpToolPath).isEqualTo(commandArgs.get(0));
+ // The final two arguments should be "-o dwpOutputFile".
+ assertThat(commandArgs.subList(commandArgs.size() - 2, commandArgs.size()))
+ .containsExactly("-o", dwpFile.getExecPathString())
+ .inOrder();
+ // The remaining arguments should be the set of .dwo inputs (in any order).
+ assertThat(commandArgs.subList(1, commandArgs.size() - 2))
+ .containsExactlyElementsIn(expectedInputs);
+ }
+
+ @Test
+ public void testLinkstaticCcLibraryOnTestFission() throws Exception {
+ createTestFiles("", "linkstatic = 1,");
+
+ AnalysisMock.get()
+ .ccSupport()
+ .setupCcToolchainConfig(
+ mockToolsConfig,
+ CcToolchainConfig.builder()
+ .withFeatures(
+ CppRuleClasses.THIN_LTO,
+ CppRuleClasses.SUPPORTS_PIC,
+ CppRuleClasses.SUPPORTS_START_END_LIB,
+ CppRuleClasses.THIN_LTO_LINKSTATIC_TESTS_USE_SHARED_NONLTO_BACKENDS,
+ MockCcSupport.HOST_AND_NONHOST_CONFIGURATION_FEATURES,
+ CppRuleClasses.PER_OBJECT_DEBUG_INFO));
+ useConfiguration(
+ "--fission=yes", "--features=thin_lto_linkstatic_tests_use_shared_nonlto_backends");
+
+ ConfiguredTarget pkg = getConfiguredTarget("//pkg:bin_test");
+ Artifact pkgArtifact = getFilesToBuild(pkg).getSingleton();
+ CppLinkAction linkAction = (CppLinkAction) getGeneratingAction(pkgArtifact);
+
+ // The cc_test source should still get LTO in this case
+ LtoBackendAction backendAction =
+ (LtoBackendAction)
+ getPredecessorByInputName(linkAction, "bin_test.lto/pkg/_objs/bin_test/bin_test.pic.o");
+ assertThat(backendAction.getMnemonic()).isEqualTo("CcLtoBackendCompile");
+ assertThat(artifactsToStrings(backendAction.getOutputs()))
+ .containsExactly(
+ "bin pkg/bin_test.lto/pkg/_objs/bin_test/bin_test.pic.o",
+ "bin pkg/bin_test.lto/pkg/_objs/bin_test/bin_test.pic.dwo");
+
+ assertThat(backendAction.getArguments()).contains("per_object_debug_info_option");
+
+ // The linkstatic cc_library source should get shared non-LTO
+ backendAction =
+ (LtoBackendAction)
+ getPredecessorByInputName(linkAction, "shared.nonlto/pkg/_objs/lib/libfile.pic.o");
+ assertThat(backendAction.getMnemonic()).isEqualTo("CcLtoBackendCompile");
+ assertThat(backendAction.getArguments()).contains("-fPIC");
+ assertThat(artifactsToStrings(backendAction.getOutputs()))
+ .containsExactly(
+ "bin shared.nonlto/pkg/_objs/lib/libfile.pic.o",
+ "bin shared.nonlto/pkg/_objs/lib/libfile.pic.dwo");
+
+ assertThat(backendAction.getArguments()).contains("per_object_debug_info_option");
+
+ // Now check the dwp action.
+ Artifact dwpFile = getFileConfiguredTarget(pkg.getLabel() + ".dwp").getArtifact();
+ PathFragment rootPrefix = dwpRootPrefix(dwpFile);
+ RuleContext ruleContext = getRuleContext(pkg);
+ CcToolchainProvider toolchain =
+ CppHelper.getToolchainUsingDefaultCcToolchainAttribute(ruleContext);
+ validateDwp(
+ ruleContext,
+ dwpFile,
+ toolchain,
+ ImmutableList.of(
+ rootPrefix + "/shared.nonlto/pkg/_objs/lib/libfile.pic.dwo",
+ rootPrefix + "/pkg/bin_test.lto/pkg/_objs/bin_test/bin_test.pic.dwo"));
+ }
+
+ @Test
+ public void testLinkstaticCcLibraryOnTest() throws Exception {
+ createTestFiles("", "linkstatic = 1,");
+
+ AnalysisMock.get()
+ .ccSupport()
+ .setupCcToolchainConfig(
+ mockToolsConfig,
+ CcToolchainConfig.builder()
+ .withFeatures(
+ CppRuleClasses.THIN_LTO,
+ CppRuleClasses.SUPPORTS_START_END_LIB,
+ CppRuleClasses.THIN_LTO_LINKSTATIC_TESTS_USE_SHARED_NONLTO_BACKENDS,
+ MockCcSupport.HOST_AND_NONHOST_CONFIGURATION_FEATURES,
+ CppRuleClasses.SUPPORTS_PIC,
+ CppRuleClasses.PER_OBJECT_DEBUG_INFO));
+ useConfiguration("--features=thin_lto_linkstatic_tests_use_shared_nonlto_backends");
+
+ ConfiguredTarget pkg = getConfiguredTarget("//pkg:bin_test");
+ Artifact pkgArtifact = getFilesToBuild(pkg).getSingleton();
+ CppLinkAction linkAction = (CppLinkAction) getGeneratingAction(pkgArtifact);
+
+ ConfiguredTarget pkg2 = getConfiguredTarget("//pkg:bin_test2");
+ Artifact pkgArtifact2 = getFilesToBuild(pkg2).getSingleton();
+ CppLinkAction linkAction2 = (CppLinkAction) getGeneratingAction(pkgArtifact2);
+
+ // The cc_test source should still get LTO in this case
+ LtoBackendAction backendAction =
+ (LtoBackendAction)
+ getPredecessorByInputName(linkAction, "bin_test.lto/pkg/_objs/bin_test/bin_test.pic.o");
+ assertThat(backendAction.getMnemonic()).isEqualTo("CcLtoBackendCompile");
+
+ // The linkstatic cc_library sources should get shared non-LTO
+
+ backendAction =
+ (LtoBackendAction)
+ getPredecessorByInputName(linkAction, "shared.nonlto/pkg/_objs/lib/libfile.pic.o");
+ assertThat(backendAction.getMnemonic()).isEqualTo("CcLtoBackendCompile");
+ assertThat(backendAction.getArguments()).contains("-fPIC");
+
+ LtoBackendAction backendAction2 =
+ (LtoBackendAction)
+ getPredecessorByInputName(linkAction2, "shared.nonlto/pkg/_objs/lib/libfile.pic.o");
+ assertThat(backendAction2.getMnemonic()).isEqualTo("CcLtoBackendCompile");
+
+ assertThat(backendAction).isEqualTo(backendAction2);
+ }
+
+ private Action getPredecessorByInputName(Action action, String str) {
+ for (Artifact a : action.getInputs().toList()) {
+ if (a.getExecPathString().contains(str)) {
+ return getGeneratingAction(a);
+ }
+ }
+ return null;
+ }
+}
diff --git a/src/test/java/com/google/devtools/build/lib/rules/cpp/SpawnGccStrategyTest.java b/src/test/java/com/google/devtools/build/lib/rules/cpp/SpawnGccStrategyTest.java
new file mode 100644
index 0000000..86df9eb
--- /dev/null
+++ b/src/test/java/com/google/devtools/build/lib/rules/cpp/SpawnGccStrategyTest.java
@@ -0,0 +1,83 @@
+// Copyright 2020 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.
+package com.google.devtools.build.lib.rules.cpp;
+
+import static com.google.common.truth.Truth.assertThat;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.when;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSet;
+import com.google.devtools.build.lib.actions.AbstractAction;
+import com.google.devtools.build.lib.actions.Artifact;
+import com.google.devtools.build.lib.actions.ArtifactRoot;
+import com.google.devtools.build.lib.actions.ExecutionRequirements;
+import com.google.devtools.build.lib.actions.Spawn;
+import com.google.devtools.build.lib.actions.util.ActionsTestUtil;
+import com.google.devtools.build.lib.clock.JavaClock;
+import com.google.devtools.build.lib.collect.nestedset.NestedSetBuilder;
+import com.google.devtools.build.lib.collect.nestedset.Order;
+import com.google.devtools.build.lib.vfs.DigestHashFunction;
+import com.google.devtools.build.lib.vfs.FileSystem;
+import com.google.devtools.build.lib.vfs.Path;
+import com.google.devtools.build.lib.vfs.inmemoryfs.InMemoryFileSystem;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+import org.mockito.ArgumentMatchers;
+import org.mockito.Mockito;
+
+/** Tests for {@link CppCompileAction#createSpawn}. */
+@RunWith(JUnit4.class)
+public final class SpawnGccStrategyTest {
+ private FileSystem fs;
+ private ArtifactRoot ar;
+ private Path execRoot;
+
+ @Before
+ public void setup() {
+ fs = new InMemoryFileSystem(new JavaClock(), DigestHashFunction.SHA256);
+ execRoot = fs.getPath("/exec/root");
+ ar = ArtifactRoot.asDerivedRoot(execRoot, execRoot.getChild("out"));
+ }
+
+ @Test
+ public void testInMemoryDotdFileAndExecutionRequirement() throws Exception {
+ // Test that when in memory dotd files are enabled the execution requirement to inline the dotd
+ // file is added to the spawn.
+
+ // arrange
+ Artifact dotdFile = ActionsTestUtil.createArtifact(ar, ar.getRoot().asPath().getChild("dot.d"));
+ CppCompileAction action = Mockito.mock(CppCompileAction.class);
+ when(action.getOutputs()).thenReturn(ImmutableSet.of());
+ when(action.getMandatoryInputs()).thenReturn(NestedSetBuilder.emptySet(Order.STABLE_ORDER));
+ when(action.getAdditionalInputs()).thenReturn(NestedSetBuilder.emptySet(Order.STABLE_ORDER));
+ when(action.getExecutionInfo()).thenReturn(ImmutableMap.of());
+ when(action.getArguments()).thenReturn(ImmutableList.of());
+ when(action.getEnvironment(ArgumentMatchers.any())).thenReturn(ImmutableMap.of());
+ when(action.getDotdFile()).thenReturn(dotdFile);
+ when(action.useInMemoryDotdFiles()).thenReturn(true);
+ when(action.estimateResourceConsumptionLocal()).thenReturn(AbstractAction.DEFAULT_RESOURCE_SET);
+ when(action.createSpawn(any())).thenCallRealMethod();
+
+ // act
+ Spawn spawn = action.createSpawn(ImmutableMap.of());
+
+ ImmutableMap<String, String> execInfo = spawn.getExecutionInfo();
+ assertThat(execInfo.get(ExecutionRequirements.REMOTE_EXECUTION_INLINE_OUTPUTS))
+ .isEqualTo(action.getDotdFile().getExecPathString());
+ }
+}