diff --git a/adapters/logrus/logger.go b/adapters/logrus/logger.go index c642591..492cc14 100644 --- a/adapters/logrus/logger.go +++ b/adapters/logrus/logger.go @@ -69,6 +69,10 @@ func (this *Logger) Error(args ...interface{}) { this.entry.Error(args...) } +func (this *Logger) Debugf(format string, args ...interface{}) { + this.entry.Debugf(format, args...) +} + func (this *Logger) Debug(args ...interface{}) { this.entry.Debug(args...) } diff --git a/domain/wollemi/BUILD.plz b/domain/wollemi/BUILD.plz index c0b1cfc..3399a89 100644 --- a/domain/wollemi/BUILD.plz +++ b/domain/wollemi/BUILD.plz @@ -3,6 +3,7 @@ go_library( srcs = glob(["*.go"], exclude = ["*_test.go"]), visibility = ["//..."], deps = [ + "//domain/optional", "//ports/golang", "//ports/logging", "//ports/please", diff --git a/domain/wollemi/service.go b/domain/wollemi/service.go index 45d7eee..5ca4587 100644 --- a/domain/wollemi/service.go +++ b/domain/wollemi/service.go @@ -268,15 +268,19 @@ func (this *Service) ParseDir(buf *bytes.Buffer, dir *Directory) *Directory { } type Directory struct { - Path string - Rule string - Gopkg *golang.Package - Build please.File - Ok bool - Rewrite bool - InRunPath bool - Files map[string]os.FileInfo - GoFiles []string - BuildFiles []string - HasGoFile bool + Path string `json:"path,omitempty"` + Rule string `json:"-"` + Gopkg *golang.Package `json:"gopkg,omitempty"` + Build please.File `json:"-"` + Ok bool `json:"-"` + Rewrite bool `json:"-"` + InRunPath bool `json:"-"` + Files map[string]os.FileInfo `json:"-"` + GoFiles []string `json:"-"` + BuildFiles []string `json:"-"` + HasGoFile bool `json:"-"` +} + +func (Directory) String() string { + return "{}" } diff --git a/domain/wollemi/service_format.go b/domain/wollemi/service_format.go index 7b75c19..0946c2f 100644 --- a/domain/wollemi/service_format.go +++ b/domain/wollemi/service_format.go @@ -9,6 +9,7 @@ import ( "sort" "strings" + "github.com/tcncloud/wollemi/domain/optional" "github.com/tcncloud/wollemi/ports/logging" "github.com/tcncloud/wollemi/ports/please" "github.com/tcncloud/wollemi/ports/wollemi" @@ -236,6 +237,8 @@ func (this *Service) GoFormat(config wollemi.Config, paths []string) error { target += ":" + name } + target = strings.TrimSuffix(target, ":") + importPath := rule.AttrString("import_path") kind := rule.Kind() @@ -246,7 +249,7 @@ func (this *Service) GoFormat(config wollemi.Config, paths []string) error { case kind != "go_test": internal[filepath.Join(this.gopkg, path)] = "//" + target - if rule.Kind() == "go_copy" { + if kind == "go_copy" { genfiles[path+".cp.go"] = target } } @@ -346,7 +349,7 @@ func (this *Service) GoFormat(config wollemi.Config, paths []string) error { dir := dir gen.Run(func() { - this.formatDirectory(log, dir) + this.formatDir(log, dir) if err := this.please.Write(dir.Build); err != nil { log.WithError(err).Warn("could not write") @@ -398,8 +401,6 @@ func (this *Service) getRuleDeps(files []string, config wollemi.Config, dir *Dir } } - please.SortDeps(resolved) - return resolved, unresolved, nil } @@ -451,301 +452,377 @@ func (this *Service) getRuleSrcs(dir *Directory, config wollemi.Config, srcFiles return srcs } -func (this *Service) formatDirectory(log logging.Logger, dir *Directory) { +func (this *Service) formatDir(log logging.Logger, dir *Directory) { if !dir.Ok || !dir.Rewrite || dir.Gopkg == nil { return } config := this.filesystem.Config(dir.Path).Merge(this.config) - filesByRule := make(map[string][]string) - rulesByFile := make(map[string][]string) - - dir.Build.GetRules(func(rule please.Rule) { - files := getSrcFilesFromExpr(rule.Attr("srcs"), dir) + log.WithFields(logging.Fields{ + "config": config, + "directory": dir, + }).Debug("formatting") - for i := 0; i < len(files); i++ { - name := files[i] + consumer := &fileConsumer{ + Rules: make(map[string][]string), + Files: make(map[string][]string), + Dir: dir, + } - switch { - case filepath.Ext(name) != ".go": - // Ignore non golang source files. - case strings.Contains(name, "/"): - // Ignore golang source files outside of this directory. - default: - if _, ok := dir.Files[name]; !ok { - // This golang source file does not exist so it should be stripped - // from this rule's source list. + var managed []please.Rule - if i+1 < len(files) { - copy(files[i:], files[i+1:]) - } + dir.Build.GetRules(func(rule please.Rule) { + kind := config.Gofmt.GetMapped(rule.Kind()) - files = files[:len(files)-1] - i-- - } - } + if inStrings(config.Gofmt.GetManage(), kind) { + managed = append(managed, rule) } - filesByRule[rule.Name()] = files - - for _, name := range files { - rulesByFile[name] = appendUniqString(rulesByFile[name], rule.Name()) - } + consumer.Update(rule) }) + sortManagedRules(config, managed) + // --------------------------------------------------------------------------- // Manage existing go rules in this directory. - dir.Build.GetRules(func(rule please.Rule) { - for _, comment := range rule.Comment().Before { - token := strings.TrimSpace(comment.Token) +ManageRules: + for { + for i, rule := range managed { + for _, comment := range rule.Comment().Before { + token := strings.TrimSpace(comment.Token) - if strings.EqualFold(token, "# wollemi:keep") { - return // TODO: write unit test for this. + if strings.EqualFold(token, "# wollemi:keep") { + return // TODO: write unit test for this. + } } - } - log := log.WithField("build_rule", rule.Name()) - - var pkgFiles []string - var newFiles []string - var external bool + log := log.WithFields(logging.Fields{ + "rule": rule.Name(), + "process": "manage", + }) - kind := config.Gofmt.GetMapped(rule.Kind()) + var pkgFiles []string + var deps []string - if !inStrings(config.Gofmt.GetManage(), kind) { - return - } + kind := config.Gofmt.GetMapped(rule.Kind()) - switch kind { - case "go_binary", "go_library": - pkgFiles = dir.Gopkg.GoFiles - case "go_test": - pkgFiles = dir.Gopkg.XTestGoFiles - external = true + external := rule.AttrLiteral("external") == "True" - if len(pkgFiles) == 0 { - pkgFiles = append(dir.Gopkg.GoFiles, dir.Gopkg.TestGoFiles...) - external = false + switch kind { + case "go_binary", "go_library": + pkgFiles = dir.Gopkg.GoFiles + case "go_test": + if external { + pkgFiles = dir.Gopkg.XTestGoFiles + } else { + pkgFiles = dir.Gopkg.TestGoFiles + } } - } - var ambiguous bool + srcFiles := consumer.Files[rule.Name()] - srcFiles := filesByRule[rule.Name()] + // ----------------------------------------------------------------------- + // Include missing golang package source files unless one or more source + // files are being consumed by another rule. Allow exceptions when + // internal go_test rules consume non test package sources. - for _, name := range pkgFiles { - if inStrings(srcFiles, name) { - continue - } + var ambiguous bool - if rulesByFile[name] == nil { - newFiles = append(newFiles, name) - continue - } + for i := 0; !ambiguous && i < len(pkgFiles); i++ { + file := pkgFiles[i] - if kind == "go_test" && inStrings(dir.Gopkg.GoFiles, name) { - newFiles = append(newFiles, name) - continue - } + switch { + case len(consumer.Rules[file]) == 0: + case inStrings(srcFiles, file): + case inStrings(dir.Gopkg.GoFiles, file): + if kind == "go_test" { + continue + } - ambiguous = true - break - } + for _, name := range consumer.Rules[file] { + other := dir.Build.GetRule(name) + if other == nil { + continue + } - if len(newFiles) > 0 && !ambiguous { - for _, name := range newFiles { - rulesByFile[name] = appendUniqString(rulesByFile[name], rule.Name()) + if config.Gofmt.GetMapped(other.Kind()) != "go_test" { + ambiguous = true + break + } + } + default: + ambiguous = true + } } - srcFiles = append(srcFiles, newFiles...) - newFiles = nil - - sort.Strings(srcFiles) - } - - if kind == "go_test" && !external { - if len(srcFiles) == len(dir.Gopkg.GoFiles) { - sort.Strings(dir.Gopkg.GoFiles) + if !ambiguous { + srcFiles = appendUniqString(srcFiles, pkgFiles...) sort.Strings(srcFiles) + } + + // ----------------------------------------------------------------------- + // Mark external go_test rule to be removed when it includes no test + // source files. + if kind == "go_test" && !external { tail := len(srcFiles) - 1 + for i := 0; i <= tail; i++ { - if srcFiles[i] != dir.Gopkg.GoFiles[i] { + if strings.HasSuffix(srcFiles[i], "_test.go") { break - } - - if i == tail { - // This internal go_test does not include any actual test source - // files therefore we are marking it to be deleted. - srcFiles = nil + } else if i == tail { + srcFiles = nil // delete } } } - } - if len(srcFiles) == 0 { - log.WithField("build_rule", rule.Name()). - WithField("reason", "no source files"). - Warn("removed") + // ----------------------------------------------------------------------- + // Remove managed build rule when it includes zero source files. - dir.Build.DelRule(rule.Name()) - return - } + if len(srcFiles) == 0 { + log.WithField("reason", "no source files").Warn("removed") + + rule.DelAttr("srcs") + consumer.Update(rule) + + dir.Build.DelRule(rule.Name()) + + managed = deleteRulesIndex(managed, i) + + continue ManageRules + } + + // ----------------------------------------------------------------------- + + if kind == "go_test" && !external { + // Allow internal go_test rule to depend on go_library rule even + // though the go code does not. In the case of please this will + // just make the pre-compiled go library code available in the + // test. + for _, dep := range rule.AttrStrings("deps") { + target := please.Split(dep) + + if target.Path == "" || target.Path == dir.Path { + rule := dir.Build.GetRule(target.Name) - var internal []string + if rule != nil && config.Gofmt.GetMapped(rule.Kind()) == "go_library" { + deps = append(deps, fmt.Sprintf(":%s", target.Name)) + srcFiles, _ = deleteStrings(srcFiles, consumer.Files[target.Name]...) + } + + if strings.HasSuffix(target.Name, "#lib") { + name := target.Name - if kind == "go_test" && !external { - // Allow internal go_test rule to depend on go_library rule even - // though the go code does not. In the case of please this will - // just make the pre-compiled go library code available in the - // test. - for _, dep := range rule.AttrStrings("deps") { - target := please.Split(dep) + name = strings.TrimSuffix(name, "#lib") + name = strings.TrimPrefix(name, "_") - if target.Path == "" || target.Path == dir.Path { - rule := dir.Build.GetRule(target.Name) + rule := dir.Build.GetRule(name) - if rule != nil && rule.Kind() == "go_library" { - internal = append(internal, fmt.Sprintf(":%s", target.Name)) - srcFiles = deleteStrings(srcFiles, filesByRule[target.Name]...) + if rule != nil && config.Gofmt.GetMapped(rule.Kind()) == "go_binary" { + deps = append(deps, fmt.Sprintf(":%s", target.Name)) + srcFiles, _ = deleteStrings(srcFiles, consumer.Files[name]...) + } + } } } } - } - srcs := this.getRuleSrcs(dir, config, srcFiles) + _, isExplicitSources := rule.Attr("srcs").(*please.ListExpr) - resolved, unresolved, err := this.getRuleDeps(srcFiles, config, dir) - if err != nil { - log.WithError(err).Warn("could not get deps") - return - } + if kind == "go_test" && !isExplicitSources { + exclude := dir.Gopkg.XTestGoFiles - resolved = append(internal, resolved...) + if external { + exclude = dir.Gopkg.TestGoFiles + } - if len(unresolved) > 0 && !config.AllowUnresolvedDependency.IsTrue() { - for _, path := range unresolved { - log.WithField("go_import", path).Error("could not resolve go import") + srcFiles, isExplicitSources = deleteStrings(srcFiles, exclude...) } - return - } + if external { + if inStrings(dir.Gopkg.TestGoFiles, srcFiles...) { + rule.DelAttr("external") + } + } else { + if inStrings(dir.Gopkg.XTestGoFiles, srcFiles...) { + rule.SetAttr("external", &please.Ident{Name: "True"}) + } + } - _, isExplicitSources := rule.Attr("srcs").(*please.ListExpr) + resolved, unresolved, err := this.getRuleDeps(srcFiles, config, dir) + if err != nil { + log.WithError(err).Warn("could not get deps") + return + } - if isExplicitSources || config.ExplicitSources.IsTrue() { - rule.SetAttr("srcs", please.Strings(srcs...)) - } + if len(unresolved) > 0 && !config.AllowUnresolvedDependency.IsTrue() { + for _, path := range unresolved { + log.WithField("go_import", path).Error("could not resolve go import") + } - if len(resolved) > 0 { - rule.SetAttr("deps", please.Strings(resolved...)) - } else { - rule.DelAttr("deps") + return + } + + if isExplicitSources || config.ExplicitSources.IsTrue() { + srcs := this.getRuleSrcs(dir, config, srcFiles) + rule.SetAttr("srcs", please.Strings(srcs...)) + } + + resolved = append(deps, resolved...) + + if len(resolved) > 0 { + please.SortDeps(resolved) + rule.SetAttr("deps", please.Strings(resolved...)) + } else { + rule.DelAttr("deps") + } + + log.Debug("managed") + + consumer.Update(rule) } - }) + + break + } // --------------------------------------------------------------------------- // Create missing go rules in this directory. - for _, kind := range []string{"go_library", "go_test"} { +CreateRules: + for _, x := range []struct { + Kind string + External bool + }{ + {Kind: "go_library"}, + {Kind: "go_test", External: false}, + {Kind: "go_test", External: true}, + } { var pkgFiles []string var include []string var exclude []string var rule please.Rule - var external bool + var deps []string - switch kind { - case "go_library": + config := config + + switch { + case x.Kind == "go_library": pkgFiles = dir.Gopkg.GoFiles if dir.Gopkg.Name == "main" { - kind = "go_binary" + x.Kind = "go_binary" } - path := filepath.Join(this.wd, dir.Path) - kind := config.Gofmt.GetMapped(kind) - rule = this.please.NewRule(kind, filepath.Base(path)) + rule = this.please.NewRule( + config.Gofmt.GetMapped(x.Kind), + filepath.Base(filepath.Join(this.wd, dir.Path)), + ) + include, exclude = []string{"*.go"}, []string{"*_test.go"} - case "go_test": - if len(dir.Gopkg.XTestGoFiles)+len(dir.Gopkg.TestGoFiles) == 0 { - continue + case x.Kind == "go_test" && x.External == false: + if len(dir.Gopkg.TestGoFiles) == 0 { + continue // No source files so nothing to be done. } - pkgFiles = dir.Gopkg.XTestGoFiles - kind := config.Gofmt.GetMapped(kind) - rule = this.please.NewRule(kind, "test") - include = []string{"*_test.go"} - external = true + pkgFiles = dir.Gopkg.TestGoFiles - if len(pkgFiles) == 0 { + if dir.Gopkg.Name == "main" { pkgFiles = append(dir.Gopkg.GoFiles, dir.Gopkg.TestGoFiles...) include = []string{"*.go"} - external = false + } else { + // Attempt to get sources through existing go_library rule. + if rule := consumer.GetRule(dir.Gopkg.GoFiles...); rule != nil { + if config.Gofmt.GetMapped(rule.Kind()) == "go_library" { + // Since there exists exactly one go_library rule which consumes all + // non-test source files for this go package it will be included as + // a dependency for this internal go_test. + + deps = append(deps, ":"+rule.Name()) + pkgFiles = dir.Gopkg.TestGoFiles + include = []string{"*_test.go"} + } + } } - } - - if !inStrings(config.Gofmt.GetCreate(), kind) { - continue - } - log := log.WithField("build_rule", rule.Name()) + exclude = dir.Gopkg.XTestGoFiles + name := "test" - srcFiles := make([]string, 0, len(pkgFiles)) - - var ambiguous bool + if len(dir.Gopkg.XTestGoFiles) > 0 { + config.ExplicitSources = optional.BoolValue(true) + name = "internal_test" + } - for _, name := range pkgFiles { - if rulesByFile[name] == nil { - srcFiles = append(srcFiles, name) - continue + rule = this.please.NewRule(config.Gofmt.GetMapped(x.Kind), name) + case x.Kind == "go_test" && x.External == true: + if len(dir.Gopkg.XTestGoFiles) == 0 { + continue // No sources files so nothing to be done. } - if kind == "go_test" && inStrings(dir.Gopkg.GoFiles, name) { - srcFiles = append(srcFiles, name) - continue + pkgFiles = dir.Gopkg.XTestGoFiles + include = []string{"*_test.go"} + exclude = dir.Gopkg.TestGoFiles + name := "test" + + if len(dir.Gopkg.TestGoFiles) > 0 { + config.ExplicitSources = optional.BoolValue(true) + name = "external_test" } - ambiguous = true - break + rule = this.please.NewRule(config.Gofmt.GetMapped(x.Kind), name) } - if ambiguous { + if !inStrings(config.Gofmt.GetCreate(), x.Kind) { continue } - for _, name := range srcFiles { - rulesByFile[name] = appendUniqString(rulesByFile[name], rule.Name()) - } + exclude = append(exclude, dir.Gopkg.IgnoredGoFiles...) + + log := log.WithFields(logging.Fields{ + "rule": rule.Name(), + "process": "create", + }) + + for _, file := range pkgFiles { + switch { + case len(consumer.Rules[file]) == 0: + case x.Kind == "go_test" && inStrings(dir.Gopkg.GoFiles, file): + default: + log.WithFields(logging.Fields{ + "rules": consumer.Rules[file], + "reason": "ambiguous", + "file": file, + }).Debug("skipped") - srcs := this.getRuleSrcs(dir, config, srcFiles) + continue CreateRules + } + } + srcs := this.getRuleSrcs(dir, config, pkgFiles) if len(srcs) == 0 { + log.WithField("reason", "no sources").Debug("skipped") + continue } if config.ExplicitSources.IsTrue() { rule.SetAttr("srcs", please.Strings(srcs...)) } else { - if !external { - exclude = append(exclude, dir.Gopkg.IgnoredGoFiles...) - } - rule.SetAttr("srcs", please.Glob(include, exclude)) } - if external { + if x.External { rule.SetAttr("external", &please.Ident{Name: "True"}) } - if kind == "go_binary" || kind == "go_library" { + if x.Kind == "go_binary" || x.Kind == "go_library" { visibility := this.getVisibility(config, dir.Path) rule.SetAttr("visibility", please.Strings(visibility)) } - resolved, unresolved, err := this.getRuleDeps(srcFiles, config, dir) + resolved, unresolved, err := this.getRuleDeps(pkgFiles, config, dir) if err != nil { log.WithError(err).Warn("could not get deps") return @@ -759,11 +836,37 @@ func (this *Service) formatDirectory(log logging.Logger, dir *Directory) { return } + resolved = append(deps, resolved...) + if len(resolved) > 0 { + please.SortDeps(resolved) rule.SetAttr("deps", please.Strings(resolved...)) } + consumer.Update(rule) + dir.Build.SetRule(rule) + + log.Debug("created") + } + + for _, files := range [][]string{ + dir.Gopkg.GoFiles, + dir.Gopkg.TestGoFiles, + dir.Gopkg.XTestGoFiles, + } { + for _, file := range files { + if _, ok := consumer.Rules[file]; !ok { + info, ok := dir.Files[file] + if !ok { + continue + } + + if info.Mode()&os.ModeSymlink == 0 { // is not symlink + log.WithField("file", file).Debug("unsourced") + } + } + } } } @@ -887,8 +990,14 @@ func isMatched(s string, patterns []string) bool { return false } -func inStrings(from []string, value string) bool { - return indexStrings(from, value) >= 0 +func inStrings(from []string, values ...string) bool { + for _, value := range values { + if indexStrings(from, value) < 0 { + return false + } + } + + return true } func appendUniqString(dest []string, from ...string) []string { @@ -911,9 +1020,13 @@ func indexStrings(from []string, value string) int { return -1 } -func deleteStrings(from []string, values ...string) []string { +func deleteStrings(from []string, values ...string) ([]string, bool) { + var deleted bool + for _, s := range values { if i := indexStrings(from, s); i >= 0 { + deleted = true + if i+1 < len(from) { copy(from[i:], from[i+1:]) } @@ -922,5 +1035,104 @@ func deleteStrings(from []string, values ...string) []string { } } - return from + return from, deleted +} + +func sortManagedRules(config wollemi.Config, rules []please.Rule) { + sort.Slice(rules, func(i, j int) bool { + irule := rules[i] + jrule := rules[j] + + ikind := config.Gofmt.GetMapped(irule.Kind()) + jkind := config.Gofmt.GetMapped(jrule.Kind()) + + switch { + case ikind == jkind: + return irule.Name() < jrule.Name() + case ikind == "go_library": + return true + case ikind == "go_binary" && jkind != "go_library": + return true + case ikind == "go_test" && jkind != "go_binary" && jkind != "go_library": + return true + default: + return ikind < jkind + } + }) +} + +func deleteRulesIndex(rules []please.Rule, i int) []please.Rule { + size := len(rules) + + if i+1 < size { + copy(rules[i:], rules[i+1:]) + } + + return rules[:size-1] +} + +type fileConsumer struct { + Rules map[string][]string + Files map[string][]string + Dir *Directory +} + +func (fc *fileConsumer) GetRule(files ...string) please.Rule { + var names []string + + for _, file := range files { + names = appendUniqString(names, fc.Rules[file]...) + if len(names) > 1 { + return nil + } + } + + var rule please.Rule + + if len(names) == 1 { + rule = fc.Dir.Build.GetRule(names[0]) + } + + return rule +} + +func (fc *fileConsumer) Update(rule please.Rule) { + delete(fc.Files, rule.Name()) + + for file, rules := range fc.Rules { + fc.Rules[file], _ = deleteStrings(rules, rule.Name()) + } + + files := getSrcFilesFromExpr(rule.Attr("srcs"), fc.Dir) + + for i := 0; i < len(files); i++ { + name := files[i] + + switch { + case filepath.Ext(name) != ".go": + // Ignore non golang source files. + case strings.Contains(name, "/"): + // Ignore golang source files outside of this directory. + case strings.HasPrefix(name, "//"), strings.HasPrefix(name, ":"): + // Ignore please rule targets + default: + if _, ok := fc.Dir.Files[name]; !ok { + // This golang source file does not exist so it should be stripped + // from this rule's source list. + + if i+1 < len(files) { + copy(files[i:], files[i+1:]) + } + + files = files[:len(files)-1] + i-- + } + } + } + + fc.Files[rule.Name()] = files + + for _, file := range files { + fc.Rules[file] = appendUniqString(fc.Rules[file], rule.Name()) + } } diff --git a/domain/wollemi/service_format_test.go b/domain/wollemi/service_format_test.go index b5a530c..c006524 100644 --- a/domain/wollemi/service_format_test.go +++ b/domain/wollemi/service_format_test.go @@ -1274,6 +1274,274 @@ func (t *ServiceSuite) TestService_GoFormat() { }, }, }, + }, { // TEST_CASE ------------------------------------------------------------ + Title: "manages mixture of internal and external go_test rules", + Data: &GoFormatTestData{ + Gosrc: gosrc, + Gopkg: gopkg, + Paths: []string{"app/server"}, + Parse: t.WithThirdPartyGo(map[string]*please.BuildFile{ + "app/server/BUILD.plz": &please.BuildFile{ + Stmt: []please.Expr{ + please.NewCallExpr("go_library", []please.Expr{ + please.NewAssignExpr("=", "name", "server"), + please.NewAssignExpr("=", "srcs", []string{"server.go"}), + please.NewAssignExpr("=", "visibility", []string{"PUBLIC"}), + }), + please.NewCallExpr("go_test", []please.Expr{ + please.NewAssignExpr("=", "name", "internal_test"), + please.NewAssignExpr("=", "srcs", []string{}), + please.NewAssignExpr("=", "deps", []string{":server"}), + }), + please.NewCallExpr("go_test", []please.Expr{ + please.NewAssignExpr("=", "name", "external_test"), + please.NewAssignExpr("=", "srcs", []string{}), + please.NewAssignExpr("=", "external", true), + }), + }, + }, + }), + ImportDir: map[string]*golang.Package{ + "app/server": &golang.Package{ + GoFiles: []string{"server.go"}, + TestGoFiles: []string{"internal_test.go"}, + XTestGoFiles: []string{"external_test.go"}, + GoFileImports: map[string][]string{ + "server.go": []string{ + "database/sql", + "strings", + }, + "internal_test.go": []string{ + "testing", + "github.com/stretchr/testify/assert", + }, + "external_test.go": []string{ + "testing", + "github.com/example/app/server", + "github.com/golang/mock/gomock", + }, + }, + }, + }, + Write: map[string]*please.BuildFile{ + "app/server/BUILD.plz": &please.BuildFile{ + Stmt: []please.Expr{ + please.NewCallExpr("go_library", []please.Expr{ + please.NewAssignExpr("=", "name", "server"), + please.NewAssignExpr("=", "srcs", []string{"server.go"}), + please.NewAssignExpr("=", "visibility", []string{"PUBLIC"}), + }), + please.NewCallExpr("go_test", []please.Expr{ + please.NewAssignExpr("=", "name", "internal_test"), + please.NewAssignExpr("=", "srcs", []string{"internal_test.go"}), + please.NewAssignExpr("=", "deps", []string{ + ":server", + "//third_party/go/github.com/stretchr:testify", + }), + }), + please.NewCallExpr("go_test", []please.Expr{ + please.NewAssignExpr("=", "name", "external_test"), + please.NewAssignExpr("=", "srcs", []string{"external_test.go"}), + please.NewAssignExpr("=", "external", true), + please.NewAssignExpr("=", "deps", []string{ + ":server", + "//third_party/go/github.com/golang:mock", + }), + }), + }, + }, + }, + }, + }, { // TEST_CASE ------------------------------------------------------------ + Title: "creates mixture of internal and external go_test rules", + Data: &GoFormatTestData{ + Gosrc: gosrc, + Gopkg: gopkg, + Paths: []string{"app/server"}, + Parse: t.WithThirdPartyGo(nil), + ImportDir: map[string]*golang.Package{ + "app/server": &golang.Package{ + GoFiles: []string{"server.go"}, + TestGoFiles: []string{"internal_test.go"}, + XTestGoFiles: []string{"external_test.go"}, + GoFileImports: map[string][]string{ + "server.go": []string{ + "database/sql", + "strings", + }, + "internal_test.go": []string{ + "testing", + "github.com/stretchr/testify/assert", + }, + "external_test.go": []string{ + "testing", + "github.com/example/app/server", + "github.com/golang/mock/gomock", + }, + }, + }, + }, + Write: map[string]*please.BuildFile{ + "app/server/BUILD.plz": &please.BuildFile{ + Stmt: []please.Expr{ + please.NewCallExpr("go_library", []please.Expr{ + please.NewAssignExpr("=", "name", "server"), + please.NewAssignExpr("=", "srcs", please.NewGlob([]string{"*.go"}, "*_test.go")), + please.NewAssignExpr("=", "visibility", []string{"PUBLIC"}), + }), + please.NewCallExpr("go_test", []please.Expr{ + please.NewAssignExpr("=", "name", "internal_test"), + please.NewAssignExpr("=", "srcs", []string{"internal_test.go"}), + please.NewAssignExpr("=", "deps", []string{ + ":server", + "//third_party/go/github.com/stretchr:testify", + }), + }), + please.NewCallExpr("go_test", []please.Expr{ + please.NewAssignExpr("=", "name", "external_test"), + please.NewAssignExpr("=", "srcs", []string{"external_test.go"}), + please.NewAssignExpr("=", "external", true), + please.NewAssignExpr("=", "deps", []string{ + ":server", + "//third_party/go/github.com/golang:mock", + }), + }), + }, + }, + }, + }, + }, { // TEST_CASE ------------------------------------------------------------ + Title: "expands go_test glob srcs into explicit list when necessary", + Data: &GoFormatTestData{ + Gosrc: gosrc, + Gopkg: gopkg, + Paths: []string{"app/server"}, + Parse: t.WithThirdPartyGo(map[string]*please.BuildFile{ + "app/server/BUILD.plz": &please.BuildFile{ + Stmt: []please.Expr{ + please.NewCallExpr("go_library", []please.Expr{ + please.NewAssignExpr("=", "name", "server"), + please.NewAssignExpr("=", "srcs", []string{"server.go"}), + please.NewAssignExpr("=", "visibility", []string{"PUBLIC"}), + }), + please.NewCallExpr("go_test", []please.Expr{ + please.NewAssignExpr("=", "name", "test"), + please.NewAssignExpr("=", "srcs", please.NewGlob([]string{"*_test.go"})), + please.NewAssignExpr("=", "external", true), + }), + }, + }, + }), + ImportDir: map[string]*golang.Package{ + "app/server": &golang.Package{ + GoFiles: []string{"server.go"}, + TestGoFiles: []string{"internal_test.go"}, + XTestGoFiles: []string{"external_test.go"}, + GoFileImports: map[string][]string{ + "server.go": []string{ + "database/sql", + "strings", + }, + "internal_test.go": []string{ + "testing", + "github.com/stretchr/testify/assert", + }, + "external_test.go": []string{ + "testing", + "github.com/example/app/server", + "github.com/golang/mock/gomock", + }, + }, + }, + }, + Write: map[string]*please.BuildFile{ + "app/server/BUILD.plz": &please.BuildFile{ + Stmt: []please.Expr{ + please.NewCallExpr("go_library", []please.Expr{ + please.NewAssignExpr("=", "name", "server"), + please.NewAssignExpr("=", "srcs", []string{"server.go"}), + please.NewAssignExpr("=", "visibility", []string{"PUBLIC"}), + }), + please.NewCallExpr("go_test", []please.Expr{ + please.NewAssignExpr("=", "name", "test"), + please.NewAssignExpr("=", "srcs", []string{"external_test.go"}), + please.NewAssignExpr("=", "external", true), + please.NewAssignExpr("=", "deps", []string{ + ":server", + "//third_party/go/github.com/golang:mock", + }), + }), + please.NewCallExpr("go_test", []please.Expr{ + please.NewAssignExpr("=", "name", "internal_test"), + please.NewAssignExpr("=", "srcs", []string{"internal_test.go"}), + please.NewAssignExpr("=", "deps", []string{ + ":server", + "//third_party/go/github.com/stretchr:testify", + }), + }), + }, + }, + }, + }, + }, { // TEST_CASE ------------------------------------------------------------ + Title: "manages rules which contain generated sources from another", + Data: &GoFormatTestData{ + Gosrc: gosrc, + Gopkg: gopkg, + Paths: []string{"app/server"}, + Lstat: map[string]*FileInfo{ + "app/server/copier.cp.go": &FileInfo{ + FileName: "copier.cp.go", + FileMode: 134218221, + }, + }, + Parse: t.WithThirdPartyGo(map[string]*please.BuildFile{ + "app/server/BUILD.plz": &please.BuildFile{ + Stmt: []please.Expr{ + please.NewCallExpr("go_library", []please.Expr{ + please.NewAssignExpr("=", "name", "server"), + please.NewAssignExpr("=", "srcs", []string{":copier", "server.go"}), + }), + please.NewCallExpr("go_copy", []please.Expr{ + please.NewAssignExpr("=", "name", "copier"), + please.NewAssignExpr("=", "src", "copier.json"), + }), + }, + }, + }), + ImportDir: map[string]*golang.Package{ + "app/server": &golang.Package{ + GoFiles: []string{"copier.cp.go", "server.go"}, + GoFileImports: map[string][]string{ + "copier.cp.go": []string{ + "google.golang.org/grpc", + "strings", + }, + "server.go": []string{ + "database/sql", + "strings", + }, + }, + }, + }, + Write: map[string]*please.BuildFile{ + "app/server/BUILD.plz": &please.BuildFile{ + Stmt: []please.Expr{ + please.NewCallExpr("go_library", []please.Expr{ + please.NewAssignExpr("=", "name", "server"), + please.NewAssignExpr("=", "srcs", []string{":copier", "server.go"}), + please.NewAssignExpr("=", "deps", []string{ + "//third_party/go/google.golang.org:grpc", + }), + }), + please.NewCallExpr("go_copy", []please.Expr{ + please.NewAssignExpr("=", "name", "copier"), + please.NewAssignExpr("=", "src", "copier.json"), + }), + }, + }, + }, + }, }} { focus := "" diff --git a/ports/logging/logger.go b/ports/logging/logger.go index af46e17..dcd5b9a 100644 --- a/ports/logging/logger.go +++ b/ports/logging/logger.go @@ -14,6 +14,7 @@ type Logger interface { Warnf(string, ...interface{}) Warn(...interface{}) Error(...interface{}) + Debugf(string, ...interface{}) Debug(...interface{}) GetLevel() Level SetLevel(Level) diff --git a/ports/wollemi/config.go b/ports/wollemi/config.go index c84814c..bce9462 100644 --- a/ports/wollemi/config.go +++ b/ports/wollemi/config.go @@ -17,6 +17,10 @@ type Config struct { ExplicitSources *optional.Bool `json:"explicit_sources,omitempty"` } +func (Config) String() string { + return "{}" +} + type Gofmt struct { Rewrite *bool `json:"rewrite,omitempty"` Create gofmtCreate `json:"create,omitempty"` @@ -53,6 +57,12 @@ func (gofmt *Gofmt) GetMapped(kind string) string { if kind, ok := gofmt.Mapped[kind]; ok { return kind } + + for fromKind, intoKind := range gofmt.Mapped { + if intoKind == kind { + return fromKind + } + } } return kind diff --git a/testdata/mem/logger.go b/testdata/mem/logger.go index c9faaed..442b238 100644 --- a/testdata/mem/logger.go +++ b/testdata/mem/logger.go @@ -9,7 +9,7 @@ import ( ) func NewLogger() *Logger { - return &Logger{lvl: logging.InfoLevel} + return &Logger{lvl: logging.TraceLevel} } type Logger struct { @@ -61,6 +61,10 @@ func (this *Logger) Info(args ...interface{}) { this.Log("info", nil, args...) } +func (this *Logger) Debugf(format string, args ...interface{}) { + this.Logf("debug", nil, format, args...) +} + func (this *Logger) Debug(args ...interface{}) { this.Log("debug", nil, args...) } @@ -193,6 +197,10 @@ func (this *LoggerEntry) Info(args ...interface{}) { this.Log("info", args...) } +func (this *LoggerEntry) Debugf(format string, args ...interface{}) { + this.Logf("debug", format, args...) +} + func (this *LoggerEntry) Debug(args ...interface{}) { this.Log("debug", args...) }