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