blob: df414ed4daca38a5bbc51c037ff213cbc09d8ae8 [file] [log] [blame]
Lloyd Pique016680d2017-11-29 17:54:29 -08001// Copyright (C) 2017 The Android Open Source Project
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
15// ---------------------------------------------------------------------------
16
17// Package wayland_protcool defines extension modules for the Soong build system
18// to make it easier to generate code from a list of Wayland protocol files.
19//
20// The primary extension module is "wayland_protocol_codegen", which applies a
21// code generation tool to a list of source protocol files.
22//
23// Note that the code generation done here is similar to what is done by the
24// base Soong "gensrcs" module, but there are two functional differences:
25//
26// 1) The output filenames are computed from the input filenames, rather
27// than needing to be specified explicitly. An optional prefix as well
28// as a suffix can be added to the protocol filename (without extension).
29//
30// 2) Code generation is done for each file independently by emitting
31// multiple Ninja build commands, rather than one build command which
32// does it all.
33package wayland_protocol
34
35import (
36 "fmt"
37 "strings"
38
39 "github.com/google/blueprint"
40 "github.com/google/blueprint/proptools"
41
42 "android/soong/android"
43 "android/soong/genrule"
44)
45
46func init() {
47 // Register out extension module type name with Soong.
48 android.RegisterModuleType(
49 "wayland_protocol_codegen", waylandCodegenModuleFactory)
50}
51
52var (
53 // Create a context for build rule output from this package
54 pctx = android.NewPackageContext("android/soong/external/wayland-protocol")
55)
56
57type hostToolDependencyTag struct {
58 blueprint.BaseDependencyTag
59}
60
61var hostToolDepTag hostToolDependencyTag
62
63// waylandCodegenProperties defines the properties that will be read in from the
64// Android.bp file for each instantiation of the module.
65type waylandCodegenProperties struct {
66 // This string gives the command line template to run on each protocol file
67 // to wayland_protocol_codegen.
68 //
69 // The string can contain one or more "$" prefixed variable names for
70 // values that can vary. At a minimum you need to use ${location}, ${out}
71 // and ${in}
72 //
73 // $(location): the path to the first entry in tools or tool_files
74 // $(location <label>): the path to the tool or tool_file with name <label>
75 // $(in): A protocol file from srcs
76 // $(out): The constructed output filename from the protocol filename.
77 // $$: a literal $
78 Cmd *string
79
80 // The string to prepend to every protcol filename to generate the
81 // corresponding output filename. The empty string by default.
82 Prefix *string
83
84 // The suffix to append to every protocol filename to generate the
85 // corresponding output filename. The empty string by default.
86 Suffix *string
87
88 // The list of protocol files to process.
89 Srcs []string
90
91 // The names of any built host executables to use for code generation. Can
92 // be left empty if a local script is used instead (specified in
93 // tool_files).
94 Tools []string
95
96 // Local files that are used for code generation. Can be scripts to run, but
97 // should also include any other files that the code generation step should
98 // depend on that might be used by the code gen tool.
99 Tool_files []string
100}
101
102// waylandGenModule defines the Soong module for each instance.
103type waylandGenModule struct {
104 android.ModuleBase
105
106 // Store a copy of the parsed properties for easy reference.
107 properties waylandCodegenProperties
108
109 // Each module emits its own blueprint (Ninja) rule. Store a reference
110 // to the one created for this instance.
111 rule blueprint.Rule
112
113 // Each module exports one or more include directories. Store the paths here
114 // here for easy retrieval.
115 exportedIncludeDirs android.Paths
116
117 // Each module has a list of files it outputs, that can be used by other
118 // modules. Store the list of paths here for easy reference.
119 outputFiles android.Paths
120}
121
122// For the uninitiated, this is an idiom to check that a given object implements
123// an interface. In this case we want to be sure that waylandGenModule
124// implements genrule.SourceFileGenerator
125var _ genrule.SourceFileGenerator = (*waylandGenModule)(nil)
126
127// Check that we implement android.SourceFileProducer
128var _ android.SourceFileProducer = (*waylandGenModule)(nil)
129
130// GeneratedSourceFiles implements the genrule.SourceFileGenerator
131// GeneratedSourceFiles method to return the list of generated source files.
132func (g *waylandGenModule) GeneratedSourceFiles() android.Paths {
133 return g.outputFiles
134}
135
136// GeneratedHeaderDirs implements the genrule.SourceFileGenerator
137// GeneratedHeaderDirs method to return the list of exported include
138// directories.
139func (g *waylandGenModule) GeneratedHeaderDirs() android.Paths {
140 return g.exportedIncludeDirs
141}
142
Dan Willemsenc70fdb62018-02-21 19:12:21 -0800143// GeneratedDeps implements the genrule.SourceFileGenerator GeneratedDeps
144// method to return the list of files to be used as dependencies when using
145// GeneratedHeaderDirs.
146func (g *waylandGenModule) GeneratedDeps() android.Paths {
147 return g.outputFiles
148}
149
Lloyd Pique016680d2017-11-29 17:54:29 -0800150// Srcs implements the android.SourceFileProducer Srcs method to return the list
151// of source files.
152func (g *waylandGenModule) Srcs() android.Paths {
153 return g.outputFiles
154}
155
156// DepsMutator implements the android.Module DepsMutator method to apply a
157// mutator context to the build graph.
158func (g *waylandGenModule) DepsMutator(ctx android.BottomUpMutatorContext) {
159 // This implementatoin duplicates the one from genrule.go, where gensrcs is
160 // defined.
161 android.ExtractSourcesDeps(ctx, g.properties.Srcs)
162 if g, ok := ctx.Module().(*waylandGenModule); ok {
163 if len(g.properties.Tools) > 0 {
Colin Crossb19afd92019-10-16 11:11:00 -0700164 ctx.AddFarVariationDependencies(ctx.Config().BuildOSTarget.Variations(),
165 hostToolDepTag, g.properties.Tools...)
Lloyd Pique016680d2017-11-29 17:54:29 -0800166 }
167 }
168}
169
170// GenerateAndroidBuildActions implements the android.Module
171// GenerateAndroidBuildActions method, which generates all the rules and builds
172// commands used by this module instance.
173func (g *waylandGenModule) GenerateAndroidBuildActions(ctx android.ModuleContext) {
174 if len(g.properties.Tools) == 0 && len(g.properties.Tool_files) == 0 {
175 ctx.ModuleErrorf("at least one `tools` or `tool_files` is required")
176 return
177 }
178
179 // Prepare the list of tools that were defined for codegen purposes.
180 tools, implicitDeps := g.prepareTools(ctx)
181
182 if ctx.Failed() {
183 return
184 }
185
186 // Emit the rule for generating for processing each source file
187 g.emitRule(ctx, tools)
188
189 if ctx.Failed() {
190 return
191 }
192
193 generatedFilenamePrefix := proptools.String(g.properties.Prefix)
194 generatedFilenameSuffix := proptools.String(g.properties.Suffix)
195 for _, src := range ctx.ExpandSources(g.properties.Srcs, nil) {
196 out := g.generateOutputPath(ctx, src, generatedFilenamePrefix, generatedFilenameSuffix)
197 if out == nil {
198 continue
199 }
200
201 g.emitBuild(ctx, src, out, implicitDeps)
202 g.outputFiles = append(g.outputFiles, out)
203 }
204
205 g.exportedIncludeDirs = append(g.exportedIncludeDirs, android.PathForModuleGen(ctx))
206}
207
208// genrateOutputPath takes an source path, a prefix, and a suffix, and use them
209// to generate and return corresponding an output file path.
210func (g *waylandGenModule) generateOutputPath(ctx android.ModuleContext, src android.Path, prefix string, suffix string) android.WritablePath {
211 // Construct a new filename by adding the requested prefix and suffix for this
212 // code generator instance. If the input file name is "wayland.xml", and the
213 // properties specify a prefix of "test-" and a suffix of "-client.cpp", we
214 // will end up with a fulename of "test-wayland-client.cpp"
215 protocolFilename, protocolExt := splitExt(src.Base())
216 if protocolExt != ".xml" {
217 ctx.ModuleErrorf("Source file %q does not end with .xml", src)
218 return nil
219 }
220 return android.PathForModuleGen(ctx, prefix+protocolFilename+suffix)
221}
222
223// emitRule is an internal function to emit each Ninja rule.
224func (g *waylandGenModule) emitRule(ctx android.ModuleContext, tools map[string]android.Path) {
225 // Get the command to run to process each protocol file. Since everything
226 // should be templated, we generate a Ninja rule that uses the command,
227 // and invoke it from each Ninja build command we emit.
228 g.rule = ctx.Rule(pctx, "generator", blueprint.RuleParams{
229 Command: g.expandCmd(ctx, tools),
230 })
231}
232
233// emitBuild is an internal function to emit each Build command.
234func (g *waylandGenModule) emitBuild(ctx android.ModuleContext, src android.Path, out android.WritablePath, implicitDeps android.Paths) android.Path {
235 ctx.Build(pctx, android.BuildParams{
236 Rule: g.rule,
237 Description: "generate " + out.Base(),
238 Output: out,
239 Inputs: android.Paths{src},
240 Implicits: implicitDeps,
241 })
242
243 return out
244}
245
246// prepareTools is an internal function to prepare a list of tools.
247func (g *waylandGenModule) prepareTools(ctx android.ModuleContext) (tools map[string]android.Path, implicitDeps android.Paths) {
248 tools = map[string]android.Path{}
249
250 // This was extracted and slightly simplifed from equivalent code in
251 // genrule.go.
252
253 // For each entry in "tool", walk the dependency graph to get more
254 // information about it.
255 if len(g.properties.Tools) > 0 {
256 ctx.VisitDirectDepsBlueprint(func(module blueprint.Module) {
257 switch ctx.OtherModuleDependencyTag(module) {
258 case android.SourceDepTag:
259 // Nothing to do
260 case hostToolDepTag:
261 tool := ctx.OtherModuleName(module)
262 var path android.OptionalPath
263
264 if t, ok := module.(genrule.HostToolProvider); ok {
265 if !t.(android.Module).Enabled() {
Colin Crossb19afd92019-10-16 11:11:00 -0700266 if ctx.Config().AllowMissingDependencies() {
Lloyd Pique016680d2017-11-29 17:54:29 -0800267 ctx.AddMissingDependencies([]string{tool})
268 } else {
269 ctx.ModuleErrorf("depends on disabled module %q", tool)
270 }
271 break
272 }
273 path = t.HostToolPath()
274 } else {
275 ctx.ModuleErrorf("%q is not a host tool provider", tool)
276 break
277 }
278
279 if path.Valid() {
280 implicitDeps = append(implicitDeps, path.Path())
281 if _, exists := tools[tool]; !exists {
282 tools[tool] = path.Path()
283 } else {
284 ctx.ModuleErrorf("multiple tools for %q, %q and %q", tool, tools[tool], path.Path().String())
285 }
286 } else {
287 ctx.ModuleErrorf("host tool %q missing output file", tool)
288 }
289 default:
290 ctx.ModuleErrorf("unknown dependency on %q", ctx.OtherModuleName(module))
291 }
292 })
293 }
294
295 // Get more information about each entry in "tool_files".
296 for _, tool := range g.properties.Tool_files {
297 toolPath := android.PathForModuleSrc(ctx, tool)
298 implicitDeps = append(implicitDeps, toolPath)
299 if _, exists := tools[tool]; !exists {
300 tools[tool] = toolPath
301 } else {
302 ctx.ModuleErrorf("multiple tools for %q, %q and %q", tool, tools[tool], toolPath.String())
303 }
304 }
305 return
306}
307
308// expandCmd is an internal function to do some expansion and any additional
309// wrapping of the generator command line. Returns the command line to use and
310// an error value.
311func (g *waylandGenModule) expandCmd(ctx android.ModuleContext, tools map[string]android.Path) (cmd string) {
312 cmd, err := android.Expand(proptools.String(g.properties.Cmd), func(name string) (string, error) {
313 switch name {
314 case "in":
315 return "$in", nil
316 case "out":
317 // We need to use the sandbox out path instead
318 //return "$sandboxOut", nil
319 return "$out", nil
320 case "location":
321 if len(g.properties.Tools) > 0 {
322 return tools[g.properties.Tools[0]].String(), nil
323 } else {
324 return tools[g.properties.Tool_files[0]].String(), nil
325 }
326 default:
327 if strings.HasPrefix(name, "location ") {
328 label := strings.TrimSpace(strings.TrimPrefix(name, "location "))
329 if tool, ok := tools[label]; ok {
330 return tool.String(), nil
331 } else {
332 return "", fmt.Errorf("unknown location label %q", label)
333 }
334 }
335 return "", fmt.Errorf("unknown variable '$(%s)'", name)
336 }
337 })
338 if err != nil {
339 ctx.PropertyErrorf("cmd", "%s", err.Error())
340 }
341 return
342}
343
344// waylandCodegenModuleFactory creates an extension module instance.
345func waylandCodegenModuleFactory() android.Module {
346 m := &waylandGenModule{}
347 m.AddProperties(&m.properties)
348 android.InitAndroidModule(m)
349 return m
350}
351
352// splitExt splits a base filename into (filename, ext) components, such that
353// input == filename + ext
354func splitExt(input string) (filename string, ext string) {
355 // There is no filepath.SplitExt() or equivalent.
356 dot := strings.LastIndex(input, ".")
357 if dot != -1 {
358 ext = input[dot:]
359 filename = input[:dot]
360 } else {
361 ext = ""
362 filename = input
363 }
364 return
365}