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