diff --git a/pipeline/build/build.go b/pipeline/build/build.go index 7cb5eed16..2ada9f970 100644 --- a/pipeline/build/build.go +++ b/pipeline/build/build.go @@ -45,7 +45,17 @@ func (Pipe) Run(ctx *context.Context) error { } func checkMain(ctx *context.Context, build config.Build) error { - var dir = strings.Replace(build.Main, "main.go", "", -1) + if strings.HasSuffix(build.Main, ".go") { + file, err := parser.ParseFile(token.NewFileSet(), build.Main, nil, 0) + if err != nil { + return errors.Wrapf(err, "failed to parse file: %s", build.Main) + } + if !hasMain(file) { + return fmt.Errorf("build for %s does not contain a main function", build.Binary) + } + return nil + } + var dir = build.Main if dir == "" { dir = "." } @@ -55,21 +65,27 @@ func checkMain(ctx *context.Context, build config.Build) error { } for _, pack := range packs { for _, file := range pack.Files { - for _, decl := range file.Decls { - fn, ok := decl.(*ast.FuncDecl) - if !ok { - continue - } - log.Info(fn.Name.Name) - if fn.Name.Name == "main" && fn.Recv == nil { - return nil - } + if hasMain(file) { + return nil } } } return fmt.Errorf("build for %s does not contain a main function", build.Binary) } +func hasMain(file *ast.File) bool { + for _, decl := range file.Decls { + fn, isFn := decl.(*ast.FuncDecl) + if !isFn { + continue + } + if fn.Name.Name == "main" && fn.Recv == nil { + return true + } + } + return false +} + func runPipeOnBuild(ctx *context.Context, build config.Build) error { if err := runHook(build.Env, build.Hooks.Pre); err != nil { return errors.Wrap(err, "pre hook failed") diff --git a/pipeline/build/build_test.go b/pipeline/build/build_test.go index a4c9306ab..f6fb6bec8 100644 --- a/pipeline/build/build_test.go +++ b/pipeline/build/build_test.go @@ -277,7 +277,7 @@ func TestRunPipeWithouMainFunc(t *testing.T) { }) t.Run("not main.go", func(t *testing.T) { ctx.Config.Builds[0].Main = "foo.go" - assert.EqualError(t, Pipe{}.Run(ctx), `failed to parse dir: foo.go: open foo.go: no such file or directory`) + assert.EqualError(t, Pipe{}.Run(ctx), `failed to parse file: foo.go: open foo.go: no such file or directory`) }) t.Run("glob", func(t *testing.T) { ctx.Config.Builds[0].Main = "." @@ -289,23 +289,62 @@ func TestRunPipeWithouMainFunc(t *testing.T) { }) } +func TestRunPipeWithMainFuncNotInMainGoFile(t *testing.T) { + folder, back := testlib.Mktmp(t) + defer back() + assert.NoError(t, ioutil.WriteFile( + filepath.Join(folder, "foo.go"), + []byte("package main\nfunc main() {println(0)}"), + 0644, + )) + var config = config.Project{ + Builds: []config.Build{ + { + Binary: "foo", + Hooks: config.Hooks{}, + Goos: []string{ + runtime.GOOS, + }, + Goarch: []string{ + runtime.GOARCH, + }, + }, + }, + } + var ctx = context.New(config) + t.Run("empty", func(t *testing.T) { + ctx.Config.Builds[0].Main = "" + assert.NoError(t, Pipe{}.Run(ctx)) + }) + t.Run("foo.go", func(t *testing.T) { + ctx.Config.Builds[0].Main = "foo.go" + assert.NoError(t, Pipe{}.Run(ctx)) + }) + t.Run("glob", func(t *testing.T) { + ctx.Config.Builds[0].Main = "." + assert.NoError(t, Pipe{}.Run(ctx)) + }) +} + func exists(file string) bool { _, err := os.Stat(file) return !os.IsNotExist(err) } func writeMainWithoutMainFunc(t *testing.T, folder string) { - writeFile(t, folder, "package main\nconst a = 2\nfunc notMain() {println(0)}") + assert.NoError(t, ioutil.WriteFile( + filepath.Join(folder, "main.go"), + []byte("package main\nconst a = 2\nfunc notMain() {println(0)}"), + 0644, + )) } func writeGoodMain(t *testing.T, folder string) { - writeFile(t, folder, "package main\nvar a = 1\nfunc main() {println(0)}") -} - -func writeFile(t *testing.T, folder, content string) { assert.NoError(t, ioutil.WriteFile( - filepath.Join(folder, "main.go"), []byte(content), 0644), - ) + filepath.Join(folder, "main.go"), + []byte("package main\nvar a = 1\nfunc main() {println(0)}"), + 0644, + )) } func assertContainsError(t *testing.T, err error, s string) {