123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609 |
- // Copyright 2016 Google Inc. All rights reserved.
- //
- // Licensed under the Apache License, Version 2.0 (the "License");
- // you may not use this file except in compliance with the License.
- // You may obtain a copy of the License at
- //
- // http://www.apache.org/licenses/LICENSE-2.0
- //
- // Unless required by applicable law or agreed to in writing, software
- // distributed under the License is distributed on an "AS IS" BASIS,
- // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- // See the License for the specific language governing permissions and
- // limitations under the License.
- package android
- import (
- "bytes"
- "fmt"
- "path/filepath"
- "runtime"
- "sort"
- "strings"
- "github.com/google/blueprint"
- "github.com/google/blueprint/pathtools"
- "github.com/google/blueprint/proptools"
- )
- func init() {
- RegisterMakeVarsProvider(pctx, androidMakeVarsProvider)
- }
- func androidMakeVarsProvider(ctx MakeVarsContext) {
- ctx.Strict("MIN_SUPPORTED_SDK_VERSION", ctx.Config().MinSupportedSdkVersion().String())
- }
- // /////////////////////////////////////////////////////////////////////////////
- // BaseMakeVarsContext contains the common functions for other packages to use
- // to declare make variables
- type BaseMakeVarsContext interface {
- Config() Config
- DeviceConfig() DeviceConfig
- AddNinjaFileDeps(deps ...string)
- Failed() bool
- // These are equivalent to Strict and Check, but do not attempt to
- // evaluate the values before writing them to the Makefile. They can
- // be used when all ninja variables have already been evaluated through
- // Eval().
- StrictRaw(name, value string)
- CheckRaw(name, value string)
- // GlobWithDeps returns a list of files that match the specified pattern but do not match any
- // of the patterns in excludes. It also adds efficient dependencies to rerun the primary
- // builder whenever a file matching the pattern as added or removed, without rerunning if a
- // file that does not match the pattern is added to a searched directory.
- GlobWithDeps(pattern string, excludes []string) ([]string, error)
- // Phony creates a phony rule in Make, which will allow additional DistForGoal
- // dependencies to be added to it. Phony can be called on the same name multiple
- // times to add additional dependencies.
- Phony(names string, deps ...Path)
- // DistForGoal creates a rule to copy one or more Paths to the artifacts
- // directory on the build server when the specified goal is built.
- DistForGoal(goal string, paths ...Path)
- // DistForGoalWithFilename creates a rule to copy a Path to the artifacts
- // directory on the build server with the given filename when the specified
- // goal is built.
- DistForGoalWithFilename(goal string, path Path, filename string)
- // DistForGoals creates a rule to copy one or more Paths to the artifacts
- // directory on the build server when any of the specified goals are built.
- DistForGoals(goals []string, paths ...Path)
- // DistForGoalsWithFilename creates a rule to copy a Path to the artifacts
- // directory on the build server with the given filename when any of the
- // specified goals are built.
- DistForGoalsWithFilename(goals []string, path Path, filename string)
- }
- // MakeVarsContext contains the set of functions available for MakeVarsProvider
- // and SingletonMakeVarsProvider implementations.
- type MakeVarsContext interface {
- BaseMakeVarsContext
- ModuleName(module blueprint.Module) string
- ModuleDir(module blueprint.Module) string
- ModuleSubDir(module blueprint.Module) string
- ModuleType(module blueprint.Module) string
- ModuleProvider(module blueprint.Module, key blueprint.ProviderKey) interface{}
- BlueprintFile(module blueprint.Module) string
- ModuleErrorf(module blueprint.Module, format string, args ...interface{})
- Errorf(format string, args ...interface{})
- VisitAllModules(visit func(Module))
- VisitAllModulesIf(pred func(Module) bool, visit func(Module))
- // Verify the make variable matches the Soong version, fail the build
- // if it does not. If the make variable is empty, just set it.
- Strict(name, ninjaStr string)
- // Check to see if the make variable matches the Soong version, warn if
- // it does not. If the make variable is empty, just set it.
- Check(name, ninjaStr string)
- // These are equivalent to the above, but sort the make and soong
- // variables before comparing them. They also show the unique entries
- // in each list when displaying the difference, instead of the entire
- // string.
- StrictSorted(name, ninjaStr string)
- CheckSorted(name, ninjaStr string)
- // Evaluates a ninja string and returns the result. Used if more
- // complicated modification needs to happen before giving it to Make.
- Eval(ninjaStr string) (string, error)
- }
- // MakeVarsModuleContext contains the set of functions available for modules
- // implementing the ModuleMakeVarsProvider interface.
- type MakeVarsModuleContext interface {
- BaseMakeVarsContext
- }
- var _ PathContext = MakeVarsContext(nil)
- type MakeVarsProvider func(ctx MakeVarsContext)
- func RegisterMakeVarsProvider(pctx PackageContext, provider MakeVarsProvider) {
- makeVarsInitProviders = append(makeVarsInitProviders, makeVarsProvider{pctx, provider})
- }
- // SingletonMakeVarsProvider is a Singleton with an extra method to provide extra values to be exported to Make.
- type SingletonMakeVarsProvider interface {
- // MakeVars uses a MakeVarsContext to provide extra values to be exported to Make.
- MakeVars(ctx MakeVarsContext)
- }
- var singletonMakeVarsProvidersKey = NewOnceKey("singletonMakeVarsProvidersKey")
- func getSingletonMakevarsProviders(config Config) *[]makeVarsProvider {
- return config.Once(singletonMakeVarsProvidersKey, func() interface{} {
- return &[]makeVarsProvider{}
- }).(*[]makeVarsProvider)
- }
- // registerSingletonMakeVarsProvider adds a singleton that implements SingletonMakeVarsProvider to
- // the list of MakeVarsProviders to run.
- func registerSingletonMakeVarsProvider(config Config, singleton SingletonMakeVarsProvider) {
- // Singletons are registered on the Context and may be different between different Contexts,
- // for example when running multiple tests. Store the SingletonMakeVarsProviders in the
- // Config so they are attached to the Context.
- singletonMakeVarsProviders := getSingletonMakevarsProviders(config)
- *singletonMakeVarsProviders = append(*singletonMakeVarsProviders,
- makeVarsProvider{pctx, singletonMakeVarsProviderAdapter(singleton)})
- }
- // singletonMakeVarsProviderAdapter converts a SingletonMakeVarsProvider to a MakeVarsProvider.
- func singletonMakeVarsProviderAdapter(singleton SingletonMakeVarsProvider) MakeVarsProvider {
- return func(ctx MakeVarsContext) { singleton.MakeVars(ctx) }
- }
- // ModuleMakeVarsProvider is a Module with an extra method to provide extra values to be exported to Make.
- type ModuleMakeVarsProvider interface {
- Module
- // MakeVars uses a MakeVarsModuleContext to provide extra values to be exported to Make.
- MakeVars(ctx MakeVarsModuleContext)
- }
- // /////////////////////////////////////////////////////////////////////////////
- func makeVarsSingletonFunc() Singleton {
- return &makeVarsSingleton{}
- }
- type makeVarsSingleton struct {
- varsForTesting []makeVarsVariable
- installsForTesting []byte
- }
- type makeVarsProvider struct {
- pctx PackageContext
- call MakeVarsProvider
- }
- // Collection of makevars providers that are registered in init() methods.
- var makeVarsInitProviders []makeVarsProvider
- type makeVarsContext struct {
- SingletonContext
- config Config
- pctx PackageContext
- vars []makeVarsVariable
- phonies []phony
- dists []dist
- }
- var _ MakeVarsContext = &makeVarsContext{}
- type makeVarsVariable struct {
- name string
- value string
- sort bool
- strict bool
- }
- type phony struct {
- name string
- deps []string
- }
- type dist struct {
- goals []string
- paths []string
- }
- func (s *makeVarsSingleton) GenerateBuildActions(ctx SingletonContext) {
- if !ctx.Config().KatiEnabled() {
- return
- }
- outFile := absolutePath(PathForOutput(ctx,
- "make_vars"+proptools.String(ctx.Config().productVariables.Make_suffix)+".mk").String())
- lateOutFile := absolutePath(PathForOutput(ctx,
- "late"+proptools.String(ctx.Config().productVariables.Make_suffix)+".mk").String())
- installsFile := absolutePath(PathForOutput(ctx,
- "installs"+proptools.String(ctx.Config().productVariables.Make_suffix)+".mk").String())
- if ctx.Failed() {
- return
- }
- var vars []makeVarsVariable
- var dists []dist
- var phonies []phony
- var katiInstalls []katiInstall
- var katiSymlinks []katiInstall
- providers := append([]makeVarsProvider(nil), makeVarsInitProviders...)
- providers = append(providers, *getSingletonMakevarsProviders(ctx.Config())...)
- for _, provider := range providers {
- mctx := &makeVarsContext{
- SingletonContext: ctx,
- pctx: provider.pctx,
- }
- provider.call(mctx)
- vars = append(vars, mctx.vars...)
- phonies = append(phonies, mctx.phonies...)
- dists = append(dists, mctx.dists...)
- }
- ctx.VisitAllModules(func(m Module) {
- if provider, ok := m.(ModuleMakeVarsProvider); ok && m.Enabled() {
- mctx := &makeVarsContext{
- SingletonContext: ctx,
- }
- provider.MakeVars(mctx)
- vars = append(vars, mctx.vars...)
- phonies = append(phonies, mctx.phonies...)
- dists = append(dists, mctx.dists...)
- }
- if m.ExportedToMake() {
- katiInstalls = append(katiInstalls, m.base().katiInstalls...)
- katiSymlinks = append(katiSymlinks, m.base().katiSymlinks...)
- }
- })
- if ctx.Failed() {
- return
- }
- sort.Slice(vars, func(i, j int) bool {
- return vars[i].name < vars[j].name
- })
- sort.Slice(phonies, func(i, j int) bool {
- return phonies[i].name < phonies[j].name
- })
- lessArr := func(a, b []string) bool {
- if len(a) == len(b) {
- for i := range a {
- if a[i] < b[i] {
- return true
- }
- }
- return false
- }
- return len(a) < len(b)
- }
- sort.Slice(dists, func(i, j int) bool {
- return lessArr(dists[i].goals, dists[j].goals) || lessArr(dists[i].paths, dists[j].paths)
- })
- outBytes := s.writeVars(vars)
- if err := pathtools.WriteFileIfChanged(outFile, outBytes, 0666); err != nil {
- ctx.Errorf(err.Error())
- }
- lateOutBytes := s.writeLate(phonies, dists)
- if err := pathtools.WriteFileIfChanged(lateOutFile, lateOutBytes, 0666); err != nil {
- ctx.Errorf(err.Error())
- }
- installsBytes := s.writeInstalls(katiInstalls, katiSymlinks)
- if err := pathtools.WriteFileIfChanged(installsFile, installsBytes, 0666); err != nil {
- ctx.Errorf(err.Error())
- }
- // Only save state for tests when testing.
- if ctx.Config().RunningInsideUnitTest() {
- s.varsForTesting = vars
- s.installsForTesting = installsBytes
- }
- }
- func (s *makeVarsSingleton) writeVars(vars []makeVarsVariable) []byte {
- buf := &bytes.Buffer{}
- fmt.Fprint(buf, `# Autogenerated file
- # Compares SOONG_$(1) against $(1), and warns if they are not equal.
- #
- # If the original variable is empty, then just set it to the SOONG_ version.
- #
- # $(1): Name of the variable to check
- # $(2): If not-empty, sort the values before comparing
- # $(3): Extra snippet to run if it does not match
- define soong-compare-var
- ifneq ($$($(1)),)
- my_val_make := $$(strip $(if $(2),$$(sort $$($(1))),$$($(1))))
- my_val_soong := $(if $(2),$$(sort $$(SOONG_$(1))),$$(SOONG_$(1)))
- ifneq ($$(my_val_make),$$(my_val_soong))
- $$(warning $(1) does not match between Make and Soong:)
- $(if $(2),$$(warning Make adds: $$(filter-out $$(my_val_soong),$$(my_val_make))),$$(warning Make : $$(my_val_make)))
- $(if $(2),$$(warning Soong adds: $$(filter-out $$(my_val_make),$$(my_val_soong))),$$(warning Soong: $$(my_val_soong)))
- $(3)
- endif
- my_val_make :=
- my_val_soong :=
- else
- $(1) := $$(SOONG_$(1))
- endif
- .KATI_READONLY := $(1) SOONG_$(1)
- endef
- my_check_failed := false
- `)
- // Write all the strict checks out first so that if one of them errors,
- // we get all of the strict errors printed, but not the non-strict
- // warnings.
- for _, v := range vars {
- if !v.strict {
- continue
- }
- sort := ""
- if v.sort {
- sort = "true"
- }
- fmt.Fprintf(buf, "SOONG_%s := %s\n", v.name, v.value)
- fmt.Fprintf(buf, "$(eval $(call soong-compare-var,%s,%s,my_check_failed := true))\n\n", v.name, sort)
- }
- fmt.Fprint(buf, `
- ifneq ($(my_check_failed),false)
- $(error Soong variable check failed)
- endif
- my_check_failed :=
- `)
- for _, v := range vars {
- if v.strict {
- continue
- }
- sort := ""
- if v.sort {
- sort = "true"
- }
- fmt.Fprintf(buf, "SOONG_%s := %s\n", v.name, v.value)
- fmt.Fprintf(buf, "$(eval $(call soong-compare-var,%s,%s))\n\n", v.name, sort)
- }
- fmt.Fprintln(buf, "\nsoong-compare-var :=")
- fmt.Fprintln(buf)
- return buf.Bytes()
- }
- func (s *makeVarsSingleton) writeLate(phonies []phony, dists []dist) []byte {
- buf := &bytes.Buffer{}
- fmt.Fprint(buf, `# Autogenerated file
- # Values written by Soong read after parsing all Android.mk files.
- `)
- for _, phony := range phonies {
- fmt.Fprintf(buf, ".PHONY: %s\n", phony.name)
- fmt.Fprintf(buf, "%s: %s\n", phony.name, strings.Join(phony.deps, "\\\n "))
- }
- fmt.Fprintln(buf)
- for _, dist := range dists {
- fmt.Fprintf(buf, ".PHONY: %s\n", strings.Join(dist.goals, " "))
- fmt.Fprintf(buf, "$(call dist-for-goals,%s,%s)\n",
- strings.Join(dist.goals, " "), strings.Join(dist.paths, " "))
- }
- return buf.Bytes()
- }
- // writeInstalls writes the list of install rules generated by Soong to a makefile. The rules
- // are exported to Make instead of written directly to the ninja file so that main.mk can add
- // the dependencies from the `required` property that are hard to resolve in Soong.
- func (s *makeVarsSingleton) writeInstalls(installs, symlinks []katiInstall) []byte {
- buf := &bytes.Buffer{}
- fmt.Fprint(buf, `# Autogenerated file
- # Values written by Soong to generate install rules that can be amended by Kati.
- `)
- preserveSymlinksFlag := "-d"
- if runtime.GOOS == "darwin" {
- preserveSymlinksFlag = "-R"
- }
- for _, install := range installs {
- // Write a rule for each install request in the form:
- // to: from [ deps ] [ | order only deps ]
- // cp -f -d $< $@ [ && chmod +x $@ ]
- fmt.Fprintf(buf, "%s: %s", install.to.String(), install.from.String())
- for _, dep := range install.implicitDeps {
- fmt.Fprintf(buf, " %s", dep.String())
- }
- if extraFiles := install.extraFiles; extraFiles != nil {
- fmt.Fprintf(buf, " %s", extraFiles.zip.String())
- }
- if len(install.orderOnlyDeps) > 0 {
- fmt.Fprintf(buf, " |")
- }
- for _, dep := range install.orderOnlyDeps {
- fmt.Fprintf(buf, " %s", dep.String())
- }
- fmt.Fprintln(buf)
- fmt.Fprintln(buf, "\t@echo \"Install: $@\"")
- fmt.Fprintf(buf, "\trm -f $@ && cp -f %s $< $@\n", preserveSymlinksFlag)
- if install.executable {
- fmt.Fprintf(buf, "\tchmod +x $@\n")
- }
- if extraFiles := install.extraFiles; extraFiles != nil {
- 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())
- fmt.Fprintf(buf, "\t ( code=$$?; if [ $$code -ne 0 -a $$code -ne 1 ]; then exit $$code; fi )\n")
- }
- fmt.Fprintln(buf)
- }
- for _, symlink := range symlinks {
- fmt.Fprintf(buf, "%s:", symlink.to.String())
- if symlink.from != nil {
- // The symlink doesn't need updating when the target is modified, but we sometimes
- // have a dependency on a symlink to a binary instead of to the binary directly, and
- // the mtime of the symlink must be updated when the binary is modified, so use a
- // normal dependency here instead of an order-only dependency.
- fmt.Fprintf(buf, " %s", symlink.from.String())
- }
- for _, dep := range symlink.implicitDeps {
- fmt.Fprintf(buf, " %s", dep.String())
- }
- if len(symlink.orderOnlyDeps) > 0 {
- fmt.Fprintf(buf, " |")
- }
- for _, dep := range symlink.orderOnlyDeps {
- fmt.Fprintf(buf, " %s", dep.String())
- }
- fmt.Fprintln(buf)
- fromStr := ""
- if symlink.from != nil {
- rel, err := filepath.Rel(filepath.Dir(symlink.to.String()), symlink.from.String())
- if err != nil {
- panic(fmt.Errorf("failed to find relative path for symlink from %q to %q: %w",
- symlink.from.String(), symlink.to.String(), err))
- }
- fromStr = rel
- } else {
- fromStr = symlink.absFrom
- }
- fmt.Fprintln(buf, "\t@echo \"Symlink: $@\"")
- fmt.Fprintf(buf, "\trm -f $@ && ln -sfn %s $@", fromStr)
- fmt.Fprintln(buf)
- fmt.Fprintln(buf)
- }
- return buf.Bytes()
- }
- func (c *makeVarsContext) DeviceConfig() DeviceConfig {
- return DeviceConfig{c.Config().deviceConfig}
- }
- var ninjaDescaper = strings.NewReplacer("$$", "$")
- func (c *makeVarsContext) Eval(ninjaStr string) (string, error) {
- s, err := c.SingletonContext.Eval(c.pctx, ninjaStr)
- if err != nil {
- return "", err
- }
- // SingletonContext.Eval returns an exapnded string that is valid for a ninja file, de-escape $$ to $ for use
- // in a Makefile
- return ninjaDescaper.Replace(s), nil
- }
- func (c *makeVarsContext) addVariableRaw(name, value string, strict, sort bool) {
- c.vars = append(c.vars, makeVarsVariable{
- name: name,
- value: value,
- strict: strict,
- sort: sort,
- })
- }
- func (c *makeVarsContext) addVariable(name, ninjaStr string, strict, sort bool) {
- value, err := c.Eval(ninjaStr)
- if err != nil {
- c.SingletonContext.Errorf(err.Error())
- }
- c.addVariableRaw(name, value, strict, sort)
- }
- func (c *makeVarsContext) addPhony(name string, deps []string) {
- c.phonies = append(c.phonies, phony{name, deps})
- }
- func (c *makeVarsContext) addDist(goals []string, paths []string) {
- c.dists = append(c.dists, dist{
- goals: goals,
- paths: paths,
- })
- }
- func (c *makeVarsContext) Strict(name, ninjaStr string) {
- c.addVariable(name, ninjaStr, true, false)
- }
- func (c *makeVarsContext) StrictSorted(name, ninjaStr string) {
- c.addVariable(name, ninjaStr, true, true)
- }
- func (c *makeVarsContext) StrictRaw(name, value string) {
- c.addVariableRaw(name, value, true, false)
- }
- func (c *makeVarsContext) Check(name, ninjaStr string) {
- c.addVariable(name, ninjaStr, false, false)
- }
- func (c *makeVarsContext) CheckSorted(name, ninjaStr string) {
- c.addVariable(name, ninjaStr, false, true)
- }
- func (c *makeVarsContext) CheckRaw(name, value string) {
- c.addVariableRaw(name, value, false, false)
- }
- func (c *makeVarsContext) Phony(name string, deps ...Path) {
- c.addPhony(name, Paths(deps).Strings())
- }
- func (c *makeVarsContext) DistForGoal(goal string, paths ...Path) {
- c.DistForGoals([]string{goal}, paths...)
- }
- func (c *makeVarsContext) DistForGoalWithFilename(goal string, path Path, filename string) {
- c.DistForGoalsWithFilename([]string{goal}, path, filename)
- }
- func (c *makeVarsContext) DistForGoals(goals []string, paths ...Path) {
- c.addDist(goals, Paths(paths).Strings())
- }
- func (c *makeVarsContext) DistForGoalsWithFilename(goals []string, path Path, filename string) {
- c.addDist(goals, []string{path.String() + ":" + filename})
- }
|