• 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 `android:"path"`
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 implementation duplicates the one from genrule.go, where gensrcs is
160	// defined.
161	if g, ok := ctx.Module().(*waylandGenModule); ok {
162		if len(g.properties.Tools) > 0 {
163			ctx.AddFarVariationDependencies(ctx.Config().BuildOSTarget.Variations(),
164				hostToolDepTag, g.properties.Tools...)
165		}
166	}
167}
168
169// GenerateAndroidBuildActions implements the android.Module
170// GenerateAndroidBuildActions method, which generates all the rules and builds
171// commands used by this module instance.
172func (g *waylandGenModule) GenerateAndroidBuildActions(ctx android.ModuleContext) {
173	if len(g.properties.Tools) == 0 && len(g.properties.Tool_files) == 0 {
174		ctx.ModuleErrorf("at least one `tools` or `tool_files` is required")
175		return
176	}
177
178	// Prepare the list of tools that were defined for codegen purposes.
179	tools, implicitDeps := g.prepareTools(ctx)
180
181	if ctx.Failed() {
182		return
183	}
184
185	// Emit the rule for generating for processing each source file
186	g.emitRule(ctx, tools)
187
188	if ctx.Failed() {
189		return
190	}
191
192	generatedFilenamePrefix := proptools.String(g.properties.Prefix)
193	generatedFilenameSuffix := proptools.String(g.properties.Suffix)
194	for _, src := range android.PathsForModuleSrc(ctx, g.properties.Srcs) {
195		out := g.generateOutputPath(ctx, src, generatedFilenamePrefix, generatedFilenameSuffix)
196		if out == nil {
197			continue
198		}
199
200		g.emitBuild(ctx, src, out, implicitDeps)
201		g.outputFiles = append(g.outputFiles, out)
202	}
203
204	g.exportedIncludeDirs = append(g.exportedIncludeDirs, android.PathForModuleGen(ctx))
205}
206
207// genrateOutputPath takes an source path, a prefix, and a suffix, and use them
208// to generate and return corresponding an output file path.
209func (g *waylandGenModule) generateOutputPath(ctx android.ModuleContext, src android.Path, prefix string, suffix string) android.WritablePath {
210	// Construct a new filename by adding the requested prefix and suffix for this
211	// code generator instance. If the input file name is "wayland.xml", and the
212	// properties specify a prefix of "test-" and a suffix of "-client.cpp", we
213	// will end up with a fulename of "test-wayland-client.cpp"
214	protocolFilename, protocolExt := splitExt(src.Base())
215	if protocolExt != ".xml" {
216		ctx.ModuleErrorf("Source file %q does not end with .xml", src)
217		return nil
218	}
219	return android.PathForModuleGen(ctx, prefix+protocolFilename+suffix)
220}
221
222// emitRule is an internal function to emit each Ninja rule.
223func (g *waylandGenModule) emitRule(ctx android.ModuleContext, tools map[string]android.Path) {
224	// Get the command to run to process each protocol file. Since everything
225	// should be templated, we generate a Ninja rule that uses the command,
226	// and invoke it from each Ninja build command we emit.
227	g.rule = ctx.Rule(pctx, "generator", blueprint.RuleParams{
228		Command: g.expandCmd(ctx, tools),
229	})
230}
231
232// emitBuild is an internal function to emit each Build command.
233func (g *waylandGenModule) emitBuild(ctx android.ModuleContext, src android.Path, out android.WritablePath, implicitDeps android.Paths) android.Path {
234	ctx.Build(pctx, android.BuildParams{
235		Rule:        g.rule,
236		Description: "generate " + out.Base(),
237		Output:      out,
238		Inputs:      android.Paths{src},
239		Implicits:   implicitDeps,
240	})
241
242	return out
243}
244
245// prepareTools is an internal function to prepare a list of tools.
246func (g *waylandGenModule) prepareTools(ctx android.ModuleContext) (tools map[string]android.Path, implicitDeps android.Paths) {
247	tools = map[string]android.Path{}
248
249	// This was extracted and slightly simplifed from equivalent code in
250	// genrule.go.
251
252	// For each entry in "tool", walk the dependency graph to get more
253	// information about it.
254	if len(g.properties.Tools) > 0 {
255		ctx.VisitDirectDepsBlueprint(func(module blueprint.Module) {
256			tag := ctx.OtherModuleDependencyTag(module)
257			if android.IsSourceDepTagWithOutputTag(tag, "") {
258				// Nothing to do
259				return
260			}
261			switch tag {
262			case hostToolDepTag:
263				tool := ctx.OtherModuleName(module)
264				var path android.OptionalPath
265
266				if t, ok := module.(genrule.HostToolProvider); ok {
267					if !t.(android.Module).Enabled() {
268						if ctx.Config().AllowMissingDependencies() {
269							ctx.AddMissingDependencies([]string{tool})
270						} else {
271							ctx.ModuleErrorf("depends on disabled module %q", tool)
272						}
273						break
274					}
275					path = t.HostToolPath()
276				} else {
277					ctx.ModuleErrorf("%q is not a host tool provider", tool)
278					break
279				}
280
281				if path.Valid() {
282					implicitDeps = append(implicitDeps, path.Path())
283					if _, exists := tools[tool]; !exists {
284						tools[tool] = path.Path()
285					} else {
286						ctx.ModuleErrorf("multiple tools for %q, %q and %q", tool, tools[tool], path.Path().String())
287					}
288				} else {
289					ctx.ModuleErrorf("host tool %q missing output file", tool)
290				}
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}
366