diff --git a/android/Android.bp b/android/Android.bp index 654110647..d904a86c7 100644 --- a/android/Android.bp +++ b/android/Android.bp @@ -34,6 +34,7 @@ bootstrap_go_package { "package_ctx.go", "path_properties.go", "paths.go", + "phony.go", "prebuilt.go", "proto.go", "register.go", diff --git a/android/makevars.go b/android/makevars.go index aba4ccec3..0acd0f665 100644 --- a/android/makevars.go +++ b/android/makevars.go @@ -17,12 +17,11 @@ package android import ( "bytes" "fmt" - "io/ioutil" - "os" "strconv" "strings" "github.com/google/blueprint" + "github.com/google/blueprint/pathtools" "github.com/google/blueprint/proptools" ) @@ -84,6 +83,11 @@ type MakeVarsContext interface { // 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) } var _ PathContext = MakeVarsContext(nil) @@ -130,9 +134,10 @@ var makeVarsProviders []makeVarsProvider type makeVarsContext struct { SingletonContext - config Config - pctx PackageContext - vars []makeVarsVariable + config Config + pctx PackageContext + vars []makeVarsVariable + phonies []phony } var _ MakeVarsContext = &makeVarsContext{} @@ -144,6 +149,11 @@ type makeVarsVariable struct { strict bool } +type phony struct { + name string + deps []string +} + func (s *makeVarsSingleton) GenerateBuildActions(ctx SingletonContext) { if !ctx.Config().EmbeddedInMake() { return @@ -152,11 +162,15 @@ func (s *makeVarsSingleton) GenerateBuildActions(ctx SingletonContext) { 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()) + if ctx.Failed() { return } vars := []makeVarsVariable{} + var phonies []phony for _, provider := range makeVarsProviders { mctx := &makeVarsContext{ SingletonContext: ctx, @@ -166,6 +180,7 @@ func (s *makeVarsSingleton) GenerateBuildActions(ctx SingletonContext) { provider.call(mctx) vars = append(vars, mctx.vars...) + phonies = append(phonies, mctx.phonies...) } if ctx.Failed() { @@ -174,17 +189,16 @@ func (s *makeVarsSingleton) GenerateBuildActions(ctx SingletonContext) { outBytes := s.writeVars(vars) - if _, err := os.Stat(absolutePath(outFile)); err == nil { - if data, err := ioutil.ReadFile(absolutePath(outFile)); err == nil { - if bytes.Equal(data, outBytes) { - return - } - } - } - - if err := ioutil.WriteFile(absolutePath(outFile), outBytes, 0666); err != nil { + if err := pathtools.WriteFileIfChanged(outFile, outBytes, 0666); err != nil { ctx.Errorf(err.Error()) } + + lateOutBytes := s.writeLate(phonies) + + if err := pathtools.WriteFileIfChanged(lateOutFile, lateOutBytes, 0666); err != nil { + ctx.Errorf(err.Error()) + } + } func (s *makeVarsSingleton) writeVars(vars []makeVarsVariable) []byte { @@ -263,6 +277,26 @@ my_check_failed := fmt.Fprintln(buf, "\nsoong-compare-var :=") + fmt.Fprintln(buf) + + return buf.Bytes() +} + +func (s *makeVarsSingleton) writeLate(phonies []phony) []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 ")) + } + return buf.Bytes() } @@ -299,6 +333,10 @@ func (c *makeVarsContext) addVariable(name, ninjaStr string, strict, sort bool) 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) Strict(name, ninjaStr string) { c.addVariable(name, ninjaStr, true, false) } @@ -318,3 +356,7 @@ func (c *makeVarsContext) CheckSorted(name, ninjaStr string) { 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()) +} diff --git a/android/module.go b/android/module.go index baf348aaf..0bf48d597 100644 --- a/android/module.go +++ b/android/module.go @@ -194,6 +194,10 @@ type ModuleContext interface { // Similar to blueprint.ModuleContext.Build, but takes Paths instead of []string, // and performs more verification. Build(pctx PackageContext, params BuildParams) + // Phony creates a Make-style phony rule, a rule with no commands that can depend on other + // phony rules or real files. Phony can be called on the same name multiple times to add + // additional dependencies. + Phony(phony string, deps ...Path) PrimaryModule() Module FinalModule() Module @@ -706,6 +710,7 @@ type ModuleBase struct { installFiles Paths checkbuildFiles Paths noticeFile OptionalPath + phonies map[string]Paths // Used by buildTargetSingleton to create checkbuild and per-directory build targets // Only set on the final variant of each module @@ -1075,26 +1080,17 @@ func (m *ModuleBase) generateModuleTarget(ctx ModuleContext) { } if len(allInstalledFiles) > 0 { - name := PathForPhony(ctx, namespacePrefix+ctx.ModuleName()+"-install") - ctx.Build(pctx, BuildParams{ - Rule: blueprint.Phony, - Output: name, - Implicits: allInstalledFiles, - Default: !ctx.Config().EmbeddedInMake(), - }) - deps = append(deps, name) - m.installTarget = name + name := namespacePrefix + ctx.ModuleName() + "-install" + ctx.Phony(name, allInstalledFiles...) + m.installTarget = PathForPhony(ctx, name) + deps = append(deps, m.installTarget) } if len(allCheckbuildFiles) > 0 { - name := PathForPhony(ctx, namespacePrefix+ctx.ModuleName()+"-checkbuild") - ctx.Build(pctx, BuildParams{ - Rule: blueprint.Phony, - Output: name, - Implicits: allCheckbuildFiles, - }) - deps = append(deps, name) - m.checkbuildTarget = name + name := namespacePrefix + ctx.ModuleName() + "-checkbuild" + ctx.Phony(name, allCheckbuildFiles...) + m.checkbuildTarget = PathForPhony(ctx, name) + deps = append(deps, m.checkbuildTarget) } if len(deps) > 0 { @@ -1103,12 +1099,7 @@ func (m *ModuleBase) generateModuleTarget(ctx ModuleContext) { suffix = "-soong" } - name := PathForPhony(ctx, namespacePrefix+ctx.ModuleName()+suffix) - ctx.Build(pctx, BuildParams{ - Rule: blueprint.Phony, - Outputs: []WritablePath{name}, - Implicits: deps, - }) + ctx.Phony(namespacePrefix+ctx.ModuleName()+suffix, deps...) m.blueprintDir = ctx.ModuleDir() } @@ -1282,6 +1273,9 @@ func (m *ModuleBase) GenerateBuildActions(blueprintCtx blueprint.ModuleContext) m.checkbuildFiles = append(m.checkbuildFiles, ctx.checkbuildFiles...) m.initRcPaths = PathsForModuleSrc(ctx, m.commonProperties.Init_rc) m.vintfFragmentsPaths = PathsForModuleSrc(ctx, m.commonProperties.Vintf_fragments) + for k, v := range ctx.phonies { + m.phonies[k] = append(m.phonies[k], v...) + } } else if ctx.Config().AllowMissingDependencies() { // If the module is not enabled it will not create any build rules, nothing will call // ctx.GetMissingDependencies(), and blueprint will consider the missing dependencies to be unhandled @@ -1419,6 +1413,7 @@ type moduleContext struct { installFiles Paths checkbuildFiles Paths module Module + phonies map[string]Paths // For tests buildParams []BuildParams @@ -1533,6 +1528,11 @@ func (m *moduleContext) Build(pctx PackageContext, params BuildParams) { m.bp.Build(pctx.PackageContext, convertBuildParams(params)) } + +func (m *moduleContext) Phony(name string, deps ...Path) { + addPhony(m.config, name, deps...) +} + func (m *moduleContext) GetMissingDependencies() []string { var missingDeps []string missingDeps = append(missingDeps, m.Module().base().commonProperties.MissingDeps...) @@ -2167,9 +2167,8 @@ type buildTargetSingleton struct{} func (c *buildTargetSingleton) GenerateBuildActions(ctx SingletonContext) { var checkbuildDeps Paths - mmTarget := func(dir string) WritablePath { - return PathForPhony(ctx, - "MODULES-IN-"+strings.Replace(filepath.Clean(dir), "/", "-", -1)) + mmTarget := func(dir string) string { + return "MODULES-IN-" + strings.Replace(filepath.Clean(dir), "/", "-", -1) } modulesInDir := make(map[string]Paths) @@ -2195,11 +2194,7 @@ func (c *buildTargetSingleton) GenerateBuildActions(ctx SingletonContext) { } // Create a top-level checkbuild target that depends on all modules - ctx.Build(pctx, BuildParams{ - Rule: blueprint.Phony, - Output: PathForPhony(ctx, "checkbuild"+suffix), - Implicits: checkbuildDeps, - }) + ctx.Phony("checkbuild"+suffix, checkbuildDeps...) // Make will generate the MODULES-IN-* targets if ctx.Config().EmbeddedInMake() { @@ -2223,7 +2218,7 @@ func (c *buildTargetSingleton) GenerateBuildActions(ctx SingletonContext) { for _, dir := range dirs { p := parentDir(dir) if p != "." && p != "/" { - modulesInDir[p] = append(modulesInDir[p], mmTarget(dir)) + modulesInDir[p] = append(modulesInDir[p], PathForPhony(ctx, mmTarget(dir))) } } @@ -2231,14 +2226,7 @@ func (c *buildTargetSingleton) GenerateBuildActions(ctx SingletonContext) { // depends on the MODULES-IN-* targets of all of its subdirectories that contain Android.bp // files. for _, dir := range dirs { - ctx.Build(pctx, BuildParams{ - Rule: blueprint.Phony, - Output: mmTarget(dir), - Implicits: modulesInDir[dir], - // HACK: checkbuild should be an optional build, but force it - // enabled for now in standalone builds - Default: !ctx.Config().EmbeddedInMake(), - }) + ctx.Phony(mmTarget(dir), modulesInDir[dir]...) } // Create (host|host-cross|target)- phony rules to build a reduced checkbuild. @@ -2265,23 +2253,15 @@ func (c *buildTargetSingleton) GenerateBuildActions(ctx SingletonContext) { continue } - name := PathForPhony(ctx, className+"-"+os.Name) - osClass[className] = append(osClass[className], name) + name := className + "-" + os.Name + osClass[className] = append(osClass[className], PathForPhony(ctx, name)) - ctx.Build(pctx, BuildParams{ - Rule: blueprint.Phony, - Output: name, - Implicits: deps, - }) + ctx.Phony(name, deps...) } // Wrap those into host|host-cross|target phony rules for _, class := range SortedStringKeys(osClass) { - ctx.Build(pctx, BuildParams{ - Rule: blueprint.Phony, - Output: PathForPhony(ctx, class), - Implicits: osClass[class], - }) + ctx.Phony(class, osClass[class]...) } } diff --git a/android/phony.go b/android/phony.go new file mode 100644 index 000000000..f8e5a4401 --- /dev/null +++ b/android/phony.go @@ -0,0 +1,75 @@ +// Copyright 2020 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 ( + "sync" + + "github.com/google/blueprint" +) + +var phonyMapOnceKey = NewOnceKey("phony") + +type phonyMap map[string]Paths + +var phonyMapLock sync.Mutex + +func getPhonyMap(config Config) phonyMap { + return config.Once(phonyMapOnceKey, func() interface{} { + return make(phonyMap) + }).(phonyMap) +} + +func addPhony(config Config, name string, deps ...Path) { + phonyMap := getPhonyMap(config) + phonyMapLock.Lock() + defer phonyMapLock.Unlock() + phonyMap[name] = append(phonyMap[name], deps...) +} + +type phonySingleton struct { + phonyMap phonyMap + phonyList []string +} + +var _ SingletonMakeVarsProvider = (*phonySingleton)(nil) + +func (p *phonySingleton) GenerateBuildActions(ctx SingletonContext) { + p.phonyMap = getPhonyMap(ctx.Config()) + p.phonyList = SortedStringKeys(p.phonyMap) + for _, phony := range p.phonyList { + p.phonyMap[phony] = SortedUniquePaths(p.phonyMap[phony]) + } + + if !ctx.Config().EmbeddedInMake() { + for _, phony := range p.phonyList { + ctx.Build(pctx, BuildParams{ + Rule: blueprint.Phony, + Outputs: []WritablePath{PathForPhony(ctx, phony)}, + Implicits: p.phonyMap[phony], + }) + } + } +} + +func (p phonySingleton) MakeVars(ctx MakeVarsContext) { + for _, phony := range p.phonyList { + ctx.Phony(phony, p.phonyMap[phony]...) + } +} + +func phonySingletonFactory() Singleton { + return &phonySingleton{} +} diff --git a/android/register.go b/android/register.go index ccfe01e74..036a8113f 100644 --- a/android/register.go +++ b/android/register.go @@ -104,6 +104,9 @@ func (ctx *Context) Register() { registerMutators(ctx.Context, preArch, preDeps, postDeps, finalDeps) + // Register phony just before makevars so it can write out its phony rules as Make rules + ctx.RegisterSingletonType("phony", SingletonFactoryAdaptor(phonySingletonFactory)) + // Register makevars after other singletons so they can export values through makevars ctx.RegisterSingletonType("makevars", SingletonFactoryAdaptor(makeVarsSingletonFunc)) diff --git a/android/singleton.go b/android/singleton.go index 568398cdd..2c51c6c48 100644 --- a/android/singleton.go +++ b/android/singleton.go @@ -36,6 +36,12 @@ type SingletonContext interface { Variable(pctx PackageContext, name, value string) Rule(pctx PackageContext, name string, params blueprint.RuleParams, argNames ...string) blueprint.Rule Build(pctx PackageContext, params BuildParams) + + // Phony creates a Make-style phony rule, a rule with no commands that can depend on other + // phony rules or real files. Phony can be called on the same name multiple times to add + // additional dependencies. + Phony(name string, deps ...Path) + RequireNinjaVersion(major, minor, micro int) // SetNinjaBuildDir sets the value of the top-level "builddir" Ninja variable @@ -156,6 +162,10 @@ func (s *singletonContextAdaptor) Build(pctx PackageContext, params BuildParams) } +func (s *singletonContextAdaptor) Phony(name string, deps ...Path) { + addPhony(s.Config(), name, deps...) +} + func (s *singletonContextAdaptor) SetNinjaBuildDir(pctx PackageContext, value string) { s.SingletonContext.SetNinjaBuildDir(pctx.PackageContext, value) } diff --git a/android/writedocs.go b/android/writedocs.go index 7262ad810..9e43e80a6 100644 --- a/android/writedocs.go +++ b/android/writedocs.go @@ -69,9 +69,5 @@ func (c *docsSingleton) GenerateBuildActions(ctx SingletonContext) { }) // Add a phony target for building the documentation - ctx.Build(pctx, BuildParams{ - Rule: blueprint.Phony, - Output: PathForPhony(ctx, "soong_docs"), - Input: docsFile, - }) + ctx.Phony("soong_docs", docsFile) } diff --git a/cc/cc.go b/cc/cc.go index 3d3a841ff..0f874f13c 100644 --- a/cc/cc.go +++ b/cc/cc.go @@ -3188,12 +3188,7 @@ func (ks *kytheExtractAllSingleton) GenerateBuildActions(ctx android.SingletonCo }) // TODO(asmundak): Perhaps emit a rule to output a warning if there were no xrefTargets if len(xrefTargets) > 0 { - ctx.Build(pctx, android.BuildParams{ - Rule: blueprint.Phony, - Output: android.PathForPhony(ctx, "xref_cxx"), - Inputs: xrefTargets, - //Default: true, - }) + ctx.Phony("xref_cxx", xrefTargets...) } } diff --git a/java/java.go b/java/java.go index b0865040e..f563a3878 100644 --- a/java/java.go +++ b/java/java.go @@ -2852,11 +2852,7 @@ func (ks *kytheExtractJavaSingleton) GenerateBuildActions(ctx android.SingletonC }) // TODO(asmundak): perhaps emit a rule to output a warning if there were no xrefTargets if len(xrefTargets) > 0 { - ctx.Build(pctx, android.BuildParams{ - Rule: blueprint.Phony, - Output: android.PathForPhony(ctx, "xref_java"), - Inputs: xrefTargets, - }) + ctx.Phony("xref_java", xrefTargets...) } }