Skip to content

Commit b68957b

Browse files
authored
Merge pull request #222 from gitpod-io/wv/fixed-build-dir
Add option to build in the same directory, to better use caches
2 parents c5d5037 + 365a667 commit b68957b

File tree

2 files changed

+90
-12
lines changed

2 files changed

+90
-12
lines changed

cmd/build.go

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -181,10 +181,12 @@ func addBuildFlags(cmd *cobra.Command) {
181181
cmd.Flags().Bool("jailed-execution", false, "Run all build commands using runc (defaults to false)")
182182
cmd.Flags().UintP("max-concurrent-tasks", "j", uint(cpus), "Limit the number of max concurrent build tasks - set to 0 to disable the limit")
183183
cmd.Flags().String("coverage-output-path", "", "Output path where test coverage file will be copied after running tests")
184+
cmd.Flags().Bool("disable-coverage", false, "Disable test coverage collection (defaults to false)")
184185
cmd.Flags().StringToString("docker-build-options", nil, "Options passed to all 'docker build' commands")
185186
cmd.Flags().String("report", "", "Generate a HTML report after the build has finished. (e.g. --report myreport.html)")
186187
cmd.Flags().String("report-segment", os.Getenv("LEEWAY_SEGMENT_KEY"), "Report build events to segment using the segment key (defaults to $LEEWAY_SEGMENT_KEY)")
187188
cmd.Flags().Bool("report-github", os.Getenv("GITHUB_OUTPUT") != "", "Report package build success/failure to GitHub Actions using the GITHUB_OUTPUT environment variable")
189+
cmd.Flags().Bool("fixed-build-dir", false, "Use a fixed build directory for each package, instead of based on the package version, to better utilize caches based on absolute paths (defaults to false)")
188190
}
189191

190192
func getBuildOpts(cmd *cobra.Command) ([]leeway.BuildOption, cache.LocalCache) {
@@ -291,6 +293,8 @@ func getBuildOpts(cmd *cobra.Command) ([]leeway.BuildOption, cache.LocalCache) {
291293
_ = os.MkdirAll(coverageOutputPath, 0644)
292294
}
293295

296+
disableCoverage, _ := cmd.Flags().GetBool("disable-coverage")
297+
294298
var dockerBuildOptions leeway.DockerBuildOptions
295299
dockerBuildOptions, err = cmd.Flags().GetStringToString("docker-build-options")
296300
if err != nil {
@@ -307,6 +311,11 @@ func getBuildOpts(cmd *cobra.Command) ([]leeway.BuildOption, cache.LocalCache) {
307311
log.Fatal(err)
308312
}
309313

314+
fixedBuildDir, err := cmd.Flags().GetBool("fixed-build-dir")
315+
if err != nil {
316+
log.Fatal(err)
317+
}
318+
310319
return []leeway.BuildOption{
311320
leeway.WithLocalCache(localCache),
312321
leeway.WithRemoteCache(remoteCache),
@@ -319,6 +328,8 @@ func getBuildOpts(cmd *cobra.Command) ([]leeway.BuildOption, cache.LocalCache) {
319328
leeway.WithDockerBuildOptions(&dockerBuildOptions),
320329
leeway.WithJailedExecution(jailedExecution),
321330
leeway.WithCompressionDisabled(dontCompress),
331+
leeway.WithFixedBuildDir(fixedBuildDir),
332+
leeway.WithDisableCoverage(disableCoverage),
322333
}, localCache
323334
}
324335

pkg/leeway/build.go

Lines changed: 79 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -264,6 +264,8 @@ type buildOptions struct {
264264
CoverageOutputPath string
265265
DockerBuildOptions *DockerBuildOptions
266266
JailedExecution bool
267+
UseFixedBuildDir bool
268+
DisableCoverage bool
267269

268270
context *buildContext
269271
}
@@ -365,6 +367,20 @@ func WithCompressionDisabled(dontCompress bool) BuildOption {
365367
}
366368
}
367369

370+
func WithFixedBuildDir(fixedBuildDir bool) BuildOption {
371+
return func(opts *buildOptions) error {
372+
opts.UseFixedBuildDir = fixedBuildDir
373+
return nil
374+
}
375+
}
376+
377+
func WithDisableCoverage(disableCoverage bool) BuildOption {
378+
return func(opts *buildOptions) error {
379+
opts.DisableCoverage = disableCoverage
380+
return nil
381+
}
382+
}
383+
368384
func withBuildContext(ctx *buildContext) BuildOption {
369385
return func(opts *buildOptions) error {
370386
opts.context = ctx
@@ -624,12 +640,6 @@ func (p *Package) build(buildctx *buildContext) error {
624640
}
625641
defer buildctx.ReleaseBuildLock(p)
626642

627-
// Get package version
628-
version, err := p.Version()
629-
if err != nil {
630-
return err
631-
}
632-
633643
// Build dependencies first
634644
if err := p.buildDependencies(buildctx); err != nil {
635645
return err
@@ -653,13 +663,24 @@ func (p *Package) build(buildctx *buildContext) error {
653663
buildctx.Reporter.PackageBuildStarted(p)
654664

655665
// Ensure we notify reporter when build finishes
666+
var err error
656667
defer func() {
657668
pkgRep.Error = err
658669
buildctx.Reporter.PackageBuildFinished(p, pkgRep)
659670
}()
660671

661672
// Prepare build directory
662-
builddir := filepath.Join(buildctx.BuildDir(), p.FilesystemSafeName()+"."+version)
673+
builddir, err := createBuildDir(buildctx, p)
674+
if err != nil {
675+
return err
676+
}
677+
defer func() {
678+
// Clean up build directory after build, such that the next build of the same component
679+
// can use the fixed build directory again.
680+
os.RemoveAll(builddir)
681+
}()
682+
683+
log.WithField("package", p.FullName()).WithField("builddir", builddir).Info("using build directory")
663684
if err := prepareDirectory(builddir); err != nil {
664685
return err
665686
}
@@ -756,6 +777,48 @@ func (p *Package) build(buildctx *buildContext) error {
756777
return buildctx.RegisterNewlyBuilt(p)
757778
}
758779

780+
func createBuildDir(buildctx *buildContext, p *Package) (string, error) {
781+
if !buildctx.UseFixedBuildDir {
782+
// Original behavior: use version as suffix to ensure a unique build directory for each package version.
783+
version, err := p.Version()
784+
if err != nil {
785+
return "", err
786+
}
787+
return filepath.Join(buildctx.BuildDir(), p.FilesystemSafeName()+"."+version), nil
788+
}
789+
790+
// New behavior: use the package name as the build directory.
791+
// This ensures the same directory is used for each new build of the package,
792+
// which is more cache-friendly. Allows e.g. Go build/test caching to work,
793+
// as well as golangci-lint caching.
794+
//
795+
// Note: This directoy is only used if it doesn't already exist, otherwise
796+
// we'll fall back to the version as suffix.
797+
// It is possible that the directory exists because the package is already
798+
// being built with a different version (e.g. different args).
799+
builddir := filepath.Join(buildctx.BuildDir(), p.FilesystemSafeName())
800+
801+
err := os.MkdirAll(filepath.Dir(builddir), 0755)
802+
if err != nil {
803+
return "", fmt.Errorf("failed to create parent directory for build directory: %w", err)
804+
}
805+
806+
err = os.Mkdir(builddir, 0755)
807+
if err == nil {
808+
return builddir, nil
809+
}
810+
if !os.IsExist(err) {
811+
return "", fmt.Errorf("failed to create build directory: %w", err)
812+
}
813+
814+
// Already exists, use version as suffix
815+
version, err := p.Version()
816+
if err != nil {
817+
return "", fmt.Errorf("failed to get version for package %s: %w", p.FullName(), err)
818+
}
819+
return filepath.Join(buildctx.BuildDir(), p.FilesystemSafeName()+"."+version), nil
820+
}
821+
759822
func prepareDirectory(dir string) error {
760823
if _, err := os.Stat(dir); !os.IsNotExist(err) {
761824
if err := os.RemoveAll(dir); err != nil {
@@ -1345,11 +1408,15 @@ func (p *Package) buildGo(buildctx *buildContext, wd, result string) (res *packa
13451408
testCommand = append(testCommand, "-v")
13461409
}
13471410

1348-
if buildctx.buildOptions.CoverageOutputPath != "" {
1349-
testCommand = append(testCommand, fmt.Sprintf("-coverprofile=%v", codecovComponentName(p.FullName())))
1350-
} else {
1351-
testCommand = append(testCommand, "-coverprofile=testcoverage.out")
1352-
reportCoverage = collectGoTestCoverage(filepath.Join(wd, "testcoverage.out"))
1411+
// Added as an option to disable test coverage until Go 1.25 is released https://github.com/golang/go/commit/6a4bc8d17eb6703baf0c483fb40e0d3e1f0f6af3
1412+
// Running with coverage stops the test cache from being used, which will be fixed in Go 1.25.
1413+
if !buildctx.DisableCoverage {
1414+
if buildctx.buildOptions.CoverageOutputPath != "" {
1415+
testCommand = append(testCommand, fmt.Sprintf("-coverprofile=%v", codecovComponentName(p.FullName())))
1416+
} else {
1417+
testCommand = append(testCommand, "-coverprofile=testcoverage.out")
1418+
reportCoverage = collectGoTestCoverage(filepath.Join(wd, "testcoverage.out"))
1419+
}
13531420
}
13541421
testCommand = append(testCommand, "./...")
13551422

0 commit comments

Comments
 (0)