blob: 4fba34f92e358382fb99cccb1bf5abeb3dd58042 [file] [log] [blame]
Treehugger Robot588aae72020-08-21 10:01:58 +00001// Copyright 2020 The Android Open Source Project
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 rust
16
17import (
Zach Johnson3df4e632020-11-06 11:56:27 -080018 "fmt"
19 "strings"
20
Treehugger Robot588aae72020-08-21 10:01:58 +000021 "android/soong/android"
22)
23
24var (
25 defaultProtobufFlags = []string{""}
26)
27
Zach Johnson3df4e632020-11-06 11:56:27 -080028const (
29 grpcSuffix = "_grpc"
30)
31
Ivan Lozano6a3d1e92020-11-02 15:06:26 -050032type PluginType int
33
34const (
35 Protobuf PluginType = iota
36 Grpc
37)
38
Treehugger Robot588aae72020-08-21 10:01:58 +000039func init() {
40 android.RegisterModuleType("rust_protobuf", RustProtobufFactory)
41 android.RegisterModuleType("rust_protobuf_host", RustProtobufHostFactory)
Ivan Lozano6a3d1e92020-11-02 15:06:26 -050042 android.RegisterModuleType("rust_grpcio", RustGrpcioFactory)
43 android.RegisterModuleType("rust_grpcio_host", RustGrpcioHostFactory)
Treehugger Robot588aae72020-08-21 10:01:58 +000044}
45
46var _ SourceProvider = (*protobufDecorator)(nil)
47
48type ProtobufProperties struct {
Ivan Lozano9d74a522020-12-01 09:25:22 -050049 // List of relative paths to proto files that will be used to generate the source
Ivan Lozano57f434e2020-10-28 09:32:10 -040050 Protos []string `android:"path,arch_variant"`
Treehugger Robot588aae72020-08-21 10:01:58 +000051
52 // List of additional flags to pass to aprotoc
53 Proto_flags []string `android:"arch_variant"`
Zach Johnson3df4e632020-11-06 11:56:27 -080054
55 // List of libraries which export include paths required for this module
Ivan Lozano9b443832020-11-17 13:39:30 -050056 Header_libs []string `android:"arch_variant,variant_prepend"`
Treehugger Robot588aae72020-08-21 10:01:58 +000057}
58
59type protobufDecorator struct {
60 *BaseSourceProvider
61
62 Properties ProtobufProperties
Ivan Lozano6a3d1e92020-11-02 15:06:26 -050063 plugin PluginType
Treehugger Robot588aae72020-08-21 10:01:58 +000064}
65
66func (proto *protobufDecorator) GenerateSource(ctx ModuleContext, deps PathDeps) android.Path {
67 var protoFlags android.ProtoFlags
Matthew Maurer5819e582020-11-06 01:40:41 +000068 var pluginPaths android.Paths
Ivan Lozano57f434e2020-10-28 09:32:10 -040069 var protoNames []string
Treehugger Robot588aae72020-08-21 10:01:58 +000070
71 protoFlags.OutTypeFlag = "--rust_out"
Ivan Lozano6a3d1e92020-11-02 15:06:26 -050072 outDir := android.PathForModuleOut(ctx)
Treehugger Robot588aae72020-08-21 10:01:58 +000073
Matthew Maurer5819e582020-11-06 01:40:41 +000074 pluginPaths, protoFlags = proto.setupPlugin(ctx, protoFlags, outDir)
Ivan Lozano6a3d1e92020-11-02 15:06:26 -050075
Treehugger Robot588aae72020-08-21 10:01:58 +000076 protoFlags.Flags = append(protoFlags.Flags, defaultProtobufFlags...)
77 protoFlags.Flags = append(protoFlags.Flags, proto.Properties.Proto_flags...)
78
Matthew Maurer5819e582020-11-06 01:40:41 +000079 protoFlags.Deps = append(protoFlags.Deps, pluginPaths...)
Treehugger Robot588aae72020-08-21 10:01:58 +000080
Ivan Lozano57f434e2020-10-28 09:32:10 -040081 protoFiles := android.PathsForModuleSrc(ctx, proto.Properties.Protos)
Treehugger Robot588aae72020-08-21 10:01:58 +000082
Ivan Lozano9d74a522020-12-01 09:25:22 -050083 if len(protoFiles) == 0 {
84 ctx.PropertyErrorf("protos", "at least one protobuf must be defined.")
85 }
86
Zach Johnson3df4e632020-11-06 11:56:27 -080087 // Add exported dependency include paths
88 for _, include := range deps.depIncludePaths {
89 protoFlags.Flags = append(protoFlags.Flags, "-I"+include.String())
90 }
91
Chih-Hung Hsiehc49649c2020-10-01 21:25:05 -070092 stem := proto.BaseSourceProvider.getStem(ctx)
Ivan Lozano57f434e2020-10-28 09:32:10 -040093
94 // The mod_stem.rs file is used to avoid collisions if this is not included as a crate.
95 stemFile := android.PathForModuleOut(ctx, "mod_"+stem+".rs")
96
97 // stemFile must be first here as the first path in BaseSourceProvider.OutputFiles is the library entry-point.
Ivan Lozano9d74a522020-12-01 09:25:22 -050098 var outputs android.WritablePaths
Treehugger Robot588aae72020-08-21 10:01:58 +000099
Colin Crossf1a035e2020-11-16 17:32:30 -0800100 rule := android.NewRuleBuilder(pctx, ctx)
Ivan Lozano57f434e2020-10-28 09:32:10 -0400101 for _, protoFile := range protoFiles {
102 protoName := strings.TrimSuffix(protoFile.Base(), ".proto")
103 protoNames = append(protoNames, protoName)
Treehugger Robot588aae72020-08-21 10:01:58 +0000104
Ivan Lozano57f434e2020-10-28 09:32:10 -0400105 protoOut := android.PathForModuleOut(ctx, protoName+".rs")
106 ruleOutputs := android.WritablePaths{android.WritablePath(protoOut)}
107
108 if proto.plugin == Grpc {
109 grpcOut := android.PathForModuleOut(ctx, protoName+grpcSuffix+".rs")
110 ruleOutputs = append(ruleOutputs, android.WritablePath(grpcOut))
111 }
112
113 depFile := android.PathForModuleOut(ctx, protoName+".d")
114
Colin Crossf1a035e2020-11-16 17:32:30 -0800115 android.ProtoRule(rule, protoFile, protoFlags, protoFlags.Deps, outDir, depFile, ruleOutputs)
Ivan Lozano57f434e2020-10-28 09:32:10 -0400116 outputs = append(outputs, ruleOutputs...)
117 }
118
Ivan Lozano9d74a522020-12-01 09:25:22 -0500119 android.WriteFileRule(ctx, stemFile, proto.genModFileContents(ctx, protoNames))
Ivan Lozano57f434e2020-10-28 09:32:10 -0400120
Colin Crossf1a035e2020-11-16 17:32:30 -0800121 rule.Build("protoc_"+ctx.ModuleName(), "protoc "+ctx.ModuleName())
Ivan Lozano57f434e2020-10-28 09:32:10 -0400122
Ivan Lozano9d74a522020-12-01 09:25:22 -0500123 // stemFile must be first here as the first path in BaseSourceProvider.OutputFiles is the library entry-point.
124 proto.BaseSourceProvider.OutputFiles = append(android.Paths{stemFile}, outputs.Paths()...)
Ivan Lozano57f434e2020-10-28 09:32:10 -0400125
126 // mod_stem.rs is the entry-point for our library modules, so this is what we return.
127 return stemFile
Treehugger Robot588aae72020-08-21 10:01:58 +0000128}
129
Ivan Lozano57f434e2020-10-28 09:32:10 -0400130func (proto *protobufDecorator) genModFileContents(ctx ModuleContext, protoNames []string) string {
Zach Johnson3df4e632020-11-06 11:56:27 -0800131 lines := []string{
Ivan Lozano57f434e2020-10-28 09:32:10 -0400132 "// @Soong generated Source",
133 }
134 for _, protoName := range protoNames {
135 lines = append(lines, fmt.Sprintf("pub mod %s;", protoName))
136
137 if proto.plugin == Grpc {
138 lines = append(lines, fmt.Sprintf("pub mod %s%s;", protoName, grpcSuffix))
139 }
Zach Johnson3df4e632020-11-06 11:56:27 -0800140 }
141
142 if proto.plugin == Grpc {
Zach Johnson3df4e632020-11-06 11:56:27 -0800143 lines = append(
144 lines,
145 "pub mod empty {",
146 " pub use protobuf::well_known_types::Empty;",
147 "}")
148 }
149
Ivan Lozano9d74a522020-12-01 09:25:22 -0500150 return strings.Join(lines, "\n")
Zach Johnson3df4e632020-11-06 11:56:27 -0800151}
152
Matthew Maurer5819e582020-11-06 01:40:41 +0000153func (proto *protobufDecorator) setupPlugin(ctx ModuleContext, protoFlags android.ProtoFlags, outDir android.ModuleOutPath) (android.Paths, android.ProtoFlags) {
154 pluginPaths := []android.Path{}
Ivan Lozano6a3d1e92020-11-02 15:06:26 -0500155
156 if proto.plugin == Protobuf {
Matthew Maurer5819e582020-11-06 01:40:41 +0000157 pluginPath := ctx.Config().HostToolPath(ctx, "protoc-gen-rust")
158 pluginPaths = append(pluginPaths, pluginPath)
Ivan Lozano6a3d1e92020-11-02 15:06:26 -0500159 protoFlags.Flags = append(protoFlags.Flags, "--plugin="+pluginPath.String())
160 } else if proto.plugin == Grpc {
Matthew Maurer5819e582020-11-06 01:40:41 +0000161 grpcPath := ctx.Config().HostToolPath(ctx, "grpc_rust_plugin")
162 protobufPath := ctx.Config().HostToolPath(ctx, "protoc-gen-rust")
163 pluginPaths = append(pluginPaths, grpcPath, protobufPath)
Ivan Lozano6a3d1e92020-11-02 15:06:26 -0500164 protoFlags.Flags = append(protoFlags.Flags, "--grpc_out="+outDir.String())
Matthew Maurer5819e582020-11-06 01:40:41 +0000165 protoFlags.Flags = append(protoFlags.Flags, "--plugin=protoc-gen-grpc="+grpcPath.String())
166 protoFlags.Flags = append(protoFlags.Flags, "--plugin=protoc-gen-rust="+protobufPath.String())
Ivan Lozano6a3d1e92020-11-02 15:06:26 -0500167 } else {
168 ctx.ModuleErrorf("Unknown protobuf plugin type requested")
169 }
170
Matthew Maurer5819e582020-11-06 01:40:41 +0000171 return pluginPaths, protoFlags
Ivan Lozano6a3d1e92020-11-02 15:06:26 -0500172}
173
Treehugger Robot588aae72020-08-21 10:01:58 +0000174func (proto *protobufDecorator) SourceProviderProps() []interface{} {
175 return append(proto.BaseSourceProvider.SourceProviderProps(), &proto.Properties)
176}
177
178func (proto *protobufDecorator) SourceProviderDeps(ctx DepsContext, deps Deps) Deps {
179 deps = proto.BaseSourceProvider.SourceProviderDeps(ctx, deps)
180 deps.Rustlibs = append(deps.Rustlibs, "libprotobuf")
Zach Johnson3df4e632020-11-06 11:56:27 -0800181 deps.HeaderLibs = append(deps.SharedLibs, proto.Properties.Header_libs...)
182
183 if proto.plugin == Grpc {
184 deps.Rustlibs = append(deps.Rustlibs, "libgrpcio", "libfutures")
185 deps.HeaderLibs = append(deps.HeaderLibs, "libprotobuf-cpp-full")
186 }
187
Treehugger Robot588aae72020-08-21 10:01:58 +0000188 return deps
189}
190
191// rust_protobuf generates protobuf rust code from the provided proto file. This uses the protoc-gen-rust plugin for
192// protoc. Additional flags to the protoc command can be passed via the proto_flags property. This module type will
193// create library variants that can be used as a crate dependency by adding it to the rlibs, dylibs, and rustlibs
194// properties of other modules.
195func RustProtobufFactory() android.Module {
196 module, _ := NewRustProtobuf(android.HostAndDeviceSupported)
197 return module.Init()
198}
199
200// A host-only variant of rust_protobuf. Refer to rust_protobuf for more details.
201func RustProtobufHostFactory() android.Module {
202 module, _ := NewRustProtobuf(android.HostSupported)
203 return module.Init()
204}
205
Ivan Lozano6a3d1e92020-11-02 15:06:26 -0500206func RustGrpcioFactory() android.Module {
207 module, _ := NewRustGrpcio(android.HostAndDeviceSupported)
208 return module.Init()
209}
210
211// A host-only variant of rust_protobuf. Refer to rust_protobuf for more details.
212func RustGrpcioHostFactory() android.Module {
213 module, _ := NewRustGrpcio(android.HostSupported)
214 return module.Init()
215}
216
Treehugger Robot588aae72020-08-21 10:01:58 +0000217func NewRustProtobuf(hod android.HostOrDeviceSupported) (*Module, *protobufDecorator) {
218 protobuf := &protobufDecorator{
219 BaseSourceProvider: NewSourceProvider(),
220 Properties: ProtobufProperties{},
Ivan Lozano6a3d1e92020-11-02 15:06:26 -0500221 plugin: Protobuf,
222 }
223
224 module := NewSourceProviderModule(hod, protobuf, false)
225
226 return module, protobuf
227}
228
229func NewRustGrpcio(hod android.HostOrDeviceSupported) (*Module, *protobufDecorator) {
230 protobuf := &protobufDecorator{
231 BaseSourceProvider: NewSourceProvider(),
232 Properties: ProtobufProperties{},
233 plugin: Grpc,
Treehugger Robot588aae72020-08-21 10:01:58 +0000234 }
235
236 module := NewSourceProviderModule(hod, protobuf, false)
237
238 return module, protobuf
239}