• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1// Copyright 2015 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 genrule
16
17import (
18	"fmt"
19	"strings"
20
21	"github.com/google/blueprint"
22
23	"android/soong/android"
24)
25
26func init() {
27	android.RegisterModuleType("gensrcs", GenSrcsFactory)
28	android.RegisterModuleType("genrule", GenRuleFactory)
29}
30
31var (
32	pctx = android.NewPackageContext("android/soong/genrule")
33)
34
35type SourceFileGenerator interface {
36	GeneratedSourceFiles() android.Paths
37	GeneratedHeaderDirs() android.Paths
38}
39
40type HostToolProvider interface {
41	HostToolPath() android.OptionalPath
42}
43
44type generatorProperties struct {
45	// command to run on one or more input files.  Available variables for substitution:
46	// $(location): the path to the first entry in tools or tool_files
47	// $(location <label>): the path to the tool or tool_file with name <label>
48	// $(in): one or more input files
49	// $(out): a single output file
50	// $(deps): a file to which dependencies will be written, if the depfile property is set to true
51	// $(genDir): the sandbox directory for this tool; contains $(out)
52	// $$: a literal $
53	//
54	// DO NOT directly reference paths to files in the source tree, or the
55	// command will be missing proper dependencies to re-run if the files
56	// change.
57	Cmd string
58
59	// Enable reading a file containing dependencies in gcc format after the command completes
60	Depfile bool
61
62	// name of the modules (if any) that produces the host executable.   Leave empty for
63	// prebuilts or scripts that do not need a module to build them.
64	Tools []string
65
66	// Local file that is used as the tool
67	Tool_files []string
68
69	// List of directories to export generated headers from
70	Export_include_dirs []string
71
72	// list of input files
73	Srcs []string
74}
75
76type generator struct {
77	android.ModuleBase
78
79	properties generatorProperties
80
81	tasks taskFunc
82
83	deps android.Paths
84	rule blueprint.Rule
85
86	exportedIncludeDirs android.Paths
87
88	outputFiles android.Paths
89}
90
91type taskFunc func(ctx android.ModuleContext, srcFiles android.Paths) []generateTask
92
93type generateTask struct {
94	in  android.Paths
95	out android.WritablePaths
96}
97
98func (g *generator) GeneratedSourceFiles() android.Paths {
99	return g.outputFiles
100}
101
102func (g *generator) Srcs() android.Paths {
103	return g.outputFiles
104}
105
106func (g *generator) GeneratedHeaderDirs() android.Paths {
107	return g.exportedIncludeDirs
108}
109
110func (g *generator) DepsMutator(ctx android.BottomUpMutatorContext) {
111	android.ExtractSourcesDeps(ctx, g.properties.Srcs)
112	if g, ok := ctx.Module().(*generator); ok {
113		if len(g.properties.Tools) > 0 {
114			ctx.AddFarVariationDependencies([]blueprint.Variation{
115				{"arch", ctx.AConfig().BuildOsVariant},
116			}, nil, g.properties.Tools...)
117		}
118	}
119}
120
121func (g *generator) GenerateAndroidBuildActions(ctx android.ModuleContext) {
122	if len(g.properties.Tools) == 0 && len(g.properties.Tool_files) == 0 {
123		ctx.ModuleErrorf("at least one `tools` or `tool_files` is required")
124		return
125	}
126
127	if len(g.properties.Export_include_dirs) > 0 {
128		for _, dir := range g.properties.Export_include_dirs {
129			g.exportedIncludeDirs = append(g.exportedIncludeDirs,
130				android.PathForModuleGen(ctx, ctx.ModuleDir(), dir))
131		}
132	} else {
133		g.exportedIncludeDirs = append(g.exportedIncludeDirs, android.PathForModuleGen(ctx, ""))
134	}
135
136	tools := map[string]android.Path{}
137
138	if len(g.properties.Tools) > 0 {
139		ctx.VisitDirectDeps(func(module blueprint.Module) {
140			if t, ok := module.(HostToolProvider); ok {
141				p := t.HostToolPath()
142				if p.Valid() {
143					g.deps = append(g.deps, p.Path())
144					tool := ctx.OtherModuleName(module)
145					if _, exists := tools[tool]; !exists {
146						tools[tool] = p.Path()
147					} else {
148						ctx.ModuleErrorf("multiple tools for %q, %q and %q", tool, tools[tool], p.Path().String())
149					}
150				} else {
151					ctx.ModuleErrorf("host tool %q missing output file", ctx.OtherModuleName(module))
152				}
153			}
154		})
155	}
156
157	for _, tool := range g.properties.Tool_files {
158		toolPath := android.PathForModuleSrc(ctx, tool)
159		g.deps = append(g.deps, toolPath)
160		if _, exists := tools[tool]; !exists {
161			tools[tool] = toolPath
162		} else {
163			ctx.ModuleErrorf("multiple tools for %q, %q and %q", tool, tools[tool], toolPath.String())
164		}
165	}
166
167	cmd, err := android.Expand(g.properties.Cmd, func(name string) (string, error) {
168		switch name {
169		case "location":
170			if len(g.properties.Tools) > 0 {
171				return tools[g.properties.Tools[0]].String(), nil
172			} else {
173				return tools[g.properties.Tool_files[0]].String(), nil
174			}
175		case "in":
176			return "${in}", nil
177		case "out":
178			return "${out}", nil
179		case "depfile":
180			if !g.properties.Depfile {
181				return "", fmt.Errorf("$(depfile) used without depfile property")
182			}
183			return "${depfile}", nil
184		case "genDir":
185			return android.PathForModuleGen(ctx, "").String(), nil
186		default:
187			if strings.HasPrefix(name, "location ") {
188				label := strings.TrimSpace(strings.TrimPrefix(name, "location "))
189				if tool, ok := tools[label]; ok {
190					return tool.String(), nil
191				} else {
192					return "", fmt.Errorf("unknown location label %q", label)
193				}
194			}
195			return "", fmt.Errorf("unknown variable '$(%s)'", name)
196		}
197	})
198
199	if err != nil {
200		ctx.PropertyErrorf("cmd", "%s", err.Error())
201	}
202
203	ruleParams := blueprint.RuleParams{
204		Command: cmd,
205	}
206	var args []string
207	if g.properties.Depfile {
208		ruleParams.Deps = blueprint.DepsGCC
209		args = append(args, "depfile")
210	}
211	g.rule = ctx.Rule(pctx, "generator", ruleParams, args...)
212
213	srcFiles := ctx.ExpandSources(g.properties.Srcs, nil)
214	for _, task := range g.tasks(ctx, srcFiles) {
215		g.generateSourceFile(ctx, task)
216	}
217}
218
219func (g *generator) generateSourceFile(ctx android.ModuleContext, task generateTask) {
220	params := android.ModuleBuildParams{
221		Rule:      g.rule,
222		Outputs:   task.out,
223		Inputs:    task.in,
224		Implicits: g.deps,
225	}
226	if g.properties.Depfile {
227		depfile := android.GenPathWithExt(ctx, "", task.out[0], task.out[0].Ext()+".d")
228		params.Depfile = depfile
229	}
230	ctx.ModuleBuild(pctx, params)
231
232	for _, outputFile := range task.out {
233		g.outputFiles = append(g.outputFiles, outputFile)
234	}
235}
236
237func generatorFactory(tasks taskFunc, props ...interface{}) (blueprint.Module, []interface{}) {
238	module := &generator{
239		tasks: tasks,
240	}
241
242	props = append(props, &module.properties)
243
244	return android.InitAndroidModule(module, props...)
245}
246
247func GenSrcsFactory() (blueprint.Module, []interface{}) {
248	properties := &genSrcsProperties{}
249
250	tasks := func(ctx android.ModuleContext, srcFiles android.Paths) []generateTask {
251		tasks := make([]generateTask, 0, len(srcFiles))
252		for _, in := range srcFiles {
253			tasks = append(tasks, generateTask{
254				in:  android.Paths{in},
255				out: android.WritablePaths{android.GenPathWithExt(ctx, "", in, properties.Output_extension)},
256			})
257		}
258		return tasks
259	}
260
261	return generatorFactory(tasks, properties)
262}
263
264type genSrcsProperties struct {
265	// extension that will be substituted for each output file
266	Output_extension string
267}
268
269func GenRuleFactory() (blueprint.Module, []interface{}) {
270	properties := &genRuleProperties{}
271
272	tasks := func(ctx android.ModuleContext, srcFiles android.Paths) []generateTask {
273		outs := make(android.WritablePaths, len(properties.Out))
274		for i, out := range properties.Out {
275			outs[i] = android.PathForModuleGen(ctx, out)
276		}
277		return []generateTask{
278			{
279				in:  srcFiles,
280				out: outs,
281			},
282		}
283	}
284
285	return generatorFactory(tasks, properties)
286}
287
288type genRuleProperties struct {
289	// names of the output files that will be generated
290	Out []string
291}
292