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:
Colin Cross 2024-01-18 12:00:20 -08:00
parent 2f0f395cc9
commit 6bc984abca
7 changed files with 141 additions and 185 deletions

View file

@ -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
}

View file

@ -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
}

View file

@ -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)

View file

@ -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{})
}
})
}

View file

@ -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()

View file

@ -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
}

View file

@ -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)
}