• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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