blob: c3759f8ad5e89bd21b9bb48199a4c7b12cf0f2e7 [file] [log] [blame]
Colin Cross38f794e2017-09-07 10:53:07 -07001// 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
Colin Cross19878da2019-03-28 14:45:07 -070017import (
Liz Kammer12615db2021-09-28 09:19:17 -040018 "android/soong/bazel"
Sam Delmericoc7681022022-02-04 21:01:20 +000019 "regexp"
Colin Cross19878da2019-03-28 14:45:07 -070020 "strings"
21
Colin Crossfe17f6f2019-03-28 19:30:56 -070022 "github.com/google/blueprint"
Colin Cross19878da2019-03-28 14:45:07 -070023 "github.com/google/blueprint/proptools"
24)
25
Liz Kammer12615db2021-09-28 09:19:17 -040026const (
27 canonicalPathFromRootDefault = true
28)
29
Sam Delmericoc7681022022-02-04 21:01:20 +000030var (
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
Colin Cross38f794e2017-09-07 10:53:07 -070038// 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
Colin Cross19878da2019-03-28 14:45:07 -070046type ProtoFlags struct {
47 Flags []string
48 CanonicalPathFromRoot bool
49 Dir ModuleGenPath
50 SubDir ModuleGenPath
51 OutTypeFlag string
52 OutParams []string
Colin Crossfe17f6f2019-03-28 19:30:56 -070053 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 != "" {
Colin Cross0f7d2ef2019-10-16 11:03:10 -070069 ctx.AddFarVariationDependencies(ctx.Config().BuildOSTarget.Variations(),
70 ProtoPluginDepTag, "protoc-gen-"+plugin)
Colin Crossfe17f6f2019-03-28 19:30:56 -070071 }
Colin Cross19878da2019-03-28 14:45:07 -070072}
Colin Crossa3b25002017-12-15 13:41:30 -080073
Colin Cross19878da2019-03-28 14:45:07 -070074func GetProtoFlags(ctx ModuleContext, p *ProtoProperties) ProtoFlags {
Colin Crossfe17f6f2019-03-28 19:30:56 -070075 var flags []string
76 var deps Paths
77
Colin Cross38f794e2017-09-07 10:53:07 -070078 if len(p.Proto.Local_include_dirs) > 0 {
79 localProtoIncludeDirs := PathsForModuleSrc(ctx, p.Proto.Local_include_dirs)
Colin Crossfe17f6f2019-03-28 19:30:56 -070080 flags = append(flags, JoinWithPrefix(localProtoIncludeDirs.Strings(), "-I"))
Colin Cross38f794e2017-09-07 10:53:07 -070081 }
82 if len(p.Proto.Include_dirs) > 0 {
83 rootProtoIncludeDirs := PathsForSource(ctx, p.Proto.Include_dirs)
Colin Crossfe17f6f2019-03-28 19:30:56 -070084 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"
Colin Cross38f794e2017-09-07 10:53:07 -0700101 }
102
Colin Cross19878da2019-03-28 14:45:07 -0700103 return ProtoFlags{
Colin Crossfe17f6f2019-03-28 19:30:56 -0700104 Flags: flags,
105 Deps: deps,
106 OutTypeFlag: protoOutFlag,
Liz Kammer12615db2021-09-28 09:19:17 -0400107 CanonicalPathFromRoot: proptools.BoolDefault(p.Proto.Canonical_path_from_root, canonicalPathFromRootDefault),
Colin Cross19878da2019-03-28 14:45:07 -0700108 Dir: PathForModuleGen(ctx, "proto"),
109 SubDir: PathForModuleGen(ctx, "proto", ctx.ModuleDir()),
Dan Willemsenab9f4262018-02-14 13:58:34 -0800110 }
Colin Cross38f794e2017-09-07 10:53:07 -0700111}
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
Colin Crossfe17f6f2019-03-28 19:30:56 -0700118 // Proto plugin to use as the generator. Must be a cc_binary_host module.
119 Plugin *string `android:"arch_variant"`
120
Colin Cross38f794e2017-09-07 10:53:07 -0700121 // 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
Dan Willemsenab9f4262018-02-14 13:58:34 -0800127
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
Colin Cross38f794e2017-09-07 10:53:07 -0700136 } `android:"arch_variant"`
137}
Colin Cross19878da2019-03-28 14:45:07 -0700138
Colin Crossf1a035e2020-11-16 17:32:30 -0800139func ProtoRule(rule *RuleBuilder, protoFile Path, flags ProtoFlags, deps Paths,
Colin Cross19878da2019-03-28 14:45:07 -0700140 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().
Colin Crossf1a035e2020-11-16 17:32:30 -0800151 BuiltTool("aprotoc").
Colin Cross19878da2019-03-28 14:45:07 -0700152 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().
Colin Crossf1a035e2020-11-16 17:32:30 -0800161 BuiltTool("dep_fixer").Flag(depFile.String())
Colin Cross19878da2019-03-28 14:45:07 -0700162}
Liz Kammer12615db2021-09-28 09:19:17 -0400163
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.
Sam Delmericoc7681022022-02-04 21:01:20 +0000177func Bp2buildProtoProperties(ctx Bp2buildMutatorContext, m *ModuleBase, srcs bazel.LabelListAttribute) (Bp2buildProtoInfo, bool) {
Liz Kammer12615db2021-09-28 09:19:17 -0400178 var info Bp2buildProtoInfo
179 if srcs.IsEmpty() {
180 return info, false
181 }
Liz Kammer12615db2021-09-28 09:19:17 -0400182
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
Liz Kammer7756c8f2022-02-14 20:49:15 -0500198 if !proptools.BoolDefault(props.Proto.Canonical_path_from_root, canonicalPathFromRootDefault) {
Liz Kammer12615db2021-09-28 09:19:17 -0400199 // 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}
Sam Delmericoc7681022022-02-04 21:01:20 +0000216
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}