1// Copyright 2016 Google Inc. All rights reserved.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15package cc
16
17import (
18	"github.com/google/blueprint/pathtools"
19
20	"android/soong/android"
21)
22
23// genProto creates a rule to convert a .proto file to generated .pb.cc and .pb.h files and returns
24// the paths to the generated files.
25func genProto(ctx android.ModuleContext, protoFile android.Path, flags builderFlags) (cc, header android.WritablePath) {
26	var ccFile, headerFile android.ModuleGenPath
27
28	srcSuffix := ".cc"
29	if flags.protoC {
30		srcSuffix = ".c"
31	}
32
33	if flags.proto.CanonicalPathFromRoot {
34		ccFile = android.GenPathWithExt(ctx, "proto", protoFile, "pb"+srcSuffix)
35		headerFile = android.GenPathWithExt(ctx, "proto", protoFile, "pb.h")
36	} else {
37		rel := protoFile.Rel()
38		ccFile = android.PathForModuleGen(ctx, "proto", pathtools.ReplaceExtension(rel, "pb"+srcSuffix))
39		headerFile = android.PathForModuleGen(ctx, "proto", pathtools.ReplaceExtension(rel, "pb.h"))
40	}
41
42	protoDeps := flags.proto.Deps
43	if flags.protoOptionsFile {
44		optionsFile := pathtools.ReplaceExtension(protoFile.String(), "options")
45		optionsPath := android.PathForSource(ctx, optionsFile)
46		protoDeps = append(android.Paths{optionsPath}, protoDeps...)
47	}
48
49	outDir := flags.proto.Dir
50	depFile := ccFile.ReplaceExtension(ctx, "d")
51	outputs := android.WritablePaths{ccFile, headerFile}
52
53	rule := android.NewRuleBuilder()
54
55	android.ProtoRule(ctx, rule, protoFile, flags.proto, protoDeps, outDir, depFile, outputs)
56
57	rule.Build(pctx, ctx, "protoc_"+protoFile.Rel(), "protoc "+protoFile.Rel())
58
59	return ccFile, headerFile
60}
61
62func protoDeps(ctx DepsContext, deps Deps, p *android.ProtoProperties, static bool) Deps {
63	var lib string
64
65	if String(p.Proto.Plugin) == "" {
66		switch String(p.Proto.Type) {
67		case "full":
68			if ctx.useSdk() {
69				lib = "libprotobuf-cpp-full-ndk"
70				static = true
71			} else {
72				lib = "libprotobuf-cpp-full"
73			}
74		case "lite", "":
75			if ctx.useSdk() {
76				lib = "libprotobuf-cpp-lite-ndk"
77				static = true
78			} else {
79				lib = "libprotobuf-cpp-lite"
80			}
81		case "nanopb-c":
82			lib = "libprotobuf-c-nano"
83			static = true
84		case "nanopb-c-enable_malloc":
85			lib = "libprotobuf-c-nano-enable_malloc"
86			static = true
87		case "nanopb-c-16bit":
88			lib = "libprotobuf-c-nano-16bit"
89			static = true
90		case "nanopb-c-enable_malloc-16bit":
91			lib = "libprotobuf-c-nano-enable_malloc-16bit"
92			static = true
93		case "nanopb-c-32bit":
94			lib = "libprotobuf-c-nano-32bit"
95			static = true
96		case "nanopb-c-enable_malloc-32bit":
97			lib = "libprotobuf-c-nano-enable_malloc-32bit"
98			static = true
99		default:
100			ctx.PropertyErrorf("proto.type", "unknown proto type %q",
101				String(p.Proto.Type))
102		}
103
104		if static {
105			deps.StaticLibs = append(deps.StaticLibs, lib)
106			deps.ReexportStaticLibHeaders = append(deps.ReexportStaticLibHeaders, lib)
107		} else {
108			deps.SharedLibs = append(deps.SharedLibs, lib)
109			deps.ReexportSharedLibHeaders = append(deps.ReexportSharedLibHeaders, lib)
110		}
111	}
112
113	return deps
114}
115
116func protoFlags(ctx ModuleContext, flags Flags, p *android.ProtoProperties) Flags {
117	flags.Local.CFlags = append(flags.Local.CFlags, "-DGOOGLE_PROTOBUF_NO_RTTI")
118
119	flags.proto = android.GetProtoFlags(ctx, p)
120	if flags.proto.CanonicalPathFromRoot {
121		flags.Local.CommonFlags = append(flags.Local.CommonFlags, "-I"+flags.proto.SubDir.String())
122	}
123	flags.Local.CommonFlags = append(flags.Local.CommonFlags, "-I"+flags.proto.Dir.String())
124
125	if String(p.Proto.Plugin) == "" {
126		var plugin string
127
128		switch String(p.Proto.Type) {
129		case "nanopb-c", "nanopb-c-enable_malloc", "nanopb-c-16bit", "nanopb-c-enable_malloc-16bit", "nanopb-c-32bit", "nanopb-c-enable_malloc-32bit":
130			flags.protoC = true
131			flags.protoOptionsFile = true
132			flags.proto.OutTypeFlag = "--nanopb_out"
133			plugin = "protoc-gen-nanopb"
134		case "full":
135			flags.proto.OutTypeFlag = "--cpp_out"
136		case "lite":
137			flags.proto.OutTypeFlag = "--cpp_out"
138			flags.proto.OutParams = append(flags.proto.OutParams, "lite")
139		case "":
140			// TODO(b/119714316): this should be equivalent to "lite" in
141			// order to match protoDeps, but some modules are depending on
142			// this behavior
143			flags.proto.OutTypeFlag = "--cpp_out"
144		default:
145			ctx.PropertyErrorf("proto.type", "unknown proto type %q",
146				String(p.Proto.Type))
147		}
148
149		if plugin != "" {
150			path := ctx.Config().HostToolPath(ctx, plugin)
151			flags.proto.Deps = append(flags.proto.Deps, path)
152			flags.proto.Flags = append(flags.proto.Flags, "--plugin="+path.String())
153		}
154	}
155
156	return flags
157}
158