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 "strings" 19 20 "android/soong/bazel" 21 22 "github.com/google/blueprint" 23 "github.com/google/blueprint/proptools" 24) 25 26const ( 27 canonicalPathFromRootDefault = true 28) 29 30// TODO(ccross): protos are often used to communicate between multiple modules. If the only 31// way to convert a proto to source is to reference it as a source file, and external modules cannot 32// reference source files in other modules, then every module that owns a proto file will need to 33// export a library for every type of external user (lite vs. full, c vs. c++ vs. java). It would 34// be better to support a proto module type that exported a proto file along with some include dirs, 35// and then external modules could depend on the proto module but use their own settings to 36// generate the source. 37 38type ProtoFlags struct { 39 Flags []string 40 CanonicalPathFromRoot bool 41 Dir ModuleGenPath 42 SubDir ModuleGenPath 43 OutTypeFlag string 44 OutParams []string 45 Deps Paths 46} 47 48type protoDependencyTag struct { 49 blueprint.BaseDependencyTag 50 name string 51} 52 53var ProtoPluginDepTag = protoDependencyTag{name: "plugin"} 54 55func ProtoDeps(ctx BottomUpMutatorContext, p *ProtoProperties) { 56 if String(p.Proto.Plugin) != "" && String(p.Proto.Type) != "" { 57 ctx.ModuleErrorf("only one of proto.type and proto.plugin can be specified.") 58 } 59 60 if plugin := String(p.Proto.Plugin); plugin != "" { 61 ctx.AddFarVariationDependencies(ctx.Config().BuildOSTarget.Variations(), 62 ProtoPluginDepTag, "protoc-gen-"+plugin) 63 } 64} 65 66func GetProtoFlags(ctx ModuleContext, p *ProtoProperties) ProtoFlags { 67 var flags []string 68 var deps Paths 69 70 if len(p.Proto.Local_include_dirs) > 0 { 71 localProtoIncludeDirs := PathsForModuleSrc(ctx, p.Proto.Local_include_dirs) 72 flags = append(flags, JoinWithPrefix(localProtoIncludeDirs.Strings(), "-I")) 73 } 74 if len(p.Proto.Include_dirs) > 0 { 75 rootProtoIncludeDirs := PathsForSource(ctx, p.Proto.Include_dirs) 76 flags = append(flags, JoinWithPrefix(rootProtoIncludeDirs.Strings(), "-I")) 77 } 78 79 ctx.VisitDirectDepsWithTag(ProtoPluginDepTag, func(dep Module) { 80 if hostTool, ok := dep.(HostToolProvider); !ok || !hostTool.HostToolPath().Valid() { 81 ctx.PropertyErrorf("proto.plugin", "module %q is not a host tool provider", 82 ctx.OtherModuleName(dep)) 83 } else { 84 plugin := String(p.Proto.Plugin) 85 deps = append(deps, hostTool.HostToolPath().Path()) 86 flags = append(flags, "--plugin=protoc-gen-"+plugin+"="+hostTool.HostToolPath().String()) 87 } 88 }) 89 90 var protoOutFlag string 91 if plugin := String(p.Proto.Plugin); plugin != "" { 92 protoOutFlag = "--" + plugin + "_out" 93 } 94 95 return ProtoFlags{ 96 Flags: flags, 97 Deps: deps, 98 OutTypeFlag: protoOutFlag, 99 CanonicalPathFromRoot: proptools.BoolDefault(p.Proto.Canonical_path_from_root, canonicalPathFromRootDefault), 100 Dir: PathForModuleGen(ctx, "proto"), 101 SubDir: PathForModuleGen(ctx, "proto", ctx.ModuleDir()), 102 } 103} 104 105type ProtoProperties struct { 106 Proto struct { 107 // Proto generator type. C++: full or lite. Java: micro, nano, stream, or lite. 108 Type *string `android:"arch_variant"` 109 110 // Proto plugin to use as the generator. Must be a cc_binary_host module. 111 Plugin *string `android:"arch_variant"` 112 113 // list of directories that will be added to the protoc include paths. 114 Include_dirs []string 115 116 // list of directories relative to the bp file that will 117 // be added to the protoc include paths. 118 Local_include_dirs []string 119 120 // whether to identify the proto files from the root of the 121 // source tree (the original method in Android, useful for 122 // android-specific protos), or relative from where they were 123 // specified (useful for external/third party protos). 124 // 125 // This defaults to true today, but is expected to default to 126 // false in the future. 127 Canonical_path_from_root *bool 128 } `android:"arch_variant"` 129} 130 131func ProtoRule(rule *RuleBuilder, protoFile Path, flags ProtoFlags, deps Paths, 132 outDir WritablePath, depFile WritablePath, outputs WritablePaths) { 133 134 var protoBase string 135 if flags.CanonicalPathFromRoot { 136 protoBase = "." 137 } else { 138 rel := protoFile.Rel() 139 protoBase = strings.TrimSuffix(protoFile.String(), rel) 140 } 141 142 rule.Command(). 143 BuiltTool("aprotoc"). 144 FlagWithArg(flags.OutTypeFlag+"=", strings.Join(flags.OutParams, ",")+":"+outDir.String()). 145 FlagWithDepFile("--dependency_out=", depFile). 146 FlagWithArg("-I ", protoBase). 147 Flags(flags.Flags). 148 Input(protoFile). 149 Implicits(deps). 150 ImplicitOutputs(outputs) 151 152 rule.Command(). 153 BuiltTool("dep_fixer").Flag(depFile.String()) 154} 155 156// Bp2buildProtoInfo contains information necessary to pass on to language specific conversion. 157type Bp2buildProtoInfo struct { 158 Type *string 159 Name string 160 Proto_libs bazel.LabelList 161} 162 163type ProtoAttrs struct { 164 Srcs bazel.LabelListAttribute 165 Strip_import_prefix *string 166 Deps bazel.LabelListAttribute 167} 168 169// For each package in the include_dirs property a proto_library target should 170// be added to the BUILD file in that package and a mapping should be added here 171var includeDirsToProtoDeps = map[string]string{ 172 "external/protobuf/src": "//external/protobuf:libprotobuf-proto", 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 var protoLibraries bazel.LabelList 184 var directProtoSrcs bazel.LabelList 185 186 // For filegroups that should be converted to proto_library just collect the 187 // labels of converted proto_library targets. 188 for _, protoSrc := range srcs.Value.Includes { 189 src := protoSrc.OriginalModuleName 190 if fg, ok := ToFileGroupAsLibrary(ctx, src); ok && 191 fg.ShouldConvertToProtoLibrary(ctx) { 192 protoLibraries.Add(&bazel.Label{ 193 Label: fg.GetProtoLibraryLabel(ctx), 194 }) 195 } else { 196 directProtoSrcs.Add(&protoSrc) 197 } 198 } 199 200 info.Name = m.Name() + "_proto" 201 202 if len(directProtoSrcs.Includes) > 0 { 203 attrs := ProtoAttrs{ 204 Srcs: bazel.MakeLabelListAttribute(directProtoSrcs), 205 } 206 attrs.Deps.Append(bazel.MakeLabelListAttribute(protoLibraries)) 207 208 for axis, configToProps := range m.GetArchVariantProperties(ctx, &ProtoProperties{}) { 209 for _, rawProps := range configToProps { 210 var props *ProtoProperties 211 var ok bool 212 if props, ok = rawProps.(*ProtoProperties); !ok { 213 ctx.ModuleErrorf("Could not cast ProtoProperties to expected type") 214 } 215 if axis == bazel.NoConfigAxis { 216 info.Type = props.Proto.Type 217 218 if !proptools.BoolDefault(props.Proto.Canonical_path_from_root, canonicalPathFromRootDefault) { 219 // an empty string indicates to strips the package path 220 path := "" 221 attrs.Strip_import_prefix = &path 222 } 223 224 for _, dir := range props.Proto.Include_dirs { 225 if dep, ok := includeDirsToProtoDeps[dir]; ok { 226 attrs.Deps.Add(bazel.MakeLabelAttribute(dep)) 227 } else { 228 ctx.PropertyErrorf("Could not find the proto_library target for include dir", dir) 229 } 230 } 231 } else if props.Proto.Type != info.Type && props.Proto.Type != nil { 232 ctx.ModuleErrorf("Cannot handle arch-variant types for protos at this time.") 233 } 234 } 235 } 236 237 tags := ApexAvailableTags(ctx.Module()) 238 239 ctx.CreateBazelTargetModule( 240 bazel.BazelTargetModuleProperties{Rule_class: "proto_library"}, 241 CommonAttributes{Name: info.Name, Tags: tags}, 242 &attrs, 243 ) 244 245 protoLibraries.Add(&bazel.Label{ 246 Label: ":" + info.Name, 247 }) 248 } 249 250 info.Proto_libs = protoLibraries 251 252 return info, true 253} 254