Initial commit of cc_proto_library.

RELNOTES: cc_proto_library generates C++ code from proto_library rules.

--
MOS_MIGRATED_REVID=140680034
diff --git a/src/main/java/com/google/devtools/build/lib/BUILD b/src/main/java/com/google/devtools/build/lib/BUILD
index 619196e..4042005 100644
--- a/src/main/java/com/google/devtools/build/lib/BUILD
+++ b/src/main/java/com/google/devtools/build/lib/BUILD
@@ -29,6 +29,7 @@
         "//src/main/java/com/google/devtools/build/lib/rules/apple:srcs",
         "//src/main/java/com/google/devtools/build/lib/rules/apple/cpp:srcs",
         "//src/main/java/com/google/devtools/build/lib/rules/cpp:srcs",
+        "//src/main/java/com/google/devtools/build/lib/rules/cpp/proto:srcs",
         "//src/main/java/com/google/devtools/build/lib/rules/genquery:srcs",
         "//src/main/java/com/google/devtools/build/lib/rules/objc:srcs",
         "//src/main/java/com/google/devtools/build/lib/sandbox:srcs",
@@ -578,6 +579,7 @@
         "//src/main/java/com/google/devtools/build/lib/rules/apple",
         "//src/main/java/com/google/devtools/build/lib/rules/apple/cpp",
         "//src/main/java/com/google/devtools/build/lib/rules/cpp",
+        "//src/main/java/com/google/devtools/build/lib/rules/cpp/proto:CcProtoLibrary",
         "//src/main/java/com/google/devtools/build/lib/rules/genquery",
         "//src/main/java/com/google/devtools/build/lib/rules/objc",
         "//src/main/java/com/google/devtools/build/skyframe",
diff --git a/src/main/java/com/google/devtools/build/lib/analysis/RuleContext.java b/src/main/java/com/google/devtools/build/lib/analysis/RuleContext.java
index 8bee13b..265ea6e 100644
--- a/src/main/java/com/google/devtools/build/lib/analysis/RuleContext.java
+++ b/src/main/java/com/google/devtools/build/lib/analysis/RuleContext.java
@@ -671,6 +671,15 @@
   }
 
   /**
+   * Returns true iff the rule, or any attached aspect, has an attribute with the given name and
+   * type.
+   */
+  public boolean isAttrDefined(String attrName, Type<?> type) {
+    Attribute attribute = getAttribute(attrName);
+    return attribute != null && attribute.getType() == type;
+  }
+
+  /**
    * Returns the dependencies through a {@code LABEL_DICT_UNARY} attribute as a map from
    * a string to a {@link TransitiveInfoCollection}.
    */
diff --git a/src/main/java/com/google/devtools/build/lib/bazel/rules/BazelRuleClassProvider.java b/src/main/java/com/google/devtools/build/lib/bazel/rules/BazelRuleClassProvider.java
index 5881098..73dfdbf 100644
--- a/src/main/java/com/google/devtools/build/lib/bazel/rules/BazelRuleClassProvider.java
+++ b/src/main/java/com/google/devtools/build/lib/bazel/rules/BazelRuleClassProvider.java
@@ -43,6 +43,8 @@
 import com.google.devtools.build.lib.bazel.rules.cpp.BazelCcLibraryRule;
 import com.google.devtools.build.lib.bazel.rules.cpp.BazelCcTestRule;
 import com.google.devtools.build.lib.bazel.rules.cpp.BazelCppRuleClasses;
+import com.google.devtools.build.lib.bazel.rules.cpp.BazelCppSemantics;
+import com.google.devtools.build.lib.bazel.rules.cpp.proto.BazelCcProtoAspect;
 import com.google.devtools.build.lib.bazel.rules.genrule.BazelGenRuleRule;
 import com.google.devtools.build.lib.bazel.rules.java.BazelJavaBinaryRule;
 import com.google.devtools.build.lib.bazel.rules.java.BazelJavaBuildInfoFactory;
@@ -103,6 +105,8 @@
 import com.google.devtools.build.lib.rules.cpp.CppBuildInfo;
 import com.google.devtools.build.lib.rules.cpp.CppConfigurationLoader;
 import com.google.devtools.build.lib.rules.cpp.CppOptions;
+import com.google.devtools.build.lib.rules.cpp.proto.CcProtoAspect;
+import com.google.devtools.build.lib.rules.cpp.proto.CcProtoLibraryRule;
 import com.google.devtools.build.lib.rules.extra.ActionListenerRule;
 import com.google.devtools.build.lib.rules.extra.ExtraActionRule;
 import com.google.devtools.build.lib.rules.genquery.GenQueryRule;
@@ -274,6 +278,7 @@
     PROTO_RULES.init(builder);
     SH_RULES.init(builder);
     CPP_RULES.init(builder);
+    CPP_PROTO_RULES.init(builder);
     JAVA_RULES.init(builder);
     JAVA_PROTO_RULES.init(builder);
     ANDROID_RULES.init(builder);
@@ -433,6 +438,21 @@
         }
       };
 
+  public static final RuleSet CPP_PROTO_RULES =
+      new RuleSet() {
+        @Override
+        public void init(Builder builder) {
+          CcProtoAspect ccProtoAspect = new BazelCcProtoAspect(BazelCppSemantics.INSTANCE);
+          builder.addNativeAspectClass(ccProtoAspect);
+          builder.addRuleDefinition(new CcProtoLibraryRule(ccProtoAspect));
+        }
+
+        @Override
+        public ImmutableList<RuleSet> requires() {
+          return ImmutableList.of(CORE_RULES, CPP_RULES);
+        }
+      };
+
   public static final RuleSet JAVA_RULES =
       new RuleSet() {
         @Override
diff --git a/src/main/java/com/google/devtools/build/lib/bazel/rules/cpp/BazelCppRuleClasses.java b/src/main/java/com/google/devtools/build/lib/bazel/rules/cpp/BazelCppRuleClasses.java
index 38c229f..100762b 100644
--- a/src/main/java/com/google/devtools/build/lib/bazel/rules/cpp/BazelCppRuleClasses.java
+++ b/src/main/java/com/google/devtools/build/lib/bazel/rules/cpp/BazelCppRuleClasses.java
@@ -178,7 +178,11 @@
 
   static final String[] DEPS_ALLOWED_RULES =
       new String[] {
-        "cc_inc_library", "cc_library", "objc_library", "experimental_objc_library",
+        "cc_inc_library",
+        "cc_library",
+        "objc_library",
+        "experimental_objc_library",
+        "cc_proto_library",
       };
 
   /**
diff --git a/src/main/java/com/google/devtools/build/lib/bazel/rules/cpp/proto/BazelCcProtoAspect.java b/src/main/java/com/google/devtools/build/lib/bazel/rules/cpp/proto/BazelCcProtoAspect.java
new file mode 100644
index 0000000..023634e
--- /dev/null
+++ b/src/main/java/com/google/devtools/build/lib/bazel/rules/cpp/proto/BazelCcProtoAspect.java
@@ -0,0 +1,31 @@
+// Copyright 2016 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.bazel.rules.cpp.proto;
+
+import static com.google.devtools.build.lib.bazel.rules.cpp.BazelCppRuleClasses.CC_TOOLCHAIN;
+
+import com.google.devtools.build.lib.rules.cpp.CppSemantics;
+import com.google.devtools.build.lib.rules.cpp.proto.CcProtoAspect;
+
+/**
+ * Part of the implementation of cc_proto_library.
+ *
+ * <p>This class is used to inject Bazel-specific constants into CcProtoAspect.
+ */
+public class BazelCcProtoAspect extends CcProtoAspect {
+  public BazelCcProtoAspect(CppSemantics cppSemantics) {
+    super(cppSemantics, CC_TOOLCHAIN);
+  }
+}
diff --git a/src/main/java/com/google/devtools/build/lib/packages/Rule.java b/src/main/java/com/google/devtools/build/lib/packages/Rule.java
index 790b8fe..5d233f0 100644
--- a/src/main/java/com/google/devtools/build/lib/packages/Rule.java
+++ b/src/main/java/com/google/devtools/build/lib/packages/Rule.java
@@ -319,6 +319,9 @@
 
   /**
    * Returns true iff the rule class has an attribute with the given name and type.
+   *
+   * <p>Note: RuleContext also has isAttrDefined(), which takes Aspects into account. Whenever
+   * possible, use RuleContext.isAttrDefined() instead of this method.
    */
   public boolean isAttrDefined(String attrName, Type<?> type) {
     return ruleClass.hasAttr(attrName, type);
diff --git a/src/main/java/com/google/devtools/build/lib/rules/cpp/BUILD b/src/main/java/com/google/devtools/build/lib/rules/cpp/BUILD
index 1c8578a..76df2a1 100644
--- a/src/main/java/com/google/devtools/build/lib/rules/cpp/BUILD
+++ b/src/main/java/com/google/devtools/build/lib/rules/cpp/BUILD
@@ -7,7 +7,10 @@
 
 java_library(
     name = "cpp",
-    srcs = glob(["**/*.java"]),
+    srcs = glob([
+        "*.java",
+        "transitions/*.java",
+    ]),
     deps = [
         "//src/main/java/com/google/devtools/build/lib:build-base",
         "//src/main/java/com/google/devtools/build/lib:collect",
@@ -15,6 +18,7 @@
         "//src/main/java/com/google/devtools/build/lib:events",
         "//src/main/java/com/google/devtools/build/lib:io",
         "//src/main/java/com/google/devtools/build/lib:packages-internal",
+        "//src/main/java/com/google/devtools/build/lib:proto-rules",
         "//src/main/java/com/google/devtools/build/lib:shell",
         "//src/main/java/com/google/devtools/build/lib:skylarkinterface",
         "//src/main/java/com/google/devtools/build/lib:util",
diff --git a/src/main/java/com/google/devtools/build/lib/rules/cpp/CcLibraryHelper.java b/src/main/java/com/google/devtools/build/lib/rules/cpp/CcLibraryHelper.java
index dbed5de..133ce45 100644
--- a/src/main/java/com/google/devtools/build/lib/rules/cpp/CcLibraryHelper.java
+++ b/src/main/java/com/google/devtools/build/lib/rules/cpp/CcLibraryHelper.java
@@ -869,7 +869,7 @@
     Preconditions.checkState(
         // 'cc_inc_library' rules do not compile, and thus are not affected by LIPO.
         ruleContext.getRule().getRuleClass().equals("cc_inc_library")
-        || ruleContext.getRule().isAttrDefined(":lipo_context_collector", BuildType.LABEL));
+            || ruleContext.isAttrDefined(":lipo_context_collector", BuildType.LABEL));
 
     if (checkDepsGenerateCpp) {
       for (LanguageDependentFragment dep :
diff --git a/src/main/java/com/google/devtools/build/lib/rules/cpp/CppHelper.java b/src/main/java/com/google/devtools/build/lib/rules/cpp/CppHelper.java
index f5e0ab5..4213adc 100644
--- a/src/main/java/com/google/devtools/build/lib/rules/cpp/CppHelper.java
+++ b/src/main/java/com/google/devtools/build/lib/rules/cpp/CppHelper.java
@@ -14,6 +14,8 @@
 
 package com.google.devtools.build.lib.rules.cpp;
 
+import static com.google.devtools.build.lib.packages.BuildType.LABEL;
+
 import com.google.common.annotations.VisibleForTesting;
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.Iterables;
@@ -34,7 +36,6 @@
 import com.google.devtools.build.lib.collect.nestedset.NestedSet;
 import com.google.devtools.build.lib.collect.nestedset.NestedSetBuilder;
 import com.google.devtools.build.lib.collect.nestedset.Order;
-import com.google.devtools.build.lib.packages.BuildType;
 import com.google.devtools.build.lib.packages.RuleErrorConsumer;
 import com.google.devtools.build.lib.rules.cpp.CcLinkParams.Linkstamp;
 import com.google.devtools.build.lib.rules.cpp.CppCompilationContext.Builder;
@@ -247,7 +248,7 @@
    * if there is no such attribute (this is currently not an error).
    */
   @Nullable public static CcToolchainProvider getToolchain(RuleContext ruleContext) {
-    if (ruleContext.attributes().getAttributeDefinition(":cc_toolchain") == null) {
+    if (!ruleContext.isAttrDefined(":cc_toolchain", LABEL)) {
       // TODO(bazel-team): Report an error or throw an exception in this case.
       return null;
     }
@@ -384,7 +385,7 @@
       scannableBuilder.addTransitive(dep.getTransitiveIncludeScannables());
     }
 
-    if (ruleContext.attributes().has("malloc", BuildType.LABEL)) {
+    if (ruleContext.attributes().has("malloc", LABEL)) {
       TransitiveInfoCollection malloc = mallocForTarget(ruleContext);
       TransitiveLipoInfoProvider provider = malloc.getProvider(TransitiveLipoInfoProvider.class);
       if (provider != null) {
diff --git a/src/main/java/com/google/devtools/build/lib/rules/cpp/proto/BUILD b/src/main/java/com/google/devtools/build/lib/rules/cpp/proto/BUILD
new file mode 100644
index 0000000..e81fe72
--- /dev/null
+++ b/src/main/java/com/google/devtools/build/lib/rules/cpp/proto/BUILD
@@ -0,0 +1,65 @@
+package(
+    default_visibility = ["//src:__subpackages__"],
+)
+
+filegroup(
+    name = "srcs",
+    srcs = glob(["**"]),
+)
+
+java_library(
+    name = "CcProtoLibrary",
+    srcs = [
+        "CcProtoAspect.java",
+        "CcProtoLibrary.java",
+        "CcProtoLibraryRule.java",
+    ],
+    deps = [
+        ":CcProtoLibraryFilesToBuilderProvider",
+        ":CcProtoLibraryOutputGroupProvider",
+        ":ProtoCcHeaderProvider",
+        "//src/main/java/com/google/devtools/build/lib:build-base",
+        "//src/main/java/com/google/devtools/build/lib:collect",
+        "//src/main/java/com/google/devtools/build/lib:concurrent",
+        "//src/main/java/com/google/devtools/build/lib:packages-internal",
+        "//src/main/java/com/google/devtools/build/lib:proto-rules",
+        "//src/main/java/com/google/devtools/build/lib:vfs",
+        "//src/main/java/com/google/devtools/build/lib/actions",
+        "//src/main/java/com/google/devtools/build/lib/rules/cpp",
+        "//third_party:guava",
+        "//third_party:jsr305",
+    ],
+)
+
+java_library(
+    name = "CcProtoLibraryFilesToBuilderProvider",
+    srcs = ["CcProtoLibraryFilesToBuilderProvider.java"],
+    deps = [
+        "//src/main/java/com/google/devtools/build/lib:collect",
+        "//src/main/java/com/google/devtools/build/lib:concurrent",
+        "//src/main/java/com/google/devtools/build/lib:transitive-info-provider",
+        "//src/main/java/com/google/devtools/build/lib/actions",
+    ],
+)
+
+java_library(
+    name = "CcProtoLibraryOutputGroupProvider",
+    srcs = ["CcProtoLibraryOutputGroupProvider.java"],
+    deps = [
+        "//src/main/java/com/google/devtools/build/lib:collect",
+        "//src/main/java/com/google/devtools/build/lib:concurrent",
+        "//src/main/java/com/google/devtools/build/lib:transitive-info-provider",
+        "//src/main/java/com/google/devtools/build/lib/actions",
+        "//third_party:guava",
+    ],
+)
+
+java_library(
+    name = "ProtoCcHeaderProvider",
+    srcs = ["ProtoCcHeaderProvider.java"],
+    deps = [
+        "//src/main/java/com/google/devtools/build/lib:collect",
+        "//src/main/java/com/google/devtools/build/lib:transitive-info-provider",
+        "//src/main/java/com/google/devtools/build/lib/actions",
+    ],
+)
diff --git a/src/main/java/com/google/devtools/build/lib/rules/cpp/proto/CcProtoAspect.java b/src/main/java/com/google/devtools/build/lib/rules/cpp/proto/CcProtoAspect.java
new file mode 100644
index 0000000..65e3c76
--- /dev/null
+++ b/src/main/java/com/google/devtools/build/lib/rules/cpp/proto/CcProtoAspect.java
@@ -0,0 +1,307 @@
+// Copyright 2016 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.proto;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static com.google.devtools.build.lib.analysis.RuleConfiguredTarget.Mode.TARGET;
+import static com.google.devtools.build.lib.packages.Attribute.attr;
+import static com.google.devtools.build.lib.packages.BuildType.LABEL;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSet;
+import com.google.devtools.build.lib.actions.Artifact;
+import com.google.devtools.build.lib.analysis.ConfiguredAspect;
+import com.google.devtools.build.lib.analysis.ConfiguredAspectFactory;
+import com.google.devtools.build.lib.analysis.ConfiguredTarget;
+import com.google.devtools.build.lib.analysis.RuleContext;
+import com.google.devtools.build.lib.analysis.TransitiveInfoCollection;
+import com.google.devtools.build.lib.analysis.TransitiveInfoProvider;
+import com.google.devtools.build.lib.analysis.TransitiveInfoProviderMap;
+import com.google.devtools.build.lib.analysis.config.BuildConfiguration;
+import com.google.devtools.build.lib.cmdline.Label;
+import com.google.devtools.build.lib.collect.nestedset.NestedSet;
+import com.google.devtools.build.lib.collect.nestedset.NestedSetBuilder;
+import com.google.devtools.build.lib.packages.AspectDefinition;
+import com.google.devtools.build.lib.packages.AspectParameters;
+import com.google.devtools.build.lib.packages.Attribute;
+import com.google.devtools.build.lib.packages.AttributeMap;
+import com.google.devtools.build.lib.packages.NativeAspectClass;
+import com.google.devtools.build.lib.packages.Rule;
+import com.google.devtools.build.lib.packages.RuleClass.ConfiguredTargetFactory.RuleErrorException;
+import com.google.devtools.build.lib.rules.cpp.CcCommon;
+import com.google.devtools.build.lib.rules.cpp.CcLibraryHelper;
+import com.google.devtools.build.lib.rules.cpp.CcToolchainFeatures.FeatureConfiguration;
+import com.google.devtools.build.lib.rules.cpp.CppConfiguration;
+import com.google.devtools.build.lib.rules.cpp.CppHelper;
+import com.google.devtools.build.lib.rules.cpp.CppRuleClasses;
+import com.google.devtools.build.lib.rules.cpp.CppSemantics;
+import com.google.devtools.build.lib.rules.proto.ProtoCommon;
+import com.google.devtools.build.lib.rules.proto.ProtoCompileActionBuilder;
+import com.google.devtools.build.lib.rules.proto.ProtoCompileActionBuilder.ToolchainInvocation;
+import com.google.devtools.build.lib.rules.proto.ProtoConfiguration;
+import com.google.devtools.build.lib.rules.proto.ProtoLangToolchainProvider;
+import com.google.devtools.build.lib.rules.proto.ProtoSourceFileBlacklist;
+import com.google.devtools.build.lib.rules.proto.ProtoSupportDataProvider;
+import com.google.devtools.build.lib.rules.proto.SupportData;
+import com.google.devtools.build.lib.vfs.FileSystemUtils;
+import com.google.devtools.build.lib.vfs.PathFragment;
+import java.util.ArrayList;
+import java.util.Collection;
+
+/** Part of the implementation of cc_proto_library. */
+public class CcProtoAspect extends NativeAspectClass implements ConfiguredAspectFactory {
+
+  private static final String PROTO_TOOLCHAIN_ATTR = ":aspect_cc_proto_toolchain";
+
+  private static final Attribute.LateBoundLabel<BuildConfiguration> PROTO_TOOLCHAIN_LABEL =
+      new Attribute.LateBoundLabel<BuildConfiguration>(
+          "@com_google_protobuf_cc//:cc_toolchain", ProtoConfiguration.class) {
+        @Override
+        public Label resolve(Rule rule, AttributeMap attributes, BuildConfiguration configuration) {
+          return configuration.getFragment(ProtoConfiguration.class).protoToolchainForCc();
+        }
+      };
+
+  private final CppSemantics cppSemantics;
+  private final Attribute.LateBoundLabel<BuildConfiguration> ccToolchainAttrValue;
+
+  public CcProtoAspect(
+      CppSemantics cppSemantics,
+      Attribute.LateBoundLabel<BuildConfiguration> ccToolchainAttrValue) {
+    this.cppSemantics = cppSemantics;
+    this.ccToolchainAttrValue = ccToolchainAttrValue;
+  }
+
+  @Override
+  public ConfiguredAspect create(
+      ConfiguredTarget base, RuleContext ruleContext, AspectParameters parameters)
+      throws InterruptedException {
+    // Get SupportData, which is provided by the proto_library rule we attach to.
+    SupportData supportData =
+        checkNotNull(base.getProvider(ProtoSupportDataProvider.class)).getSupportData();
+
+    try {
+      ConfiguredAspect.Builder result = new ConfiguredAspect.Builder(this, parameters, ruleContext);
+      new Impl(ruleContext, supportData, cppSemantics).addProviders(result);
+      return result.build();
+    } catch (RuleErrorException e) {
+      ruleContext.ruleError(e.getMessage());
+      return null;
+    }
+  }
+
+  @Override
+  public AspectDefinition getDefinition(AspectParameters aspectParameters) {
+    AspectDefinition.Builder result =
+        new AspectDefinition.Builder(this)
+            .attributeAspect("deps", this)
+            .requiresConfigurationFragments(CppConfiguration.class, ProtoConfiguration.class)
+            .requireProviders(ProtoSupportDataProvider.class)
+            .add(
+                attr(PROTO_TOOLCHAIN_ATTR, LABEL)
+                    .mandatoryNativeProviders(
+                        ImmutableList.<Class<? extends TransitiveInfoProvider>>of(
+                            ProtoLangToolchainProvider.class))
+                    .value(PROTO_TOOLCHAIN_LABEL))
+            .add(attr(":cc_toolchain", LABEL).value(ccToolchainAttrValue))
+            .add(
+                attr(":lipo_context_collector", LABEL)
+                    .cfg(CppRuleClasses.LipoTransition.LIPO_COLLECTOR)
+                    .value(CppRuleClasses.LIPO_CONTEXT_COLLECTOR)
+                    .skipPrereqValidatorCheck());
+
+    return result.build();
+  }
+
+  private static class Impl {
+
+    private final TransitiveInfoProviderMap ccLibraryProviders;
+    private final ProtoCcHeaderProvider headerProvider;
+    private final ImmutableMap<String, NestedSet<Artifact>> outputGroups;
+
+    private final RuleContext ruleContext;
+    private final SupportData supportData;
+    private final CppSemantics cppSemantics;
+    private final NestedSetBuilder<Artifact> filesBuilder;
+
+    Impl(RuleContext ruleContext, SupportData supportData, CppSemantics cppSemantics)
+        throws RuleErrorException, InterruptedException {
+      this.ruleContext = ruleContext;
+      this.supportData = supportData;
+      this.cppSemantics = cppSemantics;
+      FeatureConfiguration featureConfiguration = getFeatureConfiguration(supportData);
+
+      CcLibraryHelper helper = initializeCcLibraryHelper(featureConfiguration);
+      helper.addDeps(ruleContext.getPrerequisites("deps", TARGET));
+
+      // Compute and register files generated by this proto library.
+      Collection<Artifact> outputs = new ArrayList<>();
+      if (areSrcsBlacklisted()) {
+        registerBlacklistedSrcs(supportData, helper);
+        headerProvider = null;
+      } else if (supportData.hasProtoSources()) {
+        Collection<Artifact> headers = getHeaders(supportData);
+        Collection<Artifact> sources = getSources(supportData);
+        outputs.addAll(headers);
+        outputs.addAll(sources);
+
+        helper.addSources(sources);
+        helper.addPublicHeaders(headers);
+
+        NestedSetBuilder<Artifact> publicHeaderPaths = NestedSetBuilder.stableOrder();
+        publicHeaderPaths.addAll(headers);
+        headerProvider = new ProtoCcHeaderProvider(publicHeaderPaths.build(), true);
+      } else {
+        // If this proto_library doesn't have sources, it provides the combined headers of all its
+        // direct dependencies. Thus, if a direct dependency does have sources, the generated files
+        // are also provided by this library. If a direct dependency does not have sources, it will
+        // do the same thing, so that effectively this library looks through all source-less
+        // proto_libraries and provides all generated headers of the proto_libraries with sources
+        // that it depends on.
+        //
+        // Similar, if a proto_library, does not have sources, it forwards the information whether
+        // its transitive dependencies generated .pb.h files. If one of them doesn't, this
+        // proto_library pretends to not generate them either.
+        boolean hasDepWithoutPbH = false;
+        NestedSetBuilder<Artifact> transitiveHeaders = NestedSetBuilder.stableOrder();
+        for (ProtoCcHeaderProvider headerProvider :
+            ruleContext.getPrerequisites("deps", TARGET, ProtoCcHeaderProvider.class)) {
+          helper.addPublicTextualHeaders(headerProvider.getHeaders());
+          transitiveHeaders.addTransitive(headerProvider.getHeaders());
+          hasDepWithoutPbH = hasDepWithoutPbH || !headerProvider.getGeneratesPbH();
+        }
+        headerProvider = new ProtoCcHeaderProvider(transitiveHeaders.build(), !hasDepWithoutPbH);
+      }
+
+      filesBuilder = NestedSetBuilder.stableOrder();
+      filesBuilder.addAll(outputs);
+      createProtoCompileAction(supportData, outputs);
+
+      CcLibraryHelper.Info info = helper.build();
+      ccLibraryProviders = info.getProviders();
+      outputGroups = info.getOutputGroups();
+      info.addLinkingOutputsTo(filesBuilder);
+    }
+
+    private boolean areSrcsBlacklisted() {
+      return !new ProtoSourceFileBlacklist(
+              ruleContext, getProtoToolchainProvider().blacklistedProtos())
+          .checkSrcs(supportData.getDirectProtoSources(), "cc_proto_library");
+    }
+
+    private FeatureConfiguration getFeatureConfiguration(SupportData supportData) {
+      ImmutableSet.Builder<String> requestedFeatures = new ImmutableSet.Builder<>();
+      ImmutableSet.Builder<String> unsupportedFeatures = new ImmutableSet.Builder<>();
+      unsupportedFeatures.add(CppRuleClasses.PARSE_HEADERS);
+      unsupportedFeatures.add(CppRuleClasses.LAYERING_CHECK);
+      if (!areSrcsBlacklisted() && supportData.hasProtoSources()) {
+        requestedFeatures.add(CppRuleClasses.HEADER_MODULES);
+      } else {
+        unsupportedFeatures.add(CppRuleClasses.HEADER_MODULES);
+      }
+      FeatureConfiguration featureConfiguration =
+          CcCommon.configureFeatures(
+              ruleContext,
+              requestedFeatures.build(),
+              unsupportedFeatures.build(),
+              CcLibraryHelper.SourceCategory.CC,
+              CppHelper.getToolchain(
+                  ruleContext, ruleContext.getPrerequisite(":cc_toolchain", TARGET)));
+      return featureConfiguration;
+    }
+
+    private CcLibraryHelper initializeCcLibraryHelper(FeatureConfiguration featureConfiguration) {
+      CcLibraryHelper helper = new CcLibraryHelper(ruleContext, cppSemantics, featureConfiguration);
+      helper.enableCcSpecificLinkParamsProvider();
+      helper.enableCcNativeLibrariesProvider();
+      // TODO(dougk): Configure output artifact with action_config
+      // once proto compile action is configurable from the crosstool.
+      if (!ruleContext.getFragment(CppConfiguration.class).supportsDynamicLinker()) {
+        helper.setCreateDynamicLibrary(false);
+      }
+      TransitiveInfoCollection runtime = getProtoToolchainProvider().runtime();
+      if (runtime != null) {
+        helper.addDeps(ImmutableList.of(runtime));
+      }
+      return helper;
+    }
+
+    private Collection<Artifact> getHeaders(SupportData supportData) {
+      return ProtoCommon.getGeneratedOutputs(
+          ruleContext, supportData.getDirectProtoSources(), ".pb.h");
+    }
+
+    private Collection<Artifact> getSources(SupportData supportData) {
+      return ProtoCommon.getGeneratedOutputs(
+          ruleContext, supportData.getDirectProtoSources(), ".pb.cc");
+    }
+
+    private void registerBlacklistedSrcs(SupportData supportData, CcLibraryHelper helper) {
+      // Hack: This is a proto_library for descriptor.proto or similar.
+      //
+      // The headers of those libraries are precomputed . They are also explicitly part of normal
+      // cc_library rules that export them in their 'hdrs' attribute, and compile them as header
+      // module if requested.
+      //
+      // The sole purpose of a proto_library with blacklisted srcs is so other proto_library rules
+      // can import them from a protocol buffer, as proto_library rules can only depend on other
+      // proto library rules.
+      ImmutableList.Builder<PathFragment> headers = new ImmutableList.Builder<>();
+      for (Artifact source : supportData.getDirectProtoSources()) {
+        headers.add(FileSystemUtils.replaceExtension(source.getRootRelativePath(), ".pb.h"));
+        headers.add(FileSystemUtils.replaceExtension(source.getRootRelativePath(), ".proto.h"));
+      }
+      // We add the header to the proto_library's module map as additional (textual) header for
+      // two reasons:
+      // 1. The header will be exported via a normal cc_library, and a header must only be exported
+      //    non-textually from one library.
+      // 2. We want to allow proto_library rules that depend on the bootstrap-hack proto_library
+      //    to be layering-checked; we need to provide a module map for the layering check to work.
+      helper.addAdditionalExportedHeaders(headers.build());
+    }
+
+    private void createProtoCompileAction(SupportData supportData, Collection<Artifact> outputs) {
+      String genfilesPath = ruleContext.getConfiguration().getGenfilesFragment().getPathString();
+
+      ImmutableList.Builder<ToolchainInvocation> invocations = ImmutableList.builder();
+      invocations.add(
+          new ToolchainInvocation("C++", checkNotNull(getProtoToolchainProvider()), genfilesPath));
+      ProtoCompileActionBuilder.registerActions(
+          ruleContext,
+          invocations.build(),
+          supportData.getDirectProtoSources(),
+          supportData.getTransitiveImports(),
+          supportData.getProtosInDirectDeps(),
+          outputs,
+          "C++",
+          true /* allowServices */);
+    }
+
+    private ProtoLangToolchainProvider getProtoToolchainProvider() {
+      return ruleContext.getPrerequisite(
+          PROTO_TOOLCHAIN_ATTR, TARGET, ProtoLangToolchainProvider.class);
+    }
+
+    public void addProviders(ConfiguredAspect.Builder builder) {
+      builder.addProviders(ccLibraryProviders);
+      builder.addProvider(new CcProtoLibraryFilesToBuilderProvider(filesBuilder.build()));
+      builder.addProvider(new CcProtoLibraryOutputGroupProvider(outputGroups));
+      if (headerProvider != null) {
+        builder.addProvider(headerProvider);
+      }
+    }
+  }
+}
diff --git a/src/main/java/com/google/devtools/build/lib/rules/cpp/proto/CcProtoLibrary.java b/src/main/java/com/google/devtools/build/lib/rules/cpp/proto/CcProtoLibrary.java
new file mode 100644
index 0000000..b92501b
--- /dev/null
+++ b/src/main/java/com/google/devtools/build/lib/rules/cpp/proto/CcProtoLibrary.java
@@ -0,0 +1,81 @@
+// Copyright 2016 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.proto;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static com.google.devtools.build.lib.analysis.RuleConfiguredTarget.Mode.TARGET;
+
+import com.google.devtools.build.lib.analysis.ConfiguredTarget;
+import com.google.devtools.build.lib.analysis.RuleConfiguredTargetBuilder;
+import com.google.devtools.build.lib.analysis.RuleContext;
+import com.google.devtools.build.lib.analysis.Runfiles;
+import com.google.devtools.build.lib.analysis.RunfilesProvider;
+import com.google.devtools.build.lib.analysis.TransitiveInfoCollection;
+import com.google.devtools.build.lib.analysis.TransitiveInfoProvider;
+import com.google.devtools.build.lib.rules.RuleConfiguredTargetFactory;
+import com.google.devtools.build.lib.rules.cpp.CcExecutionDynamicLibrariesProvider;
+import com.google.devtools.build.lib.rules.cpp.CcLinkParamsProvider;
+import com.google.devtools.build.lib.rules.cpp.CcNativeLibraryProvider;
+import com.google.devtools.build.lib.rules.cpp.CcSkylarkApiProvider;
+import com.google.devtools.build.lib.rules.cpp.CcSpecificLinkParamsProvider;
+
+/** Part of the implementation of cc_proto_library. */
+public class CcProtoLibrary implements RuleConfiguredTargetFactory {
+  @Override
+  public ConfiguredTarget create(RuleContext ruleContext)
+      throws InterruptedException, RuleErrorException {
+
+    if (ruleContext.getPrerequisites("deps", TARGET).size() != 1) {
+      ruleContext.throwWithAttributeError(
+          "deps",
+          "'deps' attribute must contain exactly one label "
+              + "(we didn't name it 'dep' for consistency). "
+              + "The main use-case for multiple deps is to create a rule that contains several "
+              + "other targets. This makes dependency bloat more likely. It also makes it harder"
+              + "to remove unused deps.");
+    }
+
+    TransitiveInfoCollection dep = checkNotNull(ruleContext.getPrerequisite("deps", TARGET));
+
+    RuleConfiguredTargetBuilder result =
+        new RuleConfiguredTargetBuilder(ruleContext)
+            .setFilesToBuild(
+                dep.getProvider(CcProtoLibraryFilesToBuilderProvider.class).filesBuilder)
+            .addProvider(
+                RunfilesProvider.class, RunfilesProvider.withData(Runfiles.EMPTY, Runfiles.EMPTY));
+
+    addProviderIfNotNull(result, dep.getProvider(CcLinkParamsProvider.class));
+    addProviderIfNotNull(result, dep.getProvider(CcNativeLibraryProvider.class));
+    addProviderIfNotNull(result, dep.getProvider(CcExecutionDynamicLibrariesProvider.class));
+    addProviderIfNotNull(result, dep.getProvider(CcSpecificLinkParamsProvider.class));
+    addProviderIfNotNull(result, dep.getProvider(ProtoCcHeaderProvider.class));
+
+    result.addSkylarkTransitiveInfo(CcSkylarkApiProvider.NAME, new CcSkylarkApiProvider());
+
+    CcProtoLibraryOutputGroupProvider outputGroups =
+        dep.getProvider(CcProtoLibraryOutputGroupProvider.class);
+    if (outputGroups != null) {
+      result.addOutputGroups(outputGroups.outputGroups);
+    }
+    return result.build();
+  }
+
+  private void addProviderIfNotNull(
+      RuleConfiguredTargetBuilder result, TransitiveInfoProvider provider) {
+    if (provider != null) {
+      result.addProvider(provider);
+    }
+  }
+}
diff --git a/src/main/java/com/google/devtools/build/lib/rules/cpp/proto/CcProtoLibraryFilesToBuilderProvider.java b/src/main/java/com/google/devtools/build/lib/rules/cpp/proto/CcProtoLibraryFilesToBuilderProvider.java
new file mode 100644
index 0000000..8170cd4
--- /dev/null
+++ b/src/main/java/com/google/devtools/build/lib/rules/cpp/proto/CcProtoLibraryFilesToBuilderProvider.java
@@ -0,0 +1,33 @@
+// Copyright 2016 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.proto;
+
+import com.google.devtools.build.lib.actions.Artifact;
+import com.google.devtools.build.lib.analysis.TransitiveInfoProvider;
+import com.google.devtools.build.lib.collect.nestedset.NestedSet;
+import com.google.devtools.build.lib.concurrent.ThreadSafety;
+
+/**
+ * Used by CcProtoAspect to propagate files-to-build to CcProtoLibrary, where it is exposed via
+ * setFilesToBuild().
+ */
+@ThreadSafety.Immutable
+final class CcProtoLibraryFilesToBuilderProvider implements TransitiveInfoProvider {
+  final NestedSet<Artifact> filesBuilder;
+
+  CcProtoLibraryFilesToBuilderProvider(NestedSet<Artifact> filesBuilder) {
+    this.filesBuilder = filesBuilder;
+  }
+}
diff --git a/src/main/java/com/google/devtools/build/lib/rules/cpp/proto/CcProtoLibraryOutputGroupProvider.java b/src/main/java/com/google/devtools/build/lib/rules/cpp/proto/CcProtoLibraryOutputGroupProvider.java
new file mode 100644
index 0000000..49fe3ec
--- /dev/null
+++ b/src/main/java/com/google/devtools/build/lib/rules/cpp/proto/CcProtoLibraryOutputGroupProvider.java
@@ -0,0 +1,34 @@
+// Copyright 2016 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.proto;
+
+import com.google.common.collect.ImmutableMap;
+import com.google.devtools.build.lib.actions.Artifact;
+import com.google.devtools.build.lib.analysis.TransitiveInfoProvider;
+import com.google.devtools.build.lib.collect.nestedset.NestedSet;
+import com.google.devtools.build.lib.concurrent.ThreadSafety;
+
+/**
+ * Used by CcProtoAspect to propagate output groups to CcProtoLibrary, where it is exposed via
+ * setOutputGroups().
+ */
+@ThreadSafety.Immutable
+final class CcProtoLibraryOutputGroupProvider implements TransitiveInfoProvider {
+  final ImmutableMap<String, NestedSet<Artifact>> outputGroups;
+
+  CcProtoLibraryOutputGroupProvider(ImmutableMap<String, NestedSet<Artifact>> outputGroups) {
+    this.outputGroups = outputGroups;
+  }
+}
diff --git a/src/main/java/com/google/devtools/build/lib/rules/cpp/proto/CcProtoLibraryRule.java b/src/main/java/com/google/devtools/build/lib/rules/cpp/proto/CcProtoLibraryRule.java
new file mode 100644
index 0000000..f41b49c
--- /dev/null
+++ b/src/main/java/com/google/devtools/build/lib/rules/cpp/proto/CcProtoLibraryRule.java
@@ -0,0 +1,109 @@
+// Copyright 2016 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.proto;
+
+import static com.google.devtools.build.lib.packages.Aspect.INJECTING_RULE_KIND_PARAMETER_KEY;
+import static com.google.devtools.build.lib.packages.Attribute.attr;
+import static com.google.devtools.build.lib.packages.BuildType.LABEL_LIST;
+
+import com.google.common.base.Function;
+import com.google.devtools.build.lib.analysis.BaseRuleClasses;
+import com.google.devtools.build.lib.analysis.RuleDefinition;
+import com.google.devtools.build.lib.analysis.RuleDefinitionEnvironment;
+import com.google.devtools.build.lib.packages.AspectParameters;
+import com.google.devtools.build.lib.packages.Rule;
+import com.google.devtools.build.lib.packages.RuleClass;
+import javax.annotation.Nullable;
+
+/** Declaration part of cc_proto_library. */
+public class CcProtoLibraryRule implements RuleDefinition {
+
+  private static final Function<Rule, AspectParameters> ASPECT_PARAMETERS =
+      new Function<Rule, AspectParameters>() {
+        @Nullable
+        @Override
+        public AspectParameters apply(@Nullable Rule rule) {
+          return new AspectParameters.Builder()
+              .addAttribute(INJECTING_RULE_KIND_PARAMETER_KEY, "cc_proto_library")
+              .build();
+        }
+      };
+
+  private final CcProtoAspect ccProtoAspect;
+
+  public CcProtoLibraryRule(CcProtoAspect ccProtoAspect) {
+    this.ccProtoAspect = ccProtoAspect;
+  }
+
+  @Override
+  public RuleClass build(RuleClass.Builder builder, RuleDefinitionEnvironment environment) {
+    return builder
+        // This rule isn't ready for use yet.
+        .setUndocumented()
+        /* <!-- #BLAZE_RULE(cc_proto_library).ATTRIBUTE(deps) -->
+        The list of <a href="protocol-buffer.html#proto_library"><code>proto_library</code></a>
+        rules to generate C++ code for.
+        <!-- #END_BLAZE_RULE.ATTRIBUTE --> */
+        .override(
+            attr("deps", LABEL_LIST)
+                .allowedRuleClasses("proto_library")
+                .allowedFileTypes()
+                .aspect(ccProtoAspect, ASPECT_PARAMETERS))
+        .build();
+  }
+
+  @Override
+  public Metadata getMetadata() {
+    return RuleDefinition.Metadata.builder()
+        .name("cc_proto_library")
+        .factoryClass(CcProtoLibrary.class)
+        .ancestors(BaseRuleClasses.RuleBase.class)
+        .build();
+  }
+}
+
+/*<!-- #BLAZE_RULE (NAME = cc_proto_library, TYPE = LIBRARY, FAMILY = C / C++) -->
+
+<p>
+<code>cc_proto_library</code> generates C++ code from <code>.proto</code> files.
+</p>
+
+<p>
+<code>deps</code> must point to <a href="protocol-buffer.html#proto_library"><code>proto_library
+</code></a> rules.
+</p>
+
+<p>
+Example:
+</p>
+
+<pre class="code">
+cc_library(
+    name = "lib",
+    deps = [":foo_cc_proto"],
+)
+
+cc_proto_library(
+    name = "foo_cc_proto",
+    deps = [":foo_proto"],
+)
+
+proto_library(
+    name = "foo_proto",
+)
+</pre>
+
+
+<!-- #END_BLAZE_RULE -->*/
diff --git a/src/main/java/com/google/devtools/build/lib/rules/cpp/proto/ProtoCcHeaderProvider.java b/src/main/java/com/google/devtools/build/lib/rules/cpp/proto/ProtoCcHeaderProvider.java
new file mode 100644
index 0000000..5bf13de
--- /dev/null
+++ b/src/main/java/com/google/devtools/build/lib/rules/cpp/proto/ProtoCcHeaderProvider.java
@@ -0,0 +1,44 @@
+// Copyright 2016 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.proto;
+
+import com.google.devtools.build.lib.actions.Artifact;
+import com.google.devtools.build.lib.analysis.TransitiveInfoProvider;
+import com.google.devtools.build.lib.collect.nestedset.NestedSet;
+
+/** Provides information about generated C++ headers of dependencies. */
+public final class ProtoCcHeaderProvider implements TransitiveInfoProvider {
+  private final NestedSet<Artifact> headers;
+  private final boolean generatesPbH;
+
+  public ProtoCcHeaderProvider(NestedSet<Artifact> headers, boolean generatesPbH) {
+    this.headers = headers;
+    this.generatesPbH = generatesPbH;
+  }
+
+  /**
+   * @return the headers of the proto library for C++.
+   *     <p>Normally these are the headers generated by the proto compiler for C++; only in case of
+   *     a proto_library with blacklisted srcs will these be files from the source tree.
+   */
+  public NestedSet<Artifact> getHeaders() {
+    return headers;
+  }
+
+  /** @return true, if this proto library generates .pb.h files. */
+  public boolean getGeneratesPbH() {
+    return generatesPbH;
+  }
+}
diff --git a/src/main/java/com/google/devtools/build/lib/rules/proto/BazelProtoLibraryRule.java b/src/main/java/com/google/devtools/build/lib/rules/proto/BazelProtoLibraryRule.java
index a30a176..77145fe 100644
--- a/src/main/java/com/google/devtools/build/lib/rules/proto/BazelProtoLibraryRule.java
+++ b/src/main/java/com/google/devtools/build/lib/rules/proto/BazelProtoLibraryRule.java
@@ -77,7 +77,7 @@
                 .direct_compile_time_input()
                 .allowedFileTypes(FileType.of(".proto"), FileType.of(".protodevel")))
         .add(attr("strict_proto_deps", TRISTATE).undocumented("for migration only"))
-        .advertiseProvider(ProtoSourcesProvider.class)
+        .advertiseProvider(ProtoSourcesProvider.class, ProtoSupportDataProvider.class)
         .build();
   }
 
diff --git a/src/main/java/com/google/devtools/build/lib/rules/proto/ProtoConfiguration.java b/src/main/java/com/google/devtools/build/lib/rules/proto/ProtoConfiguration.java
index 827a3f8..bb503fc 100644
--- a/src/main/java/com/google/devtools/build/lib/rules/proto/ProtoConfiguration.java
+++ b/src/main/java/com/google/devtools/build/lib/rules/proto/ProtoConfiguration.java
@@ -133,6 +133,15 @@
     public Label protoToolchainForJava;
 
     @Option(
+      name = "proto_toolchain_for_cc",
+      defaultValue = "@com_google_protobuf_cc//:cc_toolchain",
+      category = "flags",
+      converter = BuildConfiguration.EmptyToNullLabelConverter.class,
+      help = "Label of proto_lang_toolchain() which describes how to compile C++ protos"
+    )
+    public Label protoToolchainForCc;
+
+    @Option(
       name = "use_toolchain_for_java_proto",
       defaultValue = "false",
       category = "experimental",
@@ -213,6 +222,7 @@
   private final Label protoToolchainForJava;
   private final Label protoToolchainForJavaLite;
   private final boolean useToolchainForJavaProto;
+  private final Label protoToolchainForCc;
   private final StrictDepsMode strictProtoDeps;
   private final boolean outputDescriptorSet;
 
@@ -227,6 +237,7 @@
     this.protoToolchainForJava = options.protoToolchainForJava;
     this.protoToolchainForJavaLite = options.protoToolchainForJavaLite;
     this.useToolchainForJavaProto = options.useToolchainForJavaProto;
+    this.protoToolchainForCc = options.protoToolchainForCc;
     this.strictProtoDeps = options.strictProtoDeps;
     this.outputDescriptorSet = options.outputDescriptorSet;
   }
@@ -276,6 +287,10 @@
     return useToolchainForJavaProto;
   }
 
+  public Label protoToolchainForCc() {
+    return protoToolchainForCc;
+  }
+
   public StrictDepsMode strictProtoDeps() {
     return strictProtoDeps;
   }
diff --git a/src/test/java/com/google/devtools/build/lib/BUILD b/src/test/java/com/google/devtools/build/lib/BUILD
index 778fe86..7c1b7a4 100644
--- a/src/test/java/com/google/devtools/build/lib/BUILD
+++ b/src/test/java/com/google/devtools/build/lib/BUILD
@@ -958,6 +958,21 @@
 )
 
 java_test(
+    name = "CcProtoLibraryTest",
+    srcs = glob(["rules/cpp/proto/CcProtoLibraryTest.java"]),
+    tags = ["rules"],
+    deps = [
+        ":actions_testutil",
+        ":analysis_testutil",
+        "//src/main/java/com/google/devtools/build/lib:vfs",
+        "//src/main/java/com/google/devtools/build/lib/rules/cpp/proto:ProtoCcHeaderProvider",
+        "//third_party:guava",
+        "//third_party:junit4",
+        "//third_party:truth",
+    ],
+)
+
+java_test(
     name = "objc-rules-tests",
     srcs = glob(["rules/objc/XcodeProviderTest.java"]),
     tags = ["rules"],
diff --git a/src/test/java/com/google/devtools/build/lib/rules/cpp/proto/CcProtoLibraryTest.java b/src/test/java/com/google/devtools/build/lib/rules/cpp/proto/CcProtoLibraryTest.java
new file mode 100644
index 0000000..bac8dfb
--- /dev/null
+++ b/src/test/java/com/google/devtools/build/lib/rules/cpp/proto/CcProtoLibraryTest.java
@@ -0,0 +1,110 @@
+// Copyright 2016 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.proto;
+
+import static com.google.common.truth.Truth.assertThat;
+import static com.google.devtools.build.lib.actions.util.ActionsTestUtil.prettyArtifactNames;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.eventbus.EventBus;
+import com.google.devtools.build.lib.analysis.util.BuildViewTestCase;
+import com.google.devtools.build.lib.vfs.FileSystemUtils;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+@RunWith(JUnit4.class)
+public class CcProtoLibraryTest extends BuildViewTestCase {
+  @Before
+  public void setUp() throws Exception {
+    scratch.file("third_party/protobuf/BUILD", "licenses(['notice'])", "exports_files(['protoc'])");
+    scratch.file(
+        "protobuf/BUILD",
+        "package(default_visibility=['//visibility:public'])",
+        "proto_lang_toolchain(",
+        "    name = 'cc_toolchain',",
+        "    command_line = '--cpp_out=$(OUT)',",
+        "    blacklisted_protos = [],",
+        ")");
+
+    String existingWorkspace =
+        new String(FileSystemUtils.readContentAsLatin1(rootDirectory.getRelative("WORKSPACE")));
+    mockToolsConfig.overwrite(
+        "WORKSPACE",
+        "local_repository(name = 'com_google_protobuf_cc', path = 'protobuf/')",
+        existingWorkspace);
+    invalidatePackages(); // A dash of magic to re-evaluate the WORKSPACE file.
+  }
+
+  @Test
+  public void basic() throws Exception {
+    scratch.file(
+        "x/BUILD",
+        "cc_proto_library(name = 'foo_cc_proto', deps = ['foo_proto'])",
+        "proto_library(name = 'foo_proto', srcs = ['foo.proto'])");
+    assertThat(prettyArtifactNames(getFilesToBuild(getConfiguredTarget("//x:foo_cc_proto"))))
+        .containsExactly("x/foo.pb.h", "x/foo.pb.cc", "x/libfoo_proto.a", "x/libfoo_proto.so");
+  }
+
+  @Test
+  public void canBeUsedFromCcRules() throws Exception {
+    scratch.file(
+        "x/BUILD",
+        "cc_library(name = 'foo', srcs = ['foo.cc'], deps = ['foo_cc_proto'])",
+        "cc_binary(name = 'bin', srcs = ['bin.cc'], deps = ['foo_cc_proto'])",
+        "cc_proto_library(name = 'foo_cc_proto', deps = ['foo_proto'])",
+        "proto_library(name = 'foo_proto', srcs = ['foo.proto'])");
+
+    update(
+        ImmutableList.of("//x:foo", "//x:bin"),
+        false /* keepGoing */,
+        1 /* loadingPhaseThreads */,
+        true /* doAnalysis */,
+        new EventBus());
+  }
+
+  @Test
+  public void disallowMultipleDeps() throws Exception {
+    checkError(
+        "x",
+        "foo_cc_proto",
+        "'deps' attribute must contain exactly one label",
+        "cc_proto_library(name = 'foo_cc_proto', deps = ['foo_proto', 'bar_proto'])",
+        "proto_library(name = 'foo_proto', srcs = ['foo.proto'])",
+        "proto_library(name = 'bar_proto', srcs = ['bar.proto'])");
+
+    checkError(
+        "y",
+        "foo_cc_proto",
+        "'deps' attribute must contain exactly one label",
+        "cc_proto_library(name = 'foo_cc_proto', deps = [])");
+  }
+
+  @Test
+  public void aliasProtos() throws Exception {
+    scratch.file(
+        "x/BUILD",
+        "cc_proto_library(name = 'foo_cc_proto', deps = ['alias_proto'])",
+        "proto_library(name = 'alias_proto', deps = [':foo_proto'])",
+        "proto_library(name = 'foo_proto', srcs = ['foo.proto'])");
+
+    ProtoCcHeaderProvider headers =
+        getConfiguredTarget("//x:foo_cc_proto").getProvider(ProtoCcHeaderProvider.class);
+    assertThat(prettyArtifactNames(headers.getHeaders())).containsExactly("x/foo.pb.h");
+  }
+
+  // TODO(carmi): test blacklisted protos. I don't currently understand what's the wanted behavior.
+}