diff options
author | Dmitry Lomov <dslomov@google.com> | 2015-11-26 19:22:56 +0000 |
---|---|---|
committer | Kristina Chodorow <kchodorow@google.com> | 2015-11-30 18:29:50 +0000 |
commit | ece87c29d1906ec9bc8ffad6990071cf678d299c (patch) | |
tree | b8c8b6720de3ae9fcd29657bac84d09ce5d94425 /src/test/java/com/google/devtools | |
parent | 6072b2b2f06d1b448e0406e62da332808f347565 (diff) |
Opensource more cpp rules tests.
--
MOS_MIGRATED_REVID=108806251
Diffstat (limited to 'src/test/java/com/google/devtools')
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"; } |