blob: ff7c8e4a7cd66ca6d585e18c053437cc6aef8d77 [file] [log] [blame]
Dan Willemsen4b7d5de2016-01-12 23:20:28 -08001// Copyright 2016 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
Colin Cross635c3b02016-05-18 15:37:25 -070015package android
Dan Willemsen4b7d5de2016-01-12 23:20:28 -080016
17import (
18 "bytes"
19 "fmt"
Dan Albertf5415d72017-08-17 16:19:59 -070020 "strconv"
Colin Cross31656952018-05-24 16:11:20 -070021 "strings"
Dan Willemsen4b7d5de2016-01-12 23:20:28 -080022
Colin Cross65494b92019-02-07 14:25:51 -080023 "github.com/google/blueprint"
Colin Crossc3d87d32020-06-04 13:25:17 -070024 "github.com/google/blueprint/pathtools"
Dan Willemsen4b7d5de2016-01-12 23:20:28 -080025 "github.com/google/blueprint/proptools"
26)
27
Dan Albertf5415d72017-08-17 16:19:59 -070028func init() {
29 RegisterMakeVarsProvider(pctx, androidMakeVarsProvider)
30}
31
32func androidMakeVarsProvider(ctx MakeVarsContext) {
33 ctx.Strict("MIN_SUPPORTED_SDK_VERSION", strconv.Itoa(ctx.Config().MinSupportedSdkVersion()))
34}
35
Dan Willemsen4b7d5de2016-01-12 23:20:28 -080036///////////////////////////////////////////////////////////////////////////////
37// Interface for other packages to use to declare make variables
38type MakeVarsContext interface {
39 Config() Config
Dan Willemsen3fb1fae2018-03-12 15:30:26 -070040 DeviceConfig() DeviceConfig
Colin Cross65494b92019-02-07 14:25:51 -080041 AddNinjaFileDeps(deps ...string)
Colin Cross65494b92019-02-07 14:25:51 -080042
43 ModuleName(module blueprint.Module) string
44 ModuleDir(module blueprint.Module) string
45 ModuleSubDir(module blueprint.Module) string
46 ModuleType(module blueprint.Module) string
47 BlueprintFile(module blueprint.Module) string
48
49 ModuleErrorf(module blueprint.Module, format string, args ...interface{})
50 Errorf(format string, args ...interface{})
51 Failed() bool
52
53 VisitAllModules(visit func(Module))
54 VisitAllModulesIf(pred func(Module) bool, visit func(Module))
Dan Willemsen4b7d5de2016-01-12 23:20:28 -080055
56 // Verify the make variable matches the Soong version, fail the build
57 // if it does not. If the make variable is empty, just set it.
58 Strict(name, ninjaStr string)
59 // Check to see if the make variable matches the Soong version, warn if
60 // it does not. If the make variable is empty, just set it.
61 Check(name, ninjaStr string)
62
63 // These are equivalent to the above, but sort the make and soong
64 // variables before comparing them. They also show the unique entries
65 // in each list when displaying the difference, instead of the entire
66 // string.
67 StrictSorted(name, ninjaStr string)
68 CheckSorted(name, ninjaStr string)
Dan Willemsen558e5172016-05-19 16:58:46 -070069
70 // Evaluates a ninja string and returns the result. Used if more
71 // complicated modification needs to happen before giving it to Make.
72 Eval(ninjaStr string) (string, error)
73
74 // These are equivalent to Strict and Check, but do not attempt to
75 // evaluate the values before writing them to the Makefile. They can
76 // be used when all ninja variables have already been evaluated through
77 // Eval().
78 StrictRaw(name, value string)
79 CheckRaw(name, value string)
Colin Cross8177ad22019-11-04 10:27:48 -080080
81 // GlobWithDeps returns a list of files that match the specified pattern but do not match any
82 // of the patterns in excludes. It also adds efficient dependencies to rerun the primary
83 // builder whenever a file matching the pattern as added or removed, without rerunning if a
84 // file that does not match the pattern is added to a searched directory.
85 GlobWithDeps(pattern string, excludes []string) ([]string, error)
Colin Crossc3d87d32020-06-04 13:25:17 -070086
87 // Phony creates a phony rule in Make, which will allow additional DistForGoal
88 // dependencies to be added to it. Phony can be called on the same name multiple
89 // times to add additional dependencies.
90 Phony(names string, deps ...Path)
Colin Cross3cda0d82019-09-24 13:40:07 -070091
92 // DistForGoal creates a rule to copy one or more Paths to the artifacts
93 // directory on the build server when the specified goal is built.
94 DistForGoal(goal string, paths ...Path)
95
96 // DistForGoalWithFilename creates a rule to copy a Path to the artifacts
97 // directory on the build server with the given filename when the specified
98 // goal is built.
99 DistForGoalWithFilename(goal string, path Path, filename string)
100
101 // DistForGoals creates a rule to copy one or more Paths to the artifacts
102 // directory on the build server when any of the specified goals are built.
103 DistForGoals(goals []string, paths ...Path)
104
105 // DistForGoalsWithFilename creates a rule to copy a Path to the artifacts
106 // directory on the build server with the given filename when any of the
107 // specified goals are built.
108 DistForGoalsWithFilename(goals []string, path Path, filename string)
Dan Willemsen4b7d5de2016-01-12 23:20:28 -0800109}
110
Colin Cross65494b92019-02-07 14:25:51 -0800111var _ PathContext = MakeVarsContext(nil)
112
Dan Willemsen4b7d5de2016-01-12 23:20:28 -0800113type MakeVarsProvider func(ctx MakeVarsContext)
114
Colin Cross0875c522017-11-28 17:34:01 -0800115func RegisterMakeVarsProvider(pctx PackageContext, provider MakeVarsProvider) {
Dan Willemsen4b7d5de2016-01-12 23:20:28 -0800116 makeVarsProviders = append(makeVarsProviders, makeVarsProvider{pctx, provider})
117}
118
Colin Crossed023ec2019-02-19 12:38:45 -0800119// SingletonMakeVarsProvider is a Singleton with an extra method to provide extra values to be exported to Make.
120type SingletonMakeVarsProvider interface {
121 Singleton
122
123 // MakeVars uses a MakeVarsContext to provide extra values to be exported to Make.
124 MakeVars(ctx MakeVarsContext)
125}
126
127// registerSingletonMakeVarsProvider adds a singleton that implements SingletonMakeVarsProvider to the list of
128// MakeVarsProviders to run.
129func registerSingletonMakeVarsProvider(singleton SingletonMakeVarsProvider) {
130 makeVarsProviders = append(makeVarsProviders, makeVarsProvider{pctx, SingletonmakeVarsProviderAdapter(singleton)})
131}
132
133// SingletonmakeVarsProviderAdapter converts a SingletonMakeVarsProvider to a MakeVarsProvider.
134func SingletonmakeVarsProviderAdapter(singleton SingletonMakeVarsProvider) MakeVarsProvider {
135 return func(ctx MakeVarsContext) { singleton.MakeVars(ctx) }
136}
137
Dan Willemsen4b7d5de2016-01-12 23:20:28 -0800138///////////////////////////////////////////////////////////////////////////////
139
Colin Cross0875c522017-11-28 17:34:01 -0800140func makeVarsSingletonFunc() Singleton {
Dan Willemsen4b7d5de2016-01-12 23:20:28 -0800141 return &makeVarsSingleton{}
142}
143
144type makeVarsSingleton struct{}
145
146type makeVarsProvider struct {
Colin Cross0875c522017-11-28 17:34:01 -0800147 pctx PackageContext
Dan Willemsen4b7d5de2016-01-12 23:20:28 -0800148 call MakeVarsProvider
149}
150
151var makeVarsProviders []makeVarsProvider
152
153type makeVarsContext struct {
Colin Cross65494b92019-02-07 14:25:51 -0800154 SingletonContext
Colin Crossc3d87d32020-06-04 13:25:17 -0700155 config Config
156 pctx PackageContext
157 vars []makeVarsVariable
158 phonies []phony
Colin Cross3cda0d82019-09-24 13:40:07 -0700159 dists []dist
Dan Willemsen4b7d5de2016-01-12 23:20:28 -0800160}
161
162var _ MakeVarsContext = &makeVarsContext{}
163
164type makeVarsVariable struct {
165 name string
166 value string
167 sort bool
168 strict bool
169}
170
Colin Crossc3d87d32020-06-04 13:25:17 -0700171type phony struct {
172 name string
173 deps []string
174}
175
Colin Cross3cda0d82019-09-24 13:40:07 -0700176type dist struct {
177 goals []string
178 paths []string
179}
180
Colin Cross0875c522017-11-28 17:34:01 -0800181func (s *makeVarsSingleton) GenerateBuildActions(ctx SingletonContext) {
Colin Crossaabf6792017-11-29 00:27:14 -0800182 if !ctx.Config().EmbeddedInMake() {
Dan Willemsen4b7d5de2016-01-12 23:20:28 -0800183 return
184 }
185
Colin Cross988414c2020-01-11 01:11:46 +0000186 outFile := absolutePath(PathForOutput(ctx,
187 "make_vars"+proptools.String(ctx.Config().productVariables.Make_suffix)+".mk").String())
Dan Willemsen4b7d5de2016-01-12 23:20:28 -0800188
Colin Crossc3d87d32020-06-04 13:25:17 -0700189 lateOutFile := absolutePath(PathForOutput(ctx,
190 "late"+proptools.String(ctx.Config().productVariables.Make_suffix)+".mk").String())
191
Dan Willemsen4b7d5de2016-01-12 23:20:28 -0800192 if ctx.Failed() {
193 return
194 }
195
Colin Cross3cda0d82019-09-24 13:40:07 -0700196 var vars []makeVarsVariable
197 var dists []dist
Colin Crossc3d87d32020-06-04 13:25:17 -0700198 var phonies []phony
Dan Willemsen4b7d5de2016-01-12 23:20:28 -0800199 for _, provider := range makeVarsProviders {
200 mctx := &makeVarsContext{
Colin Cross65494b92019-02-07 14:25:51 -0800201 SingletonContext: ctx,
202 pctx: provider.pctx,
Dan Willemsen4b7d5de2016-01-12 23:20:28 -0800203 }
204
205 provider.call(mctx)
206
207 vars = append(vars, mctx.vars...)
Colin Crossc3d87d32020-06-04 13:25:17 -0700208 phonies = append(phonies, mctx.phonies...)
Colin Cross3cda0d82019-09-24 13:40:07 -0700209 dists = append(dists, mctx.dists...)
Dan Willemsen4b7d5de2016-01-12 23:20:28 -0800210 }
211
212 if ctx.Failed() {
213 return
214 }
215
216 outBytes := s.writeVars(vars)
217
Colin Crossc3d87d32020-06-04 13:25:17 -0700218 if err := pathtools.WriteFileIfChanged(outFile, outBytes, 0666); err != nil {
Dan Willemsen4b7d5de2016-01-12 23:20:28 -0800219 ctx.Errorf(err.Error())
220 }
Colin Crossc3d87d32020-06-04 13:25:17 -0700221
Colin Cross3cda0d82019-09-24 13:40:07 -0700222 lateOutBytes := s.writeLate(phonies, dists)
Colin Crossc3d87d32020-06-04 13:25:17 -0700223
224 if err := pathtools.WriteFileIfChanged(lateOutFile, lateOutBytes, 0666); err != nil {
225 ctx.Errorf(err.Error())
226 }
227
Dan Willemsen4b7d5de2016-01-12 23:20:28 -0800228}
229
230func (s *makeVarsSingleton) writeVars(vars []makeVarsVariable) []byte {
231 buf := &bytes.Buffer{}
232
Dan Willemsen59339a22018-07-22 21:18:45 -0700233 fmt.Fprint(buf, `# Autogenerated file
Dan Willemsen4b7d5de2016-01-12 23:20:28 -0800234
235# Compares SOONG_$(1) against $(1), and warns if they are not equal.
236#
237# If the original variable is empty, then just set it to the SOONG_ version.
238#
239# $(1): Name of the variable to check
240# $(2): If not-empty, sort the values before comparing
241# $(3): Extra snippet to run if it does not match
242define soong-compare-var
243ifneq ($$($(1)),)
Dan Willemsen558e5172016-05-19 16:58:46 -0700244 my_val_make := $$(strip $(if $(2),$$(sort $$($(1))),$$($(1))))
Dan Willemsen4b7d5de2016-01-12 23:20:28 -0800245 my_val_soong := $(if $(2),$$(sort $$(SOONG_$(1))),$$(SOONG_$(1)))
246 ifneq ($$(my_val_make),$$(my_val_soong))
247 $$(warning $(1) does not match between Make and Soong:)
248 $(if $(2),$$(warning Make adds: $$(filter-out $$(my_val_soong),$$(my_val_make))),$$(warning Make : $$(my_val_make)))
249 $(if $(2),$$(warning Soong adds: $$(filter-out $$(my_val_make),$$(my_val_soong))),$$(warning Soong: $$(my_val_soong)))
250 $(3)
251 endif
252 my_val_make :=
253 my_val_soong :=
254else
255 $(1) := $$(SOONG_$(1))
256endif
Dan Willemsende18f472016-09-30 10:16:38 -0700257.KATI_READONLY := $(1) SOONG_$(1)
Dan Willemsen4b7d5de2016-01-12 23:20:28 -0800258endef
259
260my_check_failed := false
261
262`)
263
264 // Write all the strict checks out first so that if one of them errors,
265 // we get all of the strict errors printed, but not the non-strict
266 // warnings.
267 for _, v := range vars {
268 if !v.strict {
269 continue
270 }
271
272 sort := ""
273 if v.sort {
274 sort = "true"
275 }
276
277 fmt.Fprintf(buf, "SOONG_%s := %s\n", v.name, v.value)
278 fmt.Fprintf(buf, "$(eval $(call soong-compare-var,%s,%s,my_check_failed := true))\n\n", v.name, sort)
279 }
280
Dan Willemsen59339a22018-07-22 21:18:45 -0700281 fmt.Fprint(buf, `
Dan Willemsen4b7d5de2016-01-12 23:20:28 -0800282ifneq ($(my_check_failed),false)
283 $(error Soong variable check failed)
284endif
285my_check_failed :=
286
287
288`)
289
290 for _, v := range vars {
291 if v.strict {
292 continue
293 }
294
295 sort := ""
296 if v.sort {
297 sort = "true"
298 }
299
300 fmt.Fprintf(buf, "SOONG_%s := %s\n", v.name, v.value)
301 fmt.Fprintf(buf, "$(eval $(call soong-compare-var,%s,%s))\n\n", v.name, sort)
302 }
303
304 fmt.Fprintln(buf, "\nsoong-compare-var :=")
305
Colin Crossc3d87d32020-06-04 13:25:17 -0700306 fmt.Fprintln(buf)
307
308 return buf.Bytes()
309}
310
Colin Cross3cda0d82019-09-24 13:40:07 -0700311func (s *makeVarsSingleton) writeLate(phonies []phony, dists []dist) []byte {
Colin Crossc3d87d32020-06-04 13:25:17 -0700312 buf := &bytes.Buffer{}
313
314 fmt.Fprint(buf, `# Autogenerated file
315
316# Values written by Soong read after parsing all Android.mk files.
317
318
319`)
320
321 for _, phony := range phonies {
322 fmt.Fprintf(buf, ".PHONY: %s\n", phony.name)
323 fmt.Fprintf(buf, "%s: %s\n", phony.name, strings.Join(phony.deps, "\\\n "))
324 }
325
Colin Cross3cda0d82019-09-24 13:40:07 -0700326 fmt.Fprintln(buf)
327
328 for _, dist := range dists {
329 fmt.Fprintf(buf, "$(call dist-for-goals,%s,%s)\n",
330 strings.Join(dist.goals, " "), strings.Join(dist.paths, " "))
331 }
332
Dan Willemsen4b7d5de2016-01-12 23:20:28 -0800333 return buf.Bytes()
334}
335
Dan Willemsen3fb1fae2018-03-12 15:30:26 -0700336func (c *makeVarsContext) DeviceConfig() DeviceConfig {
Colin Cross65494b92019-02-07 14:25:51 -0800337 return DeviceConfig{c.Config().deviceConfig}
Jiyong Park374510b2018-03-19 18:23:01 +0900338}
339
Colin Cross31656952018-05-24 16:11:20 -0700340var ninjaDescaper = strings.NewReplacer("$$", "$")
341
Dan Willemsen558e5172016-05-19 16:58:46 -0700342func (c *makeVarsContext) Eval(ninjaStr string) (string, error) {
Colin Cross65494b92019-02-07 14:25:51 -0800343 s, err := c.SingletonContext.Eval(c.pctx, ninjaStr)
Colin Cross31656952018-05-24 16:11:20 -0700344 if err != nil {
345 return "", err
346 }
347 // SingletonContext.Eval returns an exapnded string that is valid for a ninja file, de-escape $$ to $ for use
348 // in a Makefile
349 return ninjaDescaper.Replace(s), nil
Dan Willemsen558e5172016-05-19 16:58:46 -0700350}
351
352func (c *makeVarsContext) addVariableRaw(name, value string, strict, sort bool) {
Dan Willemsen4b7d5de2016-01-12 23:20:28 -0800353 c.vars = append(c.vars, makeVarsVariable{
354 name: name,
355 value: value,
356 strict: strict,
357 sort: sort,
358 })
359}
360
Dan Willemsen558e5172016-05-19 16:58:46 -0700361func (c *makeVarsContext) addVariable(name, ninjaStr string, strict, sort bool) {
362 value, err := c.Eval(ninjaStr)
363 if err != nil {
Colin Cross65494b92019-02-07 14:25:51 -0800364 c.SingletonContext.Errorf(err.Error())
Dan Willemsen558e5172016-05-19 16:58:46 -0700365 }
366 c.addVariableRaw(name, value, strict, sort)
367}
368
Colin Crossc3d87d32020-06-04 13:25:17 -0700369func (c *makeVarsContext) addPhony(name string, deps []string) {
370 c.phonies = append(c.phonies, phony{name, deps})
371}
372
Colin Cross3cda0d82019-09-24 13:40:07 -0700373func (c *makeVarsContext) addDist(goals []string, paths []string) {
374 c.dists = append(c.dists, dist{
375 goals: goals,
376 paths: paths,
377 })
378}
379
Dan Willemsen4b7d5de2016-01-12 23:20:28 -0800380func (c *makeVarsContext) Strict(name, ninjaStr string) {
381 c.addVariable(name, ninjaStr, true, false)
382}
383func (c *makeVarsContext) StrictSorted(name, ninjaStr string) {
384 c.addVariable(name, ninjaStr, true, true)
385}
Dan Willemsen558e5172016-05-19 16:58:46 -0700386func (c *makeVarsContext) StrictRaw(name, value string) {
387 c.addVariableRaw(name, value, true, false)
388}
Dan Willemsen4b7d5de2016-01-12 23:20:28 -0800389
390func (c *makeVarsContext) Check(name, ninjaStr string) {
391 c.addVariable(name, ninjaStr, false, false)
392}
393func (c *makeVarsContext) CheckSorted(name, ninjaStr string) {
394 c.addVariable(name, ninjaStr, false, true)
395}
Dan Willemsen558e5172016-05-19 16:58:46 -0700396func (c *makeVarsContext) CheckRaw(name, value string) {
397 c.addVariableRaw(name, value, false, false)
398}
Colin Crossc3d87d32020-06-04 13:25:17 -0700399
400func (c *makeVarsContext) Phony(name string, deps ...Path) {
401 c.addPhony(name, Paths(deps).Strings())
402}
Colin Cross3cda0d82019-09-24 13:40:07 -0700403
404func (c *makeVarsContext) DistForGoal(goal string, paths ...Path) {
405 c.DistForGoals([]string{goal}, paths...)
406}
407
408func (c *makeVarsContext) DistForGoalWithFilename(goal string, path Path, filename string) {
409 c.DistForGoalsWithFilename([]string{goal}, path, filename)
410}
411
412func (c *makeVarsContext) DistForGoals(goals []string, paths ...Path) {
413 c.addDist(goals, Paths(paths).Strings())
414}
415
416func (c *makeVarsContext) DistForGoalsWithFilename(goals []string, path Path, filename string) {
417 c.addDist(goals, []string{path.String() + ":" + filename})
418}