1
0
mirror of https://github.com/SAP/jenkins-library.git synced 2025-01-18 05:18:24 +02:00

fix(golangBuild) properly handle multi main package builds

Co-authored-by: Pavel Busko <pavel.busko@sap.com>
This commit is contained in:
Gareth Evans 2022-04-14 12:37:07 +02:00 committed by Pavel Busko
parent 2ed1ed76fc
commit 74b6b09609
2 changed files with 140 additions and 20 deletions

View File

@ -6,6 +6,7 @@ import (
"net/http"
"os"
"path"
"path/filepath"
"regexp"
"strings"
"text/template"
@ -18,7 +19,6 @@ import (
"github.com/SAP/jenkins-library/pkg/log"
"github.com/SAP/jenkins-library/pkg/piperenv"
"github.com/SAP/jenkins-library/pkg/piperutils"
"github.com/SAP/jenkins-library/pkg/telemetry"
"github.com/SAP/jenkins-library/pkg/multiarch"
@ -182,8 +182,7 @@ func runGolangBuild(config *golangBuildOptions, telemetryData *telemetry.CustomD
log.Entry().Infof("ldflags from template: '%v'", ldflags)
}
binaries := []string{}
var binaries []string
platforms, err := multiarch.ParsePlatformStrings(config.TargetArchitectures)
if err != nil {
@ -191,14 +190,14 @@ func runGolangBuild(config *golangBuildOptions, telemetryData *telemetry.CustomD
}
for _, platform := range platforms {
binary, err := runGolangBuildPerArchitecture(config, utils, ldflags, platform)
binaryNames, err := runGolangBuildPerArchitecture(config, utils, ldflags, platform)
if err != nil {
return err
}
if len(binary) > 0 {
binaries = append(binaries, binary)
if len(binaryNames) > 0 {
binaries = append(binaries, binaryNames...)
}
}
@ -425,8 +424,8 @@ func prepareLdflags(config *golangBuildOptions, utils golangBuildUtils, envRootP
return generatedLdflags.String(), nil
}
func runGolangBuildPerArchitecture(config *golangBuildOptions, utils golangBuildUtils, ldflags string, architecture multiarch.Platform) (string, error) {
var binaryName string
func runGolangBuildPerArchitecture(config *golangBuildOptions, utils golangBuildUtils, ldflags string, architecture multiarch.Platform) ([]string, error) {
var binaryNames []string
envVars := os.Environ()
envVars = append(envVars, fmt.Sprintf("GOOS=%v", architecture.OS), fmt.Sprintf("GOARCH=%v", architecture.Arch))
@ -437,13 +436,25 @@ func runGolangBuildPerArchitecture(config *golangBuildOptions, utils golangBuild
utils.SetEnv(envVars)
buildOptions := []string{"build", "-trimpath"}
if len(config.Output) > 0 {
fileExtension := ""
if architecture.OS == "windows" {
fileExtension = ".exe"
if len(config.Packages) > 1 {
binaries, outputDir, err := getOutputBinaries(config.Output, config.Packages, utils, architecture)
if err != nil {
log.SetErrorCategory(log.ErrorBuild)
return nil, fmt.Errorf("failed to calculate output binaries or directory, error: %s", err.Error())
}
buildOptions = append(buildOptions, "-o", outputDir)
binaryNames = append(binaryNames, binaries...)
} else {
fileExtension := ""
if architecture.OS == "windows" {
fileExtension = ".exe"
}
binaryName := fmt.Sprintf("%s-%s.%s%s", strings.TrimRight(config.Output, string(os.PathSeparator)), architecture.OS, architecture.Arch, fileExtension)
buildOptions = append(buildOptions, "-o", binaryName)
binaryNames = append(binaryNames, binaryName)
}
binaryName = fmt.Sprintf("%v-%v.%v%v", config.Output, architecture.OS, architecture.Arch, fileExtension)
buildOptions = append(buildOptions, "-o", binaryName)
}
buildOptions = append(buildOptions, config.BuildFlags...)
if len(ldflags) > 0 {
@ -454,9 +465,10 @@ func runGolangBuildPerArchitecture(config *golangBuildOptions, utils golangBuild
if err := utils.RunExecutable("go", buildOptions...); err != nil {
log.Entry().Debugf("buildOptions: %v", buildOptions)
log.SetErrorCategory(log.ErrorBuild)
return "", fmt.Errorf("failed to run build for %v.%v: %w", architecture.OS, architecture.Arch, err)
return nil, fmt.Errorf("failed to run build for %v.%v: %w", architecture.OS, architecture.Arch, err)
}
return binaryName, nil
return binaryNames, nil
}
// lookupPrivateModulesRepositories returns a slice of all modules that match the given glob pattern
@ -512,3 +524,41 @@ func readGoModFile(utils golangBuildUtils) (*modfile.File, error) {
return modfile.Parse(modFilePath, modFileContent, nil)
}
func getOutputBinaries(out string, packages []string, utils golangBuildUtils, architecture multiarch.Platform) ([]string, string, error) {
var binaries []string
outDir := fmt.Sprintf("%s-%s-%s%c", strings.TrimRight(out, string(os.PathSeparator)), architecture.OS, architecture.Arch, os.PathSeparator)
for _, pkg := range packages {
ok, err := isMainPackage(utils, pkg)
if err != nil {
return nil, "", err
}
if ok {
fileExt := ""
if architecture.OS == "windows" {
fileExt = ".exe"
}
binaries = append(binaries, filepath.Join(outDir, filepath.Base(pkg)+fileExt))
}
}
return binaries, outDir, nil
}
func isMainPackage(utils golangBuildUtils, pkg string) (bool, error) {
outBuffer := bytes.NewBufferString("")
utils.Stdout(outBuffer)
utils.Stderr(outBuffer)
err := utils.RunExecutable("go", "list", "-f", "{{ .Name }}", pkg)
if err != nil {
return false, err
}
if outBuffer.String() != "main" {
return false, nil
}
return true, nil
}

View File

@ -72,7 +72,7 @@ func newGolangBuildTestsUtils() *golangBuildMockUtils {
utils := golangBuildMockUtils{
ExecMockRunner: &mock.ExecMockRunner{},
FilesMock: &mock.FilesMock{},
//clientOptions: []piperhttp.ClientOptions{},
// clientOptions: []piperhttp.ClientOptions{},
fileUploads: map[string]string{},
}
return &utils
@ -632,14 +632,15 @@ func TestRunGolangBuildPerArchitecture(t *testing.T) {
ldflags := "-X test=test"
architecture, _ := multiarch.ParsePlatformString("linux,amd64")
binaryName, err := runGolangBuildPerArchitecture(&config, utils, ldflags, architecture)
binaryNames, err := runGolangBuildPerArchitecture(&config, utils, ldflags, architecture)
assert.NoError(t, err)
assert.Contains(t, utils.Calls[0].Params, "-o")
assert.Contains(t, utils.Calls[0].Params, "testBin-linux.amd64")
assert.Contains(t, utils.Calls[0].Params, "./test/..")
assert.Contains(t, utils.Calls[0].Params, "-ldflags")
assert.Contains(t, utils.Calls[0].Params, "-X test=test")
assert.Equal(t, "testBin-linux.amd64", binaryName)
assert.Len(t, binaryNames, 1)
assert.Contains(t, binaryNames, "testBin-linux.amd64")
})
t.Run("success - windows", func(t *testing.T) {
@ -649,11 +650,80 @@ func TestRunGolangBuildPerArchitecture(t *testing.T) {
ldflags := ""
architecture, _ := multiarch.ParsePlatformString("windows,amd64")
binaryName, err := runGolangBuildPerArchitecture(&config, utils, ldflags, architecture)
binaryNames, err := runGolangBuildPerArchitecture(&config, utils, ldflags, architecture)
assert.NoError(t, err)
assert.Contains(t, utils.Calls[0].Params, "-o")
assert.Contains(t, utils.Calls[0].Params, "testBin-windows.amd64.exe")
assert.Equal(t, "testBin-windows.amd64.exe", binaryName)
assert.Len(t, binaryNames, 1)
assert.Contains(t, binaryNames, "testBin-windows.amd64.exe")
})
t.Run("success - multiple main packages (linux)", func(t *testing.T) {
t.Parallel()
config := golangBuildOptions{Output: "test/", Packages: []string{"package/foo", "package/bar"}}
utils := newGolangBuildTestsUtils()
utils.StdoutReturn = map[string]string{
"go list -f {{ .Name }} package/foo": "main",
"go list -f {{ .Name }} package/bar": "main",
}
ldflags := ""
architecture, _ := multiarch.ParsePlatformString("linux,amd64")
binaryNames, err := runGolangBuildPerArchitecture(&config, utils, ldflags, architecture)
assert.NoError(t, err)
assert.Contains(t, utils.Calls[0].Params, "list")
assert.Contains(t, utils.Calls[0].Params, "package/foo")
assert.Contains(t, utils.Calls[1].Params, "list")
assert.Contains(t, utils.Calls[1].Params, "package/bar")
assert.Len(t, binaryNames, 2)
assert.Contains(t, binaryNames, "test-linux-amd64/foo")
assert.Contains(t, binaryNames, "test-linux-amd64/bar")
})
t.Run("success - multiple main packages (windows)", func(t *testing.T) {
t.Parallel()
config := golangBuildOptions{Output: "test/", Packages: []string{"package/foo", "package/bar"}}
utils := newGolangBuildTestsUtils()
utils.StdoutReturn = map[string]string{
"go list -f {{ .Name }} package/foo": "main",
"go list -f {{ .Name }} package/bar": "main",
}
ldflags := ""
architecture, _ := multiarch.ParsePlatformString("windows,amd64")
binaryNames, err := runGolangBuildPerArchitecture(&config, utils, ldflags, architecture)
assert.NoError(t, err)
assert.Contains(t, utils.Calls[0].Params, "list")
assert.Contains(t, utils.Calls[0].Params, "package/foo")
assert.Contains(t, utils.Calls[1].Params, "list")
assert.Contains(t, utils.Calls[1].Params, "package/bar")
assert.Len(t, binaryNames, 2)
assert.Contains(t, binaryNames, "test-windows-amd64/foo.exe")
assert.Contains(t, binaryNames, "test-windows-amd64/bar.exe")
})
t.Run("success - multiple mixed packages", func(t *testing.T) {
t.Parallel()
config := golangBuildOptions{Output: "test/", Packages: []string{"package/foo", "package/bar"}}
utils := newGolangBuildTestsUtils()
utils.StdoutReturn = map[string]string{
"go list -f {{ .Name }} package/foo": "main",
"go list -f {{ .Name }} package/bar": "bar",
}
ldflags := ""
architecture, _ := multiarch.ParsePlatformString("linux,amd64")
binaryNames, err := runGolangBuildPerArchitecture(&config, utils, ldflags, architecture)
assert.NoError(t, err)
assert.Contains(t, utils.Calls[0].Params, "list")
assert.Contains(t, utils.Calls[0].Params, "package/foo")
assert.Contains(t, utils.Calls[1].Params, "list")
assert.Contains(t, utils.Calls[1].Params, "package/bar")
assert.Len(t, binaryNames, 1)
assert.Contains(t, binaryNames, "test-linux-amd64/foo")
})
t.Run("execution error", func(t *testing.T) {