• 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	varsForTesting     []makeVarsVariable
184	installsForTesting []byte
185}
186
187type makeVarsProvider struct {
188	pctx PackageContext
189	call MakeVarsProvider
190}
191
192// Collection of makevars providers that are registered in init() methods.
193var makeVarsInitProviders []makeVarsProvider
194
195type makeVarsContext struct {
196	SingletonContext
197	config  Config
198	pctx    PackageContext
199	vars    []makeVarsVariable
200	phonies []phony
201	dists   []dist
202}
203
204var _ MakeVarsContext = &makeVarsContext{}
205
206type makeVarsVariable struct {
207	name   string
208	value  string
209	sort   bool
210	strict bool
211}
212
213type phony struct {
214	name string
215	deps []string
216}
217
218type dist struct {
219	goals []string
220	paths []string
221}
222
223func (s *makeVarsSingleton) GenerateBuildActions(ctx SingletonContext) {
224	if !ctx.Config().KatiEnabled() {
225		return
226	}
227
228	outFile := absolutePath(PathForOutput(ctx,
229		"make_vars"+proptools.String(ctx.Config().productVariables.Make_suffix)+".mk").String())
230
231	lateOutFile := absolutePath(PathForOutput(ctx,
232		"late"+proptools.String(ctx.Config().productVariables.Make_suffix)+".mk").String())
233
234	installsFile := absolutePath(PathForOutput(ctx,
235		"installs"+proptools.String(ctx.Config().productVariables.Make_suffix)+".mk").String())
236
237	if ctx.Failed() {
238		return
239	}
240
241	var vars []makeVarsVariable
242	var dists []dist
243	var phonies []phony
244	var katiInstalls []katiInstall
245	var katiSymlinks []katiInstall
246
247	providers := append([]makeVarsProvider(nil), makeVarsInitProviders...)
248	providers = append(providers, *getSingletonMakevarsProviders(ctx.Config())...)
249
250	for _, provider := range providers {
251		mctx := &makeVarsContext{
252			SingletonContext: ctx,
253			pctx:             provider.pctx,
254		}
255
256		provider.call(mctx)
257
258		vars = append(vars, mctx.vars...)
259		phonies = append(phonies, mctx.phonies...)
260		dists = append(dists, mctx.dists...)
261	}
262
263	ctx.VisitAllModules(func(m Module) {
264		if provider, ok := m.(ModuleMakeVarsProvider); ok && m.Enabled() {
265			mctx := &makeVarsContext{
266				SingletonContext: ctx,
267			}
268
269			provider.MakeVars(mctx)
270
271			vars = append(vars, mctx.vars...)
272			phonies = append(phonies, mctx.phonies...)
273			dists = append(dists, mctx.dists...)
274		}
275
276		if m.ExportedToMake() {
277			katiInstalls = append(katiInstalls, m.base().katiInstalls...)
278			katiSymlinks = append(katiSymlinks, m.base().katiSymlinks...)
279		}
280	})
281
282	if ctx.Failed() {
283		return
284	}
285
286	sort.Slice(vars, func(i, j int) bool {
287		return vars[i].name < vars[j].name
288	})
289	sort.Slice(phonies, func(i, j int) bool {
290		return phonies[i].name < phonies[j].name
291	})
292	lessArr := func(a, b []string) bool {
293		if len(a) == len(b) {
294			for i := range a {
295				if a[i] < b[i] {
296					return true
297				}
298			}
299			return false
300		}
301		return len(a) < len(b)
302	}
303	sort.Slice(dists, func(i, j int) bool {
304		return lessArr(dists[i].goals, dists[j].goals) || lessArr(dists[i].paths, dists[j].paths)
305	})
306
307	outBytes := s.writeVars(vars)
308
309	if err := pathtools.WriteFileIfChanged(outFile, outBytes, 0666); err != nil {
310		ctx.Errorf(err.Error())
311	}
312
313	lateOutBytes := s.writeLate(phonies, dists)
314
315	if err := pathtools.WriteFileIfChanged(lateOutFile, lateOutBytes, 0666); err != nil {
316		ctx.Errorf(err.Error())
317	}
318
319	installsBytes := s.writeInstalls(katiInstalls, katiSymlinks)
320	if err := pathtools.WriteFileIfChanged(installsFile, installsBytes, 0666); err != nil {
321		ctx.Errorf(err.Error())
322	}
323
324	// Only save state for tests when testing.
325	if ctx.Config().RunningInsideUnitTest() {
326		s.varsForTesting = vars
327		s.installsForTesting = installsBytes
328	}
329}
330
331func (s *makeVarsSingleton) writeVars(vars []makeVarsVariable) []byte {
332	buf := &bytes.Buffer{}
333
334	fmt.Fprint(buf, `# Autogenerated file
335
336# Compares SOONG_$(1) against $(1), and warns if they are not equal.
337#
338# If the original variable is empty, then just set it to the SOONG_ version.
339#
340# $(1): Name of the variable to check
341# $(2): If not-empty, sort the values before comparing
342# $(3): Extra snippet to run if it does not match
343define soong-compare-var
344ifneq ($$($(1)),)
345  my_val_make := $$(strip $(if $(2),$$(sort $$($(1))),$$($(1))))
346  my_val_soong := $(if $(2),$$(sort $$(SOONG_$(1))),$$(SOONG_$(1)))
347  ifneq ($$(my_val_make),$$(my_val_soong))
348    $$(warning $(1) does not match between Make and Soong:)
349    $(if $(2),$$(warning Make  adds: $$(filter-out $$(my_val_soong),$$(my_val_make))),$$(warning Make : $$(my_val_make)))
350    $(if $(2),$$(warning Soong adds: $$(filter-out $$(my_val_make),$$(my_val_soong))),$$(warning Soong: $$(my_val_soong)))
351    $(3)
352  endif
353  my_val_make :=
354  my_val_soong :=
355else
356  $(1) := $$(SOONG_$(1))
357endif
358.KATI_READONLY := $(1) SOONG_$(1)
359endef
360
361my_check_failed := false
362
363`)
364
365	// Write all the strict checks out first so that if one of them errors,
366	// we get all of the strict errors printed, but not the non-strict
367	// warnings.
368	for _, v := range vars {
369		if !v.strict {
370			continue
371		}
372
373		sort := ""
374		if v.sort {
375			sort = "true"
376		}
377
378		fmt.Fprintf(buf, "SOONG_%s := %s\n", v.name, v.value)
379		fmt.Fprintf(buf, "$(eval $(call soong-compare-var,%s,%s,my_check_failed := true))\n\n", v.name, sort)
380	}
381
382	fmt.Fprint(buf, `
383ifneq ($(my_check_failed),false)
384  $(error Soong variable check failed)
385endif
386my_check_failed :=
387
388
389`)
390
391	for _, v := range vars {
392		if v.strict {
393			continue
394		}
395
396		sort := ""
397		if v.sort {
398			sort = "true"
399		}
400
401		fmt.Fprintf(buf, "SOONG_%s := %s\n", v.name, v.value)
402		fmt.Fprintf(buf, "$(eval $(call soong-compare-var,%s,%s))\n\n", v.name, sort)
403	}
404
405	fmt.Fprintln(buf, "\nsoong-compare-var :=")
406
407	fmt.Fprintln(buf)
408
409	return buf.Bytes()
410}
411
412func (s *makeVarsSingleton) writeLate(phonies []phony, dists []dist) []byte {
413	buf := &bytes.Buffer{}
414
415	fmt.Fprint(buf, `# Autogenerated file
416
417# Values written by Soong read after parsing all Android.mk files.
418
419
420`)
421
422	for _, phony := range phonies {
423		fmt.Fprintf(buf, ".PHONY: %s\n", phony.name)
424		fmt.Fprintf(buf, "%s: %s\n", phony.name, strings.Join(phony.deps, "\\\n  "))
425	}
426
427	fmt.Fprintln(buf)
428
429	for _, dist := range dists {
430		fmt.Fprintf(buf, ".PHONY: %s\n", strings.Join(dist.goals, " "))
431		fmt.Fprintf(buf, "$(call dist-for-goals,%s,%s)\n",
432			strings.Join(dist.goals, " "), strings.Join(dist.paths, " "))
433	}
434
435	return buf.Bytes()
436}
437
438// writeInstalls writes the list of install rules generated by Soong to a makefile.  The rules
439// are exported to Make instead of written directly to the ninja file so that main.mk can add
440// the dependencies from the `required` property that are hard to resolve in Soong.
441func (s *makeVarsSingleton) writeInstalls(installs, symlinks []katiInstall) []byte {
442	buf := &bytes.Buffer{}
443
444	fmt.Fprint(buf, `# Autogenerated file
445
446# Values written by Soong to generate install rules that can be amended by Kati.
447
448
449`)
450
451	preserveSymlinksFlag := "-d"
452	if runtime.GOOS == "darwin" {
453		preserveSymlinksFlag = "-R"
454	}
455
456	for _, install := range installs {
457		// Write a rule for each install request in the form:
458		//  to: from [ deps ] [ | order only deps ]
459		//       cp -f -d $< $@ [ && chmod +x $@ ]
460		fmt.Fprintf(buf, "%s: %s", install.to.String(), install.from.String())
461		for _, dep := range install.implicitDeps {
462			fmt.Fprintf(buf, " %s", dep.String())
463		}
464		if extraFiles := install.extraFiles; extraFiles != nil {
465			fmt.Fprintf(buf, " %s", extraFiles.zip.String())
466		}
467		if len(install.orderOnlyDeps) > 0 {
468			fmt.Fprintf(buf, " |")
469		}
470		for _, dep := range install.orderOnlyDeps {
471			fmt.Fprintf(buf, " %s", dep.String())
472		}
473		fmt.Fprintln(buf)
474		fmt.Fprintln(buf, "\t@echo \"Install: $@\"")
475		fmt.Fprintf(buf, "\trm -f $@ && cp -f %s $< $@\n", preserveSymlinksFlag)
476		if install.executable {
477			fmt.Fprintf(buf, "\tchmod +x $@\n")
478		}
479		if extraFiles := install.extraFiles; extraFiles != nil {
480			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())
481			fmt.Fprintf(buf, "\t  ( code=$$?; if [ $$code -ne 0 -a $$code -ne 1 ]; then exit $$code; fi )\n")
482		}
483		fmt.Fprintln(buf)
484	}
485
486	for _, symlink := range symlinks {
487		fmt.Fprintf(buf, "%s:", symlink.to.String())
488		if symlink.from != nil {
489			// The symlink doesn't need updating when the target is modified, but we sometimes
490			// have a dependency on a symlink to a binary instead of to the binary directly, and
491			// the mtime of the symlink must be updated when the binary is modified, so use a
492			// normal dependency here instead of an order-only dependency.
493			fmt.Fprintf(buf, " %s", symlink.from.String())
494		}
495		for _, dep := range symlink.implicitDeps {
496			fmt.Fprintf(buf, " %s", dep.String())
497		}
498		if len(symlink.orderOnlyDeps) > 0 {
499			fmt.Fprintf(buf, " |")
500		}
501		for _, dep := range symlink.orderOnlyDeps {
502			fmt.Fprintf(buf, " %s", dep.String())
503		}
504		fmt.Fprintln(buf)
505
506		fromStr := ""
507		if symlink.from != nil {
508			rel, err := filepath.Rel(filepath.Dir(symlink.to.String()), symlink.from.String())
509			if err != nil {
510				panic(fmt.Errorf("failed to find relative path for symlink from %q to %q: %w",
511					symlink.from.String(), symlink.to.String(), err))
512			}
513			fromStr = rel
514		} else {
515			fromStr = symlink.absFrom
516		}
517
518		fmt.Fprintln(buf, "\t@echo \"Symlink: $@\"")
519		fmt.Fprintf(buf, "\trm -f $@ && ln -sfn %s $@", fromStr)
520		fmt.Fprintln(buf)
521		fmt.Fprintln(buf)
522	}
523
524	return buf.Bytes()
525}
526
527func (c *makeVarsContext) DeviceConfig() DeviceConfig {
528	return DeviceConfig{c.Config().deviceConfig}
529}
530
531var ninjaDescaper = strings.NewReplacer("$$", "$")
532
533func (c *makeVarsContext) Eval(ninjaStr string) (string, error) {
534	s, err := c.SingletonContext.Eval(c.pctx, ninjaStr)
535	if err != nil {
536		return "", err
537	}
538	// SingletonContext.Eval returns an exapnded string that is valid for a ninja file, de-escape $$ to $ for use
539	// in a Makefile
540	return ninjaDescaper.Replace(s), nil
541}
542
543func (c *makeVarsContext) addVariableRaw(name, value string, strict, sort bool) {
544	c.vars = append(c.vars, makeVarsVariable{
545		name:   name,
546		value:  value,
547		strict: strict,
548		sort:   sort,
549	})
550}
551
552func (c *makeVarsContext) addVariable(name, ninjaStr string, strict, sort bool) {
553	value, err := c.Eval(ninjaStr)
554	if err != nil {
555		c.SingletonContext.Errorf(err.Error())
556	}
557	c.addVariableRaw(name, value, strict, sort)
558}
559
560func (c *makeVarsContext) addPhony(name string, deps []string) {
561	c.phonies = append(c.phonies, phony{name, deps})
562}
563
564func (c *makeVarsContext) addDist(goals []string, paths []string) {
565	c.dists = append(c.dists, dist{
566		goals: goals,
567		paths: paths,
568	})
569}
570
571func (c *makeVarsContext) Strict(name, ninjaStr string) {
572	c.addVariable(name, ninjaStr, true, false)
573}
574func (c *makeVarsContext) StrictSorted(name, ninjaStr string) {
575	c.addVariable(name, ninjaStr, true, true)
576}
577func (c *makeVarsContext) StrictRaw(name, value string) {
578	c.addVariableRaw(name, value, true, false)
579}
580
581func (c *makeVarsContext) Check(name, ninjaStr string) {
582	c.addVariable(name, ninjaStr, false, false)
583}
584func (c *makeVarsContext) CheckSorted(name, ninjaStr string) {
585	c.addVariable(name, ninjaStr, false, true)
586}
587func (c *makeVarsContext) CheckRaw(name, value string) {
588	c.addVariableRaw(name, value, false, false)
589}
590
591func (c *makeVarsContext) Phony(name string, deps ...Path) {
592	c.addPhony(name, Paths(deps).Strings())
593}
594
595func (c *makeVarsContext) DistForGoal(goal string, paths ...Path) {
596	c.DistForGoals([]string{goal}, paths...)
597}
598
599func (c *makeVarsContext) DistForGoalWithFilename(goal string, path Path, filename string) {
600	c.DistForGoalsWithFilename([]string{goal}, path, filename)
601}
602
603func (c *makeVarsContext) DistForGoals(goals []string, paths ...Path) {
604	c.addDist(goals, Paths(paths).Strings())
605}
606
607func (c *makeVarsContext) DistForGoalsWithFilename(goals []string, path Path, filename string) {
608	c.addDist(goals, []string{path.String() + ":" + filename})
609}
610