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