aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/test/java/com/google/devtools
diff options
context:
space:
mode:
authorGravatar Dmitry Lomov <dslomov@google.com>2015-11-26 19:22:56 +0000
committerGravatar Kristina Chodorow <kchodorow@google.com>2015-11-30 18:29:50 +0000
commitece87c29d1906ec9bc8ffad6990071cf678d299c (patch)
treeb8c8b6720de3ae9fcd29657bac84d09ce5d94425 /src/test/java/com/google/devtools
parent6072b2b2f06d1b448e0406e62da332808f347565 (diff)
Opensource more cpp rules tests.
-- MOS_MIGRATED_REVID=108806251
Diffstat (limited to 'src/test/java/com/google/devtools')
-rw-r--r--src/test/java/com/google/devtools/build/lib/BUILD5
-rw-r--r--src/test/java/com/google/devtools/build/lib/rules/cpp/CcCompileOnlyTest.java50
-rw-r--r--src/test/java/com/google/devtools/build/lib/rules/cpp/CcSkylarkApiProviderTest.java127
-rw-r--r--src/test/java/com/google/devtools/build/lib/rules/cpp/CcTransitiveGeneratedHeaderDepsTest.java270
-rw-r--r--src/test/java/com/google/devtools/build/lib/rules/cpp/CppCompilationContextTest.java131
-rw-r--r--src/test/java/com/google/devtools/build/lib/rules/cpp/CppLinkActionTest.java238
-rw-r--r--src/test/java/com/google/devtools/build/lib/rules/cpp/CppOptionsTest.java52
-rw-r--r--src/test/java/com/google/devtools/build/lib/testutil/TestConstants.java4
8 files changed, 876 insertions, 1 deletions
diff --git a/src/test/java/com/google/devtools/build/lib/BUILD b/src/test/java/com/google/devtools/build/lib/BUILD
index c12e7f17c9..d293937b51 100644
--- a/src/test/java/com/google/devtools/build/lib/BUILD
+++ b/src/test/java/com/google/devtools/build/lib/BUILD
@@ -839,7 +839,10 @@ java_test(
":testutil",
"//src/main/java/com/google/devtools/build/lib:analysis-exec-rules-skyframe",
"//src/main/java/com/google/devtools/build/lib:bazel-core",
+ "//src/main/java/com/google/devtools/build/lib:util",
"//src/main/java/com/google/devtools/build/lib:vfs",
+ "//src/main/java/com/google/devtools/build/lib/actions",
+ "//src/main/java/com/google/devtools/common/options",
"//src/main/protobuf:crosstool_config_proto",
"//third_party:guava",
"//third_party:guava-testlib",
@@ -863,8 +866,8 @@ java_test(
":test_runner",
"//src/main/java/com/google/devtools/build/lib:bazel-core",
"//src/main/java/com/google/devtools/build/lib:events",
- "//src/main/java/com/google/devtools/build/lib:util",
"//src/main/java/com/google/devtools/build/lib:vfs",
+ "//src/main/java/com/google/dtesevtools/build/lib:util",
"//src/main/protobuf:crosstool_config_proto",
"//third_party:guava",
"//third_party:junit4",
diff --git a/src/test/java/com/google/devtools/build/lib/rules/cpp/CcCompileOnlyTest.java b/src/test/java/com/google/devtools/build/lib/rules/cpp/CcCompileOnlyTest.java
new file mode 100644
index 0000000000..4537fa2925
--- /dev/null
+++ b/src/test/java/com/google/devtools/build/lib/rules/cpp/CcCompileOnlyTest.java
@@ -0,0 +1,50 @@
+// Copyright 2015 The Bazel Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+package com.google.devtools.build.lib.rules.cpp;
+
+import com.google.devtools.build.lib.analysis.ConfiguredTarget;
+import com.google.devtools.build.lib.analysis.util.CompileOnlyTestCase;
+
+/**
+ * Unit tests that validate --compile_only behavior.
+ */
+public class CcCompileOnlyTest extends CompileOnlyTestCase {
+ public void testCcCompileOnly() throws Exception {
+ scratch.file("package/BUILD",
+ "cc_binary(name='foo', srcs=['foo.cc', ':bar'], deps = [':foolib'])",
+ "cc_library(name='foolib', srcs=['foolib.cc'])",
+ "genrule(name='bar', outs=['bar.h', 'bar.cc'], cmd='touch $(OUTS)')");
+ scratch.file("package/foo.cc",
+ "#include <stdio.h>",
+ "int main() {",
+ " printf(\"Hello, world!\\n\");",
+ " return 0;",
+ "}");
+ scratch.file("package/foolib.cc",
+ "#include <stdio.h>",
+ "int printHeader() {",
+ " printf(\"Hello, library!\\n\");",
+ " return 0;",
+ "}");
+
+ ConfiguredTarget target = getConfiguredTarget("//package:foo");
+
+ assertNotNull(getArtifactByExecPathSuffix(target, "/foo.pic.o"));
+ assertNotNull(getArtifactByExecPathSuffix(target, "/bar.pic.o"));
+ // Check that deps are not built
+ assertNull(getArtifactByExecPathSuffix(target, "/foolib.pic.o"));
+ // Check that linking is not executed
+ assertNull(getArtifactByExecPathSuffix(target, "/foo"));
+ }
+}
diff --git a/src/test/java/com/google/devtools/build/lib/rules/cpp/CcSkylarkApiProviderTest.java b/src/test/java/com/google/devtools/build/lib/rules/cpp/CcSkylarkApiProviderTest.java
new file mode 100644
index 0000000000..56751a61ad
--- /dev/null
+++ b/src/test/java/com/google/devtools/build/lib/rules/cpp/CcSkylarkApiProviderTest.java
@@ -0,0 +1,127 @@
+// Copyright 2015 The Bazel Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+package com.google.devtools.build.lib.rules.cpp;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import com.google.devtools.build.lib.actions.util.ActionsTestUtil;
+import com.google.devtools.build.lib.analysis.RuleConfiguredTarget;
+import com.google.devtools.build.lib.analysis.util.BuildViewTestCase;
+import com.google.devtools.build.lib.testutil.TestConstants;
+
+/**
+ * Tests for Skylark providers for cpp rules.
+ */
+public class CcSkylarkApiProviderTest extends BuildViewTestCase {
+ private CcSkylarkApiProvider getApi(String label) throws Exception {
+ RuleConfiguredTarget rule = (RuleConfiguredTarget) getConfiguredTarget(label);
+ return (CcSkylarkApiProvider) rule.get(CcSkylarkApiProvider.NAME);
+ }
+
+ public void testTransitiveHeaders() throws Exception {
+ scratch.file(
+ "pkg/BUILD",
+ "cc_binary(",
+ " name = 'check',",
+ " srcs = ['bin.cc', 'bin.h'],",
+ " deps = [':check_lib'],",
+ ")",
+ "cc_library(",
+ " name = 'check_lib',",
+ " srcs = ['lib.cc', 'lib.h'],",
+ ")");
+ assertThat(ActionsTestUtil.baseArtifactNames(getApi("//pkg:check").getTransitiveHeaders()))
+ .containsAllOf("lib.h", "bin.h");
+ assertThat(ActionsTestUtil.baseArtifactNames(getApi("//pkg:check_lib").getTransitiveHeaders()))
+ .contains("lib.h");
+ }
+
+ public void testLinkFlags() throws Exception {
+ scratch.file(
+ "pkg/BUILD",
+ "cc_binary(",
+ " name = 'check',",
+ " srcs = ['bin.cc', 'bin.h'],",
+ " linkopts = ['-lm'],",
+ " deps = [':dependent_lib'],",
+ ")",
+ "cc_binary(",
+ " name = 'check_no_srcs',",
+ " linkopts = ['-lm'],",
+ " deps = [':dependent_lib'],",
+ ")",
+ "cc_library(",
+ " name = 'dependent_lib',",
+ " linkopts = ['-lz'],",
+ " deps = [':check_lib'],",
+ ")",
+ "cc_library(",
+ " name = 'check_lib',",
+ " defines = ['foo'],",
+ " linkopts = ['-Wl,-M'],",
+ ")");
+ assertThat(getApi("//pkg:check_lib").getLinkopts())
+ .contains("-Wl,-M");
+ assertThat(getApi("//pkg:dependent_lib").getLinkopts())
+ .containsAllOf("-lz", "-Wl,-M")
+ .inOrder();
+ assertThat(getApi("//pkg:check").getLinkopts())
+ .isEmpty();
+ assertThat(getApi("//pkg:check_no_srcs").getLinkopts())
+ .isEmpty();
+ }
+
+ public void testLibraries() throws Exception {
+ scratch.file(
+ "pkg/BUILD",
+ "cc_binary(",
+ " name = 'check',",
+ " srcs = ['bin.cc', 'bin.h'],",
+ " deps = [':check_lib'],",
+ ")",
+ "cc_binary(",
+ " name = 'check_no_srcs',",
+ " deps = [':check_lib'],",
+ ")",
+ "cc_library(",
+ " name = 'check_lib',",
+ " srcs = ['lib.cc', 'lib.h'],",
+ ")");
+ assertThat(ActionsTestUtil.baseArtifactNames(getApi("//pkg:check_lib").getLibraries()))
+ .containsExactly("libcheck_lib.a");
+ assertThat(ActionsTestUtil.baseArtifactNames(getApi("//pkg:check").getLibraries()))
+ .isEmpty();
+ assertThat(ActionsTestUtil.baseArtifactNames(getApi("//pkg:check_no_srcs").getLibraries()))
+ .isEmpty();
+ }
+
+ public void testCcFlags() throws Exception {
+ scratch.file(
+ "pkg/BUILD",
+ "cc_binary(",
+ " name = 'check',",
+ " srcs = ['bin.cc', 'bin.h'],",
+ " deps = [':check_lib'],",
+ ")",
+ "cc_library(",
+ " name = 'check_lib',",
+ " defines = ['foo'],",
+ ")");
+ // The particular values for include directories are slightly
+ // fragile because the build system changes. But check for at
+ // least one normal include, one system include, and one define.
+ assertThat(getApi("//pkg:check").getCcFlags())
+ .containsAllOf("-iquote .", "-isystem " + TestConstants.GCC_INCLUDE_PATH, "-Dfoo");
+ }
+}
diff --git a/src/test/java/com/google/devtools/build/lib/rules/cpp/CcTransitiveGeneratedHeaderDepsTest.java b/src/test/java/com/google/devtools/build/lib/rules/cpp/CcTransitiveGeneratedHeaderDepsTest.java
new file mode 100644
index 0000000000..8702ffc09b
--- /dev/null
+++ b/src/test/java/com/google/devtools/build/lib/rules/cpp/CcTransitiveGeneratedHeaderDepsTest.java
@@ -0,0 +1,270 @@
+// Copyright 2015 The Bazel Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+package com.google.devtools.build.lib.rules.cpp;
+
+import static com.google.common.truth.Truth.assertThat;
+import static com.google.devtools.build.lib.util.StringUtilities.joinLines;
+
+import com.google.common.base.Function;
+import com.google.common.base.Predicate;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Iterables;
+import com.google.common.collect.Ordering;
+import com.google.devtools.build.lib.actions.Artifact;
+import com.google.devtools.build.lib.analysis.ConfiguredTarget;
+import com.google.devtools.build.lib.analysis.util.BuildViewTestCase;
+import com.google.devtools.build.lib.util.Pair;
+import com.google.devtools.build.lib.vfs.PathFragment;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * Tests how generated header dependencies make up a little middlemen +
+ * headers DAG which hangs off of cc_library nodes.
+ */
+public class CcTransitiveGeneratedHeaderDepsTest extends BuildViewTestCase {
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ writeFiles();
+ }
+
+ private void writeFiles() throws Exception {
+ scratch.file("foo/BUILD", "cc_library(name = 'foo',",
+ " srcs = ['foo.cc'],",
+ " deps = ['//bar', '//boo'])");
+
+ scratch.file("bar/BUILD", "cc_library(name = 'bar',",
+ " srcs = ['bar.cc',",
+ " 'bargen.h',",
+ " 'bargen2.h'],",
+ " deps = ['//baz', '//boo'])",
+ "genrule(name = 'generated',",
+ " srcs = ['gen.py'],",
+ " cmd = 'gen.py',",
+ " outs = ['bargen.h'])",
+ "genrule(name = 'generated2',",
+ " srcs = ['gen.py'],",
+ " cmd = 'gen.py',",
+ " outs = ['bargen2.h'])");
+
+ scratch.file("baz/BUILD", "cc_library(name = 'baz',",
+ " srcs = ['baz.cc',",
+ " 'bazgen.h'])",
+ "genrule(name = 'generated',",
+ " srcs = ['gen.py'],",
+ " cmd = 'gen.py',",
+ " outs = ['bazgen.h'])");
+
+ scratch.file("boo/BUILD", "cc_library(name = 'boo',",
+ " srcs = ['boo.cc',",
+ " 'boogen.h'])",
+ "genrule(name = 'boogen',",
+ " srcs = ['gen.py'],",
+ " cmd = 'gen.py',",
+ " outs = ['boogen.h'])");
+ }
+
+ private ConfiguredTarget createTargets() throws Exception {
+ getConfiguredTarget("//bar:bar");
+ getConfiguredTarget("//baz:baz");
+ return getConfiguredTarget("//foo:foo");
+ }
+
+ private ConfiguredTarget setupWithOptions(String... optStrings) throws Exception {
+ useConfiguration(optStrings);
+ return createTargets();
+ }
+
+ public void testQuoteIncludeDirs() throws Exception {
+ ConfiguredTarget fooLib = setupWithOptions();
+ ImmutableList<PathFragment> quoteIncludeDirs =
+ fooLib.getProvider(CppCompilationContext.class).getQuoteIncludeDirs();
+ assertThat(filterExternalBazelTools(quoteIncludeDirs)).containsExactly(
+ PathFragment.EMPTY_FRAGMENT, targetConfig.getGenfilesFragment()).inOrder();
+ }
+
+ protected static Iterable<PathFragment> filterExternalBazelTools(
+ ImmutableList<PathFragment> quoteIncludeDirs) {
+ return Iterables.filter(
+ quoteIncludeDirs,
+ new Predicate<PathFragment>() {
+ @Override
+ public boolean apply(PathFragment pathFragment) {
+ return !pathFragment.endsWith(new PathFragment("external/bazel_tools"));
+ }
+ });
+ }
+
+ public void testGeneratesTreeOfMiddlemenAndGeneratedHeaders() throws Exception {
+ ConfiguredTarget fooLib = setupWithOptions("--noextract_generated_inclusions");
+ Set<Artifact> middlemen = fooLib.getProvider(CppCompilationContext.class)
+ .getCompilationPrerequisites();
+
+ // While normal middlemen are not created if they are depend on just one
+ // input, C++ compilation dependencies are expressed using scheduling
+ // middleman where one input optimization no longer applies.
+ assertEquals(joinLines(
+ "middleman-0",
+ " middleman-1",
+ " bargen.h",
+ " bargen2.h",
+ " middleman-2",
+ " bazgen.h",
+ " middleman-3",
+ " boogen.h",
+ " middleman-3",
+ " boogen.h",
+ ""), new MiddlemenRenderer(middlemen).toString());
+ }
+
+ public void testExtractInclusionsInActionGraph() throws Exception {
+ ConfiguredTarget fooLib = setupWithOptions("--extract_generated_inclusions");
+
+ Set<Artifact> middlemen = fooLib.getProvider(CppCompilationContext.class)
+ .getCompilationPrerequisites();
+
+ // While normal middlemen are not created if they are depend on just one
+ // input, C++ compilation dependencies are expressed using scheduling
+ // middleman where one input optimization no longer applies.
+ assertEquals(joinLines(
+ "middleman-0",
+ " middleman-1",
+ " bargen.h.includes",
+ " bargen2.h.includes",
+ " bargen.h",
+ " bargen2.h",
+ " middleman-2",
+ " bazgen.h.includes",
+ " bazgen.h",
+ " middleman-3",
+ " boogen.h.includes",
+ " boogen.h",
+ " middleman-3",
+ " boogen.h.includes",
+ " boogen.h",
+ ""), new MiddlemenRenderer(middlemen).toString());
+
+ List<Artifact> nonMiddlemen = new ArrayList<>();
+ getRealArtifacts(middlemen, nonMiddlemen);
+ assertThat(nonMiddlemen).isNotEmpty();
+ Iterable<Artifact> includes = Iterables.filter(nonMiddlemen, new Predicate<Artifact>(){
+ @Override
+ public boolean apply(Artifact artifact) {
+ return artifact.getExecPathString().endsWith(".h.includes");
+ }
+ });
+ assertThat(includes).isNotEmpty();
+ for (Artifact file : nonMiddlemen) {
+ assertNotNull(file.getExecPathString(), getGeneratingAction(file));
+ assertFalse(file.getExecPathString(), file.isSourceArtifact());
+ }
+
+ Iterable<Artifact> pregreppedArtifacts =
+ Iterables.transform(fooLib
+ .getProvider(CppCompilationContext.class)
+ .getPregreppedHeaders().toCollection(), Pair.<Artifact, Artifact>secondFunction());
+ Iterable<String> pregreppedFiles = Iterables.transform(pregreppedArtifacts,
+ new Function<Artifact, String>() {
+ @Override
+ public String apply(Artifact input) {
+ return input.getPath().getBaseName();
+ }
+ });
+ assertThat(pregreppedFiles).containsExactly("bargen.h.includes", "bargen2.h.includes",
+ "bazgen.h.includes", "boogen.h.includes");
+ }
+
+ private void getRealArtifacts(Iterable<Artifact> middlemenOrHeaders,
+ List<Artifact> artifacts) {
+ for (Artifact file : middlemenOrHeaders) {
+ if (isMiddleman(file)) {
+ getRealArtifacts(getGeneratingAction(file).getInputs(), artifacts);
+ } else {
+ artifacts.add(file);
+ }
+ }
+ }
+
+ private static boolean isMiddleman(Artifact artifact) {
+ return artifact.getExecPath().toString().contains("_middlemen");
+ }
+
+ /**
+ * Renders a little tree representation of a set of middlemen and headers
+ * into a string.
+ */
+ private class MiddlemenRenderer {
+
+ /** The rendering buffer */
+ StringBuilder buffer = new StringBuilder();
+
+ /** How much to indent while we're emitting to buffer */
+ int indent = 0;
+
+ /** Middlemen have weird path names, so we map them to integer ids */
+ int nextId = 0;
+ Map<Artifact, Integer> middlemanToIdOld = new HashMap<>();
+
+ MiddlemenRenderer(Set<Artifact> middlemen) {
+ print(middlemen);
+ }
+
+ void print(Collection<Artifact> middlemenOrHeaders) {
+ for (Artifact middleman : middlemenOrHeaders) {
+ printIndent();
+ if (isMiddleman(middleman)) {
+ printMiddleman(middleman);
+ indent += 2;
+ List<Artifact> inputs = Ordering.from(Artifact.EXEC_PATH_COMPARATOR)
+ .sortedCopy(getGeneratingAction(middleman).getInputs());
+ print(inputs);
+ indent -= 2;
+ } else {
+ printPrerequisite(middleman);
+ }
+ }
+ }
+
+ private void printPrerequisite(Artifact header) {
+ buffer.append(header.getExecPath().getBaseName() + "\n");
+ }
+
+ void printIndent() {
+ for (int i = 0; i < indent; i++) {
+ buffer.append(' ');
+ }
+ }
+
+ void printMiddleman(Artifact middleman) {
+ if (!middlemanToIdOld.containsKey(middleman)) {
+ middlemanToIdOld.put(middleman, nextId++);
+ }
+ int id = middlemanToIdOld.get(middleman);
+ buffer.append("middleman-" + id + "\n");
+ }
+
+ @Override
+ public String toString() {
+ return buffer.toString();
+ }
+ }
+
+}
diff --git a/src/test/java/com/google/devtools/build/lib/rules/cpp/CppCompilationContextTest.java b/src/test/java/com/google/devtools/build/lib/rules/cpp/CppCompilationContextTest.java
new file mode 100644
index 0000000000..89c6000ad6
--- /dev/null
+++ b/src/test/java/com/google/devtools/build/lib/rules/cpp/CppCompilationContextTest.java
@@ -0,0 +1,131 @@
+// Copyright 2015 The Bazel Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.devtools.build.lib.rules.cpp;
+
+import static com.google.devtools.build.lib.actions.util.ActionsTestUtil.NULL_ACTION_OWNER;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.testing.EqualsTester;
+import com.google.devtools.build.lib.actions.MiddlemanFactory;
+import com.google.devtools.build.lib.analysis.ConfiguredTarget;
+import com.google.devtools.build.lib.analysis.util.BuildViewTestCase;
+
+/**
+ * Tests for {@link CppCompilationContext}.
+ */
+public class CppCompilationContextTest extends BuildViewTestCase {
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ scratch.file("foo/BUILD",
+ "cc_binary(name = 'foo',",
+ " srcs = ['foo.cc'])",
+ "cc_binary(name = 'bar',",
+ " srcs = ['bar.cc'])");
+ }
+
+ public void testEqualsAndHashCode() throws Exception {
+ MiddlemanFactory middlemanFactory = getTestAnalysisEnvironment().getMiddlemanFactory();
+ ConfiguredTarget fooBin = getConfiguredTarget("//foo:foo");
+ CppCompilationContext fooContextA1 = new CppCompilationContext.Builder(
+ getRuleContext(fooBin)).build(NULL_ACTION_OWNER, middlemanFactory);
+ CppCompilationContext fooContextA2 = new CppCompilationContext.Builder(
+ getRuleContext(fooBin)).build(NULL_ACTION_OWNER, middlemanFactory);
+ CppCompilationContext fooContextB = new CppCompilationContext.Builder(
+ getRuleContext(fooBin)).addDefine("a=b")
+ .build(NULL_ACTION_OWNER, middlemanFactory);
+ ConfiguredTarget barBin = getConfiguredTarget("//foo:bar");
+ // The fact that the configured target is different does not matter in this case.
+ CppCompilationContext barContext = new CppCompilationContext.Builder(
+ getRuleContext(barBin)).build(NULL_ACTION_OWNER, middlemanFactory);
+
+ CppCompilationContext fooContextWithModuleMap =
+ new CppCompilationContext.Builder(getRuleContext(fooBin))
+ .setCppModuleMap(new CppModuleMap(getBinArtifact("foo/something.xyz", fooBin), "name"))
+ .build(NULL_ACTION_OWNER, middlemanFactory);
+
+ CppCompilationContext fooContextWithCompilationPrerequisites =
+ new CppCompilationContext.Builder(getRuleContext(fooBin))
+ .addCompilationPrerequisites(ImmutableList.of(getBinArtifact("foo/something.xyz", fooBin)))
+ .build(NULL_ACTION_OWNER, middlemanFactory);
+
+ CppCompilationContext fooContextWithModuleMapAndCompilationPrerequisites =
+ new CppCompilationContext.Builder(getRuleContext(fooBin))
+ .addCompilationPrerequisites(ImmutableList.of(getBinArtifact("foo/something.xyz", fooBin)))
+ .setCppModuleMap(new CppModuleMap(getBinArtifact("foo/something.xyz", fooBin), "name"))
+ .build(NULL_ACTION_OWNER, middlemanFactory);
+
+ CppCompilationContext fooContextWithInheritedModuleMapSameAsModuleMap =
+ new CppCompilationContext.Builder(getRuleContext(fooBin))
+ .mergeDependentContext(fooContextWithModuleMap)
+ .setCppModuleMap(new CppModuleMap(getBinArtifact("foo/something.xyz", fooBin), "name"))
+ .build(NULL_ACTION_OWNER, middlemanFactory);
+
+ CppCompilationContext fooContextWithHeaderModuleSrcs =
+ new CppCompilationContext.Builder(getRuleContext(fooBin))
+ .addDeclaredIncludeSrc(getSourceArtifact("foo/foo.h"))
+ .build(NULL_ACTION_OWNER, middlemanFactory);
+
+ CppCompilationContext fooContextWithInheritedHeaderModuleSrcs =
+ new CppCompilationContext.Builder(getRuleContext(fooBin))
+ .mergeDependentContext(fooContextWithHeaderModuleSrcs)
+ .build(NULL_ACTION_OWNER, middlemanFactory);
+
+ CppCompilationContext fooContextWithHeaderModule =
+ new CppCompilationContext.Builder(getRuleContext(fooBin))
+ .setHeaderModule(getGenfilesArtifact("foo/something.pcm", fooBin))
+ .addDeclaredIncludeSrc(getSourceArtifact("foo/bar.h"))
+ .build(NULL_ACTION_OWNER, middlemanFactory);
+
+ CppCompilationContext fooContextWithPicHeaderModule =
+ new CppCompilationContext.Builder(getRuleContext(fooBin))
+ .setPicHeaderModule(getGenfilesArtifact("foo/something.pcm", fooBin))
+ .build(NULL_ACTION_OWNER, middlemanFactory);
+
+ CppCompilationContext fooContextWithInheritedHeaderModule =
+ new CppCompilationContext.Builder(getRuleContext(fooBin))
+ .mergeDependentContext(fooContextWithHeaderModule)
+ .setHeaderModule(getGenfilesArtifact("foo/something.pcm", fooBin))
+ .addDeclaredIncludeSrc(getSourceArtifact("foo/bar.h"))
+ .build(NULL_ACTION_OWNER, middlemanFactory);
+
+ CppCompilationContext fooContextWithTransitivelyInheritedHeaderModule =
+ new CppCompilationContext.Builder(getRuleContext(fooBin))
+ .mergeDependentContext(fooContextWithInheritedHeaderModule)
+ .setHeaderModule(getGenfilesArtifact("foo/something.pcm", fooBin))
+ .build(NULL_ACTION_OWNER, middlemanFactory);
+
+ CppCompilationContext fooContextUsingHeaderModules =
+ new CppCompilationContext.Builder(getRuleContext(fooBin))
+ .setProvideTransitiveModuleMaps(true).build(NULL_ACTION_OWNER, middlemanFactory);
+
+ new EqualsTester()
+ .addEqualityGroup(fooContextA1, fooContextA2, barContext)
+ .addEqualityGroup(fooContextB)
+ .addEqualityGroup(fooContextWithModuleMap)
+ .addEqualityGroup(fooContextWithCompilationPrerequisites)
+ .addEqualityGroup(fooContextWithModuleMapAndCompilationPrerequisites)
+ .addEqualityGroup(fooContextWithInheritedModuleMapSameAsModuleMap)
+ .addEqualityGroup(fooContextWithHeaderModuleSrcs)
+ .addEqualityGroup(fooContextWithInheritedHeaderModuleSrcs)
+ .addEqualityGroup(fooContextWithHeaderModule)
+ .addEqualityGroup(fooContextWithPicHeaderModule)
+ .addEqualityGroup(fooContextWithInheritedHeaderModule)
+ .addEqualityGroup(fooContextWithTransitivelyInheritedHeaderModule)
+ .addEqualityGroup(fooContextUsingHeaderModules)
+ .testEquals();
+ }
+}
diff --git a/src/test/java/com/google/devtools/build/lib/rules/cpp/CppLinkActionTest.java b/src/test/java/com/google/devtools/build/lib/rules/cpp/CppLinkActionTest.java
new file mode 100644
index 0000000000..8ed44605c1
--- /dev/null
+++ b/src/test/java/com/google/devtools/build/lib/rules/cpp/CppLinkActionTest.java
@@ -0,0 +1,238 @@
+// Copyright 2015 The Bazel Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.devtools.build.lib.rules.cpp;
+
+import static com.google.devtools.build.lib.actions.util.ActionsTestUtil.NULL_ACTION_OWNER;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Iterables;
+import com.google.devtools.build.lib.actions.Action;
+import com.google.devtools.build.lib.actions.Artifact;
+import com.google.devtools.build.lib.actions.ResourceSet;
+import com.google.devtools.build.lib.actions.Root;
+import com.google.devtools.build.lib.actions.util.ActionsTestUtil;
+import com.google.devtools.build.lib.analysis.AnalysisEnvironment;
+import com.google.devtools.build.lib.analysis.RuleContext;
+import com.google.devtools.build.lib.analysis.util.ActionTester;
+import com.google.devtools.build.lib.analysis.util.ActionTester.ActionCombinationFactory;
+import com.google.devtools.build.lib.analysis.util.AnalysisTestUtil;
+import com.google.devtools.build.lib.analysis.util.BuildViewTestCase;
+import com.google.devtools.build.lib.collect.nestedset.NestedSetBuilder;
+import com.google.devtools.build.lib.collect.nestedset.Order;
+import com.google.devtools.build.lib.rules.cpp.CppLinkAction.Builder;
+import com.google.devtools.build.lib.rules.cpp.Link.LinkStaticness;
+import com.google.devtools.build.lib.rules.cpp.Link.LinkTargetType;
+import com.google.devtools.build.lib.rules.cpp.LinkerInputs.LibraryToLink;
+import com.google.devtools.build.lib.vfs.PathFragment;
+
+/**
+ * Tests for {@link CppLinkAction}.
+ */
+public class CppLinkActionTest extends BuildViewTestCase {
+ private RuleContext createDummyRuleContext() throws Exception {
+ return view.getRuleContextForTesting(reporter, scratchConfiguredTarget(
+ "dummyRuleContext", "dummyRuleContext",
+ // CppLinkAction creation requires a CcToolchainProvider.
+ "cc_library(name = 'dummyRuleContext')"),
+ new StubAnalysisEnvironment() {
+ @Override
+ public void registerAction(Action... action) {
+ // No-op.
+ }
+
+ @Override
+ public Artifact getDerivedArtifact(PathFragment rootRelativePath, Root root) {
+ return CppLinkActionTest.this.getDerivedArtifact(
+ rootRelativePath, root, ActionsTestUtil.NULL_ARTIFACT_OWNER);
+ }
+ }, masterConfig);
+ }
+
+ /**
+ * This mainly checks that non-static links don't have identical keys. Many options are only
+ * allowed on non-static links, and we test several of them here.
+ */
+ public void testComputeKeyNonStatic() throws Exception {
+ final RuleContext ruleContext = createDummyRuleContext();
+ final PathFragment outputPath = new PathFragment("dummyRuleContext/output/path.xyz");
+ final Artifact outputFile = getBinArtifactWithNoOwner(outputPath.getPathString());
+ final Artifact oFile = getSourceArtifact("cc/a.o");
+ final Artifact oFile2 = getSourceArtifact("cc/a2.o");
+ final Artifact interfaceSoBuilder = getBinArtifactWithNoOwner("foo/build_interface_so");
+ ActionTester.runTest(
+ 128,
+ new ActionCombinationFactory() {
+
+ @Override
+ public Action generate(int i) {
+ CppLinkAction.Builder builder =
+ new CppLinkAction.Builder(ruleContext, outputFile) {
+ @Override
+ protected Artifact getInterfaceSoBuilder() {
+ return interfaceSoBuilder;
+ }
+ };
+ builder.addCompilationInputs(
+ (i & 1) == 0 ? ImmutableList.of(oFile) : ImmutableList.of(oFile2));
+ builder.setLinkType(
+ (i & 2) == 0 ? LinkTargetType.DYNAMIC_LIBRARY : LinkTargetType.EXECUTABLE);
+ builder.setLinkStaticness(LinkStaticness.DYNAMIC);
+ builder.setNativeDeps((i & 4) == 0);
+ builder.setUseTestOnlyFlags((i & 8) == 0);
+ builder.setWholeArchive((i & 16) == 0);
+ builder.setFake((i & 32) == 0);
+ builder.setRuntimeSolibDir((i & 64) == 0 ? null : new PathFragment("so1"));
+ return builder.build();
+ }
+ });
+ }
+
+ /**
+ * This mainly checks that static library links don't have identical keys, and it also compares
+ * them with simple dynamic library links.
+ */
+ public void testComputeKeyStatic() throws Exception {
+ final RuleContext ruleContext = createDummyRuleContext();
+ final PathFragment outputPath = new PathFragment("dummyRuleContext/output/path.xyz");
+ final Artifact outputFile = getBinArtifactWithNoOwner(outputPath.getPathString());
+ final Artifact oFile = getSourceArtifact("cc/a.o");
+ final Artifact oFile2 = getSourceArtifact("cc/a2.o");
+ final Artifact interfaceSoBuilder = getBinArtifactWithNoOwner("foo/build_interface_so");
+ ActionTester.runTest(
+ 4,
+ new ActionCombinationFactory() {
+
+ @Override
+ public Action generate(int i) {
+ CppLinkAction.Builder builder =
+ new CppLinkAction.Builder(ruleContext, outputFile) {
+ @Override
+ protected Artifact getInterfaceSoBuilder() {
+ return interfaceSoBuilder;
+ }
+ };
+ builder.addCompilationInputs(
+ (i & 1) == 0 ? ImmutableList.of(oFile) : ImmutableList.of(oFile2));
+ builder.setLinkType(
+ (i & 2) == 0 ? LinkTargetType.STATIC_LIBRARY : LinkTargetType.DYNAMIC_LIBRARY);
+ return builder.build();
+ }
+ });
+ }
+
+ public void testCommandLineSplitting() throws Exception {
+ RuleContext ruleContext = createDummyRuleContext();
+ Artifact output = getDerivedArtifact(
+ new PathFragment("output/path.xyz"), getTargetConfiguration().getBinDirectory(),
+ ActionsTestUtil.NULL_ARTIFACT_OWNER);
+ final Artifact outputIfso = getDerivedArtifact(
+ new PathFragment("output/path.ifso"), getTargetConfiguration().getBinDirectory(),
+ ActionsTestUtil.NULL_ARTIFACT_OWNER);
+ CppLinkAction.Builder builder = new CppLinkAction.Builder(ruleContext, output);
+ builder.setLinkType(LinkTargetType.STATIC_LIBRARY);
+ assertTrue(builder.canSplitCommandLine());
+
+ builder.setLinkType(LinkTargetType.DYNAMIC_LIBRARY);
+ assertTrue(builder.canSplitCommandLine());
+
+ builder.setInterfaceOutput(outputIfso);
+ assertFalse(builder.canSplitCommandLine());
+
+ builder.setInterfaceOutput(null);
+ builder.setLinkType(LinkTargetType.INTERFACE_DYNAMIC_LIBRARY);
+ assertFalse(builder.canSplitCommandLine());
+ }
+
+ /**
+ * Links a small target.
+ * Checks that resource estimates are above the minimum and scale correctly.
+ */
+ public void testSmallLocalLinkResourceEstimate() throws Exception {
+ assertLinkSizeAccuracy(3);
+ }
+
+ /**
+ * Fake links a large target.
+ * Checks that resource estimates are above the minimum and scale correctly.
+ * The actual link action is irrelevant; we are just checking the estimate.
+ */
+ public void testLargeLocalLinkResourceEstimate() throws Exception {
+ assertLinkSizeAccuracy(7000);
+ }
+
+ private void assertLinkSizeAccuracy(int inputs) throws Exception {
+ ImmutableList.Builder<Artifact> objects = ImmutableList.builder();
+ for (int i = 0; i < inputs; i++) {
+ objects.add(getOutputArtifact("object" + i + ".o"));
+ }
+
+ CppLinkAction linkAction = createLinkBuilder(
+ Link.LinkTargetType.EXECUTABLE, "binary2", objects.build(),
+ ImmutableList.<LibraryToLink>of())
+ .setFake(true)
+ .build();
+
+ // Ensure that minima are enforced.
+ ResourceSet resources = ((CppLinkAction) linkAction).estimateResourceConsumptionLocal();
+ assertTrue(resources.getMemoryMb() >= CppLinkAction.MIN_STATIC_LINK_RESOURCES.getMemoryMb());
+ assertTrue(resources.getCpuUsage() >= CppLinkAction.MIN_STATIC_LINK_RESOURCES.getCpuUsage());
+ assertTrue(resources.getIoUsage() >= CppLinkAction.MIN_STATIC_LINK_RESOURCES.getIoUsage());
+
+ final int linkSize = Iterables.size(linkAction.getLinkCommandLine().getLinkerInputs());
+ ResourceSet scaledSet = ResourceSet.createWithRamCpuIo(
+ CppLinkAction.LINK_RESOURCES_PER_INPUT.getMemoryMb() * linkSize,
+ CppLinkAction.LINK_RESOURCES_PER_INPUT.getCpuUsage() * linkSize,
+ CppLinkAction.LINK_RESOURCES_PER_INPUT.getIoUsage() * linkSize
+ );
+
+ // Ensure that anything above the minimum is properly scaled.
+ assertTrue(resources.getMemoryMb() == CppLinkAction.MIN_STATIC_LINK_RESOURCES.getMemoryMb()
+ || resources.getMemoryMb() == scaledSet.getMemoryMb());
+ assertTrue(resources.getCpuUsage() == CppLinkAction.MIN_STATIC_LINK_RESOURCES.getCpuUsage()
+ || resources.getCpuUsage() == scaledSet.getCpuUsage());
+ assertTrue(resources.getIoUsage() == CppLinkAction.MIN_STATIC_LINK_RESOURCES.getIoUsage()
+ || resources.getIoUsage() == scaledSet.getIoUsage());
+ }
+ private Builder createLinkBuilder(Link.LinkTargetType type, String outputPath,
+ Iterable<Artifact> nonLibraryInputs, ImmutableList<LibraryToLink> libraryInputs) {
+ return createLinkBuilder(type, outputPath, nonLibraryInputs, libraryInputs,
+ AnalysisTestUtil.STUB_ANALYSIS_ENVIRONMENT);
+ }
+
+ private Builder createLinkBuilder(Link.LinkTargetType type, String outputPath,
+ Iterable<Artifact> nonLibraryInputs, ImmutableList<LibraryToLink> libraryInputs,
+ AnalysisEnvironment analysisEnv) {
+ Builder builder = CppLinkAction.Builder.createTestBuilder(
+ NULL_ACTION_OWNER,
+ analysisEnv,
+ new Artifact(new PathFragment(outputPath), getTargetConfiguration().getBinDirectory()),
+ getTargetConfiguration())
+ .addNonLibraryInputs(nonLibraryInputs)
+ .addLibraries(NestedSetBuilder.wrap(Order.LINK_ORDER, libraryInputs))
+ .setLinkType(type)
+ .setCrosstoolInputs(NestedSetBuilder.<Artifact>emptySet(Order.STABLE_ORDER))
+ .setLinkStaticness(type.isStaticLibraryLink()
+ ? LinkStaticness.FULLY_STATIC
+ : LinkStaticness.MOSTLY_STATIC);
+ return builder;
+ }
+
+ public Artifact getOutputArtifact(String relpath) {
+ return new Artifact(
+ getTargetConfiguration().getBinDirectory().getPath().getRelative(relpath),
+ getTargetConfiguration().getBinDirectory(),
+ getTargetConfiguration().getBinFragment().getRelative(relpath));
+ }
+}
diff --git a/src/test/java/com/google/devtools/build/lib/rules/cpp/CppOptionsTest.java b/src/test/java/com/google/devtools/build/lib/rules/cpp/CppOptionsTest.java
new file mode 100644
index 0000000000..c34e0cf815
--- /dev/null
+++ b/src/test/java/com/google/devtools/build/lib/rules/cpp/CppOptionsTest.java
@@ -0,0 +1,52 @@
+// Copyright 2015 The Bazel Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+package com.google.devtools.build.lib.rules.cpp;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import com.google.devtools.build.lib.analysis.util.DefaultsPackageUtil;
+import com.google.devtools.build.lib.testutil.TestConstants;
+import com.google.devtools.common.options.OptionsParsingException;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+/**
+ * Tests {@link CppOptions}.
+ */
+@RunWith(JUnit4.class)
+public class CppOptionsTest {
+
+ @Test
+ public void testGetDefaultsPackage() throws Exception {
+ String content = DefaultsPackageUtil.getDefaultsPackageForOptions(CppOptions.class);
+ assertThat(content).contains("filegroup(name = 'crosstool',\n"
+ + " srcs = ['" + TestConstants.TOOLS_REPOSITORY + "//tools/cpp:toolchain'])");
+ }
+
+ @Test
+ public void testGetDefaultsPackageHostCrosstoolTop() throws OptionsParsingException {
+ String content = DefaultsPackageUtil.getDefaultsPackageForOptions(
+ CppOptions.class, "--host_crosstool_top=//some/package:crosstool");
+ assertThat(content).contains("//some/package:crosstool");
+ }
+
+ @Test
+ public void testGetDefaultsPackageGrteTop() throws OptionsParsingException {
+ String content = DefaultsPackageUtil.getDefaultsPackageForOptions(
+ CppOptions.class, "--grte_top=//some/grte:other");
+ assertThat(content).contains("//some/grte:everything");
+ }
+}
diff --git a/src/test/java/com/google/devtools/build/lib/testutil/TestConstants.java b/src/test/java/com/google/devtools/build/lib/testutil/TestConstants.java
index f8185f9d40..1c51bd59eb 100644
--- a/src/test/java/com/google/devtools/build/lib/testutil/TestConstants.java
+++ b/src/test/java/com/google/devtools/build/lib/testutil/TestConstants.java
@@ -64,4 +64,8 @@ public class TestConstants {
public static final ImmutableList<String> IGNORED_MESSAGE_PREFIXES = ImmutableList.<String>of();
public static final boolean THIS_IS_BAZEL = true;
+
+ public static final String GCC_INCLUDE_PATH = "external/bazel_tools/tools/cpp/gcc3";
+
+ public static final String TOOLS_REPOSITORY = "@bazel_tools";
}