• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1// 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
15package android
16
17import (
18	"bytes"
19	"cmp"
20	"fmt"
21	"path/filepath"
22	"runtime"
23	"slices"
24	"sort"
25	"strings"
26
27	"github.com/google/blueprint"
28	"github.com/google/blueprint/pathtools"
29	"github.com/google/blueprint/proptools"
30)
31
32func init() {
33	RegisterMakeVarsProvider(pctx, androidMakeVarsProvider)
34}
35
36func androidMakeVarsProvider(ctx MakeVarsContext) {
37	ctx.Strict("MIN_SUPPORTED_SDK_VERSION", ctx.Config().MinSupportedSdkVersion().String())
38}
39
40// /////////////////////////////////////////////////////////////////////////////
41
42// BaseMakeVarsContext contains the common functions for other packages to use
43// to declare make variables
44type BaseMakeVarsContext interface {
45	Config() Config
46	DeviceConfig() DeviceConfig
47	AddNinjaFileDeps(deps ...string)
48
49	Failed() bool
50
51	// These are equivalent to Strict and Check, but do not attempt to
52	// evaluate the values before writing them to the Makefile. They can
53	// be used when all ninja variables have already been evaluated through
54	// Eval().
55	StrictRaw(name, value string)
56	CheckRaw(name, value string)
57
58	// GlobWithDeps returns a list of files that match the specified pattern but do not match any
59	// of the patterns in excludes.  It also adds efficient dependencies to rerun the primary
60	// builder whenever a file matching the pattern as added or removed, without rerunning if a
61	// file that does not match the pattern is added to a searched directory.
62	GlobWithDeps(pattern string, excludes []string) ([]string, error)
63
64	// Phony creates a phony rule in Make, which will allow additional DistForGoal
65	// dependencies to be added to it.  Phony can be called on the same name multiple
66	// times to add additional dependencies.
67	Phony(names string, deps ...Path)
68}
69
70// MakeVarsContext contains the set of functions available for MakeVarsProvider
71// and SingletonMakeVarsProvider implementations.
72type MakeVarsContext interface {
73	BaseMakeVarsContext
74
75	ModuleName(module blueprint.Module) string
76	ModuleDir(module blueprint.Module) string
77	ModuleSubDir(module blueprint.Module) string
78	ModuleType(module blueprint.Module) string
79	otherModuleProvider(module blueprint.Module, key blueprint.AnyProviderKey) (any, bool)
80	BlueprintFile(module blueprint.Module) string
81
82	ModuleErrorf(module blueprint.Module, format string, args ...interface{})
83	OtherModulePropertyErrorf(module Module, property, format string, args ...interface{})
84	Errorf(format string, args ...interface{})
85
86	VisitAllModules(visit func(Module))
87	VisitAllModuleProxies(visit func(proxy ModuleProxy))
88	VisitAllModulesIf(pred func(Module) bool, visit func(Module))
89
90	// Verify the make variable matches the Soong version, fail the build
91	// if it does not. If the make variable is empty, just set it.
92	Strict(name, ninjaStr string)
93	// Check to see if the make variable matches the Soong version, warn if
94	// it does not. If the make variable is empty, just set it.
95	Check(name, ninjaStr string)
96
97	// These are equivalent to the above, but sort the make and soong
98	// variables before comparing them. They also show the unique entries
99	// in each list when displaying the difference, instead of the entire
100	// string.
101	StrictSorted(name, ninjaStr string)
102	CheckSorted(name, ninjaStr string)
103
104	// Evaluates a ninja string and returns the result. Used if more
105	// complicated modification needs to happen before giving it to Make.
106	Eval(ninjaStr string) (string, error)
107}
108
109// MakeVarsModuleContext contains the set of functions available for modules
110// implementing the ModuleMakeVarsProvider interface.
111type MakeVarsModuleContext interface {
112	Config() Config
113}
114
115var _ PathContext = MakeVarsContext(nil)
116
117type MakeVarsProvider func(ctx MakeVarsContext)
118
119func RegisterMakeVarsProvider(pctx PackageContext, provider MakeVarsProvider) {
120	makeVarsInitProviders = append(makeVarsInitProviders, makeVarsProvider{pctx, provider})
121}
122
123// SingletonMakeVarsProvider is a Singleton with an extra method to provide extra values to be exported to Make.
124type SingletonMakeVarsProvider interface {
125	// MakeVars uses a MakeVarsContext to provide extra values to be exported to Make.
126	MakeVars(ctx MakeVarsContext)
127}
128
129var singletonMakeVarsProvidersKey = NewOnceKey("singletonMakeVarsProvidersKey")
130
131func getSingletonMakevarsProviders(config Config) *[]makeVarsProvider {
132	return config.Once(singletonMakeVarsProvidersKey, func() interface{} {
133		return &[]makeVarsProvider{}
134	}).(*[]makeVarsProvider)
135}
136
137// registerSingletonMakeVarsProvider adds a singleton that implements SingletonMakeVarsProvider to
138// the list of MakeVarsProviders to run.
139func registerSingletonMakeVarsProvider(config Config, singleton SingletonMakeVarsProvider) {
140	// Singletons are registered on the Context and may be different between different Contexts,
141	// for example when running multiple tests.  Store the SingletonMakeVarsProviders in the
142	// Config so they are attached to the Context.
143	singletonMakeVarsProviders := getSingletonMakevarsProviders(config)
144
145	*singletonMakeVarsProviders = append(*singletonMakeVarsProviders,
146		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
154type ModuleMakeVarsValue struct {
155	// Make variable name.
156	Name string
157	// Make variable value.
158	Value string
159}
160
161// ModuleMakeVarsProvider is a Module with an extra method to provide extra values to be exported to Make.
162type ModuleMakeVarsProvider interface {
163	// MakeVars uses a MakeVarsModuleContext to provide extra values to be exported to Make.
164	MakeVars(ctx MakeVarsModuleContext) []ModuleMakeVarsValue
165}
166
167var ModuleMakeVarsInfoProvider = blueprint.NewProvider[[]ModuleMakeVarsValue]()
168
169// /////////////////////////////////////////////////////////////////////////////
170
171func makeVarsSingletonFunc() Singleton {
172	return &makeVarsSingleton{}
173}
174
175type makeVarsSingleton struct {
176	varsForTesting     []makeVarsVariable
177	installsForTesting []byte
178	lateForTesting     []byte
179}
180
181type makeVarsProvider struct {
182	pctx PackageContext
183	call MakeVarsProvider
184}
185
186// Collection of makevars providers that are registered in init() methods.
187var makeVarsInitProviders []makeVarsProvider
188
189type makeVarsContext struct {
190	SingletonContext
191	pctx    PackageContext
192	vars    []makeVarsVariable
193	phonies []phony
194}
195
196var _ MakeVarsContext = &makeVarsContext{}
197
198type makeVarsVariable struct {
199	name   string
200	value  string
201	sort   bool
202	strict bool
203}
204
205type phony struct {
206	name string
207	deps []string
208}
209
210type dist struct {
211	goals []string
212	paths distCopies
213}
214
215func (s *makeVarsSingleton) GenerateBuildActions(ctx SingletonContext) {
216	if !ctx.Config().KatiEnabled() {
217		return
218	}
219
220	outFile := absolutePath(PathForOutput(ctx,
221		"make_vars"+proptools.String(ctx.Config().productVariables.Make_suffix)+".mk").String())
222
223	lateOutFile := absolutePath(PathForOutput(ctx,
224		"late"+proptools.String(ctx.Config().productVariables.Make_suffix)+".mk").String())
225
226	installsFile := absolutePath(PathForOutput(ctx,
227		"installs"+proptools.String(ctx.Config().productVariables.Make_suffix)+".mk").String())
228
229	if ctx.Failed() {
230		return
231	}
232
233	var vars []makeVarsVariable
234	var dists []dist
235	var phonies []phony
236	var katiInstalls []katiInstall
237	var katiInitRcInstalls []katiInstall
238	var katiVintfManifestInstalls []katiInstall
239	var katiSymlinks []katiInstall
240
241	providers := append([]makeVarsProvider(nil), makeVarsInitProviders...)
242	providers = append(providers, *getSingletonMakevarsProviders(ctx.Config())...)
243
244	for _, provider := range providers {
245		mctx := &makeVarsContext{
246			SingletonContext: ctx,
247			pctx:             provider.pctx,
248		}
249
250		provider.call(mctx)
251
252		vars = append(vars, mctx.vars...)
253		phonies = append(phonies, mctx.phonies...)
254	}
255
256	singletonDists := getSingletonDists(ctx.Config())
257	singletonDists.lock.Lock()
258	dists = append(dists, singletonDists.dists...)
259	singletonDists.lock.Unlock()
260
261	ctx.VisitAllModuleProxies(func(m ModuleProxy) {
262		commonInfo := OtherModulePointerProviderOrDefault(ctx, m, CommonModuleInfoProvider)
263		if provider, ok := OtherModuleProvider(ctx, m, ModuleMakeVarsInfoProvider); ok &&
264			commonInfo.Enabled {
265			mctx := &makeVarsContext{
266				SingletonContext: ctx,
267			}
268			for _, val := range provider {
269				if val.Name != "" {
270					mctx.StrictRaw(val.Name, val.Value)
271				}
272			}
273
274			vars = append(vars, mctx.vars...)
275			phonies = append(phonies, mctx.phonies...)
276		}
277
278		if commonInfo.ExportedToMake {
279			info := OtherModuleProviderOrDefault(ctx, m, InstallFilesProvider)
280			katiInstalls = append(katiInstalls, info.KatiInstalls...)
281			katiInitRcInstalls = append(katiInitRcInstalls, info.KatiInitRcInstalls...)
282			katiVintfManifestInstalls = append(katiVintfManifestInstalls, info.KatiVintfInstalls...)
283			katiSymlinks = append(katiSymlinks, info.KatiSymlinks...)
284		}
285
286		if distInfo, ok := OtherModuleProvider(ctx, m, DistProvider); ok {
287			dists = append(dists, distInfo.Dists...)
288		}
289	})
290
291	compareKatiInstalls := func(a, b katiInstall) int {
292		aTo, bTo := a.to.String(), b.to.String()
293		if cmpTo := cmp.Compare(aTo, bTo); cmpTo != 0 {
294			return cmpTo
295		}
296
297		aFrom, bFrom := a.from.String(), b.from.String()
298		return cmp.Compare(aFrom, bFrom)
299	}
300
301	slices.SortFunc(katiInitRcInstalls, compareKatiInstalls)
302	katiInitRcInstalls = slices.CompactFunc(katiInitRcInstalls, func(a, b katiInstall) bool {
303		return compareKatiInstalls(a, b) == 0
304	})
305	katiInstalls = append(katiInstalls, katiInitRcInstalls...)
306
307	slices.SortFunc(katiVintfManifestInstalls, compareKatiInstalls)
308	katiVintfManifestInstalls = slices.CompactFunc(katiVintfManifestInstalls, func(a, b katiInstall) bool {
309		return compareKatiInstalls(a, b) == 0
310	})
311
312	if ctx.Failed() {
313		return
314	}
315
316	sort.Slice(vars, func(i, j int) bool {
317		return vars[i].name < vars[j].name
318	})
319	sort.Slice(phonies, func(i, j int) bool {
320		return phonies[i].name < phonies[j].name
321	})
322	sort.Slice(dists, func(i, j int) bool {
323		goals := slices.Compare(dists[i].goals, dists[j].goals)
324		if goals != 0 {
325			return goals < 0
326		}
327		return slices.Compare(dists[i].paths.Strings(), dists[j].paths.Strings()) < 0
328	})
329
330	outBytes := s.writeVars(vars)
331
332	if err := pathtools.WriteFileIfChanged(outFile, outBytes, 0666); err != nil {
333		ctx.Errorf(err.Error())
334	}
335
336	lateOutBytes := s.writeLate(phonies, dists)
337
338	if err := pathtools.WriteFileIfChanged(lateOutFile, lateOutBytes, 0666); err != nil {
339		ctx.Errorf(err.Error())
340	}
341
342	installsBytes := s.writeInstalls(katiInstalls, katiSymlinks, katiVintfManifestInstalls)
343	if err := pathtools.WriteFileIfChanged(installsFile, installsBytes, 0666); err != nil {
344		ctx.Errorf(err.Error())
345	}
346
347	// Only save state for tests when testing.
348	if ctx.Config().RunningInsideUnitTest() {
349		s.varsForTesting = vars
350		s.installsForTesting = installsBytes
351		s.lateForTesting = lateOutBytes
352	}
353}
354
355func (s *makeVarsSingleton) writeVars(vars []makeVarsVariable) []byte {
356	buf := &bytes.Buffer{}
357
358	fmt.Fprint(buf, `# Autogenerated file
359
360# Compares SOONG_$(1) against $(1), and warns if they are not equal.
361#
362# If the original variable is empty, then just set it to the SOONG_ version.
363#
364# $(1): Name of the variable to check
365# $(2): If not-empty, sort the values before comparing
366# $(3): Extra snippet to run if it does not match
367define soong-compare-var
368ifneq ($$($(1)),)
369  my_val_make := $$(strip $(if $(2),$$(sort $$($(1))),$$($(1))))
370  my_val_soong := $(if $(2),$$(sort $$(SOONG_$(1))),$$(SOONG_$(1)))
371  ifneq ($$(my_val_make),$$(my_val_soong))
372    $$(warning $(1) does not match between Make and Soong:)
373    $(if $(2),$$(warning Make  adds: $$(filter-out $$(my_val_soong),$$(my_val_make))),$$(warning Make : $$(my_val_make)))
374    $(if $(2),$$(warning Soong adds: $$(filter-out $$(my_val_make),$$(my_val_soong))),$$(warning Soong: $$(my_val_soong)))
375    $(3)
376  endif
377  my_val_make :=
378  my_val_soong :=
379else
380  $(1) := $$(SOONG_$(1))
381endif
382.KATI_READONLY := $(1) SOONG_$(1)
383endef
384
385my_check_failed := false
386
387`)
388
389	// Write all the strict checks out first so that if one of them errors,
390	// we get all of the strict errors printed, but not the non-strict
391	// warnings.
392	for _, v := range vars {
393		if !v.strict {
394			continue
395		}
396
397		sort := ""
398		if v.sort {
399			sort = "true"
400		}
401
402		fmt.Fprintf(buf, "SOONG_%s := %s\n", v.name, v.value)
403		fmt.Fprintf(buf, "$(eval $(call soong-compare-var,%s,%s,my_check_failed := true))\n\n", v.name, sort)
404	}
405
406	fmt.Fprint(buf, `
407ifneq ($(my_check_failed),false)
408  $(error Soong variable check failed)
409endif
410my_check_failed :=
411
412
413`)
414
415	for _, v := range vars {
416		if v.strict {
417			continue
418		}
419
420		sort := ""
421		if v.sort {
422			sort = "true"
423		}
424
425		fmt.Fprintf(buf, "SOONG_%s := %s\n", v.name, v.value)
426		fmt.Fprintf(buf, "$(eval $(call soong-compare-var,%s,%s))\n\n", v.name, sort)
427	}
428
429	fmt.Fprintln(buf, "\nsoong-compare-var :=")
430
431	fmt.Fprintln(buf)
432
433	return buf.Bytes()
434}
435
436func (s *makeVarsSingleton) writeLate(phonies []phony, dists []dist) []byte {
437	buf := &bytes.Buffer{}
438
439	fmt.Fprint(buf, `# Autogenerated file
440
441# Values written by Soong read after parsing all Android.mk files.
442
443
444`)
445
446	for _, phony := range phonies {
447		fmt.Fprintf(buf, ".PHONY: %s\n", phony.name)
448		fmt.Fprintf(buf, "%s: %s\n", phony.name, strings.Join(phony.deps, "\\\n  "))
449	}
450
451	fmt.Fprintln(buf)
452
453	for _, dist := range dists {
454		fmt.Fprintf(buf, ".PHONY: %s\n", strings.Join(dist.goals, " "))
455		fmt.Fprintf(buf, "$(call dist-for-goals,%s,%s)\n",
456			strings.Join(dist.goals, " "), strings.Join(dist.paths.Strings(), " "))
457	}
458
459	return buf.Bytes()
460}
461
462// writeInstalls writes the list of install rules generated by Soong to a makefile.  The rules
463// are exported to Make instead of written directly to the ninja file so that main.mk can add
464// the dependencies from the `required` property that are hard to resolve in Soong.
465func (s *makeVarsSingleton) writeInstalls(installs, symlinks, katiVintfManifestInstalls []katiInstall) []byte {
466	buf := &bytes.Buffer{}
467
468	fmt.Fprint(buf, `# Autogenerated file
469
470# Values written by Soong to generate install rules that can be amended by Kati.
471
472EXTRA_INSTALL_ZIPS :=
473`)
474
475	preserveSymlinksFlag := "-d"
476	if runtime.GOOS == "darwin" {
477		preserveSymlinksFlag = "-R"
478	}
479
480	for _, install := range installs {
481		// Write a rule for each install request in the form:
482		//  to: from [ deps ] [ | order only deps ]
483		//       cp -f -d $< $@ [ && chmod +x $@ ]
484		fmt.Fprintf(buf, "%s: %s", install.to.String(), install.from.String())
485		for _, dep := range install.implicitDeps {
486			fmt.Fprintf(buf, " %s", dep.String())
487		}
488		if extraFiles := install.extraFiles; extraFiles != nil {
489			fmt.Fprintf(buf, " %s", extraFiles.zip.String())
490		}
491		if len(install.orderOnlyDeps) > 0 {
492			fmt.Fprintf(buf, " |")
493		}
494		for _, dep := range install.orderOnlyDeps {
495			fmt.Fprintf(buf, " %s", dep.String())
496		}
497		fmt.Fprintln(buf)
498		fmt.Fprintln(buf, "\t@echo \"Install: $@\"")
499		fmt.Fprintf(buf, "\trm -f $@ && cp -f %s $< $@\n", preserveSymlinksFlag)
500		if install.executable {
501			fmt.Fprintf(buf, "\tchmod +x $@\n")
502		}
503		if extraFiles := install.extraFiles; extraFiles != nil {
504			fmt.Fprintf(buf, "\t( unzip -qDD -d '%s' '%s' 2>&1 | grep -v \"zipfile is empty\"; exit $${PIPESTATUS[0]} ) || \\\n", extraFiles.dir.String(), extraFiles.zip.String())
505			fmt.Fprintf(buf, "\t  ( code=$$?; if [ $$code -ne 0 -a $$code -ne 1 ]; then exit $$code; fi )\n")
506			fmt.Fprintf(buf, "EXTRA_INSTALL_ZIPS += %s:%s:%s\n", install.to.String(), extraFiles.dir.String(), extraFiles.zip.String())
507		}
508
509		fmt.Fprintln(buf)
510	}
511	fmt.Fprintf(buf, ".KATI_READONLY := EXTRA_INSTALL_ZIPS\n")
512	fmt.Fprintf(buf, "$(KATI_visibility_prefix EXTRA_INSTALL_ZIPS,build/make/core/Makefile)\n")
513
514	for _, symlink := range symlinks {
515		fmt.Fprintf(buf, "%s:", symlink.to.String())
516		if symlink.from != nil {
517			// The katiVintfManifestInstall doesn't need updating when the target is modified, but we sometimes
518			// have a dependency on a katiVintfManifestInstall to a binary instead of to the binary directly, and
519			// the mtime of the katiVintfManifestInstall must be updated when the binary is modified, so use a
520			// normal dependency here instead of an order-only dependency.
521			fmt.Fprintf(buf, " %s", symlink.from.String())
522		}
523		for _, dep := range symlink.implicitDeps {
524			fmt.Fprintf(buf, " %s", dep.String())
525		}
526		if len(symlink.orderOnlyDeps) > 0 {
527			fmt.Fprintf(buf, " |")
528		}
529		for _, dep := range symlink.orderOnlyDeps {
530			fmt.Fprintf(buf, " %s", dep.String())
531		}
532		fmt.Fprintln(buf)
533
534		fromStr := ""
535		if symlink.from != nil {
536			rel, err := filepath.Rel(filepath.Dir(symlink.to.String()), symlink.from.String())
537			if err != nil {
538				panic(fmt.Errorf("failed to find relative path for katiVintfManifestInstall from %q to %q: %w",
539					symlink.from.String(), symlink.to.String(), err))
540			}
541			fromStr = rel
542		} else {
543			fromStr = symlink.absFrom
544		}
545
546		fmt.Fprintln(buf, "\t@echo \"Symlink: $@\"")
547		fmt.Fprintf(buf, "\trm -f $@ && ln -sfn %s $@", fromStr)
548		fmt.Fprintln(buf)
549		fmt.Fprintln(buf)
550	}
551
552	for _, install := range katiVintfManifestInstalls {
553		// Write a rule for each vintf install request that calls the copy-vintf-manifest-chedk make function.
554		fmt.Fprintf(buf, "$(eval $(call copy-vintf-manifest-checked, %s, %s))\n", install.from.String(), install.to.String())
555
556		if len(install.implicitDeps) > 0 {
557			panic(fmt.Errorf("unsupported implicitDeps %q in vintf install rule %q", install.implicitDeps, install.to))
558		}
559		if len(install.orderOnlyDeps) > 0 {
560			panic(fmt.Errorf("unsupported orderOnlyDeps %q in vintf install rule %q", install.orderOnlyDeps, install.to))
561		}
562
563		fmt.Fprintln(buf)
564	}
565	return buf.Bytes()
566}
567
568func (c *makeVarsContext) DeviceConfig() DeviceConfig {
569	return DeviceConfig{c.Config().deviceConfig}
570}
571
572var ninjaDescaper = strings.NewReplacer("$$", "$")
573
574func (c *makeVarsContext) Eval(ninjaStr string) (string, error) {
575	s, err := c.SingletonContext.Eval(c.pctx, ninjaStr)
576	if err != nil {
577		return "", err
578	}
579	// SingletonContext.Eval returns an exapnded string that is valid for a ninja file, de-escape $$ to $ for use
580	// in a Makefile
581	return ninjaDescaper.Replace(s), nil
582}
583
584func (c *makeVarsContext) addVariableRaw(name, value string, strict, sort bool) {
585	c.vars = append(c.vars, makeVarsVariable{
586		name:   name,
587		value:  value,
588		strict: strict,
589		sort:   sort,
590	})
591}
592
593func (c *makeVarsContext) addVariable(name, ninjaStr string, strict, sort bool) {
594	value, err := c.Eval(ninjaStr)
595	if err != nil {
596		c.SingletonContext.Errorf(err.Error())
597	}
598	c.addVariableRaw(name, value, strict, sort)
599}
600
601func (c *makeVarsContext) addPhony(name string, deps []string) {
602	c.phonies = append(c.phonies, phony{name, deps})
603}
604
605func (c *makeVarsContext) Strict(name, ninjaStr string) {
606	c.addVariable(name, ninjaStr, true, false)
607}
608func (c *makeVarsContext) StrictSorted(name, ninjaStr string) {
609	c.addVariable(name, ninjaStr, true, true)
610}
611func (c *makeVarsContext) StrictRaw(name, value string) {
612	c.addVariableRaw(name, value, true, false)
613}
614
615func (c *makeVarsContext) Check(name, ninjaStr string) {
616	c.addVariable(name, ninjaStr, false, false)
617}
618func (c *makeVarsContext) CheckSorted(name, ninjaStr string) {
619	c.addVariable(name, ninjaStr, false, true)
620}
621func (c *makeVarsContext) CheckRaw(name, value string) {
622	c.addVariableRaw(name, value, false, false)
623}
624
625func (c *makeVarsContext) Phony(name string, deps ...Path) {
626	c.addPhony(name, Paths(deps).Strings())
627}
628