Opensource more cpp rules tests.

--
MOS_MIGRATED_REVID=108806251
diff --git a/src/test/java/com/google/devtools/build/lib/BUILD b/src/test/java/com/google/devtools/build/lib/BUILD
index c12e7f1..d293937 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 @@
         ":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 @@
         ":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 0000000..4537fa2
--- /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 0000000..56751a6
--- /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 0000000..8702ffc
--- /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 0000000..89c6000
--- /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 0000000..8ed4460
--- /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 0000000..c34e0cf
--- /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 f8185f9..1c51bd5 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 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";
 }