Move name memoization out of variables
memoizeFullName was added to variables, rules and pools as an optimization to prevent recomputing the full name repeatedly, but the storage of variables, rules and pools are generally global and not tied to the Context. When running multiple tests in parallel there will be multiple Context objects all trying to update the memoized names on the global variables, causing a data race. Package names were previously memoized via a pkgNames map stored on the Context. Expand pkgNames to a nameTracker object that contains maps for packages, variables, rules and pools, and replace calls to fullName with calls through nameTracker. Test: context_test.go Change-Id: I15040b85a6d1dab9ab3cff44f227b22985acee18
This commit is contained in:
parent
2f0f395cc9
commit
6bc984abca
7 changed files with 141 additions and 185 deletions
128
context.go
128
context.go
|
@ -16,6 +16,7 @@ package blueprint
|
|||
|
||||
import (
|
||||
"bytes"
|
||||
"cmp"
|
||||
"context"
|
||||
"crypto/sha256"
|
||||
"encoding/base64"
|
||||
|
@ -29,6 +30,7 @@ import (
|
|||
"reflect"
|
||||
"runtime"
|
||||
"runtime/pprof"
|
||||
"slices"
|
||||
"sort"
|
||||
"strings"
|
||||
"sync"
|
||||
|
@ -100,7 +102,7 @@ type Context struct {
|
|||
allowMissingDependencies bool
|
||||
|
||||
// set during PrepareBuildActions
|
||||
pkgNames map[*packageContext]string
|
||||
nameTracker *nameTracker
|
||||
liveGlobals *liveTracker
|
||||
globalVariables map[Variable]*ninjaString
|
||||
globalPools map[Pool]*poolDef
|
||||
|
@ -2741,7 +2743,7 @@ func jsonModuleFromModuleInfo(m *moduleInfo) *JsonModule {
|
|||
return result
|
||||
}
|
||||
|
||||
func jsonModuleWithActionsFromModuleInfo(m *moduleInfo) *JsonModule {
|
||||
func jsonModuleWithActionsFromModuleInfo(m *moduleInfo, nameTracker *nameTracker) *JsonModule {
|
||||
result := &JsonModule{
|
||||
jsonModuleName: jsonModuleName{
|
||||
Name: m.Name(),
|
||||
|
@ -2758,17 +2760,17 @@ func jsonModuleWithActionsFromModuleInfo(m *moduleInfo) *JsonModule {
|
|||
Inputs: append(append(append(
|
||||
bDef.InputStrings,
|
||||
bDef.ImplicitStrings...),
|
||||
getNinjaStringsWithNilPkgNames(bDef.Inputs)...),
|
||||
getNinjaStringsWithNilPkgNames(bDef.Implicits)...),
|
||||
getNinjaStrings(bDef.Inputs, nameTracker)...),
|
||||
getNinjaStrings(bDef.Implicits, nameTracker)...),
|
||||
|
||||
Outputs: append(append(append(
|
||||
bDef.OutputStrings,
|
||||
bDef.ImplicitOutputStrings...),
|
||||
getNinjaStringsWithNilPkgNames(bDef.Outputs)...),
|
||||
getNinjaStringsWithNilPkgNames(bDef.ImplicitOutputs)...),
|
||||
getNinjaStrings(bDef.Outputs, nameTracker)...),
|
||||
getNinjaStrings(bDef.ImplicitOutputs, nameTracker)...),
|
||||
}
|
||||
if d, ok := bDef.Variables["description"]; ok {
|
||||
a.Desc = d.Value(nil)
|
||||
a.Desc = d.Value(nameTracker)
|
||||
}
|
||||
actions = append(actions, a)
|
||||
}
|
||||
|
@ -2786,12 +2788,11 @@ func jsonModuleWithActionsFromModuleInfo(m *moduleInfo) *JsonModule {
|
|||
return result
|
||||
}
|
||||
|
||||
// Gets a list of strings from the given list of ninjaStrings by invoking ninjaString.Value with
|
||||
// nil pkgNames on each of the input ninjaStrings.
|
||||
func getNinjaStringsWithNilPkgNames(nStrs []*ninjaString) []string {
|
||||
// Gets a list of strings from the given list of ninjaStrings by invoking ninjaString.Value on each.
|
||||
func getNinjaStrings(nStrs []*ninjaString, nameTracker *nameTracker) []string {
|
||||
var strs []string
|
||||
for _, nstr := range nStrs {
|
||||
strs = append(strs, nstr.Value(nil))
|
||||
strs = append(strs, nstr.Value(nameTracker))
|
||||
}
|
||||
return strs
|
||||
}
|
||||
|
@ -2799,7 +2800,7 @@ func getNinjaStringsWithNilPkgNames(nStrs []*ninjaString) []string {
|
|||
func (c *Context) GetWeightedOutputsFromPredicate(predicate func(*JsonModule) (bool, int)) map[string]int {
|
||||
outputToWeight := make(map[string]int)
|
||||
for _, m := range c.modulesSorted {
|
||||
jmWithActions := jsonModuleWithActionsFromModuleInfo(m)
|
||||
jmWithActions := jsonModuleWithActionsFromModuleInfo(m, c.nameTracker)
|
||||
if ok, weight := predicate(jmWithActions); ok {
|
||||
for _, a := range jmWithActions.Module["Actions"].([]JSONAction) {
|
||||
for _, o := range a.Outputs {
|
||||
|
@ -2831,7 +2832,7 @@ func (c *Context) PrintJSONGraphAndActions(wGraph io.Writer, wActions io.Writer)
|
|||
modulesToActions := make([]*JsonModule, 0)
|
||||
for _, m := range c.modulesSorted {
|
||||
jm := jsonModuleFromModuleInfo(m)
|
||||
jmWithActions := jsonModuleWithActionsFromModuleInfo(m)
|
||||
jmWithActions := jsonModuleWithActionsFromModuleInfo(m, c.nameTracker)
|
||||
for _, d := range m.directDeps {
|
||||
jm.Deps = append(jm.Deps, jsonDep{
|
||||
jsonModuleName: *jsonModuleNameFromModuleInfo(d.module),
|
||||
|
@ -2918,12 +2919,12 @@ func (c *Context) PrepareBuildActions(config interface{}) (deps []string, errs [
|
|||
|
||||
deps = append(deps, depsPackages...)
|
||||
|
||||
c.memoizeFullNames(c.liveGlobals, pkgNames)
|
||||
nameTracker := c.memoizeFullNames(c.liveGlobals, pkgNames)
|
||||
|
||||
// This will panic if it finds a problem since it's a programming error.
|
||||
c.checkForVariableReferenceCycles(c.liveGlobals.variables, pkgNames)
|
||||
c.checkForVariableReferenceCycles(c.liveGlobals.variables, nameTracker)
|
||||
|
||||
c.pkgNames = pkgNames
|
||||
c.nameTracker = nameTracker
|
||||
c.globalVariables = c.liveGlobals.variables
|
||||
c.globalPools = c.liveGlobals.pools
|
||||
c.globalRules = c.liveGlobals.rules
|
||||
|
@ -3862,20 +3863,27 @@ func (c *Context) makeUniquePackageNames(
|
|||
// memoizeFullNames stores the full name of each live global variable, rule and pool since each is
|
||||
// guaranteed to be used at least twice, once in the definition and once for each usage, and many
|
||||
// are used much more than once.
|
||||
func (c *Context) memoizeFullNames(liveGlobals *liveTracker, pkgNames map[*packageContext]string) {
|
||||
func (c *Context) memoizeFullNames(liveGlobals *liveTracker, pkgNames map[*packageContext]string) *nameTracker {
|
||||
nameTracker := &nameTracker{
|
||||
pkgNames: pkgNames,
|
||||
variables: make(map[Variable]string),
|
||||
rules: make(map[Rule]string),
|
||||
pools: make(map[Pool]string),
|
||||
}
|
||||
for v := range liveGlobals.variables {
|
||||
v.memoizeFullName(pkgNames)
|
||||
nameTracker.variables[v] = v.fullName(pkgNames)
|
||||
}
|
||||
for r := range liveGlobals.rules {
|
||||
r.memoizeFullName(pkgNames)
|
||||
nameTracker.rules[r] = r.fullName(pkgNames)
|
||||
}
|
||||
for p := range liveGlobals.pools {
|
||||
p.memoizeFullName(pkgNames)
|
||||
nameTracker.pools[p] = p.fullName(pkgNames)
|
||||
}
|
||||
return nameTracker
|
||||
}
|
||||
|
||||
func (c *Context) checkForVariableReferenceCycles(
|
||||
variables map[Variable]*ninjaString, pkgNames map[*packageContext]string) {
|
||||
variables map[Variable]*ninjaString, nameTracker *nameTracker) {
|
||||
|
||||
visited := make(map[Variable]bool) // variables that were already checked
|
||||
checking := make(map[Variable]bool) // variables actively being checked
|
||||
|
@ -3905,12 +3913,12 @@ func (c *Context) checkForVariableReferenceCycles(
|
|||
msgs := []string{"detected variable reference cycle:"}
|
||||
|
||||
// Iterate backwards through the cycle list.
|
||||
curName := v.fullName(pkgNames)
|
||||
curValue := value.Value(pkgNames)
|
||||
curName := nameTracker.Variable(v)
|
||||
curValue := value.Value(nameTracker)
|
||||
for i := len(cycle) - 1; i >= 0; i-- {
|
||||
next := cycle[i]
|
||||
nextName := next.fullName(pkgNames)
|
||||
nextValue := variables[next].Value(pkgNames)
|
||||
nextName := nameTracker.Variable(next)
|
||||
nextValue := variables[next].Value(nameTracker)
|
||||
|
||||
msgs = append(msgs, fmt.Sprintf(
|
||||
" %q depends on %q", curName, nextName))
|
||||
|
@ -3958,7 +3966,7 @@ func (c *Context) AllTargets() (map[string]string, error) {
|
|||
targets := map[string]string{}
|
||||
var collectTargets = func(actionDefs localBuildActions) error {
|
||||
for _, buildDef := range actionDefs.buildDefs {
|
||||
ruleName := buildDef.Rule.fullName(c.pkgNames)
|
||||
ruleName := c.nameTracker.Rule(buildDef.Rule)
|
||||
for _, output := range append(buildDef.Outputs, buildDef.ImplicitOutputs...) {
|
||||
outputValue, err := output.Eval(c.globalVariables)
|
||||
if err != nil {
|
||||
|
@ -4266,7 +4274,7 @@ func (c *Context) writeBuildFileHeader(nw *ninjaWriter) error {
|
|||
|
||||
var pkgs []pkgAssociation
|
||||
maxNameLen := 0
|
||||
for pkg, name := range c.pkgNames {
|
||||
for pkg, name := range c.nameTracker.pkgNames {
|
||||
pkgs = append(pkgs, pkgAssociation{
|
||||
PkgName: name,
|
||||
PkgPath: pkg.pkgPath,
|
||||
|
@ -4319,7 +4327,7 @@ func (c *Context) writeSubninjas(nw *ninjaWriter) error {
|
|||
|
||||
func (c *Context) writeBuildDir(nw *ninjaWriter) error {
|
||||
if c.outDir != nil {
|
||||
err := nw.Assign("builddir", c.outDir.Value(c.pkgNames))
|
||||
err := nw.Assign("builddir", c.outDir.Value(c.nameTracker))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -4332,29 +4340,6 @@ func (c *Context) writeBuildDir(nw *ninjaWriter) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
type globalEntity interface {
|
||||
fullName(pkgNames map[*packageContext]string) string
|
||||
}
|
||||
|
||||
type globalEntitySorter struct {
|
||||
pkgNames map[*packageContext]string
|
||||
entities []globalEntity
|
||||
}
|
||||
|
||||
func (s *globalEntitySorter) Len() int {
|
||||
return len(s.entities)
|
||||
}
|
||||
|
||||
func (s *globalEntitySorter) Less(i, j int) bool {
|
||||
iName := s.entities[i].fullName(s.pkgNames)
|
||||
jName := s.entities[j].fullName(s.pkgNames)
|
||||
return iName < jName
|
||||
}
|
||||
|
||||
func (s *globalEntitySorter) Swap(i, j int) {
|
||||
s.entities[i], s.entities[j] = s.entities[j], s.entities[i]
|
||||
}
|
||||
|
||||
func (c *Context) writeGlobalVariables(nw *ninjaWriter) error {
|
||||
visited := make(map[Variable]bool)
|
||||
|
||||
|
@ -4373,7 +4358,7 @@ func (c *Context) writeGlobalVariables(nw *ninjaWriter) error {
|
|||
}
|
||||
}
|
||||
|
||||
err := nw.Assign(v.fullName(c.pkgNames), value.Value(c.pkgNames))
|
||||
err := nw.Assign(c.nameTracker.Variable(v), value.Value(c.nameTracker))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -4386,15 +4371,16 @@ func (c *Context) writeGlobalVariables(nw *ninjaWriter) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
globalVariables := make([]globalEntity, 0, len(c.globalVariables))
|
||||
globalVariables := make([]Variable, 0, len(c.globalVariables))
|
||||
for variable := range c.globalVariables {
|
||||
globalVariables = append(globalVariables, variable)
|
||||
}
|
||||
|
||||
sort.Sort(&globalEntitySorter{c.pkgNames, globalVariables})
|
||||
slices.SortFunc(globalVariables, func(a, b Variable) int {
|
||||
return cmp.Compare(c.nameTracker.Variable(a), c.nameTracker.Variable(b))
|
||||
})
|
||||
|
||||
for _, entity := range globalVariables {
|
||||
v := entity.(Variable)
|
||||
for _, v := range globalVariables {
|
||||
if !visited[v] {
|
||||
err := walk(v)
|
||||
if err != nil {
|
||||
|
@ -4407,16 +4393,17 @@ func (c *Context) writeGlobalVariables(nw *ninjaWriter) error {
|
|||
}
|
||||
|
||||
func (c *Context) writeGlobalPools(nw *ninjaWriter) error {
|
||||
globalPools := make([]globalEntity, 0, len(c.globalPools))
|
||||
globalPools := make([]Pool, 0, len(c.globalPools))
|
||||
for pool := range c.globalPools {
|
||||
globalPools = append(globalPools, pool)
|
||||
}
|
||||
|
||||
sort.Sort(&globalEntitySorter{c.pkgNames, globalPools})
|
||||
slices.SortFunc(globalPools, func(a, b Pool) int {
|
||||
return cmp.Compare(c.nameTracker.Pool(a), c.nameTracker.Pool(b))
|
||||
})
|
||||
|
||||
for _, entity := range globalPools {
|
||||
pool := entity.(Pool)
|
||||
name := pool.fullName(c.pkgNames)
|
||||
for _, pool := range globalPools {
|
||||
name := c.nameTracker.Pool(pool)
|
||||
def := c.globalPools[pool]
|
||||
err := def.WriteTo(nw, name)
|
||||
if err != nil {
|
||||
|
@ -4433,18 +4420,19 @@ func (c *Context) writeGlobalPools(nw *ninjaWriter) error {
|
|||
}
|
||||
|
||||
func (c *Context) writeGlobalRules(nw *ninjaWriter) error {
|
||||
globalRules := make([]globalEntity, 0, len(c.globalRules))
|
||||
globalRules := make([]Rule, 0, len(c.globalRules))
|
||||
for rule := range c.globalRules {
|
||||
globalRules = append(globalRules, rule)
|
||||
}
|
||||
|
||||
sort.Sort(&globalEntitySorter{c.pkgNames, globalRules})
|
||||
slices.SortFunc(globalRules, func(a, b Rule) int {
|
||||
return cmp.Compare(c.nameTracker.Rule(a), c.nameTracker.Rule(b))
|
||||
})
|
||||
|
||||
for _, entity := range globalRules {
|
||||
rule := entity.(Rule)
|
||||
name := rule.fullName(c.pkgNames)
|
||||
for _, rule := range globalRules {
|
||||
name := c.nameTracker.Rule(rule)
|
||||
def := c.globalRules[rule]
|
||||
err := def.WriteTo(nw, name, c.pkgNames)
|
||||
err := def.WriteTo(nw, name, c.nameTracker)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -4766,7 +4754,7 @@ func (c *Context) writeLocalBuildActions(nw *ninjaWriter,
|
|||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
err = nw.Assign(name, value.Value(c.pkgNames))
|
||||
err = nw.Assign(name, value.Value(c.nameTracker))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -4789,7 +4777,7 @@ func (c *Context) writeLocalBuildActions(nw *ninjaWriter,
|
|||
panic(err)
|
||||
}
|
||||
|
||||
err = def.WriteTo(nw, name, c.pkgNames)
|
||||
err = def.WriteTo(nw, name, c.nameTracker)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -4802,7 +4790,7 @@ func (c *Context) writeLocalBuildActions(nw *ninjaWriter,
|
|||
|
||||
// Write the build definitions.
|
||||
for _, buildDef := range defs.buildDefs {
|
||||
err := buildDef.WriteTo(nw, c.pkgNames)
|
||||
err := buildDef.WriteTo(nw, c.nameTracker)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
|
@ -229,8 +229,7 @@ func parseRuleParams(scope scope, params *RuleParams) (*ruleDef,
|
|||
return r, nil
|
||||
}
|
||||
|
||||
func (r *ruleDef) WriteTo(nw *ninjaWriter, name string,
|
||||
pkgNames map[*packageContext]string) error {
|
||||
func (r *ruleDef) WriteTo(nw *ninjaWriter, name string, nameTracker *nameTracker) error {
|
||||
|
||||
if r.Comment != "" {
|
||||
err := nw.Comment(r.Comment)
|
||||
|
@ -245,13 +244,13 @@ func (r *ruleDef) WriteTo(nw *ninjaWriter, name string,
|
|||
}
|
||||
|
||||
if r.Pool != nil {
|
||||
err = nw.ScopedAssign("pool", r.Pool.fullName(pkgNames))
|
||||
err = nw.ScopedAssign("pool", nameTracker.Pool(r.Pool))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
err = writeVariables(nw, r.Variables, pkgNames)
|
||||
err = writeVariables(nw, r.Variables, nameTracker)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -415,10 +414,10 @@ func parseBuildParams(scope scope, params *BuildParams,
|
|||
return b, nil
|
||||
}
|
||||
|
||||
func (b *buildDef) WriteTo(nw *ninjaWriter, pkgNames map[*packageContext]string) error {
|
||||
func (b *buildDef) WriteTo(nw *ninjaWriter, nameTracker *nameTracker) error {
|
||||
var (
|
||||
comment = b.Comment
|
||||
rule = b.Rule.fullName(pkgNames)
|
||||
rule = nameTracker.Rule(b.Rule)
|
||||
outputs = b.Outputs
|
||||
implicitOuts = b.ImplicitOutputs
|
||||
explicitDeps = b.Inputs
|
||||
|
@ -441,12 +440,12 @@ func (b *buildDef) WriteTo(nw *ninjaWriter, pkgNames map[*packageContext]string)
|
|||
err := nw.Build(comment, rule, outputs, implicitOuts, explicitDeps, implicitDeps, orderOnlyDeps, validations,
|
||||
outputStrings, implicitOutStrings, explicitDepStrings,
|
||||
implicitDepStrings, orderOnlyDepStrings, validationStrings,
|
||||
pkgNames)
|
||||
nameTracker)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = writeVariables(nw, b.Variables, pkgNames)
|
||||
err = writeVariables(nw, b.Variables, nameTracker)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -458,8 +457,8 @@ func (b *buildDef) WriteTo(nw *ninjaWriter, pkgNames map[*packageContext]string)
|
|||
args := make([]nameValuePair, 0, len(b.Args))
|
||||
|
||||
for argVar, value := range b.Args {
|
||||
fullName := argVar.fullName(pkgNames)
|
||||
args = append(args, nameValuePair{fullName, value.Value(pkgNames)})
|
||||
fullName := nameTracker.Variable(argVar)
|
||||
args = append(args, nameValuePair{fullName, value.Value(nameTracker)})
|
||||
}
|
||||
sort.Slice(args, func(i, j int) bool { return args[i].name < args[j].name })
|
||||
|
||||
|
@ -471,7 +470,7 @@ func (b *buildDef) WriteTo(nw *ninjaWriter, pkgNames map[*packageContext]string)
|
|||
}
|
||||
|
||||
if !b.Optional {
|
||||
err = nw.Default(pkgNames, outputs, outputStrings)
|
||||
err = nw.Default(nameTracker, outputs, outputStrings)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -480,8 +479,7 @@ func (b *buildDef) WriteTo(nw *ninjaWriter, pkgNames map[*packageContext]string)
|
|||
return nw.BlankLine()
|
||||
}
|
||||
|
||||
func writeVariables(nw *ninjaWriter, variables map[string]*ninjaString,
|
||||
pkgNames map[*packageContext]string) error {
|
||||
func writeVariables(nw *ninjaWriter, variables map[string]*ninjaString, nameTracker *nameTracker) error {
|
||||
var keys []string
|
||||
for k := range variables {
|
||||
keys = append(keys, k)
|
||||
|
@ -489,7 +487,7 @@ func writeVariables(nw *ninjaWriter, variables map[string]*ninjaString,
|
|||
sort.Strings(keys)
|
||||
|
||||
for _, name := range keys {
|
||||
err := nw.ScopedAssign(name, variables[name].Value(pkgNames))
|
||||
err := nw.ScopedAssign(name, variables[name].Value(nameTracker))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
|
@ -324,17 +324,16 @@ func parseNinjaOrSimpleStrings(scope scope, strs []string) ([]*ninjaString, []st
|
|||
return ninjaStrings, simpleStrings, nil
|
||||
}
|
||||
|
||||
func (n *ninjaString) Value(pkgNames map[*packageContext]string) string {
|
||||
func (n *ninjaString) Value(nameTracker *nameTracker) string {
|
||||
if n.variables == nil || len(*n.variables) == 0 {
|
||||
return defaultEscaper.Replace(n.str)
|
||||
}
|
||||
str := &strings.Builder{}
|
||||
n.ValueWithEscaper(str, pkgNames, defaultEscaper)
|
||||
n.ValueWithEscaper(str, nameTracker, defaultEscaper)
|
||||
return str.String()
|
||||
}
|
||||
|
||||
func (n *ninjaString) ValueWithEscaper(w io.StringWriter, pkgNames map[*packageContext]string,
|
||||
escaper *strings.Replacer) {
|
||||
func (n *ninjaString) ValueWithEscaper(w io.StringWriter, nameTracker *nameTracker, escaper *strings.Replacer) {
|
||||
|
||||
if n.variables == nil || len(*n.variables) == 0 {
|
||||
w.WriteString(escaper.Replace(n.str))
|
||||
|
@ -348,7 +347,7 @@ func (n *ninjaString) ValueWithEscaper(w io.StringWriter, pkgNames map[*packageC
|
|||
w.WriteString("$ ")
|
||||
} else {
|
||||
w.WriteString("${")
|
||||
w.WriteString(v.variable.fullName(pkgNames))
|
||||
w.WriteString(nameTracker.Variable(v.variable))
|
||||
w.WriteString("}")
|
||||
}
|
||||
i = int(v.end)
|
||||
|
|
|
@ -164,7 +164,7 @@ func TestParseNinjaString(t *testing.T) {
|
|||
|
||||
output, err := parseNinjaString(scope, testCase.input)
|
||||
if err == nil {
|
||||
if g, w := output.Value(nil), testCase.value; g != w {
|
||||
if g, w := output.Value(&nameTracker{}), testCase.value; g != w {
|
||||
t.Errorf("incorrect Value output, want %q, got %q", w, g)
|
||||
}
|
||||
|
||||
|
@ -191,7 +191,11 @@ func TestParseNinjaString(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestParseNinjaStringWithImportedVar(t *testing.T) {
|
||||
ImpVar := &staticVariable{name_: "ImpVar", fullName_: "g.impPkg.ImpVar"}
|
||||
pctx := &packageContext{}
|
||||
pkgNames := map[*packageContext]string{
|
||||
pctx: "impPkg",
|
||||
}
|
||||
ImpVar := &staticVariable{pctx: pctx, name_: "ImpVar"}
|
||||
impScope := newScope(nil)
|
||||
impScope.AddVariable(ImpVar)
|
||||
scope := newScope(nil)
|
||||
|
@ -211,7 +215,7 @@ func TestParseNinjaStringWithImportedVar(t *testing.T) {
|
|||
t.Errorf(" got: %#v", *output.variables)
|
||||
}
|
||||
|
||||
if g, w := output.Value(nil), "abc def ${g.impPkg.ImpVar} ghi"; g != w {
|
||||
if g, w := output.Value(&nameTracker{pkgNames: pkgNames}), "abc def ${g.impPkg.ImpVar} ghi"; g != w {
|
||||
t.Errorf("incorrect Value output, want %q got %q", w, g)
|
||||
}
|
||||
}
|
||||
|
@ -289,7 +293,7 @@ func Test_parseNinjaOrSimpleStrings(t *testing.T) {
|
|||
if gotNinjaStrings != nil {
|
||||
evaluatedNinjaStrings = make([]string, 0, len(gotNinjaStrings))
|
||||
for _, ns := range gotNinjaStrings {
|
||||
evaluatedNinjaStrings = append(evaluatedNinjaStrings, ns.Value(nil))
|
||||
evaluatedNinjaStrings = append(evaluatedNinjaStrings, ns.Value(&nameTracker{}))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -364,7 +368,7 @@ func BenchmarkNinjaString_Value(b *testing.B) {
|
|||
b.Run(strconv.Itoa(l), func(b *testing.B) {
|
||||
b.ReportAllocs()
|
||||
for n := 0; n < b.N; n++ {
|
||||
ns.Value(nil)
|
||||
ns.Value(&nameTracker{})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
@ -377,7 +381,7 @@ func BenchmarkNinjaString_Value(b *testing.B) {
|
|||
b.Run(strconv.Itoa(l), func(b *testing.B) {
|
||||
b.ReportAllocs()
|
||||
for n := 0; n < b.N; n++ {
|
||||
ns.Value(nil)
|
||||
ns.Value(&nameTracker{})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
@ -394,7 +398,7 @@ func BenchmarkNinjaString_Value(b *testing.B) {
|
|||
b.Run(strconv.Itoa(l), func(b *testing.B) {
|
||||
b.ReportAllocs()
|
||||
for n := 0; n < b.N; n++ {
|
||||
ns.Value(nil)
|
||||
ns.Value(&nameTracker{})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
|
|
@ -117,7 +117,7 @@ func (n *ninjaWriter) Build(comment string, rule string, outputs, implicitOuts,
|
|||
explicitDeps, implicitDeps, orderOnlyDeps, validations []*ninjaString,
|
||||
outputStrings, implicitOutStrings, explicitDepStrings,
|
||||
implicitDepStrings, orderOnlyDepStrings, validationStrings []string,
|
||||
pkgNames map[*packageContext]string) error {
|
||||
nameTracker *nameTracker) error {
|
||||
|
||||
n.justDidBlankLine = false
|
||||
|
||||
|
@ -144,7 +144,7 @@ func (n *ninjaWriter) Build(comment string, rule string, outputs, implicitOuts,
|
|||
}
|
||||
for _, output := range outputs {
|
||||
wrapper.Space()
|
||||
output.ValueWithEscaper(wrapper, pkgNames, outputEscaper)
|
||||
output.ValueWithEscaper(wrapper, nameTracker, outputEscaper)
|
||||
}
|
||||
|
||||
if len(implicitOuts) > 0 || len(implicitOutStrings) > 0 {
|
||||
|
@ -156,7 +156,7 @@ func (n *ninjaWriter) Build(comment string, rule string, outputs, implicitOuts,
|
|||
}
|
||||
for _, out := range implicitOuts {
|
||||
wrapper.Space()
|
||||
out.ValueWithEscaper(wrapper, pkgNames, outputEscaper)
|
||||
out.ValueWithEscaper(wrapper, nameTracker, outputEscaper)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -170,7 +170,7 @@ func (n *ninjaWriter) Build(comment string, rule string, outputs, implicitOuts,
|
|||
}
|
||||
for _, dep := range explicitDeps {
|
||||
wrapper.Space()
|
||||
dep.ValueWithEscaper(wrapper, pkgNames, inputEscaper)
|
||||
dep.ValueWithEscaper(wrapper, nameTracker, inputEscaper)
|
||||
}
|
||||
|
||||
if len(implicitDeps) > 0 || len(implicitDepStrings) > 0 {
|
||||
|
@ -182,7 +182,7 @@ func (n *ninjaWriter) Build(comment string, rule string, outputs, implicitOuts,
|
|||
}
|
||||
for _, dep := range implicitDeps {
|
||||
wrapper.Space()
|
||||
dep.ValueWithEscaper(wrapper, pkgNames, inputEscaper)
|
||||
dep.ValueWithEscaper(wrapper, nameTracker, inputEscaper)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -195,7 +195,7 @@ func (n *ninjaWriter) Build(comment string, rule string, outputs, implicitOuts,
|
|||
}
|
||||
for _, dep := range orderOnlyDeps {
|
||||
wrapper.Space()
|
||||
dep.ValueWithEscaper(wrapper, pkgNames, inputEscaper)
|
||||
dep.ValueWithEscaper(wrapper, nameTracker, inputEscaper)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -208,7 +208,7 @@ func (n *ninjaWriter) Build(comment string, rule string, outputs, implicitOuts,
|
|||
}
|
||||
for _, dep := range validations {
|
||||
wrapper.Space()
|
||||
dep.ValueWithEscaper(wrapper, pkgNames, inputEscaper)
|
||||
dep.ValueWithEscaper(wrapper, nameTracker, inputEscaper)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -261,7 +261,7 @@ func (n *ninjaWriter) ScopedAssign(name, value string) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (n *ninjaWriter) Default(pkgNames map[*packageContext]string, targets []*ninjaString, targetStrings []string) error {
|
||||
func (n *ninjaWriter) Default(nameTracker *nameTracker, targets []*ninjaString, targetStrings []string) error {
|
||||
n.justDidBlankLine = false
|
||||
|
||||
const lineWrapLen = len(" $")
|
||||
|
@ -280,7 +280,7 @@ func (n *ninjaWriter) Default(pkgNames map[*packageContext]string, targets []*ni
|
|||
}
|
||||
for _, target := range targets {
|
||||
wrapper.Space()
|
||||
target.ValueWithEscaper(wrapper, pkgNames, outputEscaper)
|
||||
target.ValueWithEscaper(wrapper, nameTracker, outputEscaper)
|
||||
}
|
||||
|
||||
return wrapper.Flush()
|
||||
|
|
|
@ -250,10 +250,9 @@ func (p *packageContext) ImportAs(as, pkgPath string) {
|
|||
}
|
||||
|
||||
type staticVariable struct {
|
||||
pctx *packageContext
|
||||
name_ string
|
||||
value_ string
|
||||
fullName_ string
|
||||
pctx *packageContext
|
||||
name_ string
|
||||
value_ string
|
||||
}
|
||||
|
||||
// StaticVariable returns a Variable whose value does not depend on any
|
||||
|
@ -294,16 +293,9 @@ func (v *staticVariable) name() string {
|
|||
}
|
||||
|
||||
func (v *staticVariable) fullName(pkgNames map[*packageContext]string) string {
|
||||
if v.fullName_ != "" {
|
||||
return v.fullName_
|
||||
}
|
||||
return packageNamespacePrefix(pkgNames[v.pctx]) + v.name_
|
||||
}
|
||||
|
||||
func (v *staticVariable) memoizeFullName(pkgNames map[*packageContext]string) {
|
||||
v.fullName_ = v.fullName(pkgNames)
|
||||
}
|
||||
|
||||
func (v *staticVariable) value(VariableFuncContext, interface{}) (*ninjaString, error) {
|
||||
ninjaStr, err := parseNinjaString(v.pctx.scope, v.value_)
|
||||
if err != nil {
|
||||
|
@ -318,10 +310,9 @@ func (v *staticVariable) String() string {
|
|||
}
|
||||
|
||||
type variableFunc struct {
|
||||
pctx *packageContext
|
||||
name_ string
|
||||
value_ func(VariableFuncContext, interface{}) (string, error)
|
||||
fullName_ string
|
||||
pctx *packageContext
|
||||
name_ string
|
||||
value_ func(VariableFuncContext, interface{}) (string, error)
|
||||
}
|
||||
|
||||
// VariableFuncContext is passed to VariableFunc functions.
|
||||
|
@ -430,16 +421,9 @@ func (v *variableFunc) name() string {
|
|||
}
|
||||
|
||||
func (v *variableFunc) fullName(pkgNames map[*packageContext]string) string {
|
||||
if v.fullName_ != "" {
|
||||
return v.fullName_
|
||||
}
|
||||
return packageNamespacePrefix(pkgNames[v.pctx]) + v.name_
|
||||
}
|
||||
|
||||
func (v *variableFunc) memoizeFullName(pkgNames map[*packageContext]string) {
|
||||
v.fullName_ = v.fullName(pkgNames)
|
||||
}
|
||||
|
||||
func (v *variableFunc) value(ctx VariableFuncContext, config interface{}) (*ninjaString, error) {
|
||||
value, err := v.value_(ctx, config)
|
||||
if err != nil {
|
||||
|
@ -500,10 +484,6 @@ func (v *argVariable) fullName(pkgNames map[*packageContext]string) string {
|
|||
return v.name_
|
||||
}
|
||||
|
||||
func (v *argVariable) memoizeFullName(pkgNames map[*packageContext]string) {
|
||||
// Nothing to do, full name is known at initialization.
|
||||
}
|
||||
|
||||
func (v *argVariable) value(ctx VariableFuncContext, config interface{}) (*ninjaString, error) {
|
||||
return nil, errVariableIsArg
|
||||
}
|
||||
|
@ -513,10 +493,9 @@ func (v *argVariable) String() string {
|
|||
}
|
||||
|
||||
type staticPool struct {
|
||||
pctx *packageContext
|
||||
name_ string
|
||||
params PoolParams
|
||||
fullName_ string
|
||||
pctx *packageContext
|
||||
name_ string
|
||||
params PoolParams
|
||||
}
|
||||
|
||||
// StaticPool returns a Pool whose value does not depend on any configuration
|
||||
|
@ -558,16 +537,9 @@ func (p *staticPool) name() string {
|
|||
}
|
||||
|
||||
func (p *staticPool) fullName(pkgNames map[*packageContext]string) string {
|
||||
if p.fullName_ != "" {
|
||||
return p.fullName_
|
||||
}
|
||||
return packageNamespacePrefix(pkgNames[p.pctx]) + p.name_
|
||||
}
|
||||
|
||||
func (p *staticPool) memoizeFullName(pkgNames map[*packageContext]string) {
|
||||
p.fullName_ = p.fullName(pkgNames)
|
||||
}
|
||||
|
||||
func (p *staticPool) def(config interface{}) (*poolDef, error) {
|
||||
def, err := parsePoolParams(p.pctx.scope, &p.params)
|
||||
if err != nil {
|
||||
|
@ -584,7 +556,6 @@ type poolFunc struct {
|
|||
pctx *packageContext
|
||||
name_ string
|
||||
paramsFunc func(interface{}) (PoolParams, error)
|
||||
fullName_ string
|
||||
}
|
||||
|
||||
// PoolFunc returns a Pool whose value is determined by a function that takes a
|
||||
|
@ -629,16 +600,9 @@ func (p *poolFunc) name() string {
|
|||
}
|
||||
|
||||
func (p *poolFunc) fullName(pkgNames map[*packageContext]string) string {
|
||||
if p.fullName_ != "" {
|
||||
return p.fullName_
|
||||
}
|
||||
return packageNamespacePrefix(pkgNames[p.pctx]) + p.name_
|
||||
}
|
||||
|
||||
func (p *poolFunc) memoizeFullName(pkgNames map[*packageContext]string) {
|
||||
p.fullName_ = p.fullName(pkgNames)
|
||||
}
|
||||
|
||||
func (p *poolFunc) def(config interface{}) (*poolDef, error) {
|
||||
params, err := p.paramsFunc(config)
|
||||
if err != nil {
|
||||
|
@ -671,10 +635,6 @@ func (p *builtinPool) fullName(pkgNames map[*packageContext]string) string {
|
|||
return p.name_
|
||||
}
|
||||
|
||||
func (p *builtinPool) memoizeFullName(pkgNames map[*packageContext]string) {
|
||||
// Nothing to do, full name is known at initialization.
|
||||
}
|
||||
|
||||
func (p *builtinPool) def(config interface{}) (*poolDef, error) {
|
||||
return nil, errPoolIsBuiltin
|
||||
}
|
||||
|
@ -696,7 +656,6 @@ type staticRule struct {
|
|||
params RuleParams
|
||||
argNames map[string]bool
|
||||
scope_ *basicScope
|
||||
fullName_ string
|
||||
sync.Mutex // protects scope_ during lazy creation
|
||||
}
|
||||
|
||||
|
@ -764,16 +723,9 @@ func (r *staticRule) name() string {
|
|||
}
|
||||
|
||||
func (r *staticRule) fullName(pkgNames map[*packageContext]string) string {
|
||||
if r.fullName_ != "" {
|
||||
return r.fullName_
|
||||
}
|
||||
return packageNamespacePrefix(pkgNames[r.pctx]) + r.name_
|
||||
}
|
||||
|
||||
func (r *staticRule) memoizeFullName(pkgNames map[*packageContext]string) {
|
||||
r.fullName_ = r.fullName(pkgNames)
|
||||
}
|
||||
|
||||
func (r *staticRule) def(interface{}) (*ruleDef, error) {
|
||||
def, err := parseRuleParams(r.scope(), &r.params)
|
||||
if err != nil {
|
||||
|
@ -809,7 +761,6 @@ type ruleFunc struct {
|
|||
paramsFunc func(interface{}) (RuleParams, error)
|
||||
argNames map[string]bool
|
||||
scope_ *basicScope
|
||||
fullName_ string
|
||||
sync.Mutex // protects scope_ during lazy creation
|
||||
}
|
||||
|
||||
|
@ -878,16 +829,9 @@ func (r *ruleFunc) name() string {
|
|||
}
|
||||
|
||||
func (r *ruleFunc) fullName(pkgNames map[*packageContext]string) string {
|
||||
if r.fullName_ != "" {
|
||||
return r.fullName_
|
||||
}
|
||||
return packageNamespacePrefix(pkgNames[r.pctx]) + r.name_
|
||||
}
|
||||
|
||||
func (r *ruleFunc) memoizeFullName(pkgNames map[*packageContext]string) {
|
||||
r.fullName_ = r.fullName(pkgNames)
|
||||
}
|
||||
|
||||
func (r *ruleFunc) def(config interface{}) (*ruleDef, error) {
|
||||
params, err := r.paramsFunc(config)
|
||||
if err != nil {
|
||||
|
@ -939,10 +883,6 @@ func (r *builtinRule) fullName(pkgNames map[*packageContext]string) string {
|
|||
return r.name_
|
||||
}
|
||||
|
||||
func (r *builtinRule) memoizeFullName(pkgNames map[*packageContext]string) {
|
||||
// Nothing to do, full name is known at initialization.
|
||||
}
|
||||
|
||||
func (r *builtinRule) def(config interface{}) (*ruleDef, error) {
|
||||
return nil, errRuleIsBuiltin
|
||||
}
|
||||
|
|
49
scope.go
49
scope.go
|
@ -28,7 +28,6 @@ type Variable interface {
|
|||
packageContext() *packageContext
|
||||
name() string // "foo"
|
||||
fullName(pkgNames map[*packageContext]string) string // "pkg.foo" or "path.to.pkg.foo"
|
||||
memoizeFullName(pkgNames map[*packageContext]string) // precompute fullName if desired
|
||||
value(ctx VariableFuncContext, config interface{}) (*ninjaString, error)
|
||||
String() string
|
||||
}
|
||||
|
@ -39,7 +38,6 @@ type Pool interface {
|
|||
packageContext() *packageContext
|
||||
name() string // "foo"
|
||||
fullName(pkgNames map[*packageContext]string) string // "pkg.foo" or "path.to.pkg.foo"
|
||||
memoizeFullName(pkgNames map[*packageContext]string) // precompute fullName if desired
|
||||
def(config interface{}) (*poolDef, error)
|
||||
String() string
|
||||
}
|
||||
|
@ -50,7 +48,6 @@ type Rule interface {
|
|||
packageContext() *packageContext
|
||||
name() string // "foo"
|
||||
fullName(pkgNames map[*packageContext]string) string // "pkg.foo" or "path.to.pkg.foo"
|
||||
memoizeFullName(pkgNames map[*packageContext]string) // precompute fullName if desired
|
||||
def(config interface{}) (*ruleDef, error)
|
||||
scope() *basicScope
|
||||
isArg(argName string) bool
|
||||
|
@ -369,10 +366,6 @@ func (l *localVariable) fullName(pkgNames map[*packageContext]string) string {
|
|||
return l.fullName_
|
||||
}
|
||||
|
||||
func (l *localVariable) memoizeFullName(pkgNames map[*packageContext]string) {
|
||||
// Nothing to do, full name is known at initialization.
|
||||
}
|
||||
|
||||
func (l *localVariable) value(VariableFuncContext, interface{}) (*ninjaString, error) {
|
||||
return l.value_, nil
|
||||
}
|
||||
|
@ -401,10 +394,6 @@ func (l *localRule) fullName(pkgNames map[*packageContext]string) string {
|
|||
return l.fullName_
|
||||
}
|
||||
|
||||
func (l *localRule) memoizeFullName(pkgNames map[*packageContext]string) {
|
||||
// Nothing to do, full name is known at initialization.
|
||||
}
|
||||
|
||||
func (l *localRule) def(interface{}) (*ruleDef, error) {
|
||||
return l.def_, nil
|
||||
}
|
||||
|
@ -420,3 +409,41 @@ func (r *localRule) isArg(argName string) bool {
|
|||
func (r *localRule) String() string {
|
||||
return "<local rule>:" + r.fullName_
|
||||
}
|
||||
|
||||
type nameTracker struct {
|
||||
variables map[Variable]string
|
||||
rules map[Rule]string
|
||||
pools map[Pool]string
|
||||
|
||||
pkgNames map[*packageContext]string
|
||||
}
|
||||
|
||||
func (m *nameTracker) Variable(v Variable) string {
|
||||
if m == nil {
|
||||
return v.fullName(nil)
|
||||
}
|
||||
if name, ok := m.variables[v]; ok {
|
||||
return name
|
||||
}
|
||||
return v.fullName(m.pkgNames)
|
||||
}
|
||||
|
||||
func (m *nameTracker) Rule(r Rule) string {
|
||||
if m == nil {
|
||||
return r.fullName(nil)
|
||||
}
|
||||
if name, ok := m.rules[r]; ok {
|
||||
return name
|
||||
}
|
||||
return r.fullName(m.pkgNames)
|
||||
}
|
||||
|
||||
func (m *nameTracker) Pool(p Pool) string {
|
||||
if m == nil {
|
||||
return p.fullName(nil)
|
||||
}
|
||||
if name, ok := m.pools[p]; ok {
|
||||
return name
|
||||
}
|
||||
return p.fullName(m.pkgNames)
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue