• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1// 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
143// 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
150// 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 {
164			ctx.AddFarVariationDependencies([]blueprint.Variation{
165				{"arch", ctx.AConfig().BuildOsVariant},
166			}, hostToolDepTag, g.properties.Tools...)
167		}
168	}
169}
170
171// GenerateAndroidBuildActions implements the android.Module
172// GenerateAndroidBuildActions method, which generates all the rules and builds
173// commands used by this module instance.
174func (g *waylandGenModule) GenerateAndroidBuildActions(ctx android.ModuleContext) {
175	if len(g.properties.Tools) == 0 && len(g.properties.Tool_files) == 0 {
176		ctx.ModuleErrorf("at least one `tools` or `tool_files` is required")
177		return
178	}
179
180	// Prepare the list of tools that were defined for codegen purposes.
181	tools, implicitDeps := g.prepareTools(ctx)
182
183	if ctx.Failed() {
184		return
185	}
186
187	// Emit the rule for generating for processing each source file
188	g.emitRule(ctx, tools)
189
190	if ctx.Failed() {
191		return
192	}
193
194	generatedFilenamePrefix := proptools.String(g.properties.Prefix)
195	generatedFilenameSuffix := proptools.String(g.properties.Suffix)
196	for _, src := range ctx.ExpandSources(g.properties.Srcs, nil) {
197		out := g.generateOutputPath(ctx, src, generatedFilenamePrefix, generatedFilenameSuffix)
198		if out == nil {
199			continue
200		}
201
202		g.emitBuild(ctx, src, out, implicitDeps)
203		g.outputFiles = append(g.outputFiles, out)
204	}
205
206	g.exportedIncludeDirs = append(g.exportedIncludeDirs, android.PathForModuleGen(ctx))
207}
208
209// genrateOutputPath takes an source path, a prefix, and a suffix, and use them
210// to generate and return corresponding an output file path.
211func (g *waylandGenModule) generateOutputPath(ctx android.ModuleContext, src android.Path, prefix string, suffix string) android.WritablePath {
212	// Construct a new filename by adding the requested prefix and suffix for this
213	// code generator instance. If the input file name is "wayland.xml", and the
214	// properties specify a prefix of "test-" and a suffix of "-client.cpp", we
215	// will end up with a fulename of "test-wayland-client.cpp"
216	protocolFilename, protocolExt := splitExt(src.Base())
217	if protocolExt != ".xml" {
218		ctx.ModuleErrorf("Source file %q does not end with .xml", src)
219		return nil
220	}
221	return android.PathForModuleGen(ctx, prefix+protocolFilename+suffix)
222}
223
224// emitRule is an internal function to emit each Ninja rule.
225func (g *waylandGenModule) emitRule(ctx android.ModuleContext, tools map[string]android.Path) {
226	// Get the command to run to process each protocol file. Since everything
227	// should be templated, we generate a Ninja rule that uses the command,
228	// and invoke it from each Ninja build command we emit.
229	g.rule = ctx.Rule(pctx, "generator", blueprint.RuleParams{
230		Command: g.expandCmd(ctx, tools),
231	})
232}
233
234// emitBuild is an internal function to emit each Build command.
235func (g *waylandGenModule) emitBuild(ctx android.ModuleContext, src android.Path, out android.WritablePath, implicitDeps android.Paths) android.Path {
236	ctx.Build(pctx, android.BuildParams{
237		Rule:        g.rule,
238		Description: "generate " + out.Base(),
239		Output:      out,
240		Inputs:      android.Paths{src},
241		Implicits:   implicitDeps,
242	})
243
244	return out
245}
246
247// prepareTools is an internal function to prepare a list of tools.
248func (g *waylandGenModule) prepareTools(ctx android.ModuleContext) (tools map[string]android.Path, implicitDeps android.Paths) {
249	tools = map[string]android.Path{}
250
251	// This was extracted and slightly simplifed from equivalent code in
252	// genrule.go.
253
254	// For each entry in "tool", walk the dependency graph to get more
255	// information about it.
256	if len(g.properties.Tools) > 0 {
257		ctx.VisitDirectDepsBlueprint(func(module blueprint.Module) {
258			switch ctx.OtherModuleDependencyTag(module) {
259			case android.SourceDepTag:
260				// Nothing to do
261			case hostToolDepTag:
262				tool := ctx.OtherModuleName(module)
263				var path android.OptionalPath
264
265				if t, ok := module.(genrule.HostToolProvider); ok {
266					if !t.(android.Module).Enabled() {
267						if ctx.AConfig().AllowMissingDependencies() {
268							ctx.AddMissingDependencies([]string{tool})
269						} else {
270							ctx.ModuleErrorf("depends on disabled module %q", tool)
271						}
272						break
273					}
274					path = t.HostToolPath()
275				} else {
276					ctx.ModuleErrorf("%q is not a host tool provider", tool)
277					break
278				}
279
280				if path.Valid() {
281					implicitDeps = append(implicitDeps, path.Path())
282					if _, exists := tools[tool]; !exists {
283						tools[tool] = path.Path()
284					} else {
285						ctx.ModuleErrorf("multiple tools for %q, %q and %q", tool, tools[tool], path.Path().String())
286					}
287				} else {
288					ctx.ModuleErrorf("host tool %q missing output file", tool)
289				}
290			default:
291				ctx.ModuleErrorf("unknown dependency on %q", ctx.OtherModuleName(module))
292			}
293		})
294	}
295
296	// Get more information about each entry in "tool_files".
297	for _, tool := range g.properties.Tool_files {
298		toolPath := android.PathForModuleSrc(ctx, tool)
299		implicitDeps = append(implicitDeps, toolPath)
300		if _, exists := tools[tool]; !exists {
301			tools[tool] = toolPath
302		} else {
303			ctx.ModuleErrorf("multiple tools for %q, %q and %q", tool, tools[tool], toolPath.String())
304		}
305	}
306	return
307}
308
309// expandCmd is an internal function to do some expansion and any additional
310// wrapping of the generator command line. Returns the command line to use and
311// an error value.
312func (g *waylandGenModule) expandCmd(ctx android.ModuleContext, tools map[string]android.Path) (cmd string) {
313	cmd, err := android.Expand(proptools.String(g.properties.Cmd), func(name string) (string, error) {
314		switch name {
315		case "in":
316			return "$in", nil
317		case "out":
318			// We need to use the sandbox out path instead
319			//return "$sandboxOut", nil
320			return "$out", nil
321		case "location":
322			if len(g.properties.Tools) > 0 {
323				return tools[g.properties.Tools[0]].String(), nil
324			} else {
325				return tools[g.properties.Tool_files[0]].String(), nil
326			}
327		default:
328			if strings.HasPrefix(name, "location ") {
329				label := strings.TrimSpace(strings.TrimPrefix(name, "location "))
330				if tool, ok := tools[label]; ok {
331					return tool.String(), nil
332				} else {
333					return "", fmt.Errorf("unknown location label %q", label)
334				}
335			}
336			return "", fmt.Errorf("unknown variable '$(%s)'", name)
337		}
338	})
339	if err != nil {
340		ctx.PropertyErrorf("cmd", "%s", err.Error())
341	}
342	return
343}
344
345// waylandCodegenModuleFactory creates an extension module instance.
346func waylandCodegenModuleFactory() android.Module {
347	m := &waylandGenModule{}
348	m.AddProperties(&m.properties)
349	android.InitAndroidModule(m)
350	return m
351}
352
353// splitExt splits a base filename into (filename, ext) components, such that
354// input == filename + ext
355func splitExt(input string) (filename string, ext string) {
356	// There is no filepath.SplitExt() or equivalent.
357	dot := strings.LastIndex(input, ".")
358	if dot != -1 {
359		ext = input[dot:]
360		filename = input[:dot]
361	} else {
362		ext = ""
363		filename = input
364	}
365	return
366}
367