blob: 2982efdf28eb8d9072db3612e08610afc18e7b36 [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"
Vinh Tran0c4b9ec2023-08-24 11:10:01 -040022 "android/soong/bazel"
Ivan Lozanod106efe2023-09-21 23:30:26 -040023 "android/soong/cc"
Vinh Tran0c4b9ec2023-08-24 11:10:01 -040024
25 "github.com/google/blueprint/proptools"
Treehugger Robot588aae72020-08-21 10:01:58 +000026)
27
28var (
29 defaultProtobufFlags = []string{""}
30)
31
Zach Johnson3df4e632020-11-06 11:56:27 -080032const (
33 grpcSuffix = "_grpc"
34)
35
Ivan Lozano6a3d1e92020-11-02 15:06:26 -050036type PluginType int
37
Treehugger Robot588aae72020-08-21 10:01:58 +000038func init() {
39 android.RegisterModuleType("rust_protobuf", RustProtobufFactory)
40 android.RegisterModuleType("rust_protobuf_host", RustProtobufHostFactory)
41}
42
43var _ SourceProvider = (*protobufDecorator)(nil)
44
45type ProtobufProperties struct {
Ivan Lozano6eff9002020-12-11 15:25:59 -050046 // List of relative paths to proto files that will be used to generate the source.
47 // Either this or grpc_protos must be defined.
Ivan Lozano57f434e2020-10-28 09:32:10 -040048 Protos []string `android:"path,arch_variant"`
Treehugger Robot588aae72020-08-21 10:01:58 +000049
Ivan Lozano6eff9002020-12-11 15:25:59 -050050 // List of relative paths to GRPC-containing proto files that will be used to generate the source.
51 // Either this or protos must be defined.
52 Grpc_protos []string `android:"path,arch_variant"`
53
Treehugger Robot588aae72020-08-21 10:01:58 +000054 // List of additional flags to pass to aprotoc
55 Proto_flags []string `android:"arch_variant"`
Zach Johnson3df4e632020-11-06 11:56:27 -080056
57 // List of libraries which export include paths required for this module
Ivan Lozano9b443832020-11-17 13:39:30 -050058 Header_libs []string `android:"arch_variant,variant_prepend"`
Jeff Vander Stoepc1490ec2023-04-24 11:28:25 +020059
60 // Use protobuf version 3.x. This will be deleted once we migrate all current users
61 // of protobuf off of 2.x.
62 Use_protobuf3 *bool
Ivan Lozanod106efe2023-09-21 23:30:26 -040063
64 // List of exported include paths containing proto files for dependent rust_protobuf modules.
65 Exported_include_dirs []string
Treehugger Robot588aae72020-08-21 10:01:58 +000066}
67
68type protobufDecorator struct {
69 *BaseSourceProvider
70
Ivan Lozanod106efe2023-09-21 23:30:26 -040071 Properties ProtobufProperties
72 protoNames []string
73 additionalCrates []string
74 grpcNames []string
Ivan Lozano6eff9002020-12-11 15:25:59 -050075
76 grpcProtoFlags android.ProtoFlags
77 protoFlags android.ProtoFlags
Treehugger Robot588aae72020-08-21 10:01:58 +000078}
79
Jeff Vander Stoepc1490ec2023-04-24 11:28:25 +020080func (proto *protobufDecorator) useProtobuf3() bool {
81 return Bool(proto.Properties.Use_protobuf3)
82}
83
Treehugger Robot588aae72020-08-21 10:01:58 +000084func (proto *protobufDecorator) GenerateSource(ctx ModuleContext, deps PathDeps) android.Path {
85 var protoFlags android.ProtoFlags
Ivan Lozano6eff9002020-12-11 15:25:59 -050086 var grpcProtoFlags android.ProtoFlags
87 var commonProtoFlags []string
Treehugger Robot588aae72020-08-21 10:01:58 +000088
Ivan Lozano6a3d1e92020-11-02 15:06:26 -050089 outDir := android.PathForModuleOut(ctx)
Ivan Lozano57f434e2020-10-28 09:32:10 -040090 protoFiles := android.PathsForModuleSrc(ctx, proto.Properties.Protos)
Ivan Lozano6eff9002020-12-11 15:25:59 -050091 grpcFiles := android.PathsForModuleSrc(ctx, proto.Properties.Grpc_protos)
Jeff Vander Stoepc1490ec2023-04-24 11:28:25 +020092
93 // For now protobuf2 (the deprecated version) remains the default. This will change in the
94 // future as we update the various users.
Jeff Vander Stoep91c04662023-03-22 15:09:00 +010095 protoPluginPath := ctx.Config().HostToolPath(ctx, "protoc-gen-rust-deprecated")
Jeff Vander Stoepc1490ec2023-04-24 11:28:25 +020096 if proto.useProtobuf3() == true {
97 protoPluginPath = ctx.Config().HostToolPath(ctx, "protoc-gen-rust")
98 }
Treehugger Robot588aae72020-08-21 10:01:58 +000099
Ivan Lozano6eff9002020-12-11 15:25:59 -0500100 commonProtoFlags = append(commonProtoFlags, defaultProtobufFlags...)
101 commonProtoFlags = append(commonProtoFlags, proto.Properties.Proto_flags...)
102 commonProtoFlags = append(commonProtoFlags, "--plugin=protoc-gen-rust="+protoPluginPath.String())
103
104 if len(protoFiles) > 0 {
105 protoFlags.OutTypeFlag = "--rust_out"
106 protoFlags.Flags = append(protoFlags.Flags, commonProtoFlags...)
107
108 protoFlags.Deps = append(protoFlags.Deps, protoPluginPath)
109 }
110
111 if len(grpcFiles) > 0 {
112 grpcPath := ctx.Config().HostToolPath(ctx, "grpc_rust_plugin")
113
114 grpcProtoFlags.OutTypeFlag = "--rust_out"
115 grpcProtoFlags.Flags = append(grpcProtoFlags.Flags, "--grpc_out="+outDir.String())
116 grpcProtoFlags.Flags = append(grpcProtoFlags.Flags, "--plugin=protoc-gen-grpc="+grpcPath.String())
117 grpcProtoFlags.Flags = append(grpcProtoFlags.Flags, commonProtoFlags...)
118
119 grpcProtoFlags.Deps = append(grpcProtoFlags.Deps, grpcPath, protoPluginPath)
120 }
121
122 if len(protoFiles) == 0 && len(grpcFiles) == 0 {
123 ctx.PropertyErrorf("protos",
124 "at least one protobuf must be defined in either protos or grpc_protos.")
Ivan Lozano9d74a522020-12-01 09:25:22 -0500125 }
126
Zach Johnson3df4e632020-11-06 11:56:27 -0800127 // Add exported dependency include paths
128 for _, include := range deps.depIncludePaths {
129 protoFlags.Flags = append(protoFlags.Flags, "-I"+include.String())
Ivan Lozano6eff9002020-12-11 15:25:59 -0500130 grpcProtoFlags.Flags = append(grpcProtoFlags.Flags, "-I"+include.String())
Zach Johnson3df4e632020-11-06 11:56:27 -0800131 }
132
Chih-Hung Hsiehc49649c2020-10-01 21:25:05 -0700133 stem := proto.BaseSourceProvider.getStem(ctx)
Ivan Lozano57f434e2020-10-28 09:32:10 -0400134
135 // The mod_stem.rs file is used to avoid collisions if this is not included as a crate.
136 stemFile := android.PathForModuleOut(ctx, "mod_"+stem+".rs")
137
138 // stemFile must be first here as the first path in BaseSourceProvider.OutputFiles is the library entry-point.
Ivan Lozano9d74a522020-12-01 09:25:22 -0500139 var outputs android.WritablePaths
Treehugger Robot588aae72020-08-21 10:01:58 +0000140
Colin Crossf1a035e2020-11-16 17:32:30 -0800141 rule := android.NewRuleBuilder(pctx, ctx)
Ivan Lozano6eff9002020-12-11 15:25:59 -0500142
Ivan Lozano57f434e2020-10-28 09:32:10 -0400143 for _, protoFile := range protoFiles {
Ivan Lozano6eff9002020-12-11 15:25:59 -0500144 // Since we're iterating over the protoFiles already, make sure they're not redeclared in grpcFiles
145 if android.InList(protoFile.String(), grpcFiles.Strings()) {
146 ctx.PropertyErrorf("protos",
147 "A proto can only be added once to either grpc_protos or protos. %q is declared in both properties",
148 protoFile.String())
Ivan Lozano57f434e2020-10-28 09:32:10 -0400149 }
150
Ivan Lozano6eff9002020-12-11 15:25:59 -0500151 protoName := strings.TrimSuffix(protoFile.Base(), ".proto")
152 proto.protoNames = append(proto.protoNames, protoName)
153
154 protoOut := android.PathForModuleOut(ctx, protoName+".rs")
Ivan Lozano57f434e2020-10-28 09:32:10 -0400155 depFile := android.PathForModuleOut(ctx, protoName+".d")
156
Ivan Lozano6eff9002020-12-11 15:25:59 -0500157 ruleOutputs := android.WritablePaths{protoOut, depFile}
158
Colin Crossf1a035e2020-11-16 17:32:30 -0800159 android.ProtoRule(rule, protoFile, protoFlags, protoFlags.Deps, outDir, depFile, ruleOutputs)
Ivan Lozano57f434e2020-10-28 09:32:10 -0400160 outputs = append(outputs, ruleOutputs...)
161 }
162
Ivan Lozano6eff9002020-12-11 15:25:59 -0500163 for _, grpcFile := range grpcFiles {
164 grpcName := strings.TrimSuffix(grpcFile.Base(), ".proto")
165 proto.grpcNames = append(proto.grpcNames, grpcName)
166
167 // GRPC protos produce two files, a proto.rs and a proto_grpc.rs
168 protoOut := android.WritablePath(android.PathForModuleOut(ctx, grpcName+".rs"))
169 grpcOut := android.WritablePath(android.PathForModuleOut(ctx, grpcName+grpcSuffix+".rs"))
170 depFile := android.PathForModuleOut(ctx, grpcName+".d")
171
172 ruleOutputs := android.WritablePaths{protoOut, grpcOut, depFile}
173
174 android.ProtoRule(rule, grpcFile, grpcProtoFlags, grpcProtoFlags.Deps, outDir, depFile, ruleOutputs)
175 outputs = append(outputs, ruleOutputs...)
176 }
177
178 // Check that all proto base filenames are unique as outputs are written to the same directory.
179 baseFilenames := append(proto.protoNames, proto.grpcNames...)
180 if len(baseFilenames) != len(android.FirstUniqueStrings(baseFilenames)) {
181 ctx.PropertyErrorf("protos", "proto filenames must be unique across 'protos' and 'grpc_protos' "+
182 "to be used in the same rust_protobuf module. For example, foo.proto and src/foo.proto will conflict.")
183 }
184
185 android.WriteFileRule(ctx, stemFile, proto.genModFileContents())
Ivan Lozano57f434e2020-10-28 09:32:10 -0400186
Colin Crossf1a035e2020-11-16 17:32:30 -0800187 rule.Build("protoc_"+ctx.ModuleName(), "protoc "+ctx.ModuleName())
Ivan Lozano57f434e2020-10-28 09:32:10 -0400188
Ivan Lozano9d74a522020-12-01 09:25:22 -0500189 // stemFile must be first here as the first path in BaseSourceProvider.OutputFiles is the library entry-point.
190 proto.BaseSourceProvider.OutputFiles = append(android.Paths{stemFile}, outputs.Paths()...)
Ivan Lozano57f434e2020-10-28 09:32:10 -0400191
Ivan Lozanod106efe2023-09-21 23:30:26 -0400192 ctx.SetProvider(cc.FlagExporterInfoProvider, cc.FlagExporterInfo{
193 IncludeDirs: android.PathsForModuleSrc(ctx, proto.Properties.Exported_include_dirs),
194 })
195
Ivan Lozano57f434e2020-10-28 09:32:10 -0400196 // mod_stem.rs is the entry-point for our library modules, so this is what we return.
197 return stemFile
Treehugger Robot588aae72020-08-21 10:01:58 +0000198}
199
Ivan Lozano6eff9002020-12-11 15:25:59 -0500200func (proto *protobufDecorator) genModFileContents() string {
Zach Johnson3df4e632020-11-06 11:56:27 -0800201 lines := []string{
Ivan Lozano57f434e2020-10-28 09:32:10 -0400202 "// @Soong generated Source",
203 }
Ivan Lozanod106efe2023-09-21 23:30:26 -0400204
Ivan Lozano6eff9002020-12-11 15:25:59 -0500205 for _, protoName := range proto.protoNames {
Ivan Lozano57f434e2020-10-28 09:32:10 -0400206 lines = append(lines, fmt.Sprintf("pub mod %s;", protoName))
Zach Johnson3df4e632020-11-06 11:56:27 -0800207 }
208
Ivan Lozanod106efe2023-09-21 23:30:26 -0400209 for _, crate := range proto.additionalCrates {
210 lines = append(lines, fmt.Sprintf("pub use %s::*;", crate))
211
212 }
213
Ivan Lozano6eff9002020-12-11 15:25:59 -0500214 for _, grpcName := range proto.grpcNames {
215 lines = append(lines, fmt.Sprintf("pub mod %s;", grpcName))
216 lines = append(lines, fmt.Sprintf("pub mod %s%s;", grpcName, grpcSuffix))
217 }
218 if len(proto.grpcNames) > 0 {
Zach Johnson3df4e632020-11-06 11:56:27 -0800219 lines = append(
220 lines,
221 "pub mod empty {",
222 " pub use protobuf::well_known_types::Empty;",
David Duarteb6be48d2022-01-04 08:51:21 +0000223 "}",
224 "pub mod wrappers {",
225 " pub use protobuf::well_known_types::{",
226 " DoubleValue, FloatValue, Int64Value, UInt64Value, Int32Value, UInt32Value,",
227 " BoolValue, StringValue, BytesValue",
228 " };",
Zach Johnson3df4e632020-11-06 11:56:27 -0800229 "}")
230 }
231
Ivan Lozano9d74a522020-12-01 09:25:22 -0500232 return strings.Join(lines, "\n")
Zach Johnson3df4e632020-11-06 11:56:27 -0800233}
234
Treehugger Robot588aae72020-08-21 10:01:58 +0000235func (proto *protobufDecorator) SourceProviderProps() []interface{} {
236 return append(proto.BaseSourceProvider.SourceProviderProps(), &proto.Properties)
237}
238
239func (proto *protobufDecorator) SourceProviderDeps(ctx DepsContext, deps Deps) Deps {
240 deps = proto.BaseSourceProvider.SourceProviderDeps(ctx, deps)
Jeff Vander Stoepc1490ec2023-04-24 11:28:25 +0200241 useProtobuf3 := proto.useProtobuf3()
242 if useProtobuf3 == true {
243 deps.Rustlibs = append(deps.Rustlibs, "libprotobuf")
244 } else {
245 deps.Rustlibs = append(deps.Rustlibs, "libprotobuf_deprecated")
246 }
Zach Johnson3df4e632020-11-06 11:56:27 -0800247 deps.HeaderLibs = append(deps.SharedLibs, proto.Properties.Header_libs...)
248
Ivan Lozano6eff9002020-12-11 15:25:59 -0500249 if len(proto.Properties.Grpc_protos) > 0 {
Jeff Vander Stoepc1490ec2023-04-24 11:28:25 +0200250 if useProtobuf3 == true {
251 ctx.PropertyErrorf("protos", "rust_protobuf with grpc_protos defined must currently use "+
252 "`use_protobuf3: false,` in the Android.bp file. This is temporary until the "+
253 "grpcio crate is updated to use the current version of the protobuf crate.")
254 }
Zach Johnson3df4e632020-11-06 11:56:27 -0800255 deps.Rustlibs = append(deps.Rustlibs, "libgrpcio", "libfutures")
256 deps.HeaderLibs = append(deps.HeaderLibs, "libprotobuf-cpp-full")
257 }
258
Treehugger Robot588aae72020-08-21 10:01:58 +0000259 return deps
260}
261
262// rust_protobuf generates protobuf rust code from the provided proto file. This uses the protoc-gen-rust plugin for
263// protoc. Additional flags to the protoc command can be passed via the proto_flags property. This module type will
Vinh Tran4eeb2a92023-08-14 13:29:30 -0400264// create library variants that can be used as a crate dependency by adding it to the rlibs and rustlibs
Treehugger Robot588aae72020-08-21 10:01:58 +0000265// properties of other modules.
266func RustProtobufFactory() android.Module {
267 module, _ := NewRustProtobuf(android.HostAndDeviceSupported)
268 return module.Init()
269}
270
271// A host-only variant of rust_protobuf. Refer to rust_protobuf for more details.
272func RustProtobufHostFactory() android.Module {
273 module, _ := NewRustProtobuf(android.HostSupported)
274 return module.Init()
275}
276
277func NewRustProtobuf(hod android.HostOrDeviceSupported) (*Module, *protobufDecorator) {
278 protobuf := &protobufDecorator{
279 BaseSourceProvider: NewSourceProvider(),
280 Properties: ProtobufProperties{},
Treehugger Robot588aae72020-08-21 10:01:58 +0000281 }
282
Matthew Maurere94f3e72022-08-10 20:25:50 +0000283 module := NewSourceProviderModule(hod, protobuf, false, false)
Treehugger Robot588aae72020-08-21 10:01:58 +0000284
Vinh Tran0c4b9ec2023-08-24 11:10:01 -0400285 android.InitBazelModule(module)
286
Treehugger Robot588aae72020-08-21 10:01:58 +0000287 return module, protobuf
288}
Vinh Tran0c4b9ec2023-08-24 11:10:01 -0400289
290type rustProtoAttributes struct {
291 Srcs bazel.LabelListAttribute
292 Crate_name bazel.StringAttribute
293 Deps bazel.LabelListAttribute
294}
295
296type protoLibraryAttributes struct {
297 Srcs bazel.LabelListAttribute
298}
299
Chris Parsons637458d2023-09-19 20:09:00 +0000300func protoLibraryBp2build(ctx android.Bp2buildMutatorContext, m *Module) {
Vinh Tran0c4b9ec2023-08-24 11:10:01 -0400301 var protoFiles []string
302
303 for _, propsInterface := range m.sourceProvider.SourceProviderProps() {
304 if possibleProps, ok := propsInterface.(*ProtobufProperties); ok {
305 protoFiles = possibleProps.Protos
306 break
307 }
308 }
309
310 protoLibraryName := m.Name() + "_proto"
311
312 protoDeps := bazel.LabelListAttribute{
313 Value: bazel.LabelList{
314 Includes: []bazel.Label{
315 {
316 Label: ":" + protoLibraryName,
317 OriginalModuleName: m.Name(),
318 },
319 },
320 },
321 }
322
Vinh Tran47faaad2023-09-25 14:47:19 -0400323 // TODO(b/295918553): Remove androidRestriction after rust toolchain for android is checked in.
324 var androidRestriction bazel.BoolAttribute
325 androidRestriction.SetSelectValue(bazel.OsConfigurationAxis, "android", proptools.BoolPtr(false))
326
327 ctx.CreateBazelTargetModuleWithRestrictions(
Vinh Tran0c4b9ec2023-08-24 11:10:01 -0400328 bazel.BazelTargetModuleProperties{
329 Rule_class: "proto_library",
330 },
331 android.CommonAttributes{
332 Name: protoLibraryName,
333 },
334 &protoLibraryAttributes{
335 Srcs: bazel.MakeLabelListAttribute(
336 android.BazelLabelForModuleSrc(ctx, protoFiles),
337 ),
338 },
Vinh Tran47faaad2023-09-25 14:47:19 -0400339 androidRestriction,
Vinh Tran0c4b9ec2023-08-24 11:10:01 -0400340 )
341
Vinh Tran47faaad2023-09-25 14:47:19 -0400342 ctx.CreateBazelTargetModuleWithRestrictions(
Vinh Tran0c4b9ec2023-08-24 11:10:01 -0400343 bazel.BazelTargetModuleProperties{
344 Rule_class: "rust_proto_library",
345 Bzl_load_location: "@rules_rust//proto/protobuf:defs.bzl",
346 },
347 android.CommonAttributes{
348 Name: m.Name(),
349 },
350 &rustProtoAttributes{
351 Crate_name: bazel.StringAttribute{
352 Value: proptools.StringPtr(m.CrateName()),
353 },
354 Deps: protoDeps,
355 },
Vinh Tran47faaad2023-09-25 14:47:19 -0400356 androidRestriction,
Vinh Tran0c4b9ec2023-08-24 11:10:01 -0400357 )
358}