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	"github.com/google/blueprint"
21	"github.com/google/blueprint/proptools"
22)
23
24// TODO(ccross): protos are often used to communicate between multiple modules.  If the only
25// way to convert a proto to source is to reference it as a source file, and external modules cannot
26// reference source files in other modules, then every module that owns a proto file will need to
27// export a library for every type of external user (lite vs. full, c vs. c++ vs. java).  It would
28// be better to support a proto module type that exported a proto file along with some include dirs,
29// and then external modules could depend on the proto module but use their own settings to
30// generate the source.
31
32type ProtoFlags struct {
33	Flags                 []string
34	CanonicalPathFromRoot bool
35	Dir                   ModuleGenPath
36	SubDir                ModuleGenPath
37	OutTypeFlag           string
38	OutParams             []string
39	Deps                  Paths
40}
41
42type protoDependencyTag struct {
43	blueprint.BaseDependencyTag
44	name string
45}
46
47var ProtoPluginDepTag = protoDependencyTag{name: "plugin"}
48
49func ProtoDeps(ctx BottomUpMutatorContext, p *ProtoProperties) {
50	if String(p.Proto.Plugin) != "" && String(p.Proto.Type) != "" {
51		ctx.ModuleErrorf("only one of proto.type and proto.plugin can be specified.")
52	}
53
54	if plugin := String(p.Proto.Plugin); plugin != "" {
55		ctx.AddFarVariationDependencies(ctx.Config().BuildOSTarget.Variations(),
56			ProtoPluginDepTag, "protoc-gen-"+plugin)
57	}
58}
59
60func GetProtoFlags(ctx ModuleContext, p *ProtoProperties) ProtoFlags {
61	var flags []string
62	var deps Paths
63
64	if len(p.Proto.Local_include_dirs) > 0 {
65		localProtoIncludeDirs := PathsForModuleSrc(ctx, p.Proto.Local_include_dirs)
66		flags = append(flags, JoinWithPrefix(localProtoIncludeDirs.Strings(), "-I"))
67	}
68	if len(p.Proto.Include_dirs) > 0 {
69		rootProtoIncludeDirs := PathsForSource(ctx, p.Proto.Include_dirs)
70		flags = append(flags, JoinWithPrefix(rootProtoIncludeDirs.Strings(), "-I"))
71	}
72
73	ctx.VisitDirectDepsWithTag(ProtoPluginDepTag, func(dep Module) {
74		if hostTool, ok := dep.(HostToolProvider); !ok || !hostTool.HostToolPath().Valid() {
75			ctx.PropertyErrorf("proto.plugin", "module %q is not a host tool provider",
76				ctx.OtherModuleName(dep))
77		} else {
78			plugin := String(p.Proto.Plugin)
79			deps = append(deps, hostTool.HostToolPath().Path())
80			flags = append(flags, "--plugin=protoc-gen-"+plugin+"="+hostTool.HostToolPath().String())
81		}
82	})
83
84	var protoOutFlag string
85	if plugin := String(p.Proto.Plugin); plugin != "" {
86		protoOutFlag = "--" + plugin + "_out"
87	}
88
89	return ProtoFlags{
90		Flags:                 flags,
91		Deps:                  deps,
92		OutTypeFlag:           protoOutFlag,
93		CanonicalPathFromRoot: proptools.BoolDefault(p.Proto.Canonical_path_from_root, true),
94		Dir:                   PathForModuleGen(ctx, "proto"),
95		SubDir:                PathForModuleGen(ctx, "proto", ctx.ModuleDir()),
96	}
97}
98
99type ProtoProperties struct {
100	Proto struct {
101		// Proto generator type.  C++: full or lite.  Java: micro, nano, stream, or lite.
102		Type *string `android:"arch_variant"`
103
104		// Proto plugin to use as the generator.  Must be a cc_binary_host module.
105		Plugin *string `android:"arch_variant"`
106
107		// list of directories that will be added to the protoc include paths.
108		Include_dirs []string
109
110		// list of directories relative to the bp file that will
111		// be added to the protoc include paths.
112		Local_include_dirs []string
113
114		// whether to identify the proto files from the root of the
115		// source tree (the original method in Android, useful for
116		// android-specific protos), or relative from where they were
117		// specified (useful for external/third party protos).
118		//
119		// This defaults to true today, but is expected to default to
120		// false in the future.
121		Canonical_path_from_root *bool
122	} `android:"arch_variant"`
123}
124
125func ProtoRule(rule *RuleBuilder, protoFile Path, flags ProtoFlags, deps Paths,
126	outDir WritablePath, depFile WritablePath, outputs WritablePaths) {
127
128	var protoBase string
129	if flags.CanonicalPathFromRoot {
130		protoBase = "."
131	} else {
132		rel := protoFile.Rel()
133		protoBase = strings.TrimSuffix(protoFile.String(), rel)
134	}
135
136	rule.Command().
137		BuiltTool("aprotoc").
138		FlagWithArg(flags.OutTypeFlag+"=", strings.Join(flags.OutParams, ",")+":"+outDir.String()).
139		FlagWithDepFile("--dependency_out=", depFile).
140		FlagWithArg("-I ", protoBase).
141		Flags(flags.Flags).
142		Input(protoFile).
143		Implicits(deps).
144		ImplicitOutputs(outputs)
145
146	rule.Command().
147		BuiltTool("dep_fixer").Flag(depFile.String())
148}
149