Add a simple proto_cc_library rule

It only supports a single .proto file without any dependencies, but
that's enough for now.

Change-Id: Ibd80c8594b90956c3c5b0b46db10cf991d7ed1ad
diff --git a/build_tests/BUILD b/build_tests/BUILD
index cce2e01..be6a201 100644
--- a/build_tests/BUILD
+++ b/build_tests/BUILD
@@ -1,5 +1,6 @@
 load('/tools/build_rules/ruby', 'ruby_binary')
 load('/aos/build/queues', 'queue_library')
+load('/tools/build_rules/protobuf', 'proto_cc_library')
 
 cc_test(
   name = 'gflags_build_test',
@@ -69,3 +70,20 @@
   ],
   size = 'small',
 )
+
+proto_cc_library(
+  name = 'proto_build_test_library',
+  src = 'proto.proto',
+)
+
+cc_test(
+  name = 'proto_build_test',
+  srcs = [
+    'proto.cc',
+  ],
+  deps = [
+    ':proto_build_test_library',
+    '//aos/testing:googletest',
+  ],
+  size = 'small',
+)
diff --git a/build_tests/proto.cc b/build_tests/proto.cc
new file mode 100644
index 0000000..23ff879
--- /dev/null
+++ b/build_tests/proto.cc
@@ -0,0 +1,16 @@
+#include "gtest/gtest.h"
+
+#include "build_tests/proto.pb.h"
+
+TEST(ProtoBuildTest, Serialize) {
+  ::frc971::TestProto test_proto1, test_proto2;
+  test_proto1.set_s("Hi!");
+  test_proto1.set_i(971);
+
+  ::std::string serialized;
+  ASSERT_TRUE(test_proto1.SerializeToString(&serialized));
+  ASSERT_TRUE(test_proto2.ParseFromString(serialized));
+
+  EXPECT_EQ("Hi!", test_proto2.s());
+  EXPECT_EQ(971, test_proto2.i());
+}
diff --git a/build_tests/proto.proto b/build_tests/proto.proto
new file mode 100644
index 0000000..367a650
--- /dev/null
+++ b/build_tests/proto.proto
@@ -0,0 +1,12 @@
+syntax = "proto3";
+
+package frc971;
+
+import "google/protobuf/empty.proto";
+
+message TestProto {
+  string s = 1;
+  int32 i = 2;
+  // Making sure that well-known protos work.
+  .google.protobuf.Empty empty = 3;
+}
diff --git a/third_party/protobuf/BUILD b/third_party/protobuf/BUILD
index bb5d889..6686800 100644
--- a/third_party/protobuf/BUILD
+++ b/third_party/protobuf/BUILD
@@ -171,6 +171,12 @@
 
 WELL_KNOWN_PROTOS = ["src/" + s for s in RELATIVE_WELL_KNOWN_PROTOS]
 
+filegroup(
+    name = "well_known_protos",
+    srcs = WELL_KNOWN_PROTOS,
+    visibility = ["//visibility:public"],
+)
+
 cc_proto_library(
     name = "cc_wkt_protos",
     srcs = WELL_KNOWN_PROTOS,
diff --git a/tools/build_rules/protobuf.bzl b/tools/build_rules/protobuf.bzl
new file mode 100644
index 0000000..fe6af4f
--- /dev/null
+++ b/tools/build_rules/protobuf.bzl
@@ -0,0 +1,71 @@
+def _do_proto_cc_library_impl(ctx):
+  message = 'Building %s and %s from %s' % (ctx.outputs.pb_h.short_path,
+                                            ctx.outputs.pb_cc.short_path,
+                                            ctx.file.src.short_path)
+  ctx.action(
+    inputs = ctx.files.src + ctx.files._well_known_protos,
+    executable = ctx.executable._protoc,
+    arguments = [
+      '--cpp_out=%s' % ctx.configuration.genfiles_dir.path,
+      '-I.',
+      '-Ithird_party/protobuf/src',
+      ctx.file.src.path,
+    ],
+    mnemonic = 'ProtocCc',
+    progress_message = message,
+    outputs = [
+      ctx.outputs.pb_h,
+      ctx.outputs.pb_cc,
+    ],
+  )
+
+def _do_proto_cc_library_outputs(attr):
+  basename = attr.src.name[:-len('.proto')]
+  return {
+    'pb_h': '%s.pb.h' % basename,
+    'pb_cc': '%s.pb.cc' % basename,
+  }
+
+_do_proto_cc_library = rule(
+  implementation = _do_proto_cc_library_impl,
+  attrs = {
+    'src': attr.label(
+      allow_files = FileType(['.proto']),
+      mandatory = True,
+      single_file = True,
+    ),
+    '_protoc': attr.label(
+      default = Label('//third_party/protobuf:protoc'),
+      executable = True,
+      cfg = HOST_CFG,
+    ),
+    '_well_known_protos': attr.label(
+      default = Label('//third_party/protobuf:well_known_protos'),
+    ),
+  },
+  outputs = _do_proto_cc_library_outputs,
+  output_to_genfiles = True,
+)
+
+def proto_cc_library(name, src, visibility = None):
+  '''Generates a cc_library from a single .proto file. Does not support
+  dependencies on any .proto files except the well-known ones protobuf comes
+  with (which are unconditionally depended on).
+
+  Attrs:
+    src: The .proto file.
+  '''
+
+  _do_proto_cc_library(
+    name = '%s__proto_srcs' % name,
+    src = src,
+    visibility = ['//visibility:private'],
+  )
+  basename = src[:-len('.proto')]
+  native.cc_library(
+    name = name,
+    srcs = [ '%s.pb.cc' % basename ],
+    hdrs = [ '%s.pb.h' % basename ],
+    deps = [ '//third_party/protobuf' ],
+    visibility = visibility,
+  )