blob: 86f4b424b321deabfeeee2a7cccb65bbeeb0d65e [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 Willemsen6a6478d2020-07-17 19:28:53 -070020 "sort"
Dan Albertf5415d72017-08-17 16:19:59 -070021 "strconv"
Colin Cross31656952018-05-24 16:11:20 -070022 "strings"
Dan Willemsen4b7d5de2016-01-12 23:20:28 -080023
Colin Cross65494b92019-02-07 14:25:51 -080024 "github.com/google/blueprint"
Colin Crossc3d87d32020-06-04 13:25:17 -070025 "github.com/google/blueprint/pathtools"
Dan Willemsen4b7d5de2016-01-12 23:20:28 -080026 "github.com/google/blueprint/proptools"
27)
28
Dan Albertf5415d72017-08-17 16:19:59 -070029func init() {
30 RegisterMakeVarsProvider(pctx, androidMakeVarsProvider)
31}
32
33func androidMakeVarsProvider(ctx MakeVarsContext) {
34 ctx.Strict("MIN_SUPPORTED_SDK_VERSION", strconv.Itoa(ctx.Config().MinSupportedSdkVersion()))
35}
36
Dan Willemsen4b7d5de2016-01-12 23:20:28 -080037///////////////////////////////////////////////////////////////////////////////
Dan Willemsen6a6478d2020-07-17 19:28:53 -070038
39// BaseMakeVarsContext contains the common functions for other packages to use
40// to declare make variables
41type BaseMakeVarsContext interface {
Dan Willemsen4b7d5de2016-01-12 23:20:28 -080042 Config() Config
Dan Willemsen3fb1fae2018-03-12 15:30:26 -070043 DeviceConfig() DeviceConfig
Colin Cross65494b92019-02-07 14:25:51 -080044 AddNinjaFileDeps(deps ...string)
Colin Cross65494b92019-02-07 14:25:51 -080045
Colin Cross65494b92019-02-07 14:25:51 -080046 Failed() bool
47
Dan Willemsen558e5172016-05-19 16:58:46 -070048 // These are equivalent to Strict and Check, but do not attempt to
49 // evaluate the values before writing them to the Makefile. They can
50 // be used when all ninja variables have already been evaluated through
51 // Eval().
52 StrictRaw(name, value string)
53 CheckRaw(name, value string)
Colin Cross8177ad22019-11-04 10:27:48 -080054
55 // GlobWithDeps returns a list of files that match the specified pattern but do not match any
56 // of the patterns in excludes. It also adds efficient dependencies to rerun the primary
57 // builder whenever a file matching the pattern as added or removed, without rerunning if a
58 // file that does not match the pattern is added to a searched directory.
59 GlobWithDeps(pattern string, excludes []string) ([]string, error)
Colin Crossc3d87d32020-06-04 13:25:17 -070060
61 // Phony creates a phony rule in Make, which will allow additional DistForGoal
62 // dependencies to be added to it. Phony can be called on the same name multiple
63 // times to add additional dependencies.
64 Phony(names string, deps ...Path)
Colin Cross3cda0d82019-09-24 13:40:07 -070065
66 // DistForGoal creates a rule to copy one or more Paths to the artifacts
67 // directory on the build server when the specified goal is built.
68 DistForGoal(goal string, paths ...Path)
69
70 // DistForGoalWithFilename creates a rule to copy a Path to the artifacts
71 // directory on the build server with the given filename when the specified
72 // goal is built.
73 DistForGoalWithFilename(goal string, path Path, filename string)
74
75 // DistForGoals creates a rule to copy one or more Paths to the artifacts
76 // directory on the build server when any of the specified goals are built.
77 DistForGoals(goals []string, paths ...Path)
78
79 // DistForGoalsWithFilename creates a rule to copy a Path to the artifacts
80 // directory on the build server with the given filename when any of the
81 // specified goals are built.
82 DistForGoalsWithFilename(goals []string, path Path, filename string)
Dan Willemsen4b7d5de2016-01-12 23:20:28 -080083}
84
Dan Willemsen6a6478d2020-07-17 19:28:53 -070085// MakeVarsContext contains the set of functions available for MakeVarsProvider
86// and SingletonMakeVarsProvider implementations.
87type MakeVarsContext interface {
88 BaseMakeVarsContext
89
90 ModuleName(module blueprint.Module) string
91 ModuleDir(module blueprint.Module) string
92 ModuleSubDir(module blueprint.Module) string
93 ModuleType(module blueprint.Module) string
94 BlueprintFile(module blueprint.Module) string
95
96 ModuleErrorf(module blueprint.Module, format string, args ...interface{})
97 Errorf(format string, args ...interface{})
98
99 VisitAllModules(visit func(Module))
100 VisitAllModulesIf(pred func(Module) bool, visit func(Module))
101
102 // Verify the make variable matches the Soong version, fail the build
103 // if it does not. If the make variable is empty, just set it.
104 Strict(name, ninjaStr string)
105 // Check to see if the make variable matches the Soong version, warn if
106 // it does not. If the make variable is empty, just set it.
107 Check(name, ninjaStr string)
108
109 // These are equivalent to the above, but sort the make and soong
110 // variables before comparing them. They also show the unique entries
111 // in each list when displaying the difference, instead of the entire
112 // string.
113 StrictSorted(name, ninjaStr string)
114 CheckSorted(name, ninjaStr string)
115
116 // Evaluates a ninja string and returns the result. Used if more
117 // complicated modification needs to happen before giving it to Make.
118 Eval(ninjaStr string) (string, error)
119}
120
121// MakeVarsModuleContext contains the set of functions available for modules
122// implementing the ModuleMakeVarsProvider interface.
123type MakeVarsModuleContext interface {
124 BaseMakeVarsContext
125}
126
Colin Cross65494b92019-02-07 14:25:51 -0800127var _ PathContext = MakeVarsContext(nil)
128
Dan Willemsen4b7d5de2016-01-12 23:20:28 -0800129type MakeVarsProvider func(ctx MakeVarsContext)
130
Colin Cross0875c522017-11-28 17:34:01 -0800131func RegisterMakeVarsProvider(pctx PackageContext, provider MakeVarsProvider) {
Dan Willemsen4b7d5de2016-01-12 23:20:28 -0800132 makeVarsProviders = append(makeVarsProviders, makeVarsProvider{pctx, provider})
133}
134
Colin Crossed023ec2019-02-19 12:38:45 -0800135// SingletonMakeVarsProvider is a Singleton with an extra method to provide extra values to be exported to Make.
136type SingletonMakeVarsProvider interface {
137 Singleton
138
139 // MakeVars uses a MakeVarsContext to provide extra values to be exported to Make.
140 MakeVars(ctx MakeVarsContext)
141}
142
143// registerSingletonMakeVarsProvider adds a singleton that implements SingletonMakeVarsProvider to the list of
144// MakeVarsProviders to run.
145func registerSingletonMakeVarsProvider(singleton SingletonMakeVarsProvider) {
146 makeVarsProviders = append(makeVarsProviders, makeVarsProvider{pctx, SingletonmakeVarsProviderAdapter(singleton)})
147}
148
149// SingletonmakeVarsProviderAdapter converts a SingletonMakeVarsProvider to a MakeVarsProvider.
150func SingletonmakeVarsProviderAdapter(singleton SingletonMakeVarsProvider) MakeVarsProvider {
151 return func(ctx MakeVarsContext) { singleton.MakeVars(ctx) }
152}
153
Dan Willemsen6a6478d2020-07-17 19:28:53 -0700154// ModuleMakeVarsProvider is a Module with an extra method to provide extra values to be exported to Make.
155type ModuleMakeVarsProvider interface {
156 Module
157
158 // MakeVars uses a MakeVarsModuleContext to provide extra values to be exported to Make.
159 MakeVars(ctx MakeVarsModuleContext)
160}
161
Dan Willemsen4b7d5de2016-01-12 23:20:28 -0800162///////////////////////////////////////////////////////////////////////////////
163
Colin Cross0875c522017-11-28 17:34:01 -0800164func makeVarsSingletonFunc() Singleton {
Dan Willemsen4b7d5de2016-01-12 23:20:28 -0800165 return &makeVarsSingleton{}
166}
167
168type makeVarsSingleton struct{}
169
170type makeVarsProvider struct {
Colin Cross0875c522017-11-28 17:34:01 -0800171 pctx PackageContext
Dan Willemsen4b7d5de2016-01-12 23:20:28 -0800172 call MakeVarsProvider
173}
174
175var makeVarsProviders []makeVarsProvider
176
177type makeVarsContext struct {
Colin Cross65494b92019-02-07 14:25:51 -0800178 SingletonContext
Colin Crossc3d87d32020-06-04 13:25:17 -0700179 config Config
180 pctx PackageContext
181 vars []makeVarsVariable
182 phonies []phony
Colin Cross3cda0d82019-09-24 13:40:07 -0700183 dists []dist
Dan Willemsen4b7d5de2016-01-12 23:20:28 -0800184}
185
186var _ MakeVarsContext = &makeVarsContext{}
187
188type makeVarsVariable struct {
189 name string
190 value string
191 sort bool
192 strict bool
193}
194
Colin Crossc3d87d32020-06-04 13:25:17 -0700195type phony struct {
196 name string
197 deps []string
198}
199
Colin Cross3cda0d82019-09-24 13:40:07 -0700200type dist struct {
201 goals []string
202 paths []string
203}
204
Colin Cross0875c522017-11-28 17:34:01 -0800205func (s *makeVarsSingleton) GenerateBuildActions(ctx SingletonContext) {
Colin Crossaabf6792017-11-29 00:27:14 -0800206 if !ctx.Config().EmbeddedInMake() {
Dan Willemsen4b7d5de2016-01-12 23:20:28 -0800207 return
208 }
209
Colin Cross988414c2020-01-11 01:11:46 +0000210 outFile := absolutePath(PathForOutput(ctx,
211 "make_vars"+proptools.String(ctx.Config().productVariables.Make_suffix)+".mk").String())
Dan Willemsen4b7d5de2016-01-12 23:20:28 -0800212
Colin Crossc3d87d32020-06-04 13:25:17 -0700213 lateOutFile := absolutePath(PathForOutput(ctx,
214 "late"+proptools.String(ctx.Config().productVariables.Make_suffix)+".mk").String())
215
Dan Willemsen4b7d5de2016-01-12 23:20:28 -0800216 if ctx.Failed() {
217 return
218 }
219
Colin Cross3cda0d82019-09-24 13:40:07 -0700220 var vars []makeVarsVariable
221 var dists []dist
Colin Crossc3d87d32020-06-04 13:25:17 -0700222 var phonies []phony
Dan Willemsen4b7d5de2016-01-12 23:20:28 -0800223 for _, provider := range makeVarsProviders {
224 mctx := &makeVarsContext{
Colin Cross65494b92019-02-07 14:25:51 -0800225 SingletonContext: ctx,
226 pctx: provider.pctx,
Dan Willemsen4b7d5de2016-01-12 23:20:28 -0800227 }
228
229 provider.call(mctx)
230
231 vars = append(vars, mctx.vars...)
Colin Crossc3d87d32020-06-04 13:25:17 -0700232 phonies = append(phonies, mctx.phonies...)
Colin Cross3cda0d82019-09-24 13:40:07 -0700233 dists = append(dists, mctx.dists...)
Dan Willemsen4b7d5de2016-01-12 23:20:28 -0800234 }
235
Dan Willemsen6a6478d2020-07-17 19:28:53 -0700236 ctx.VisitAllModules(func(m Module) {
237 if provider, ok := m.(ModuleMakeVarsProvider); ok {
238 mctx := &makeVarsContext{
239 SingletonContext: ctx,
240 }
241
242 provider.MakeVars(mctx)
243
244 vars = append(vars, mctx.vars...)
245 phonies = append(phonies, mctx.phonies...)
246 dists = append(dists, mctx.dists...)
247 }
248 })
249
Dan Willemsen4b7d5de2016-01-12 23:20:28 -0800250 if ctx.Failed() {
251 return
252 }
253
Dan Willemsen6a6478d2020-07-17 19:28:53 -0700254 sort.Slice(vars, func(i, j int) bool {
255 return vars[i].name < vars[j].name
256 })
257 sort.Slice(phonies, func(i, j int) bool {
258 return phonies[i].name < phonies[j].name
259 })
260 lessArr := func(a, b []string) bool {
261 if len(a) == len(b) {
262 for i := range a {
263 if a[i] < b[i] {
264 return true
265 }
266 }
267 return false
268 }
269 return len(a) < len(b)
270 }
271 sort.Slice(dists, func(i, j int) bool {
272 return lessArr(dists[i].goals, dists[j].goals) || lessArr(dists[i].paths, dists[j].paths)
273 })
274
Dan Willemsen4b7d5de2016-01-12 23:20:28 -0800275 outBytes := s.writeVars(vars)
276
Colin Crossc3d87d32020-06-04 13:25:17 -0700277 if err := pathtools.WriteFileIfChanged(outFile, outBytes, 0666); err != nil {
Dan Willemsen4b7d5de2016-01-12 23:20:28 -0800278 ctx.Errorf(err.Error())
279 }
Colin Crossc3d87d32020-06-04 13:25:17 -0700280
Colin Cross3cda0d82019-09-24 13:40:07 -0700281 lateOutBytes := s.writeLate(phonies, dists)
Colin Crossc3d87d32020-06-04 13:25:17 -0700282
283 if err := pathtools.WriteFileIfChanged(lateOutFile, lateOutBytes, 0666); err != nil {
284 ctx.Errorf(err.Error())
285 }
286
Dan Willemsen4b7d5de2016-01-12 23:20:28 -0800287}
288
289func (s *makeVarsSingleton) writeVars(vars []makeVarsVariable) []byte {
290 buf := &bytes.Buffer{}
291
Dan Willemsen59339a22018-07-22 21:18:45 -0700292 fmt.Fprint(buf, `# Autogenerated file
Dan Willemsen4b7d5de2016-01-12 23:20:28 -0800293
294# Compares SOONG_$(1) against $(1), and warns if they are not equal.
295#
296# If the original variable is empty, then just set it to the SOONG_ version.
297#
298# $(1): Name of the variable to check
299# $(2): If not-empty, sort the values before comparing
300# $(3): Extra snippet to run if it does not match
301define soong-compare-var
302ifneq ($$($(1)),)
Dan Willemsen558e5172016-05-19 16:58:46 -0700303 my_val_make := $$(strip $(if $(2),$$(sort $$($(1))),$$($(1))))
Dan Willemsen4b7d5de2016-01-12 23:20:28 -0800304 my_val_soong := $(if $(2),$$(sort $$(SOONG_$(1))),$$(SOONG_$(1)))
305 ifneq ($$(my_val_make),$$(my_val_soong))
306 $$(warning $(1) does not match between Make and Soong:)
307 $(if $(2),$$(warning Make adds: $$(filter-out $$(my_val_soong),$$(my_val_make))),$$(warning Make : $$(my_val_make)))
308 $(if $(2),$$(warning Soong adds: $$(filter-out $$(my_val_make),$$(my_val_soong))),$$(warning Soong: $$(my_val_soong)))
309 $(3)
310 endif
311 my_val_make :=
312 my_val_soong :=
313else
314 $(1) := $$(SOONG_$(1))
315endif
Dan Willemsende18f472016-09-30 10:16:38 -0700316.KATI_READONLY := $(1) SOONG_$(1)
Dan Willemsen4b7d5de2016-01-12 23:20:28 -0800317endef
318
319my_check_failed := false
320
321`)
322
323 // Write all the strict checks out first so that if one of them errors,
324 // we get all of the strict errors printed, but not the non-strict
325 // warnings.
326 for _, v := range vars {
327 if !v.strict {
328 continue
329 }
330
331 sort := ""
332 if v.sort {
333 sort = "true"
334 }
335
336 fmt.Fprintf(buf, "SOONG_%s := %s\n", v.name, v.value)
337 fmt.Fprintf(buf, "$(eval $(call soong-compare-var,%s,%s,my_check_failed := true))\n\n", v.name, sort)
338 }
339
Dan Willemsen59339a22018-07-22 21:18:45 -0700340 fmt.Fprint(buf, `
Dan Willemsen4b7d5de2016-01-12 23:20:28 -0800341ifneq ($(my_check_failed),false)
342 $(error Soong variable check failed)
343endif
344my_check_failed :=
345
346
347`)
348
349 for _, v := range vars {
350 if v.strict {
351 continue
352 }
353
354 sort := ""
355 if v.sort {
356 sort = "true"
357 }
358
359 fmt.Fprintf(buf, "SOONG_%s := %s\n", v.name, v.value)
360 fmt.Fprintf(buf, "$(eval $(call soong-compare-var,%s,%s))\n\n", v.name, sort)
361 }
362
363 fmt.Fprintln(buf, "\nsoong-compare-var :=")
364
Colin Crossc3d87d32020-06-04 13:25:17 -0700365 fmt.Fprintln(buf)
366
367 return buf.Bytes()
368}
369
Colin Cross3cda0d82019-09-24 13:40:07 -0700370func (s *makeVarsSingleton) writeLate(phonies []phony, dists []dist) []byte {
Colin Crossc3d87d32020-06-04 13:25:17 -0700371 buf := &bytes.Buffer{}
372
373 fmt.Fprint(buf, `# Autogenerated file
374
375# Values written by Soong read after parsing all Android.mk files.
376
377
378`)
379
380 for _, phony := range phonies {
381 fmt.Fprintf(buf, ".PHONY: %s\n", phony.name)
382 fmt.Fprintf(buf, "%s: %s\n", phony.name, strings.Join(phony.deps, "\\\n "))
383 }
384
Colin Cross3cda0d82019-09-24 13:40:07 -0700385 fmt.Fprintln(buf)
386
387 for _, dist := range dists {
388 fmt.Fprintf(buf, "$(call dist-for-goals,%s,%s)\n",
389 strings.Join(dist.goals, " "), strings.Join(dist.paths, " "))
390 }
391
Dan Willemsen4b7d5de2016-01-12 23:20:28 -0800392 return buf.Bytes()
393}
394
Dan Willemsen3fb1fae2018-03-12 15:30:26 -0700395func (c *makeVarsContext) DeviceConfig() DeviceConfig {
Colin Cross65494b92019-02-07 14:25:51 -0800396 return DeviceConfig{c.Config().deviceConfig}
Jiyong Park374510b2018-03-19 18:23:01 +0900397}
398
Colin Cross31656952018-05-24 16:11:20 -0700399var ninjaDescaper = strings.NewReplacer("$$", "$")
400
Dan Willemsen558e5172016-05-19 16:58:46 -0700401func (c *makeVarsContext) Eval(ninjaStr string) (string, error) {
Colin Cross65494b92019-02-07 14:25:51 -0800402 s, err := c.SingletonContext.Eval(c.pctx, ninjaStr)
Colin Cross31656952018-05-24 16:11:20 -0700403 if err != nil {
404 return "", err
405 }
406 // SingletonContext.Eval returns an exapnded string that is valid for a ninja file, de-escape $$ to $ for use
407 // in a Makefile
408 return ninjaDescaper.Replace(s), nil
Dan Willemsen558e5172016-05-19 16:58:46 -0700409}
410
411func (c *makeVarsContext) addVariableRaw(name, value string, strict, sort bool) {
Dan Willemsen4b7d5de2016-01-12 23:20:28 -0800412 c.vars = append(c.vars, makeVarsVariable{
413 name: name,
414 value: value,
415 strict: strict,
416 sort: sort,
417 })
418}
419
Dan Willemsen558e5172016-05-19 16:58:46 -0700420func (c *makeVarsContext) addVariable(name, ninjaStr string, strict, sort bool) {
421 value, err := c.Eval(ninjaStr)
422 if err != nil {
Colin Cross65494b92019-02-07 14:25:51 -0800423 c.SingletonContext.Errorf(err.Error())
Dan Willemsen558e5172016-05-19 16:58:46 -0700424 }
425 c.addVariableRaw(name, value, strict, sort)
426}
427
Colin Crossc3d87d32020-06-04 13:25:17 -0700428func (c *makeVarsContext) addPhony(name string, deps []string) {
429 c.phonies = append(c.phonies, phony{name, deps})
430}
431
Colin Cross3cda0d82019-09-24 13:40:07 -0700432func (c *makeVarsContext) addDist(goals []string, paths []string) {
433 c.dists = append(c.dists, dist{
434 goals: goals,
435 paths: paths,
436 })
437}
438
Dan Willemsen4b7d5de2016-01-12 23:20:28 -0800439func (c *makeVarsContext) Strict(name, ninjaStr string) {
440 c.addVariable(name, ninjaStr, true, false)
441}
442func (c *makeVarsContext) StrictSorted(name, ninjaStr string) {
443 c.addVariable(name, ninjaStr, true, true)
444}
Dan Willemsen558e5172016-05-19 16:58:46 -0700445func (c *makeVarsContext) StrictRaw(name, value string) {
446 c.addVariableRaw(name, value, true, false)
447}
Dan Willemsen4b7d5de2016-01-12 23:20:28 -0800448
449func (c *makeVarsContext) Check(name, ninjaStr string) {
450 c.addVariable(name, ninjaStr, false, false)
451}
452func (c *makeVarsContext) CheckSorted(name, ninjaStr string) {
453 c.addVariable(name, ninjaStr, false, true)
454}
Dan Willemsen558e5172016-05-19 16:58:46 -0700455func (c *makeVarsContext) CheckRaw(name, value string) {
456 c.addVariableRaw(name, value, false, false)
457}
Colin Crossc3d87d32020-06-04 13:25:17 -0700458
459func (c *makeVarsContext) Phony(name string, deps ...Path) {
460 c.addPhony(name, Paths(deps).Strings())
461}
Colin Cross3cda0d82019-09-24 13:40:07 -0700462
463func (c *makeVarsContext) DistForGoal(goal string, paths ...Path) {
464 c.DistForGoals([]string{goal}, paths...)
465}
466
467func (c *makeVarsContext) DistForGoalWithFilename(goal string, path Path, filename string) {
468 c.DistForGoalsWithFilename([]string{goal}, path, filename)
469}
470
471func (c *makeVarsContext) DistForGoals(goals []string, paths ...Path) {
472 c.addDist(goals, Paths(paths).Strings())
473}
474
475func (c *makeVarsContext) DistForGoalsWithFilename(goals []string, path Path, filename string) {
476 c.addDist(goals, []string{path.String() + ":" + filename})
477}