blob: 25cecf41161cd113cd314cc3f2521d0855a84d8d [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 (
18 "strings"
19
Yu Liu2d136142022-08-18 14:46:13 -070020 "android/soong/bazel"
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
Colin Cross38f794e2017-09-07 10:53:07 -070030// 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
Colin Cross19878da2019-03-28 14:45:07 -070038type ProtoFlags struct {
39 Flags []string
40 CanonicalPathFromRoot bool
41 Dir ModuleGenPath
42 SubDir ModuleGenPath
43 OutTypeFlag string
44 OutParams []string
Colin Crossfe17f6f2019-03-28 19:30:56 -070045 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 != "" {
Colin Cross0f7d2ef2019-10-16 11:03:10 -070061 ctx.AddFarVariationDependencies(ctx.Config().BuildOSTarget.Variations(),
62 ProtoPluginDepTag, "protoc-gen-"+plugin)
Colin Crossfe17f6f2019-03-28 19:30:56 -070063 }
Colin Cross19878da2019-03-28 14:45:07 -070064}
Colin Crossa3b25002017-12-15 13:41:30 -080065
Colin Cross19878da2019-03-28 14:45:07 -070066func GetProtoFlags(ctx ModuleContext, p *ProtoProperties) ProtoFlags {
Colin Crossfe17f6f2019-03-28 19:30:56 -070067 var flags []string
68 var deps Paths
69
Colin Cross38f794e2017-09-07 10:53:07 -070070 if len(p.Proto.Local_include_dirs) > 0 {
71 localProtoIncludeDirs := PathsForModuleSrc(ctx, p.Proto.Local_include_dirs)
Colin Crossfe17f6f2019-03-28 19:30:56 -070072 flags = append(flags, JoinWithPrefix(localProtoIncludeDirs.Strings(), "-I"))
Colin Cross38f794e2017-09-07 10:53:07 -070073 }
74 if len(p.Proto.Include_dirs) > 0 {
75 rootProtoIncludeDirs := PathsForSource(ctx, p.Proto.Include_dirs)
Colin Crossfe17f6f2019-03-28 19:30:56 -070076 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"
Colin Cross38f794e2017-09-07 10:53:07 -070093 }
94
Colin Cross19878da2019-03-28 14:45:07 -070095 return ProtoFlags{
Colin Crossfe17f6f2019-03-28 19:30:56 -070096 Flags: flags,
97 Deps: deps,
98 OutTypeFlag: protoOutFlag,
Liz Kammer12615db2021-09-28 09:19:17 -040099 CanonicalPathFromRoot: proptools.BoolDefault(p.Proto.Canonical_path_from_root, canonicalPathFromRootDefault),
Colin Cross19878da2019-03-28 14:45:07 -0700100 Dir: PathForModuleGen(ctx, "proto"),
101 SubDir: PathForModuleGen(ctx, "proto", ctx.ModuleDir()),
Dan Willemsenab9f4262018-02-14 13:58:34 -0800102 }
Colin Cross38f794e2017-09-07 10:53:07 -0700103}
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
Colin Crossfe17f6f2019-03-28 19:30:56 -0700110 // Proto plugin to use as the generator. Must be a cc_binary_host module.
111 Plugin *string `android:"arch_variant"`
112
Colin Cross38f794e2017-09-07 10:53:07 -0700113 // 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
Dan Willemsenab9f4262018-02-14 13:58:34 -0800119
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
Colin Cross38f794e2017-09-07 10:53:07 -0700128 } `android:"arch_variant"`
129}
Colin Cross19878da2019-03-28 14:45:07 -0700130
Colin Crossf1a035e2020-11-16 17:32:30 -0800131func ProtoRule(rule *RuleBuilder, protoFile Path, flags ProtoFlags, deps Paths,
Colin Cross19878da2019-03-28 14:45:07 -0700132 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().
Colin Crossf1a035e2020-11-16 17:32:30 -0800143 BuiltTool("aprotoc").
Colin Cross19878da2019-03-28 14:45:07 -0700144 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().
Colin Crossf1a035e2020-11-16 17:32:30 -0800153 BuiltTool("dep_fixer").Flag(depFile.String())
Colin Cross19878da2019-03-28 14:45:07 -0700154}
Liz Kammer12615db2021-09-28 09:19:17 -0400155
156// Bp2buildProtoInfo contains information necessary to pass on to language specific conversion.
157type Bp2buildProtoInfo struct {
158 Type *string
159 Name string
160}
161
162type protoAttrs struct {
163 Srcs bazel.LabelListAttribute
164 Strip_import_prefix *string
Yu Liu2d136142022-08-18 14:46:13 -0700165 Deps bazel.LabelListAttribute
166}
167
168// For each package in the include_dirs property a proto_library target should
169// be added to the BUILD file in that package and a mapping should be added here
170var includeDirsToProtoDeps = map[string]string{
171 "external/protobuf/src": "//external/protobuf:libprotobuf-proto",
Liz Kammer12615db2021-09-28 09:19:17 -0400172}
173
174// Bp2buildProtoProperties converts proto properties, creating a proto_library and returning the
175// information necessary for language-specific handling.
Sam Delmericoc7681022022-02-04 21:01:20 +0000176func Bp2buildProtoProperties(ctx Bp2buildMutatorContext, m *ModuleBase, srcs bazel.LabelListAttribute) (Bp2buildProtoInfo, bool) {
Liz Kammer12615db2021-09-28 09:19:17 -0400177 var info Bp2buildProtoInfo
178 if srcs.IsEmpty() {
179 return info, false
180 }
Liz Kammer12615db2021-09-28 09:19:17 -0400181
182 info.Name = m.Name() + "_proto"
183 attrs := protoAttrs{
184 Srcs: srcs,
185 }
186
187 for axis, configToProps := range m.GetArchVariantProperties(ctx, &ProtoProperties{}) {
188 for _, rawProps := range configToProps {
189 var props *ProtoProperties
190 var ok bool
191 if props, ok = rawProps.(*ProtoProperties); !ok {
192 ctx.ModuleErrorf("Could not cast ProtoProperties to expected type")
193 }
194 if axis == bazel.NoConfigAxis {
195 info.Type = props.Proto.Type
196
Liz Kammer7756c8f2022-02-14 20:49:15 -0500197 if !proptools.BoolDefault(props.Proto.Canonical_path_from_root, canonicalPathFromRootDefault) {
Liz Kammer12615db2021-09-28 09:19:17 -0400198 // an empty string indicates to strips the package path
199 path := ""
200 attrs.Strip_import_prefix = &path
201 }
Yu Liu2d136142022-08-18 14:46:13 -0700202
203 for _, dir := range props.Proto.Include_dirs {
204 if dep, ok := includeDirsToProtoDeps[dir]; ok {
205 attrs.Deps.Add(bazel.MakeLabelAttribute(dep))
206 } else {
207 ctx.PropertyErrorf("Could not find the proto_library target for include dir", dir)
208 }
209 }
Liz Kammer12615db2021-09-28 09:19:17 -0400210 } else if props.Proto.Type != info.Type && props.Proto.Type != nil {
211 ctx.ModuleErrorf("Cannot handle arch-variant types for protos at this time.")
212 }
213 }
214 }
215
216 ctx.CreateBazelTargetModule(
217 bazel.BazelTargetModuleProperties{Rule_class: "proto_library"},
218 CommonAttributes{Name: info.Name},
219 &attrs)
220
221 return info, true
222}