| // 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 ( |
| "io" |
| "runtime" |
| "sort" |
| "strings" |
| "sync" |
| |
| "github.com/google/blueprint" |
| ) |
| |
| //go:generate go run ../../blueprint/gobtools/codegen |
| |
| type phonyMap map[string]Paths |
| |
| // @auto-generate: gob |
| type PhonyInfo struct { |
| Phonies phonyMap |
| } |
| |
| var SingletonPhonyProvider = blueprint.NewSingletonProvider[PhonyInfo]() |
| |
| type phonySingleton struct { |
| phonyMap phonyMap |
| phonyList []string |
| |
| soongDist |
| } |
| |
| var _ SingletonMakeVarsProvider = (*phonySingleton)(nil) |
| |
| func (p *phonySingleton) GenerateBuildActions(ctx SingletonContext) { |
| p.phonyMap = make(phonyMap) |
| ctx.VisitAllModuleProxies(func(m ModuleProxy) { |
| if info, ok := OtherModuleProvider(ctx, m, CommonModuleInfoProvider); ok && info.Phonies != nil { |
| for k, v := range info.Phonies.Phonies { |
| p.phonyMap[k] = append(p.phonyMap[k], v...) |
| } |
| } |
| }) |
| |
| ctx.VisitAllSingletons(func(s blueprint.SingletonProxy) { |
| if info, ok := OtherSingletonProvider(ctx, s, SingletonPhonyProvider); ok { |
| for k, v := range info.Phonies { |
| p.phonyMap[k] = append(p.phonyMap[k], v...) |
| } |
| } |
| }) |
| |
| if !ctx.Config().KatiEnabled() { |
| p.soongDist.collectDists(ctx) |
| // Every dist goal gets an extra dependency on an extra phony with a __dist suffix. On builds without dist |
| // enabled the phony will have no dependencies, on builds with dist enabled the phony will depend on all |
| // the copy rules for that goal. |
| p.soongDist.addDistsToPhonyMap(ctx, p.phonyMap) |
| |
| // Define well-known goals and their dependency graph that they've |
| // traditionally had in make builds. |
| addPhony := func(target string, deps ...Path) { |
| p.phonyMap[target] = append(p.phonyMap[target], deps...) |
| } |
| addPhony("droid", PathForPhony(ctx, "droid_targets")) |
| addPhony("droid_targets", PathForPhony(ctx, "droidcore"), PathForPhony(ctx, "dist_files")) |
| addPhony("droidcore", PathForPhony(ctx, "droidcore-unbundled")) |
| addPhony("dist_files") |
| addPhony("droidcore-unbundled") |
| } |
| |
| // We will sort phonyList in parallel with other stuff later, but for now copy it into |
| // a slice in series so that we don't read and write to phonyMap concurrently. |
| p.phonyList = make([]string, 0, len(p.phonyMap)) |
| for phony := range p.phonyMap { |
| p.phonyList = append(p.phonyList, phony) |
| } |
| |
| type phonyDef struct { |
| name string |
| deps Paths |
| } |
| |
| sortChan := make(chan phonyDef, len(p.phonyMap)) |
| resultsChan := make(chan phonyDef) |
| var wg sync.WaitGroup |
| |
| // Sorting the phony deps in parallel saves about 2 seconds. Nothing runs in parallel with |
| // the phony singleton so it's time off of wall clock. |
| for i := 0; i < 2*runtime.NumCPU(); i++ { |
| wg.Add(1) |
| go func() { |
| for toSort := range sortChan { |
| toSort.deps = SortedUniquePaths(toSort.deps) |
| resultsChan <- toSort |
| } |
| wg.Done() |
| }() |
| } |
| |
| go func() { |
| sort.Strings(p.phonyList) |
| wg.Wait() |
| close(resultsChan) |
| }() |
| |
| for phony, deps := range p.phonyMap { |
| sortChan <- phonyDef{ |
| name: phony, |
| deps: deps, |
| } |
| } |
| close(sortChan) |
| |
| for result := range resultsChan { |
| p.phonyMap[result.name] = result.deps |
| } |
| |
| if !ctx.Config().KatiEnabled() { |
| // The filename suffix style used by soong (".$TARGET_PRODUCT") is not available here, fake it |
| // using katiSuffix ("-$TARGET_PRODUCT"). |
| suffix := "." + strings.TrimPrefix(ctx.Config().katiSuffix, "-") |
| soongPhonyNinja := PathForOutput(ctx, "build"+suffix+".phony.ninja") |
| soongDistFile := PathForOutput(ctx, "build"+suffix+".dist.ninja") |
| soongNoDistFile := PathForOutput(ctx, "build"+suffix+".nodist.ninja") |
| ctx.addSubninja(soongPhonyNinja.String()) |
| ctx.addSubninja(PathForOutput(ctx).String() + "/build" + suffix + ".$dist.ninja") |
| |
| wg := WaitGroupWithErrorCollector{} |
| |
| wg.Go(func() error { |
| f, err := openBufferedFile(absolutePath(soongPhonyNinja.String())) |
| if err != nil { |
| return err |
| } |
| defer f.Close() |
| return p.writeSoongPhonyNinja(f) |
| }) |
| |
| wg.GoWithMultipleErrors(func() []error { |
| return p.soongDist.writeNinjaFiles(soongDistFile, soongNoDistFile, ctx.Config().REWrapperRemoteBuild()) |
| }) |
| |
| wg.Wait() |
| |
| for _, err := range wg.Errors() { |
| ctx.Errorf("%s", err) |
| } |
| } |
| } |
| |
| func (p phonySingleton) MakeVars(ctx MakeVarsContext) { |
| for _, phony := range p.phonyList { |
| ctx.Phony(phony, p.phonyMap[phony]...) |
| } |
| } |
| |
| func phonySingletonFactory() Singleton { |
| return &phonySingleton{} |
| } |
| |
| func (p *phonySingleton) IncrementalSupported() bool { |
| return true |
| } |
| |
| // writeSoongPhonyNinja writes a ninja file containing phony rules for each phony target requested in Soong-only builds. |
| func (p *phonySingleton) writeSoongPhonyNinja(w io.StringWriter) error { |
| var err error |
| write := func(s string) { |
| if err == nil { |
| _, err = w.WriteString(s) |
| } |
| } |
| for _, phony := range p.phonyList { |
| write("build ") |
| write(phony) |
| write(": phony") |
| for _, dep := range p.phonyMap[phony] { |
| write(" ") |
| write(dep.String()) |
| } |
| write("\n") |
| write(" phony_output = true\n") |
| } |
| |
| write("default droid\n") |
| |
| return err |
| } |