Add support for .proto files

.proto files are translated to .pb.cc and .pb.h files, which are then
compiled normally.

Bug: 32286026
Test: mmma -j system/extras/perfprofd
Change-Id: I538071424d667aacf35b4b8bfebe217f5f092726
diff --git a/cc/builder.go b/cc/builder.go
index b813783..60aad0b 100644
--- a/cc/builder.go
+++ b/cc/builder.go
@@ -173,6 +173,7 @@
 	ldFlags     string
 	libFlags    string
 	yaccFlags   string
+	protoFlags  string
 	toolchain   config.Toolchain
 	clang       bool
 
diff --git a/cc/cc.go b/cc/cc.go
index 529a329..33c9611 100644
--- a/cc/cc.go
+++ b/cc/cc.go
@@ -97,6 +97,7 @@
 	ConlyFlags  []string // Flags that apply to C source files
 	CppFlags    []string // Flags that apply to C++ source files
 	YaccFlags   []string // Flags that apply to Yacc source files
+	protoFlags  []string // Flags that apply to proto source files
 	LdFlags     []string // Flags that apply to linker command lines
 	libFlags    []string // Flags to add libraries early to the link order
 
diff --git a/cc/compiler.go b/cc/compiler.go
index 32d98e4..0184ee9 100644
--- a/cc/compiler.go
+++ b/cc/compiler.go
@@ -100,6 +100,7 @@
 
 type baseCompiler struct {
 	Properties BaseCompilerProperties
+	Proto      ProtoProperties
 	deps       android.Paths
 }
 
@@ -123,6 +124,10 @@
 	deps.GeneratedSources = append(deps.GeneratedSources, compiler.Properties.Generated_sources...)
 	deps.GeneratedHeaders = append(deps.GeneratedHeaders, compiler.Properties.Generated_headers...)
 
+	if compiler.hasProto() {
+		deps = protoDeps(ctx, deps, &compiler.Proto)
+	}
+
 	return deps
 }
 
@@ -160,11 +165,7 @@
 				"${config.CommonNativehelperInclude}")
 		}
 
-		flags.GlobalFlags = append(flags.GlobalFlags, []string{
-			"-I" + android.PathForModuleSrc(ctx).String(),
-			"-I" + android.PathForModuleOut(ctx).String(),
-			"-I" + android.PathForModuleGen(ctx).String(),
-		}...)
+		flags.GlobalFlags = append(flags.GlobalFlags, "-I"+android.PathForModuleSrc(ctx).String())
 	}
 
 	if ctx.sdk() {
@@ -321,9 +322,23 @@
 		flags.CFlags = append(flags.CFlags, "-DANDROID_STRICT")
 	}
 
+	if compiler.hasProto() {
+		flags = protoFlags(ctx, flags, &compiler.Proto)
+	}
+
 	return flags
 }
 
+func (compiler *baseCompiler) hasProto() bool {
+	for _, src := range compiler.Properties.Srcs {
+		if filepath.Ext(src) == ".proto" {
+			return true
+		}
+	}
+
+	return false
+}
+
 var gnuToCReplacer = strings.NewReplacer("gnu", "c")
 
 func ndkPathDeps(ctx ModuleContext) android.Paths {
diff --git a/cc/gen.go b/cc/gen.go
index b35eee5..1451895 100644
--- a/cc/gen.go
+++ b/cc/gen.go
@@ -95,6 +95,10 @@
 			cppFile := android.GenPathWithExt(ctx, srcFile, "cpp")
 			srcFiles[i] = cppFile
 			genLex(ctx, srcFile, cppFile)
+		case ".proto":
+			cppFile, headerFile := genProto(ctx, srcFile, buildFlags.protoFlags)
+			srcFiles[i] = cppFile
+			deps = append(deps, headerFile)
 		}
 	}
 
diff --git a/cc/library.go b/cc/library.go
index d3d142e..99a9b48 100644
--- a/cc/library.go
+++ b/cc/library.go
@@ -55,6 +55,11 @@
 	// rename host libraries to prevent overlap with system installed libraries
 	Unique_host_soname *bool
 
+	Proto struct {
+		// export headers generated from .proto sources
+		Export_proto_headers bool
+	}
+
 	VariantName string `blueprint:"mutated"`
 
 	// Build a static variant
@@ -473,6 +478,16 @@
 	library.reexportFlags(deps.ReexportedFlags)
 	library.reexportDeps(deps.ReexportedFlagsDeps)
 
+	if library.baseCompiler.hasProto() {
+		if library.Properties.Proto.Export_proto_headers {
+			library.reexportFlags([]string{
+				"-I" + protoSubDir(ctx).String(),
+				"-I" + protoDir(ctx).String(),
+			})
+			library.reexportDeps(library.baseCompiler.deps) // TODO: restrict to proto deps
+		}
+	}
+
 	return out
 }
 
diff --git a/cc/proto.go b/cc/proto.go
new file mode 100644
index 0000000..3d3ca59
--- /dev/null
+++ b/cc/proto.go
@@ -0,0 +1,131 @@
+// Copyright 2016 Google Inc. 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 cc
+
+import (
+	"strings"
+
+	"github.com/google/blueprint"
+
+	"android/soong/android"
+)
+
+func init() {
+	pctx.HostBinToolVariable("protocCmd", "aprotoc")
+}
+
+var (
+	proto = pctx.AndroidStaticRule("protoc",
+		blueprint.RuleParams{
+			Command:     "$protocCmd --cpp_out=$outDir $protoFlags $in",
+			CommandDeps: []string{"$protocCmd"},
+			Description: "protoc $out",
+		}, "protoFlags", "outDir")
+)
+
+// TODO(ccross): protos are often used to communicate between multiple modules.  If the only
+// way to convert a proto to source is to reference it as a source file, and external modules cannot
+// reference source files in other modules, then every module that owns a proto file will need to
+// export a library for every type of external user (lite vs. full, c vs. c++ vs. java).  It would
+// be better to support a proto module type that exported a proto file along with some include dirs,
+// and then external modules could depend on the proto module but use their own settings to
+// generate the source.
+
+func genProto(ctx android.ModuleContext, protoFile android.Path,
+	protoFlags string) (android.ModuleGenPath, android.ModuleGenPath) {
+
+	outDir := android.PathForModuleGen(ctx, "proto")
+	baseName := strings.TrimSuffix(protoFile.Base(), protoFile.Ext())
+
+	outFile := android.PathForModuleGen(ctx, "proto", ctx.ModuleDir(), baseName+".pb.cc")
+	headerFile := android.PathForModuleGen(ctx, "proto", ctx.ModuleDir(), baseName+".pb.h")
+	ctx.ModuleBuild(pctx, android.ModuleBuildParams{
+		Rule:    proto,
+		Outputs: android.WritablePaths{outFile, headerFile},
+		Input:   protoFile,
+		Args: map[string]string{
+			"outDir":     outDir.String(),
+			"protoFlags": protoFlags,
+		},
+	})
+
+	return outFile, headerFile
+}
+
+// protoDir returns the module's "gen/proto" directory
+func protoDir(ctx android.ModuleContext) android.ModuleGenPath {
+	return android.PathForModuleGen(ctx, "proto")
+}
+
+// protoSubDir returns the module's "gen/proto/path/to/module" directory
+func protoSubDir(ctx android.ModuleContext) android.ModuleGenPath {
+	return android.PathForModuleGen(ctx, "proto", ctx.ModuleDir())
+}
+
+type ProtoProperties struct {
+	Proto struct {
+		// Proto generator type (full, lite)
+		Type string
+		// Link statically against the protobuf runtime
+		Static bool
+	}
+}
+
+func protoDeps(ctx BaseModuleContext, deps Deps, p *ProtoProperties) Deps {
+	var lib string
+	var static bool
+
+	switch p.Proto.Type {
+	case "full":
+		if ctx.sdk() {
+			lib = "libprotobuf-cpp-full-ndk"
+			static = true
+		} else {
+			lib = "libprotobuf-cpp-full"
+		}
+	case "lite", "":
+		if ctx.sdk() {
+			lib = "libprotobuf-cpp-lite-ndk"
+			static = true
+		} else {
+			lib = "libprotobuf-cpp-lite"
+			if p.Proto.Static {
+				static = true
+			}
+		}
+	default:
+		ctx.PropertyErrorf("proto.type", "unknown proto type %q", p.Proto.Type)
+	}
+
+	if static {
+		deps.StaticLibs = append(deps.StaticLibs, lib)
+		deps.ReexportStaticLibHeaders = append(deps.ReexportStaticLibHeaders, lib)
+	} else {
+		deps.SharedLibs = append(deps.SharedLibs, lib)
+		deps.ReexportSharedLibHeaders = append(deps.ReexportSharedLibHeaders, lib)
+	}
+
+	return deps
+}
+
+func protoFlags(ctx ModuleContext, flags Flags, p *ProtoProperties) Flags {
+	flags.CFlags = append(flags.CFlags, "-DGOOGLE_PROTOBUF_NO_RTTI")
+	flags.GlobalFlags = append(flags.GlobalFlags,
+		"-I"+protoSubDir(ctx).String(),
+		"-I"+protoDir(ctx).String(),
+	)
+
+	return flags
+}
diff --git a/cc/util.go b/cc/util.go
index 9ab74c3..07296b9 100644
--- a/cc/util.go
+++ b/cc/util.go
@@ -93,6 +93,7 @@
 		conlyFlags:  strings.Join(in.ConlyFlags, " "),
 		cppFlags:    strings.Join(in.CppFlags, " "),
 		yaccFlags:   strings.Join(in.YaccFlags, " "),
+		protoFlags:  strings.Join(in.protoFlags, " "),
 		ldFlags:     strings.Join(in.LdFlags, " "),
 		libFlags:    strings.Join(in.libFlags, " "),
 		toolchain:   in.Toolchain,