diff --git a/config/config.go b/config/config.go index 22859c7..e20ff1f 100644 --- a/config/config.go +++ b/config/config.go @@ -18,6 +18,7 @@ type KindConfig struct { ProvidedDeps []string `json:"providedDeps"` DefaultVisibility []string `json:"defaultVisibility"` SrcsArg string `json:"srcsArg"` + SrcsRuleTemplate string `json:"srcsRuleTemplate"` } func (kc *KindConfig) srcsArg() string { @@ -177,24 +178,27 @@ func (c *Config) GetKind(kind string) *kinds.Kind { SrcsAttr: k.srcsArg(), DefaultVisibility: k.DefaultVisibility, NonGoSources: k.NonGoSources, + SrcsRuleTemplate: k.SrcsRuleTemplate, } } if k, ok := c.TestKinds[kind]; ok { return &kinds.Kind{ - Name: kind, - Type: kinds.Test, - ProvidedDeps: k.ProvidedDeps, - SrcsAttr: k.srcsArg(), - NonGoSources: k.NonGoSources, + Name: kind, + Type: kinds.Test, + ProvidedDeps: k.ProvidedDeps, + SrcsAttr: k.srcsArg(), + NonGoSources: k.NonGoSources, + SrcsRuleTemplate: k.SrcsRuleTemplate, } } if k, ok := c.BinKinds[kind]; ok { return &kinds.Kind{ - Name: kind, - Type: kinds.Bin, - ProvidedDeps: k.ProvidedDeps, - SrcsAttr: k.srcsArg(), - NonGoSources: k.NonGoSources, + Name: kind, + Type: kinds.Bin, + ProvidedDeps: k.ProvidedDeps, + SrcsAttr: k.srcsArg(), + NonGoSources: k.NonGoSources, + SrcsRuleTemplate: k.SrcsRuleTemplate, } } if c.base != nil { diff --git a/eval/eval.go b/eval/eval.go index 14f0302..2d9464d 100644 --- a/eval/eval.go +++ b/eval/eval.go @@ -31,12 +31,8 @@ func LookLikeBuildLabel(l string) bool { return strings.HasPrefix("//", l) } -func (e *Eval) EvalGlobs(dir string, rule *build.Rule, attrName string) ([]string, error) { - return e.evalGlobs(dir, rule.Attr(attrName)) -} - -func (e *Eval) evalGlobs(dir string, val build.Expr) ([]string, error) { - switch expr := val.(type) { +func (e *Eval) EvalGlobs(dir string, srcs build.Expr) ([]string, error) { + switch expr := srcs.(type) { case *build.CallExpr: globArgs := parseGlob(expr) if globArgs == nil { @@ -47,11 +43,11 @@ func (e *Eval) evalGlobs(dir string, val build.Expr) ([]string, error) { if expr.Op != "+" { return nil, fmt.Errorf("encountered a binary expression with operation %s. Only + is supported", expr.Op) } - x, err := e.evalGlobs(dir, expr.X) + x, err := e.EvalGlobs(dir, expr.X) if err != nil { return nil, err } - y, err := e.evalGlobs(dir, expr.Y) + y, err := e.EvalGlobs(dir, expr.Y) if err != nil { return nil, err } @@ -61,8 +57,8 @@ func (e *Eval) evalGlobs(dir string, val build.Expr) ([]string, error) { } } -func (e *Eval) BuildSources(plzPath, dir string, rule *build.Rule, srcsArg string) ([]string, error) { - srcs, err := e.EvalGlobs(dir, rule, srcsArg) +func (e *Eval) BuildSources(plzPath, dir string, srcsExpr build.Expr) ([]string, error) { + srcs, err := e.EvalGlobs(dir, srcsExpr) if err != nil { return nil, err } diff --git a/eval/eval_test.go b/eval/eval_test.go index bf14d28..30c746d 100644 --- a/eval/eval_test.go +++ b/eval/eval_test.go @@ -90,7 +90,7 @@ func TestEvalGlob(t *testing.T) { file, err := build.ParseBuild(test.name, []byte(test.code)) require.NoError(t, err) require.Len(t, file.Stmt, 1) - got, err := e.evalGlobs("test_project", file.Stmt[0]) + got, err := e.EvalGlobs("test_project", file.Stmt[0]) require.NoError(t, err) assert.ElementsMatch(t, test.expected, got) }) diff --git a/generate/generate.go b/generate/generate.go index 71bea83..a2995ae 100644 --- a/generate/generate.go +++ b/generate/generate.go @@ -251,7 +251,7 @@ func (u *updater) addNewModules(conf *config.Config) error { // the source doesn't actually exist. In which case, this should be removed from the rule, as the user likely deleted // the file. func (u *updater) allSources(conf *config.Config, r *rule, sourceMap map[string]*GoFile) (passedSources []string, goFiles map[string]*GoFile, err error) { - srcs, err := u.eval.BuildSources(conf.GetPlzPath(), r.dir, r.Rule, r.SrcsAttr()) + srcs, err := u.eval.BuildSources(conf.GetPlzPath(), r.dir, r.Srcs()) if err != nil { return nil, nil, err } @@ -492,7 +492,7 @@ func (u *updater) unallocatedSources(srcs map[string]*GoFile, rules []*rule) ([] break } - ruleSrcs, err := u.eval.EvalGlobs(rule.dir, rule.Rule, rule.SrcsAttr()) + ruleSrcs, err := u.eval.EvalGlobs(rule.dir, rule.Srcs()) if err != nil { return nil, err } diff --git a/generate/generate_test.go b/generate/generate_test.go index 68cfb93..43e876c 100644 --- a/generate/generate_test.go +++ b/generate/generate_test.go @@ -299,7 +299,7 @@ func TestUpdateDeps(t *testing.T) { func mustGetSources(t *testing.T, u *updater, rule *rule) []string { t.Helper() - srcs, err := u.eval.EvalGlobs(rule.dir, rule.Rule, rule.SrcsAttr()) + srcs, err := u.eval.EvalGlobs(rule.dir, rule.Srcs()) require.NoError(t, err) return srcs } diff --git a/generate/rule.go b/generate/rule.go index acd939e..3e7a514 100644 --- a/generate/rule.go +++ b/generate/rule.go @@ -1,6 +1,9 @@ package generate import ( + "bytes" + "text/template" + "github.com/please-build/buildtools/build" "github.com/please-build/puku/edit" @@ -11,6 +14,7 @@ type rule struct { dir string kind *kinds.Kind *build.Rule + SrcsOveride build.Expr } // setOrDeleteAttr will make sure the attribute with the given name matches the values passed in. It will keep the @@ -66,12 +70,18 @@ func (rule *rule) SrcsAttr() string { } func (rule *rule) addSrc(src string) { + if rule.SrcsOveride != nil { + return + } srcsAttr := rule.SrcsAttr() srcs := rule.AttrStrings(srcsAttr) rule.setOrDeleteAttr(srcsAttr, append(srcs, src)) } func (rule *rule) removeSrc(rem string) { + if rule.SrcsOveride != nil { + return + } srcsAttr := rule.SrcsAttr() srcs := rule.AttrStrings(srcsAttr) set := make([]string, 0, len(srcs)) @@ -115,8 +125,39 @@ func (rule *rule) isExternal() bool { func newRule(r *build.Rule, kindType *kinds.Kind, pkgDir string) *rule { return &rule{ - dir: pkgDir, - kind: kindType, - Rule: r, + dir: pkgDir, + kind: kindType, + Rule: r, + SrcsOveride: templateSrcs(kindType.SrcsRuleTemplate, r), + } +} + +func (rule *rule) Srcs() build.Expr { + if rule.SrcsOveride != nil { + return rule.SrcsOveride + } + return rule.Attr(rule.SrcsAttr()) +} + +func templateSrcs(templStr string, r *build.Rule) build.Expr { + if templStr == "" { + return nil + } + tmpl, err := template.New("test").Parse(templStr) + if err != nil { + return nil + } + var buf bytes.Buffer + err = tmpl.Execute(&buf, r) + if err != nil { + return nil + } + file, err := build.ParseBuild("template", buf.Bytes()) + if err != nil { + return nil + } + if len(file.Stmt) != 1 { + return nil } + return file.Stmt[0] } diff --git a/kinds/kinds.go b/kinds/kinds.go index 98079a2..1a045eb 100644 --- a/kinds/kinds.go +++ b/kinds/kinds.go @@ -20,7 +20,8 @@ type Kind struct { SrcsAttr string // NonGoSources indicates the puku that the sources to this rule are not go so we shouldn't try to parse them to // infer their deps, for example, proto_library. - NonGoSources bool + NonGoSources bool + SrcsRuleTemplate string } // IsProvided returns whether the dependency is already provided by the kind, and therefore can be omitted from the deps