diff --git a/ginkgo/build/build_command.go b/ginkgo/build/build_command.go index 5db5d1a7b..fd1726084 100644 --- a/ginkgo/build/build_command.go +++ b/ginkgo/build/build_command.go @@ -2,6 +2,8 @@ package build import ( "fmt" + "os" + "path" "github.com/onsi/ginkgo/v2/ginkgo/command" "github.com/onsi/ginkgo/v2/ginkgo/internal" @@ -53,7 +55,18 @@ func buildSpecs(args []string, cliConfig types.CLIConfig, goFlagsConfig types.Go if suite.State.Is(internal.TestSuiteStateFailedToCompile) { fmt.Println(suite.CompilationError.Error()) } else { - fmt.Printf("Compiled %s.test\n", suite.PackageName) + if len(goFlagsConfig.O) == 0 { + goFlagsConfig.O = path.Join(suite.Path, suite.PackageName+".test") + } else { + stat, err := os.Stat(goFlagsConfig.O) + if err != nil { + panic(err) + } + if stat.IsDir() { + goFlagsConfig.O += "/" + suite.PackageName + ".test" + } + } + fmt.Printf("Compiled %s\n", goFlagsConfig.O) } } diff --git a/ginkgo/internal/compile.go b/ginkgo/internal/compile.go index 86da7340d..48827cc5e 100644 --- a/ginkgo/internal/compile.go +++ b/ginkgo/internal/compile.go @@ -25,6 +25,18 @@ func CompileSuite(suite TestSuite, goFlagsConfig types.GoFlagsConfig) TestSuite return suite } + if len(goFlagsConfig.O) > 0 { + userDefinedPath, err := filepath.Abs(goFlagsConfig.O) + if err != nil { + suite.State = TestSuiteStateFailedToCompile + suite.CompilationError = fmt.Errorf("Failed to compute compilation target path %s:\n%s", goFlagsConfig.O, err.Error()) + return suite + } + path = userDefinedPath + } + + goFlagsConfig.O = path + ginkgoInvocationPath, _ := os.Getwd() ginkgoInvocationPath, _ = filepath.Abs(ginkgoInvocationPath) packagePath := suite.AbsPath() @@ -34,7 +46,7 @@ func CompileSuite(suite TestSuite, goFlagsConfig types.GoFlagsConfig) TestSuite suite.CompilationError = fmt.Errorf("Failed to get relative path from package to the current working directory:\n%s", err.Error()) return suite } - args, err := types.GenerateGoTestCompileArgs(goFlagsConfig, path, "./", pathToInvocationPath) + args, err := types.GenerateGoTestCompileArgs(goFlagsConfig, "./", pathToInvocationPath) if err != nil { suite.State = TestSuiteStateFailedToCompile suite.CompilationError = fmt.Errorf("Failed to generate go test compile flags:\n%s", err.Error()) diff --git a/integration/precompiled_test.go b/integration/precompiled_test.go index 201f6cbf3..b11c79a2e 100644 --- a/integration/precompiled_test.go +++ b/integration/precompiled_test.go @@ -1,12 +1,15 @@ package integration_test import ( + "os" "os/exec" + "path" - . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" "github.com/onsi/gomega/gbytes" "github.com/onsi/gomega/gexec" + + . "github.com/onsi/ginkgo/v2" ) var _ = Describe("ginkgo build", func() { @@ -44,3 +47,34 @@ var _ = Describe("ginkgo build", func() { Ω(session).Should(gbytes.Say("Running in parallel across 2 processes")) }) }) + +var _ = Describe("ginkgo build with custom output", Label("build"), func() { + const customPath = "mycustomdir" + var fullPath string + + BeforeEach(func() { + fm.MountFixture("passing_ginkgo_tests") + fullPath = fm.PathTo("passing_ginkgo_tests", customPath) + Ω(os.Mkdir(fullPath, 0777)).To(Succeed()) + + DeferCleanup(func() { + Ω(os.RemoveAll(fullPath)).Should(Succeed()) + }) + }) + + It("should build with custom path", func() { + session := startGinkgo(fm.PathTo("passing_ginkgo_tests"), "build", "-o", customPath+"/mytestapp") + Eventually(session).Should(gexec.Exit(0)) + output := string(session.Out.Contents()) + Ω(output).Should(And(ContainSubstring("Compiled"), ContainSubstring(customPath+"/mytestapp"))) + Ω(path.Join(fullPath, "/mytestapp")).Should(BeAnExistingFile()) + }) + + It("should build with custom directory", func() { + session := startGinkgo(fm.PathTo("passing_ginkgo_tests"), "build", "-o", customPath) + Eventually(session).Should(gexec.Exit(0)) + output := string(session.Out.Contents()) + Ω(output).Should(And(ContainSubstring("Compiled"), ContainSubstring(customPath+"/passing_ginkgo_tests.test"))) + Ω(path.Join(fullPath, "/passing_ginkgo_tests.test")).Should(BeAnExistingFile()) + }) +}) diff --git a/types/config.go b/types/config.go index 66463cf5e..efd539a6b 100644 --- a/types/config.go +++ b/types/config.go @@ -219,6 +219,7 @@ type GoFlagsConfig struct { ToolExec string Work bool X bool + O string } func NewDefaultGoFlagsConfig() GoFlagsConfig { @@ -561,6 +562,8 @@ var GoBuildFlags = GinkgoFlags{ Usage: "print the name of the temporary work directory and do not delete it when exiting."}, {KeyPath: "Go.X", Name: "x", SectionKey: "go-build", Usage: "print the commands."}, + {KeyPath: "Go.O", Name: "o", SectionKey: "go-build", + Usage: "output binary path (including name)."}, } // GoRunFlags provides flags for the Ginkgo CLI run, and watch commands that capture go's run-time flags. These are passed to the compiled test binary by the ginkgo CLI @@ -614,7 +617,7 @@ func VetAndInitializeCLIAndGoConfig(cliConfig CLIConfig, goFlagsConfig GoFlagsCo } // GenerateGoTestCompileArgs is used by the Ginkgo CLI to generate command line arguments to pass to the go test -c command when compiling the test -func GenerateGoTestCompileArgs(goFlagsConfig GoFlagsConfig, destination string, packageToBuild string, pathToInvocationPath string) ([]string, error) { +func GenerateGoTestCompileArgs(goFlagsConfig GoFlagsConfig, packageToBuild string, pathToInvocationPath string) ([]string, error) { // if the user has set the CoverProfile run-time flag make sure to set the build-time cover flag to make sure // the built test binary can generate a coverprofile if goFlagsConfig.CoverProfile != "" { @@ -637,7 +640,7 @@ func GenerateGoTestCompileArgs(goFlagsConfig GoFlagsConfig, destination string, goFlagsConfig.CoverPkg = strings.Join(adjustedCoverPkgs, ",") } - args := []string{"test", "-c", "-o", destination, packageToBuild} + args := []string{"test", "-c", packageToBuild} goArgs, err := GenerateFlagArgs( GoBuildFlags, map[string]interface{}{