From 01ea833dfea1989154dbd1c2217cc1bd0f1ad875 Mon Sep 17 00:00:00 2001 From: Masaki Hara Date: Tue, 10 Jan 2023 17:57:37 +0900 Subject: [PATCH] Allow gradual migration to go run --- cmd/gex/main.go | 12 +- pkg/manager/dep/manager.go | 4 + pkg/manager/manager.go | 1 + pkg/manager/mod/manager.go | 10 ++ .../.snapshots/TestWriter_Write-dep-tools.go | 1 + .../.snapshots/TestWriter_Write-mod-tools.go | 6 +- .../TestWriter_Write-mod_all_nobuild-tools.go | 16 +++ ...tWriter_Write-mod_default_nobuild-tools.go | 24 ++++ pkg/tool/manifest.go | 40 ++++-- pkg/tool/parser.go | 40 +++++- pkg/tool/parser_test.go | 117 ++++++++++++++---- pkg/tool/repository.go | 31 +++-- pkg/tool/tool.go | 25 +++- pkg/tool/writer.go | 50 +++++++- pkg/tool/writer_test.go | 70 +++++++++-- .../TestGex_Dep-add_2_tools-tools.go | 1 + ..._tool_that_has_already_been_added-tools.go | 1 + .../TestGex_Dep-add_first_tool-tools.go | 1 + ...ools_included_in_the_same_package-tools.go | 1 + ...t_its_root_proejct_has_been_added-tools.go | 28 ----- ...me_package_has_already_been_added-tools.go | 1 + ..._tools_with_version_specification-tools.go | 1 + .../TestGex_Mod-add_2_tools-tools.go | 1 + ..._tool_that_has_already_been_added-tools.go | 1 + .../TestGex_Mod-add_first_tool-tools.go | 1 + ...ools_included_in_the_same_package-tools.go | 1 + ...t_its_root_proejct_has_been_added-tools.go | 28 ----- ...me_package_has_already_been_added-tools.go | 1 + ..._tools_with_version_specification-tools.go | 1 + 29 files changed, 398 insertions(+), 117 deletions(-) create mode 100644 pkg/tool/.snapshots/TestWriter_Write-mod_all_nobuild-tools.go create mode 100644 pkg/tool/.snapshots/TestWriter_Write-mod_default_nobuild-tools.go delete mode 100644 tests/e2e/.snapshots/TestGex_Dep-add_tools_that_its_root_proejct_has_been_added-tools.go delete mode 100644 tests/e2e/.snapshots/TestGex_Mod-add_tools_that_its_root_proejct_has_been_added-tools.go diff --git a/cmd/gex/main.go b/cmd/gex/main.go index 86c51d0..bf8eb6b 100644 --- a/cmd/gex/main.go +++ b/cmd/gex/main.go @@ -20,6 +20,8 @@ const ( var ( pkgsToBeAdded []string + flagBin bool + flagNoBin bool flagBuild bool flagInit bool flagRegen bool @@ -31,6 +33,8 @@ var ( func init() { pflag.SetInterspersed(false) pflag.StringArrayVar(&pkgsToBeAdded, "add", []string{}, "Add new tools") + pflag.BoolVar(&flagBin, "bin", false, "Enable build for the added tools") + pflag.BoolVar(&flagNoBin, "no-bin", false, "Disable build for the added tools") pflag.BoolVar(&flagInit, "init", false, "Initialize tools manifest") pflag.BoolVar(&flagBuild, "build", false, "Build all tools") pflag.BoolVar(&flagRegen, "regen", false, "Regenerate manifest") @@ -68,7 +72,13 @@ func run() error { switch { case len(pkgsToBeAdded) > 0: - err = toolRepo.Add(ctx, pkgsToBeAdded...) + buildMode := tool.BuildModeUnknown + if flagBin { + buildMode = tool.BuildModeBin + } else if flagNoBin { + buildMode = tool.BuildModeNoBin + } + err = toolRepo.AddOpt(ctx, buildMode, pkgsToBeAdded...) case flagVersion: fmt.Fprintf(os.Stdout, "%s %s\n", cliName, gex.Version) case flagHelp: diff --git a/pkg/manager/dep/manager.go b/pkg/manager/dep/manager.go index b6623d1..3794718 100644 --- a/pkg/manager/dep/manager.go +++ b/pkg/manager/dep/manager.go @@ -64,6 +64,10 @@ func (m *managerImpl) Build(ctx context.Context, binPath, pkg string, verbose bo return errors.WithStack(m.executor.Exec(ctx, "go", args...)) } +func (m *managerImpl) RunInPlace(ctx context.Context, pkg string, verbose bool, args ...string) error { + return errors.New("Package must be built before run when in Dep mode") +} + func (m *managerImpl) Sync(ctx context.Context, verbose bool) error { args := []string{"ensure"} if verbose { diff --git a/pkg/manager/manager.go b/pkg/manager/manager.go index e70c332..8cd7984 100644 --- a/pkg/manager/manager.go +++ b/pkg/manager/manager.go @@ -5,5 +5,6 @@ import "context" type Interface interface { Add(ctx context.Context, pkgs []string, verbose bool) error Build(ctx context.Context, binPath, pkg string, verbose bool) error + RunInPlace(ctx context.Context, pkg string, verbose bool, args ...string) error Sync(ctx context.Context, verbose bool) error } diff --git a/pkg/manager/mod/manager.go b/pkg/manager/mod/manager.go index b496184..cff9266 100644 --- a/pkg/manager/mod/manager.go +++ b/pkg/manager/mod/manager.go @@ -37,6 +37,16 @@ func (m *managerImpl) Build(ctx context.Context, binPath, pkg string, verbose bo return errors.WithStack(m.executor.Exec(ctx, "go", args...)) } +func (m *managerImpl) RunInPlace(ctx context.Context, pkg string, verbose bool, commandArgs ...string) error { + args := []string{"run"} + if verbose { + args = append(args, "-v") + } + args = append(args, pkg) + args = append(args, commandArgs...) + return errors.WithStack(m.executor.Exec(ctx, "go", args...)) +} + func (m *managerImpl) Sync(ctx context.Context, verbose bool) error { args := []string{"mod", "tidy"} if verbose { diff --git a/pkg/tool/.snapshots/TestWriter_Write-dep-tools.go b/pkg/tool/.snapshots/TestWriter_Write-dep-tools.go index 50d285a..1dc1521 100644 --- a/pkg/tool/.snapshots/TestWriter_Write-dep-tools.go +++ b/pkg/tool/.snapshots/TestWriter_Write-dep-tools.go @@ -1,6 +1,7 @@ // Code generated by github.com/izumin5210/gex. DO NOT EDIT. // +build tools +//gex:bin package tools diff --git a/pkg/tool/.snapshots/TestWriter_Write-mod-tools.go b/pkg/tool/.snapshots/TestWriter_Write-mod-tools.go index 8e6a5da..0dd3113 100644 --- a/pkg/tool/.snapshots/TestWriter_Write-mod-tools.go +++ b/pkg/tool/.snapshots/TestWriter_Write-mod-tools.go @@ -1,6 +1,7 @@ // Code generated by github.com/izumin5210/gex. DO NOT EDIT. // +build tools +//gex:bin package tools @@ -8,8 +9,11 @@ package tools import ( _ "github.com/gogo/protobuf/protoc-gen-gogofast" _ "github.com/grpc-ecosystem/grpc-gateway/protoc-gen-grpc-gateway" + //gex:bin _ "github.com/grpc-ecosystem/grpc-gateway/protoc-gen-swagger" + //gex:nobin _ "github.com/volatiletech/sqlboiler" + //gex:nobin _ "github.com/volatiletech/sqlboiler/drivers/sqlboiler-psql" ) @@ -19,6 +23,4 @@ import ( //go:generate go build -v -o=./bin/protoc-gen-gogofast github.com/gogo/protobuf/protoc-gen-gogofast //go:generate go build -v -o=./bin/protoc-gen-grpc-gateway github.com/grpc-ecosystem/grpc-gateway/protoc-gen-grpc-gateway //go:generate go build -v -o=./bin/protoc-gen-swagger github.com/grpc-ecosystem/grpc-gateway/protoc-gen-swagger -//go:generate go build -v -o=./bin/sqlboiler github.com/volatiletech/sqlboiler -//go:generate go build -v -o=./bin/sqlboiler-psql github.com/volatiletech/sqlboiler/drivers/sqlboiler-psql diff --git a/pkg/tool/.snapshots/TestWriter_Write-mod_all_nobuild-tools.go b/pkg/tool/.snapshots/TestWriter_Write-mod_all_nobuild-tools.go new file mode 100644 index 0000000..fae956d --- /dev/null +++ b/pkg/tool/.snapshots/TestWriter_Write-mod_all_nobuild-tools.go @@ -0,0 +1,16 @@ +// Code generated by github.com/izumin5210/gex. DO NOT EDIT. + +// +build tools +//gex:nobin + +package tools + +// tool dependencies +import ( + _ "github.com/gogo/protobuf/protoc-gen-gogofast" + _ "github.com/grpc-ecosystem/grpc-gateway/protoc-gen-grpc-gateway" + _ "github.com/grpc-ecosystem/grpc-gateway/protoc-gen-swagger" + _ "github.com/volatiletech/sqlboiler" + _ "github.com/volatiletech/sqlboiler/drivers/sqlboiler-psql" +) + diff --git a/pkg/tool/.snapshots/TestWriter_Write-mod_default_nobuild-tools.go b/pkg/tool/.snapshots/TestWriter_Write-mod_default_nobuild-tools.go new file mode 100644 index 0000000..bca9e92 --- /dev/null +++ b/pkg/tool/.snapshots/TestWriter_Write-mod_default_nobuild-tools.go @@ -0,0 +1,24 @@ +// Code generated by github.com/izumin5210/gex. DO NOT EDIT. + +// +build tools +//gex:nobin + +package tools + +// tool dependencies +import ( + _ "github.com/gogo/protobuf/protoc-gen-gogofast" + _ "github.com/grpc-ecosystem/grpc-gateway/protoc-gen-grpc-gateway" + //gex:bin + _ "github.com/grpc-ecosystem/grpc-gateway/protoc-gen-swagger" + //gex:nobin + _ "github.com/volatiletech/sqlboiler" + //gex:nobin + _ "github.com/volatiletech/sqlboiler/drivers/sqlboiler-psql" +) + +// If you want to use tools, please run the following command: +// go generate ./tools.go +// +//go:generate go build -v -o=./bin/protoc-gen-swagger github.com/grpc-ecosystem/grpc-gateway/protoc-gen-swagger + diff --git a/pkg/tool/manifest.go b/pkg/tool/manifest.go index 615d4e0..ee07b3f 100644 --- a/pkg/tool/manifest.go +++ b/pkg/tool/manifest.go @@ -2,27 +2,38 @@ package tool import ( "sort" + "strings" "github.com/izumin5210/gex/pkg/manager" ) // Manifest contains tool list type Manifest struct { - toolMap map[string]Tool - managerType manager.Type + toolMap map[string]Tool + managerType manager.Type + defaultBuildMode BuildMode } // NewManifest creates a new Manifest instance. -func NewManifest(tools []Tool, mType manager.Type) *Manifest { +func NewManifest(tools []Tool, mType manager.Type, options ...ManifestOption) *Manifest { toolMap := make(map[string]Tool, len(tools)) for _, t := range tools { toolMap[t.Name()] = t } - return &Manifest{toolMap: toolMap, managerType: mType} + m := &Manifest{toolMap: toolMap, managerType: mType} + for _, option := range options { + option(m) + } + if m.defaultBuildMode == BuildModeUnknown { + m.defaultBuildMode = BuildModeBin + } + return m } func (m *Manifest) ManagerType() manager.Type { return m.managerType } +func (m *Manifest) DefaultBuildMode() BuildMode { return m.defaultBuildMode } + // AddTool adds a new tool to the manifest. func (m *Manifest) AddTool(tool Tool) { m.toolMap[tool.Name()] = tool @@ -37,18 +48,25 @@ func (m *Manifest) FindTool(name string) (t Tool, ok bool) { // Tools returns a tool list. func (m *Manifest) Tools() []Tool { n := len(m.toolMap) - s := make([]string, 0, n) + ts := make([]Tool, 0, n) for _, t := range m.toolMap { - s = append(s, string(t)) - } - sort.StringSlice(s).Sort() - ts := make([]Tool, n, n) - for i, t := range s { - ts[i] = Tool(t) + ts = append(ts, t) } + sort.Slice(ts, func(i int, j int) bool { + return strings.Compare(ts[i].ImportPath, ts[j].ImportPath) < 0 + }) return ts } func (m *Manifest) addTool(t Tool) { m.toolMap[t.Name()] = t } + +type ManifestOption func(m *Manifest) + +// WithDefaultBuildMode configures default build mode. +func WithDefaultBuildMode(buildMode BuildMode) ManifestOption { + return func(m *Manifest) { + m.defaultBuildMode = buildMode + } +} diff --git a/pkg/tool/parser.go b/pkg/tool/parser.go index 63cddd5..9cf8b13 100644 --- a/pkg/tool/parser.go +++ b/pkg/tool/parser.go @@ -1,9 +1,11 @@ package tool import ( + "go/ast" "go/parser" "go/token" "strconv" + "strings" "github.com/izumin5210/gex/pkg/manager" "github.com/pkg/errors" @@ -35,18 +37,50 @@ func (p *parserImpl) Parse(path string) (*Manifest, error) { } fset := token.NewFileSet() - f, err := parser.ParseFile(fset, "", string(data), parser.ImportsOnly) + f, err := parser.ParseFile(fset, "", string(data), parser.ImportsOnly|parser.ParseComments) if err != nil { return nil, errors.Wrapf(err, "failed to parse %q", path) } tools := make([]Tool, 0, len(f.Imports)) + comments := commentTraverser{comments: f.Comments} + fileComments := comments.consume(f.Pos()) + fileBuildMode := parseBuildMode(fileComments) for _, s := range f.Imports { if pkg, err := strconv.Unquote(s.Path.Value); err == nil { - tools = append(tools, Tool(pkg)) + importComments := comments.consume(s.Pos()) + buildMode := parseBuildMode(importComments) + tools = append(tools, Tool{ImportPath: pkg, BuildMode: buildMode}) } } - return NewManifest(tools, p.mType), nil + return NewManifest(tools, p.mType, WithDefaultBuildMode(fileBuildMode)), nil +} + +type commentTraverser struct { + comments []*ast.CommentGroup + index int +} + +func (t *commentTraverser) consume(pos token.Pos) []*ast.CommentGroup { + start := t.index + for t.index < len(t.comments) && t.comments[t.index].Pos() < pos { + t.index++ + } + return t.comments[start:t.index] +} + +func parseBuildMode(groups []*ast.CommentGroup) BuildMode { + for _, group := range groups { + for _, element := range group.List { + if strings.Contains(element.Text, "gex:bin") { + return BuildModeBin + } + if strings.Contains(element.Text, "gex:nobin") { + return BuildModeNoBin + } + } + } + return BuildModeUnknown } diff --git a/pkg/tool/parser_test.go b/pkg/tool/parser_test.go index 2d7ea7a..0a28023 100644 --- a/pkg/tool/parser_test.go +++ b/pkg/tool/parser_test.go @@ -14,8 +14,15 @@ func TestParser_Parse(t *testing.T) { fs := afero.NewMemMapFs() parser := tool.NewParser(fs, manager.TypeModules) - var ( - toolsGo = `// Code generated by github.comm/izumin5210/gex. DO NOT EDIT. + testcases := []struct { + name string + toolsGo string + expectTools []tool.Tool + expectDefaultBuildMode tool.BuildMode + }{ + { + name: "no default build mode", + toolsGo: `// Code generated by github.comm/izumin5210/gex. DO NOT EDIT. // +build tools @@ -24,34 +31,102 @@ package tools import ( _ "github.com/gogo/protobuf/protoc-gen-gogofast" _ "github.com/grpc-ecosystem/grpc-gateway/protoc-gen-grpc-gateway" + //gex:bin _ "github.com/grpc-ecosystem/grpc-gateway/protoc-gen-swagger" + //gex:nobin _ "github.com/volatiletech/sqlboiler" + //gex:nobin _ "github.com/volatiletech/sqlboiler/drivers/sqlboiler-psql" ) -` - ) - path := "/home/src/awesomeapp/tools" +`, + expectTools: []tool.Tool{ + {"github.com/gogo/protobuf/protoc-gen-gogofast", tool.BuildModeUnknown}, + {"github.com/grpc-ecosystem/grpc-gateway/protoc-gen-grpc-gateway", tool.BuildModeUnknown}, + {"github.com/grpc-ecosystem/grpc-gateway/protoc-gen-swagger", tool.BuildModeBin}, + {"github.com/volatiletech/sqlboiler", tool.BuildModeNoBin}, + {"github.com/volatiletech/sqlboiler/drivers/sqlboiler-psql", tool.BuildModeNoBin}, + }, + expectDefaultBuildMode: tool.BuildModeBin, + }, + { + name: "default build mode set to build", + toolsGo: `// Code generated by github.comm/izumin5210/gex. DO NOT EDIT. - err := afero.WriteFile(fs, path, []byte(toolsGo), 0644) - if err != nil { - t.Fatalf("faield to write %s: %v", path, err) - } +// +build tools +//gex:bin - out, err := parser.Parse(path) +package tools - if err != nil { - t.Fatalf("Parse() returned an error: %v", err) - } +import ( + _ "github.com/gogo/protobuf/protoc-gen-gogofast" + _ "github.com/grpc-ecosystem/grpc-gateway/protoc-gen-grpc-gateway" + //gex:bin + _ "github.com/grpc-ecosystem/grpc-gateway/protoc-gen-swagger" + //gex:nobin + _ "github.com/volatiletech/sqlboiler" + //gex:nobin + _ "github.com/volatiletech/sqlboiler/drivers/sqlboiler-psql" +) +`, + expectTools: []tool.Tool{ + {"github.com/gogo/protobuf/protoc-gen-gogofast", tool.BuildModeUnknown}, + {"github.com/grpc-ecosystem/grpc-gateway/protoc-gen-grpc-gateway", tool.BuildModeUnknown}, + {"github.com/grpc-ecosystem/grpc-gateway/protoc-gen-swagger", tool.BuildModeBin}, + {"github.com/volatiletech/sqlboiler", tool.BuildModeNoBin}, + {"github.com/volatiletech/sqlboiler/drivers/sqlboiler-psql", tool.BuildModeNoBin}, + }, + expectDefaultBuildMode: tool.BuildModeBin, + }, + { + name: "default build mode set to nobuild", + toolsGo: `// Code generated by github.comm/izumin5210/gex. DO NOT EDIT. + +// +build tools +//gex:nobin - want := []tool.Tool{ - tool.Tool("github.com/gogo/protobuf/protoc-gen-gogofast"), - tool.Tool("github.com/grpc-ecosystem/grpc-gateway/protoc-gen-grpc-gateway"), - tool.Tool("github.com/grpc-ecosystem/grpc-gateway/protoc-gen-swagger"), - tool.Tool("github.com/volatiletech/sqlboiler"), - tool.Tool("github.com/volatiletech/sqlboiler/drivers/sqlboiler-psql"), +package tools + +import ( + _ "github.com/gogo/protobuf/protoc-gen-gogofast" + _ "github.com/grpc-ecosystem/grpc-gateway/protoc-gen-grpc-gateway" + //gex:bin + _ "github.com/grpc-ecosystem/grpc-gateway/protoc-gen-swagger" + //gex:nobin + _ "github.com/volatiletech/sqlboiler" + //gex:nobin + _ "github.com/volatiletech/sqlboiler/drivers/sqlboiler-psql" +) +`, + expectTools: []tool.Tool{ + {"github.com/gogo/protobuf/protoc-gen-gogofast", tool.BuildModeUnknown}, + {"github.com/grpc-ecosystem/grpc-gateway/protoc-gen-grpc-gateway", tool.BuildModeUnknown}, + {"github.com/grpc-ecosystem/grpc-gateway/protoc-gen-swagger", tool.BuildModeBin}, + {"github.com/volatiletech/sqlboiler", tool.BuildModeNoBin}, + {"github.com/volatiletech/sqlboiler/drivers/sqlboiler-psql", tool.BuildModeNoBin}, + }, + expectDefaultBuildMode: tool.BuildModeNoBin, + }, } + for _, testcase := range testcases { + path := "/home/src/awesomeapp/tools" + + err := afero.WriteFile(fs, path, []byte(testcase.toolsGo), 0644) + if err != nil { + t.Fatalf("faield to write %s: %v", path, err) + } + + out, err := parser.Parse(path) + + if err != nil { + t.Fatalf("Parse() returned an error: %v", err) + } + + if diff := cmp.Diff(testcase.expectTools, out.Tools()); diff != "" { + t.Errorf("tool differs: (-want +got)\n%s", diff) + } - if diff := cmp.Diff(out.Tools(), want); diff != "" { - t.Errorf("tool differs: (-want +got)\n%s", diff) + if diff := cmp.Diff(testcase.expectDefaultBuildMode, out.DefaultBuildMode()); diff != "" { + t.Errorf("tool differs: (-want +got)\n%s", diff) + } } } diff --git a/pkg/tool/repository.go b/pkg/tool/repository.go index 6c738dd..c99f5da 100644 --- a/pkg/tool/repository.go +++ b/pkg/tool/repository.go @@ -14,6 +14,7 @@ import ( type Repository interface { List(ctx context.Context) ([]Tool, error) Add(ctx context.Context, pkgs ...string) error + AddOpt(ctx context.Context, buildMode BuildMode, pkgs ...string) error Build(ctx context.Context, t Tool) (string, error) BuildAll(ctx context.Context) error Run(ctx context.Context, name string, args ...string) error @@ -50,6 +51,10 @@ func (r *repositoryImpl) List(ctx context.Context) ([]Tool, error) { } func (r *repositoryImpl) Add(ctx context.Context, pkgs ...string) error { + return r.AddOpt(ctx, BuildModeUnknown, pkgs...) +} + +func (r *repositoryImpl) AddOpt(ctx context.Context, buildMode BuildMode, pkgs ...string) error { r.Log.Println("add", strings.Join(pkgs, ", ")) for _, pkg := range pkgs { @@ -71,7 +76,7 @@ func (r *repositoryImpl) Add(ctx context.Context, pkgs ...string) error { for i, pkg := range pkgs { pkg = strings.SplitN(pkg, "@", 2)[0] - t := Tool(pkg) + t := Tool{ImportPath: pkg, BuildMode: buildMode} m.AddTool(t) tools[i] = t } @@ -87,6 +92,9 @@ func (r *repositoryImpl) Add(ctx context.Context, pkgs ...string) error { } for _, t := range tools { + if !t.NeedBuild(m.DefaultBuildMode()) { + continue + } _, err = r.Build(ctx, t) if err != nil { return errors.WithStack(err) @@ -100,9 +108,9 @@ func (r *repositoryImpl) Build(ctx context.Context, t Tool) (string, error) { if st, err := r.FS.Stat(binPath); err != nil { r.Log.Println("build", t) - err := r.manager.Build(ctx, binPath, string(t), r.Verbose) + err := r.manager.Build(ctx, binPath, t.ImportPath, r.Verbose) if err != nil { - return "", errors.Wrapf(err, "failed to build %s", t) + return "", errors.Wrapf(err, "failed to build %s", t.ImportPath) } } else if st.IsDir() { return "", errors.Errorf("%q is a directory", t.Name()) @@ -124,6 +132,9 @@ func (r *repositoryImpl) BuildAll(ctx context.Context) error { for _, t := range m.Tools() { t := t + if !t.NeedBuild(m.DefaultBuildMode()) { + continue + } wg.Add(1) go func() { defer wg.Done() @@ -154,12 +165,16 @@ func (r *repositoryImpl) Run(ctx context.Context, name string, args ...string) e return errors.Errorf("failed to find the tool %q", name) } - bin, err := r.Build(ctx, t) - if err != nil { - return errors.WithStack(err) - } + if t.NeedBuild(m.DefaultBuildMode()) { + bin, err := r.Build(ctx, t) + if err != nil { + return errors.WithStack(err) + } - return errors.WithStack(r.executor.Exec(ctx, bin, args...)) + return errors.WithStack(r.executor.Exec(ctx, bin, args...)) + } else { + return errors.WithStack(r.manager.RunInPlace(ctx, t.ImportPath, r.Verbose, args...)) + } } func (r *repositoryImpl) getManifest() (*Manifest, error) { diff --git a/pkg/tool/tool.go b/pkg/tool/tool.go index d4d6e32..8940dc2 100644 --- a/pkg/tool/tool.go +++ b/pkg/tool/tool.go @@ -3,9 +3,30 @@ package tool import "path/filepath" // Tool represents a go package of a tool dependency. -type Tool string +type Tool struct { + ImportPath string + BuildMode BuildMode +} // Name returns an executable name. func (t Tool) Name() string { - return filepath.Base(string(t)) + return filepath.Base(t.ImportPath) +} + +// NeedBuild determines if the tool should be built before run. +func (t Tool) NeedBuild(defaultBuildMode BuildMode) bool { + buildMode := t.BuildMode + if buildMode == BuildModeUnknown { + buildMode = defaultBuildMode + } + return buildMode == BuildModeBin } + +// BuildMode records whether the package should be build ahead of time. +type BuildMode int + +const ( + BuildModeUnknown BuildMode = iota + BuildModeBin + BuildModeNoBin +) diff --git a/pkg/tool/writer.go b/pkg/tool/writer.go index 0a86197..5177ce9 100644 --- a/pkg/tool/writer.go +++ b/pkg/tool/writer.go @@ -2,7 +2,7 @@ package tool import ( "bytes" - "html/template" + "text/template" "github.com/pkg/errors" "github.com/spf13/afero" @@ -38,24 +38,66 @@ func (w *writerImpl) Write(path string, m *Manifest) error { } var ( - toolsGoTemplate = template.Must(template.New("tools.go").Parse(`// Code generated by github.com/izumin5210/gex. DO NOT EDIT. + toolsGoTemplate = template.Must(template.New("tools.go").Funcs(template.FuncMap{ + "hasBuildMode": hasBuildMode, + "buildModeComment": buildModeComment, + "needBuild": needBuild, + "needBuildAny": needBuildAny, + }).Parse(`// Code generated by github.com/izumin5210/gex. DO NOT EDIT. // +build tools +{{- if hasBuildMode .DefaultBuildMode }} +{{ buildModeComment .DefaultBuildMode }} +{{- end }} package tools // tool dependencies import ( {{- range $t := .Tools}} - _ "{{$t}}" +{{- if hasBuildMode $t.BuildMode }} + {{ buildModeComment $t.BuildMode }} +{{- end }} + _ "{{$t.ImportPath}}" {{- end}} ) +{{- if needBuildAny $ }} // If you want to use tools, please run the following command: // go generate ./tools.go // {{- range $t := .Tools}} -//go:generate go build -v -o=./bin/{{$t.Name}} {{if $.ManagerType.Vendor}}./vendor/{{end}}{{$t}} +{{- if needBuild $ $t }} +//go:generate go build -v -o=./bin/{{$t.Name}} {{if $.ManagerType.Vendor}}./vendor/{{end}}{{$t.ImportPath}} +{{- end}} +{{- end}} {{- end}} `)) ) + +func hasBuildMode(buildMode BuildMode) bool { + return buildMode != BuildModeUnknown +} + +func buildModeComment(buildMode BuildMode) string { + switch buildMode { + case BuildModeBin: + return "//gex:bin" + case BuildModeNoBin: + return "//gex:nobin" + } + return "" +} + +func needBuild(m *Manifest, t Tool) bool { + return t.NeedBuild(m.DefaultBuildMode()) +} + +func needBuildAny(m *Manifest) bool { + for _, t := range m.Tools() { + if needBuild(m, t) { + return true + } + } + return false +} diff --git a/pkg/tool/writer_test.go b/pkg/tool/writer_test.go index de54dd4..e5f588d 100644 --- a/pkg/tool/writer_test.go +++ b/pkg/tool/writer_test.go @@ -14,15 +14,67 @@ func TestWriter_Write(t *testing.T) { fs := afero.NewMemMapFs() writer := tool.NewWriter(fs) - for _, typ := range []manager.Type{manager.TypeModules, manager.TypeDep} { - t.Run(typ.String(), func(t *testing.T) { - in := tool.NewManifest([]tool.Tool{ - "github.com/gogo/protobuf/protoc-gen-gogofast", - "github.com/grpc-ecosystem/grpc-gateway/protoc-gen-grpc-gateway", - "github.com/grpc-ecosystem/grpc-gateway/protoc-gen-swagger", - "github.com/volatiletech/sqlboiler", - "github.com/volatiletech/sqlboiler/drivers/sqlboiler-psql", - }, typ) + testcases := []struct { + name string + managerType manager.Type + tools []tool.Tool + defaultBuildMode tool.BuildMode + }{ + { + name: "dep", + managerType: manager.TypeDep, + tools: []tool.Tool{ + {"github.com/gogo/protobuf/protoc-gen-gogofast", tool.BuildModeUnknown}, + {"github.com/grpc-ecosystem/grpc-gateway/protoc-gen-grpc-gateway", tool.BuildModeUnknown}, + {"github.com/grpc-ecosystem/grpc-gateway/protoc-gen-swagger", tool.BuildModeUnknown}, + {"github.com/volatiletech/sqlboiler", tool.BuildModeUnknown}, + {"github.com/volatiletech/sqlboiler/drivers/sqlboiler-psql", tool.BuildModeUnknown}, + }, + defaultBuildMode: tool.BuildModeBin, + }, + { + name: "mod", + managerType: manager.TypeModules, + tools: []tool.Tool{ + {"github.com/gogo/protobuf/protoc-gen-gogofast", tool.BuildModeUnknown}, + {"github.com/grpc-ecosystem/grpc-gateway/protoc-gen-grpc-gateway", tool.BuildModeUnknown}, + {"github.com/grpc-ecosystem/grpc-gateway/protoc-gen-swagger", tool.BuildModeBin}, + {"github.com/volatiletech/sqlboiler", tool.BuildModeNoBin}, + {"github.com/volatiletech/sqlboiler/drivers/sqlboiler-psql", tool.BuildModeNoBin}, + }, + defaultBuildMode: tool.BuildModeBin, + }, + { + name: "mod default nobuild", + managerType: manager.TypeModules, + tools: []tool.Tool{ + {"github.com/gogo/protobuf/protoc-gen-gogofast", tool.BuildModeUnknown}, + {"github.com/grpc-ecosystem/grpc-gateway/protoc-gen-grpc-gateway", tool.BuildModeUnknown}, + {"github.com/grpc-ecosystem/grpc-gateway/protoc-gen-swagger", tool.BuildModeBin}, + {"github.com/volatiletech/sqlboiler", tool.BuildModeNoBin}, + {"github.com/volatiletech/sqlboiler/drivers/sqlboiler-psql", tool.BuildModeNoBin}, + }, + defaultBuildMode: tool.BuildModeNoBin, + }, + { + name: "mod all nobuild", + managerType: manager.TypeModules, + tools: []tool.Tool{ + {"github.com/gogo/protobuf/protoc-gen-gogofast", tool.BuildModeUnknown}, + {"github.com/grpc-ecosystem/grpc-gateway/protoc-gen-grpc-gateway", tool.BuildModeUnknown}, + {"github.com/grpc-ecosystem/grpc-gateway/protoc-gen-swagger", tool.BuildModeUnknown}, + {"github.com/volatiletech/sqlboiler", tool.BuildModeUnknown}, + {"github.com/volatiletech/sqlboiler/drivers/sqlboiler-psql", tool.BuildModeUnknown}, + }, + defaultBuildMode: tool.BuildModeNoBin, + }, + } + + for _, testcase := range testcases { + t.Run(testcase.name, func(t *testing.T) { + in := tool.NewManifest(testcase.tools, testcase.managerType, tool.WithDefaultBuildMode( + testcase.defaultBuildMode, + )) path := "/home/src/awesomeapp/tools" err := writer.Write(path, in) diff --git a/tests/e2e/.snapshots/TestGex_Dep-add_2_tools-tools.go b/tests/e2e/.snapshots/TestGex_Dep-add_2_tools-tools.go index 879127e..d141c6b 100644 --- a/tests/e2e/.snapshots/TestGex_Dep-add_2_tools-tools.go +++ b/tests/e2e/.snapshots/TestGex_Dep-add_2_tools-tools.go @@ -1,6 +1,7 @@ // Code generated by github.com/izumin5210/gex. DO NOT EDIT. // +build tools +//gex:bin package tools diff --git a/tests/e2e/.snapshots/TestGex_Dep-add_a_tool_that_has_already_been_added-tools.go b/tests/e2e/.snapshots/TestGex_Dep-add_a_tool_that_has_already_been_added-tools.go index 879127e..d141c6b 100644 --- a/tests/e2e/.snapshots/TestGex_Dep-add_a_tool_that_has_already_been_added-tools.go +++ b/tests/e2e/.snapshots/TestGex_Dep-add_a_tool_that_has_already_been_added-tools.go @@ -1,6 +1,7 @@ // Code generated by github.com/izumin5210/gex. DO NOT EDIT. // +build tools +//gex:bin package tools diff --git a/tests/e2e/.snapshots/TestGex_Dep-add_first_tool-tools.go b/tests/e2e/.snapshots/TestGex_Dep-add_first_tool-tools.go index ae3fe06..bf2eadc 100644 --- a/tests/e2e/.snapshots/TestGex_Dep-add_first_tool-tools.go +++ b/tests/e2e/.snapshots/TestGex_Dep-add_first_tool-tools.go @@ -1,6 +1,7 @@ // Code generated by github.com/izumin5210/gex. DO NOT EDIT. // +build tools +//gex:bin package tools diff --git a/tests/e2e/.snapshots/TestGex_Dep-add_tools_included_in_the_same_package-tools.go b/tests/e2e/.snapshots/TestGex_Dep-add_tools_included_in_the_same_package-tools.go index 4b4b3b3..8aa97af 100644 --- a/tests/e2e/.snapshots/TestGex_Dep-add_tools_included_in_the_same_package-tools.go +++ b/tests/e2e/.snapshots/TestGex_Dep-add_tools_included_in_the_same_package-tools.go @@ -1,6 +1,7 @@ // Code generated by github.com/izumin5210/gex. DO NOT EDIT. // +build tools +//gex:bin package tools diff --git a/tests/e2e/.snapshots/TestGex_Dep-add_tools_that_its_root_proejct_has_been_added-tools.go b/tests/e2e/.snapshots/TestGex_Dep-add_tools_that_its_root_proejct_has_been_added-tools.go deleted file mode 100644 index 288a32a..0000000 --- a/tests/e2e/.snapshots/TestGex_Dep-add_tools_that_its_root_proejct_has_been_added-tools.go +++ /dev/null @@ -1,28 +0,0 @@ -// Code generated by github.com/izumin5210/gex. DO NOT EDIT. - -// +build tools - -package tools - -// tool dependencies -import ( - _ "github.com/gogo/protobuf/protoc-gen-gogo" - _ "github.com/gogo/protobuf/protoc-gen-gogofast" - _ "github.com/golang/mock/mockgen" - _ "github.com/grpc-ecosystem/grpc-gateway/protoc-gen-grpc-gateway" - _ "github.com/grpc-ecosystem/grpc-gateway/protoc-gen-swagger" - _ "github.com/srvc/wraperr/cmd/wraperr" - _ "golang.org/x/lint/golint" -) - -// If you want to use tools, please run the following command: -// go generate ./tools.go -// -//go:generate go build -v -o=./bin/protoc-gen-gogo ./vendor/github.com/gogo/protobuf/protoc-gen-gogo -//go:generate go build -v -o=./bin/protoc-gen-gogofast ./vendor/github.com/gogo/protobuf/protoc-gen-gogofast -//go:generate go build -v -o=./bin/mockgen ./vendor/github.com/golang/mock/mockgen -//go:generate go build -v -o=./bin/protoc-gen-grpc-gateway ./vendor/github.com/grpc-ecosystem/grpc-gateway/protoc-gen-grpc-gateway -//go:generate go build -v -o=./bin/protoc-gen-swagger ./vendor/github.com/grpc-ecosystem/grpc-gateway/protoc-gen-swagger -//go:generate go build -v -o=./bin/wraperr ./vendor/github.com/srvc/wraperr/cmd/wraperr -//go:generate go build -v -o=./bin/golint ./vendor/golang.org/x/lint/golint - diff --git a/tests/e2e/.snapshots/TestGex_Dep-add_tools_that_the_tool_has_the_same_package_has_already_been_added-tools.go b/tests/e2e/.snapshots/TestGex_Dep-add_tools_that_the_tool_has_the_same_package_has_already_been_added-tools.go index 5d1c25b..803c0a2 100644 --- a/tests/e2e/.snapshots/TestGex_Dep-add_tools_that_the_tool_has_the_same_package_has_already_been_added-tools.go +++ b/tests/e2e/.snapshots/TestGex_Dep-add_tools_that_the_tool_has_the_same_package_has_already_been_added-tools.go @@ -1,6 +1,7 @@ // Code generated by github.com/izumin5210/gex. DO NOT EDIT. // +build tools +//gex:bin package tools diff --git a/tests/e2e/.snapshots/TestGex_Dep-add_tools_with_version_specification-tools.go b/tests/e2e/.snapshots/TestGex_Dep-add_tools_with_version_specification-tools.go index d1fdb4f..1f4d769 100644 --- a/tests/e2e/.snapshots/TestGex_Dep-add_tools_with_version_specification-tools.go +++ b/tests/e2e/.snapshots/TestGex_Dep-add_tools_with_version_specification-tools.go @@ -1,6 +1,7 @@ // Code generated by github.com/izumin5210/gex. DO NOT EDIT. // +build tools +//gex:bin package tools diff --git a/tests/e2e/.snapshots/TestGex_Mod-add_2_tools-tools.go b/tests/e2e/.snapshots/TestGex_Mod-add_2_tools-tools.go index 9e7dfd9..a7da579 100644 --- a/tests/e2e/.snapshots/TestGex_Mod-add_2_tools-tools.go +++ b/tests/e2e/.snapshots/TestGex_Mod-add_2_tools-tools.go @@ -1,6 +1,7 @@ // Code generated by github.com/izumin5210/gex. DO NOT EDIT. // +build tools +//gex:bin package tools diff --git a/tests/e2e/.snapshots/TestGex_Mod-add_a_tool_that_has_already_been_added-tools.go b/tests/e2e/.snapshots/TestGex_Mod-add_a_tool_that_has_already_been_added-tools.go index 9e7dfd9..a7da579 100644 --- a/tests/e2e/.snapshots/TestGex_Mod-add_a_tool_that_has_already_been_added-tools.go +++ b/tests/e2e/.snapshots/TestGex_Mod-add_a_tool_that_has_already_been_added-tools.go @@ -1,6 +1,7 @@ // Code generated by github.com/izumin5210/gex. DO NOT EDIT. // +build tools +//gex:bin package tools diff --git a/tests/e2e/.snapshots/TestGex_Mod-add_first_tool-tools.go b/tests/e2e/.snapshots/TestGex_Mod-add_first_tool-tools.go index 161d36e..186286b 100644 --- a/tests/e2e/.snapshots/TestGex_Mod-add_first_tool-tools.go +++ b/tests/e2e/.snapshots/TestGex_Mod-add_first_tool-tools.go @@ -1,6 +1,7 @@ // Code generated by github.com/izumin5210/gex. DO NOT EDIT. // +build tools +//gex:bin package tools diff --git a/tests/e2e/.snapshots/TestGex_Mod-add_tools_included_in_the_same_package-tools.go b/tests/e2e/.snapshots/TestGex_Mod-add_tools_included_in_the_same_package-tools.go index 69c1425..2822168 100644 --- a/tests/e2e/.snapshots/TestGex_Mod-add_tools_included_in_the_same_package-tools.go +++ b/tests/e2e/.snapshots/TestGex_Mod-add_tools_included_in_the_same_package-tools.go @@ -1,6 +1,7 @@ // Code generated by github.com/izumin5210/gex. DO NOT EDIT. // +build tools +//gex:bin package tools diff --git a/tests/e2e/.snapshots/TestGex_Mod-add_tools_that_its_root_proejct_has_been_added-tools.go b/tests/e2e/.snapshots/TestGex_Mod-add_tools_that_its_root_proejct_has_been_added-tools.go deleted file mode 100644 index 80059a0..0000000 --- a/tests/e2e/.snapshots/TestGex_Mod-add_tools_that_its_root_proejct_has_been_added-tools.go +++ /dev/null @@ -1,28 +0,0 @@ -// Code generated by github.com/izumin5210/gex. DO NOT EDIT. - -// +build tools - -package tools - -// tool dependencies -import ( - _ "github.com/gogo/protobuf/protoc-gen-gogo" - _ "github.com/gogo/protobuf/protoc-gen-gogofast" - _ "github.com/golang/mock/mockgen" - _ "github.com/grpc-ecosystem/grpc-gateway/protoc-gen-grpc-gateway" - _ "github.com/grpc-ecosystem/grpc-gateway/protoc-gen-swagger" - _ "github.com/srvc/wraperr/cmd/wraperr" - _ "golang.org/x/lint/golint" -) - -// If you want to use tools, please run the following command: -// go generate ./tools.go -// -//go:generate go build -v -o=./bin/protoc-gen-gogo github.com/gogo/protobuf/protoc-gen-gogo -//go:generate go build -v -o=./bin/protoc-gen-gogofast github.com/gogo/protobuf/protoc-gen-gogofast -//go:generate go build -v -o=./bin/mockgen github.com/golang/mock/mockgen -//go:generate go build -v -o=./bin/protoc-gen-grpc-gateway github.com/grpc-ecosystem/grpc-gateway/protoc-gen-grpc-gateway -//go:generate go build -v -o=./bin/protoc-gen-swagger github.com/grpc-ecosystem/grpc-gateway/protoc-gen-swagger -//go:generate go build -v -o=./bin/wraperr github.com/srvc/wraperr/cmd/wraperr -//go:generate go build -v -o=./bin/golint golang.org/x/lint/golint - diff --git a/tests/e2e/.snapshots/TestGex_Mod-add_tools_that_the_tool_has_the_same_package_has_already_been_added-tools.go b/tests/e2e/.snapshots/TestGex_Mod-add_tools_that_the_tool_has_the_same_package_has_already_been_added-tools.go index 2942681..15df6b9 100644 --- a/tests/e2e/.snapshots/TestGex_Mod-add_tools_that_the_tool_has_the_same_package_has_already_been_added-tools.go +++ b/tests/e2e/.snapshots/TestGex_Mod-add_tools_that_the_tool_has_the_same_package_has_already_been_added-tools.go @@ -1,6 +1,7 @@ // Code generated by github.com/izumin5210/gex. DO NOT EDIT. // +build tools +//gex:bin package tools diff --git a/tests/e2e/.snapshots/TestGex_Mod-add_tools_with_version_specification-tools.go b/tests/e2e/.snapshots/TestGex_Mod-add_tools_with_version_specification-tools.go index 5487089..5514676 100644 --- a/tests/e2e/.snapshots/TestGex_Mod-add_tools_with_version_specification-tools.go +++ b/tests/e2e/.snapshots/TestGex_Mod-add_tools_with_version_specification-tools.go @@ -1,6 +1,7 @@ // Code generated by github.com/izumin5210/gex. DO NOT EDIT. // +build tools +//gex:bin package tools