1// Copyright 2017 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 android 16 17import ( 18 "android/soong/bazel" 19 "regexp" 20 "strings" 21 22 "github.com/google/blueprint" 23 "github.com/google/blueprint/proptools" 24) 25 26const ( 27 canonicalPathFromRootDefault = true 28) 29 30var ( 31 // ignoring case, checks for proto or protos as an independent word in the name, whether at the 32 // beginning, end, or middle. e.g. "proto.foo", "bar-protos", "baz_proto_srcs" would all match 33 filegroupLikelyProtoPattern = regexp.MustCompile("(?i)(^|[^a-z])proto(s)?([^a-z]|$)") 34 35 ProtoSrcLabelPartition = bazel.LabelPartition{Extensions: []string{".proto"}, LabelMapper: isProtoFilegroup} 36) 37 38// TODO(ccross): protos are often used to communicate between multiple modules. If the only 39// way to convert a proto to source is to reference it as a source file, and external modules cannot 40// reference source files in other modules, then every module that owns a proto file will need to 41// export a library for every type of external user (lite vs. full, c vs. c++ vs. java). It would 42// be better to support a proto module type that exported a proto file along with some include dirs, 43// and then external modules could depend on the proto module but use their own settings to 44// generate the source. 45 46type ProtoFlags struct { 47 Flags []string 48 CanonicalPathFromRoot bool 49 Dir ModuleGenPath 50 SubDir ModuleGenPath 51 OutTypeFlag string 52 OutParams []string 53 Deps Paths 54} 55 56type protoDependencyTag struct { 57 blueprint.BaseDependencyTag 58 name string 59} 60 61var ProtoPluginDepTag = protoDependencyTag{name: "plugin"} 62 63func ProtoDeps(ctx BottomUpMutatorContext, p *ProtoProperties) { 64 if String(p.Proto.Plugin) != "" && String(p.Proto.Type) != "" { 65 ctx.ModuleErrorf("only one of proto.type and proto.plugin can be specified.") 66 } 67 68 if plugin := String(p.Proto.Plugin); plugin != "" { 69 ctx.AddFarVariationDependencies(ctx.Config().BuildOSTarget.Variations(), 70 ProtoPluginDepTag, "protoc-gen-"+plugin) 71 } 72} 73 74func GetProtoFlags(ctx ModuleContext, p *ProtoProperties) ProtoFlags { 75 var flags []string 76 var deps Paths 77 78 if len(p.Proto.Local_include_dirs) > 0 { 79 localProtoIncludeDirs := PathsForModuleSrc(ctx, p.Proto.Local_include_dirs) 80 flags = append(flags, JoinWithPrefix(localProtoIncludeDirs.Strings(), "-I")) 81 } 82 if len(p.Proto.Include_dirs) > 0 { 83 rootProtoIncludeDirs := PathsForSource(ctx, p.Proto.Include_dirs) 84 flags = append(flags, JoinWithPrefix(rootProtoIncludeDirs.Strings(), "-I")) 85 } 86 87 ctx.VisitDirectDepsWithTag(ProtoPluginDepTag, func(dep Module) { 88 if hostTool, ok := dep.(HostToolProvider); !ok || !hostTool.HostToolPath().Valid() { 89 ctx.PropertyErrorf("proto.plugin", "module %q is not a host tool provider", 90 ctx.OtherModuleName(dep)) 91 } else { 92 plugin := String(p.Proto.Plugin) 93 deps = append(deps, hostTool.HostToolPath().Path()) 94 flags = append(flags, "--plugin=protoc-gen-"+plugin+"="+hostTool.HostToolPath().String()) 95 } 96 }) 97 98 var protoOutFlag string 99 if plugin := String(p.Proto.Plugin); plugin != "" { 100 protoOutFlag = "--" + plugin + "_out" 101 } 102 103 return ProtoFlags{ 104 Flags: flags, 105 Deps: deps, 106 OutTypeFlag: protoOutFlag, 107 CanonicalPathFromRoot: proptools.BoolDefault(p.Proto.Canonical_path_from_root, canonicalPathFromRootDefault), 108 Dir: PathForModuleGen(ctx, "proto"), 109 SubDir: PathForModuleGen(ctx, "proto", ctx.ModuleDir()), 110 } 111} 112 113type ProtoProperties struct { 114 Proto struct { 115 // Proto generator type. C++: full or lite. Java: micro, nano, stream, or lite. 116 Type *string `android:"arch_variant"` 117 118 // Proto plugin to use as the generator. Must be a cc_binary_host module. 119 Plugin *string `android:"arch_variant"` 120 121 // list of directories that will be added to the protoc include paths. 122 Include_dirs []string 123 124 // list of directories relative to the bp file that will 125 // be added to the protoc include paths. 126 Local_include_dirs []string 127 128 // whether to identify the proto files from the root of the 129 // source tree (the original method in Android, useful for 130 // android-specific protos), or relative from where they were 131 // specified (useful for external/third party protos). 132 // 133 // This defaults to true today, but is expected to default to 134 // false in the future. 135 Canonical_path_from_root *bool 136 } `android:"arch_variant"` 137} 138 139func ProtoRule(rule *RuleBuilder, protoFile Path, flags ProtoFlags, deps Paths, 140 outDir WritablePath, depFile WritablePath, outputs WritablePaths) { 141 142 var protoBase string 143 if flags.CanonicalPathFromRoot { 144 protoBase = "." 145 } else { 146 rel := protoFile.Rel() 147 protoBase = strings.TrimSuffix(protoFile.String(), rel) 148 } 149 150 rule.Command(). 151 BuiltTool("aprotoc"). 152 FlagWithArg(flags.OutTypeFlag+"=", strings.Join(flags.OutParams, ",")+":"+outDir.String()). 153 FlagWithDepFile("--dependency_out=", depFile). 154 FlagWithArg("-I ", protoBase). 155 Flags(flags.Flags). 156 Input(protoFile). 157 Implicits(deps). 158 ImplicitOutputs(outputs) 159 160 rule.Command(). 161 BuiltTool("dep_fixer").Flag(depFile.String()) 162} 163 164// Bp2buildProtoInfo contains information necessary to pass on to language specific conversion. 165type Bp2buildProtoInfo struct { 166 Type *string 167 Name string 168} 169 170type protoAttrs struct { 171 Srcs bazel.LabelListAttribute 172 Strip_import_prefix *string 173} 174 175// Bp2buildProtoProperties converts proto properties, creating a proto_library and returning the 176// information necessary for language-specific handling. 177func Bp2buildProtoProperties(ctx Bp2buildMutatorContext, m *ModuleBase, srcs bazel.LabelListAttribute) (Bp2buildProtoInfo, bool) { 178 var info Bp2buildProtoInfo 179 if srcs.IsEmpty() { 180 return info, false 181 } 182 183 info.Name = m.Name() + "_proto" 184 attrs := protoAttrs{ 185 Srcs: srcs, 186 } 187 188 for axis, configToProps := range m.GetArchVariantProperties(ctx, &ProtoProperties{}) { 189 for _, rawProps := range configToProps { 190 var props *ProtoProperties 191 var ok bool 192 if props, ok = rawProps.(*ProtoProperties); !ok { 193 ctx.ModuleErrorf("Could not cast ProtoProperties to expected type") 194 } 195 if axis == bazel.NoConfigAxis { 196 info.Type = props.Proto.Type 197 198 if !proptools.BoolDefault(props.Proto.Canonical_path_from_root, canonicalPathFromRootDefault) { 199 // an empty string indicates to strips the package path 200 path := "" 201 attrs.Strip_import_prefix = &path 202 } 203 } else if props.Proto.Type != info.Type && props.Proto.Type != nil { 204 ctx.ModuleErrorf("Cannot handle arch-variant types for protos at this time.") 205 } 206 } 207 } 208 209 ctx.CreateBazelTargetModule( 210 bazel.BazelTargetModuleProperties{Rule_class: "proto_library"}, 211 CommonAttributes{Name: info.Name}, 212 &attrs) 213 214 return info, true 215} 216 217func isProtoFilegroup(ctx bazel.OtherModuleContext, label bazel.Label) (string, bool) { 218 m, exists := ctx.ModuleFromName(label.OriginalModuleName) 219 labelStr := label.Label 220 if !exists || !IsFilegroup(ctx, m) { 221 return labelStr, false 222 } 223 likelyProtos := filegroupLikelyProtoPattern.MatchString(label.OriginalModuleName) 224 return labelStr, likelyProtos 225} 226