1// 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 ( 18 "fmt" 19 "strings" 20 21 "android/soong/android" 22) 23 24var ( 25 defaultProtobufFlags = []string{""} 26) 27 28const ( 29 grpcSuffix = "_grpc" 30) 31 32type PluginType int 33 34func init() { 35 android.RegisterModuleType("rust_protobuf", RustProtobufFactory) 36 android.RegisterModuleType("rust_protobuf_host", RustProtobufHostFactory) 37} 38 39var _ SourceProvider = (*protobufDecorator)(nil) 40 41type ProtobufProperties struct { 42 // List of relative paths to proto files that will be used to generate the source. 43 // Either this or grpc_protos must be defined. 44 Protos []string `android:"path,arch_variant"` 45 46 // List of relative paths to GRPC-containing proto files that will be used to generate the source. 47 // Either this or protos must be defined. 48 Grpc_protos []string `android:"path,arch_variant"` 49 50 // List of additional flags to pass to aprotoc 51 Proto_flags []string `android:"arch_variant"` 52 53 // List of libraries which export include paths required for this module 54 Header_libs []string `android:"arch_variant,variant_prepend"` 55} 56 57type protobufDecorator struct { 58 *BaseSourceProvider 59 60 Properties ProtobufProperties 61 protoNames []string 62 grpcNames []string 63 64 grpcProtoFlags android.ProtoFlags 65 protoFlags android.ProtoFlags 66} 67 68func (proto *protobufDecorator) GenerateSource(ctx ModuleContext, deps PathDeps) android.Path { 69 var protoFlags android.ProtoFlags 70 var grpcProtoFlags android.ProtoFlags 71 var commonProtoFlags []string 72 73 outDir := android.PathForModuleOut(ctx) 74 protoFiles := android.PathsForModuleSrc(ctx, proto.Properties.Protos) 75 grpcFiles := android.PathsForModuleSrc(ctx, proto.Properties.Grpc_protos) 76 protoPluginPath := ctx.Config().HostToolPath(ctx, "protoc-gen-rust") 77 78 commonProtoFlags = append(commonProtoFlags, defaultProtobufFlags...) 79 commonProtoFlags = append(commonProtoFlags, proto.Properties.Proto_flags...) 80 commonProtoFlags = append(commonProtoFlags, "--plugin=protoc-gen-rust="+protoPluginPath.String()) 81 82 if len(protoFiles) > 0 { 83 protoFlags.OutTypeFlag = "--rust_out" 84 protoFlags.Flags = append(protoFlags.Flags, commonProtoFlags...) 85 86 protoFlags.Deps = append(protoFlags.Deps, protoPluginPath) 87 } 88 89 if len(grpcFiles) > 0 { 90 grpcPath := ctx.Config().HostToolPath(ctx, "grpc_rust_plugin") 91 92 grpcProtoFlags.OutTypeFlag = "--rust_out" 93 grpcProtoFlags.Flags = append(grpcProtoFlags.Flags, "--grpc_out="+outDir.String()) 94 grpcProtoFlags.Flags = append(grpcProtoFlags.Flags, "--plugin=protoc-gen-grpc="+grpcPath.String()) 95 grpcProtoFlags.Flags = append(grpcProtoFlags.Flags, commonProtoFlags...) 96 97 grpcProtoFlags.Deps = append(grpcProtoFlags.Deps, grpcPath, protoPluginPath) 98 } 99 100 if len(protoFiles) == 0 && len(grpcFiles) == 0 { 101 ctx.PropertyErrorf("protos", 102 "at least one protobuf must be defined in either protos or grpc_protos.") 103 } 104 105 // Add exported dependency include paths 106 for _, include := range deps.depIncludePaths { 107 protoFlags.Flags = append(protoFlags.Flags, "-I"+include.String()) 108 grpcProtoFlags.Flags = append(grpcProtoFlags.Flags, "-I"+include.String()) 109 } 110 111 stem := proto.BaseSourceProvider.getStem(ctx) 112 113 // The mod_stem.rs file is used to avoid collisions if this is not included as a crate. 114 stemFile := android.PathForModuleOut(ctx, "mod_"+stem+".rs") 115 116 // stemFile must be first here as the first path in BaseSourceProvider.OutputFiles is the library entry-point. 117 var outputs android.WritablePaths 118 119 rule := android.NewRuleBuilder(pctx, ctx) 120 121 for _, protoFile := range protoFiles { 122 // Since we're iterating over the protoFiles already, make sure they're not redeclared in grpcFiles 123 if android.InList(protoFile.String(), grpcFiles.Strings()) { 124 ctx.PropertyErrorf("protos", 125 "A proto can only be added once to either grpc_protos or protos. %q is declared in both properties", 126 protoFile.String()) 127 } 128 129 protoName := strings.TrimSuffix(protoFile.Base(), ".proto") 130 proto.protoNames = append(proto.protoNames, protoName) 131 132 protoOut := android.PathForModuleOut(ctx, protoName+".rs") 133 depFile := android.PathForModuleOut(ctx, protoName+".d") 134 135 ruleOutputs := android.WritablePaths{protoOut, depFile} 136 137 android.ProtoRule(rule, protoFile, protoFlags, protoFlags.Deps, outDir, depFile, ruleOutputs) 138 outputs = append(outputs, ruleOutputs...) 139 } 140 141 for _, grpcFile := range grpcFiles { 142 grpcName := strings.TrimSuffix(grpcFile.Base(), ".proto") 143 proto.grpcNames = append(proto.grpcNames, grpcName) 144 145 // GRPC protos produce two files, a proto.rs and a proto_grpc.rs 146 protoOut := android.WritablePath(android.PathForModuleOut(ctx, grpcName+".rs")) 147 grpcOut := android.WritablePath(android.PathForModuleOut(ctx, grpcName+grpcSuffix+".rs")) 148 depFile := android.PathForModuleOut(ctx, grpcName+".d") 149 150 ruleOutputs := android.WritablePaths{protoOut, grpcOut, depFile} 151 152 android.ProtoRule(rule, grpcFile, grpcProtoFlags, grpcProtoFlags.Deps, outDir, depFile, ruleOutputs) 153 outputs = append(outputs, ruleOutputs...) 154 } 155 156 // Check that all proto base filenames are unique as outputs are written to the same directory. 157 baseFilenames := append(proto.protoNames, proto.grpcNames...) 158 if len(baseFilenames) != len(android.FirstUniqueStrings(baseFilenames)) { 159 ctx.PropertyErrorf("protos", "proto filenames must be unique across 'protos' and 'grpc_protos' "+ 160 "to be used in the same rust_protobuf module. For example, foo.proto and src/foo.proto will conflict.") 161 } 162 163 android.WriteFileRule(ctx, stemFile, proto.genModFileContents()) 164 165 rule.Build("protoc_"+ctx.ModuleName(), "protoc "+ctx.ModuleName()) 166 167 // stemFile must be first here as the first path in BaseSourceProvider.OutputFiles is the library entry-point. 168 proto.BaseSourceProvider.OutputFiles = append(android.Paths{stemFile}, outputs.Paths()...) 169 170 // mod_stem.rs is the entry-point for our library modules, so this is what we return. 171 return stemFile 172} 173 174func (proto *protobufDecorator) genModFileContents() string { 175 lines := []string{ 176 "// @Soong generated Source", 177 } 178 for _, protoName := range proto.protoNames { 179 lines = append(lines, fmt.Sprintf("pub mod %s;", protoName)) 180 } 181 182 for _, grpcName := range proto.grpcNames { 183 lines = append(lines, fmt.Sprintf("pub mod %s;", grpcName)) 184 lines = append(lines, fmt.Sprintf("pub mod %s%s;", grpcName, grpcSuffix)) 185 } 186 if len(proto.grpcNames) > 0 { 187 lines = append( 188 lines, 189 "pub mod empty {", 190 " pub use protobuf::well_known_types::Empty;", 191 "}") 192 } 193 194 return strings.Join(lines, "\n") 195} 196 197func (proto *protobufDecorator) SourceProviderProps() []interface{} { 198 return append(proto.BaseSourceProvider.SourceProviderProps(), &proto.Properties) 199} 200 201func (proto *protobufDecorator) SourceProviderDeps(ctx DepsContext, deps Deps) Deps { 202 deps = proto.BaseSourceProvider.SourceProviderDeps(ctx, deps) 203 deps.Rustlibs = append(deps.Rustlibs, "libprotobuf") 204 deps.HeaderLibs = append(deps.SharedLibs, proto.Properties.Header_libs...) 205 206 if len(proto.Properties.Grpc_protos) > 0 { 207 deps.Rustlibs = append(deps.Rustlibs, "libgrpcio", "libfutures") 208 deps.HeaderLibs = append(deps.HeaderLibs, "libprotobuf-cpp-full") 209 } 210 211 return deps 212} 213 214// rust_protobuf generates protobuf rust code from the provided proto file. This uses the protoc-gen-rust plugin for 215// protoc. Additional flags to the protoc command can be passed via the proto_flags property. This module type will 216// create library variants that can be used as a crate dependency by adding it to the rlibs, dylibs, and rustlibs 217// properties of other modules. 218func RustProtobufFactory() android.Module { 219 module, _ := NewRustProtobuf(android.HostAndDeviceSupported) 220 return module.Init() 221} 222 223// A host-only variant of rust_protobuf. Refer to rust_protobuf for more details. 224func RustProtobufHostFactory() android.Module { 225 module, _ := NewRustProtobuf(android.HostSupported) 226 return module.Init() 227} 228 229func NewRustProtobuf(hod android.HostOrDeviceSupported) (*Module, *protobufDecorator) { 230 protobuf := &protobufDecorator{ 231 BaseSourceProvider: NewSourceProvider(), 232 Properties: ProtobufProperties{}, 233 } 234 235 module := NewSourceProviderModule(hod, protobuf, false) 236 237 return module, protobuf 238} 239