You've already forked sap-jenkins-library
mirror of
https://github.com/SAP/jenkins-library.git
synced 2025-07-15 01:34:38 +02:00
fix(golangBuild) properly handle multi main package builds
Co-authored-by: Pavel Busko <pavel.busko@sap.com>
This commit is contained in:
committed by
Pavel Busko
parent
2ed1ed76fc
commit
74b6b09609
@ -6,6 +6,7 @@ import (
|
|||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
"path"
|
"path"
|
||||||
|
"path/filepath"
|
||||||
"regexp"
|
"regexp"
|
||||||
"strings"
|
"strings"
|
||||||
"text/template"
|
"text/template"
|
||||||
@ -18,7 +19,6 @@ import (
|
|||||||
"github.com/SAP/jenkins-library/pkg/log"
|
"github.com/SAP/jenkins-library/pkg/log"
|
||||||
"github.com/SAP/jenkins-library/pkg/piperenv"
|
"github.com/SAP/jenkins-library/pkg/piperenv"
|
||||||
"github.com/SAP/jenkins-library/pkg/piperutils"
|
"github.com/SAP/jenkins-library/pkg/piperutils"
|
||||||
|
|
||||||
"github.com/SAP/jenkins-library/pkg/telemetry"
|
"github.com/SAP/jenkins-library/pkg/telemetry"
|
||||||
|
|
||||||
"github.com/SAP/jenkins-library/pkg/multiarch"
|
"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)
|
log.Entry().Infof("ldflags from template: '%v'", ldflags)
|
||||||
}
|
}
|
||||||
|
|
||||||
binaries := []string{}
|
var binaries []string
|
||||||
|
|
||||||
platforms, err := multiarch.ParsePlatformStrings(config.TargetArchitectures)
|
platforms, err := multiarch.ParsePlatformStrings(config.TargetArchitectures)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -191,14 +190,14 @@ func runGolangBuild(config *golangBuildOptions, telemetryData *telemetry.CustomD
|
|||||||
}
|
}
|
||||||
|
|
||||||
for _, platform := range platforms {
|
for _, platform := range platforms {
|
||||||
binary, err := runGolangBuildPerArchitecture(config, utils, ldflags, platform)
|
binaryNames, err := runGolangBuildPerArchitecture(config, utils, ldflags, platform)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(binary) > 0 {
|
if len(binaryNames) > 0 {
|
||||||
binaries = append(binaries, binary)
|
binaries = append(binaries, binaryNames...)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -425,8 +424,8 @@ func prepareLdflags(config *golangBuildOptions, utils golangBuildUtils, envRootP
|
|||||||
return generatedLdflags.String(), nil
|
return generatedLdflags.String(), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func runGolangBuildPerArchitecture(config *golangBuildOptions, utils golangBuildUtils, ldflags string, architecture multiarch.Platform) (string, error) {
|
func runGolangBuildPerArchitecture(config *golangBuildOptions, utils golangBuildUtils, ldflags string, architecture multiarch.Platform) ([]string, error) {
|
||||||
var binaryName string
|
var binaryNames []string
|
||||||
|
|
||||||
envVars := os.Environ()
|
envVars := os.Environ()
|
||||||
envVars = append(envVars, fmt.Sprintf("GOOS=%v", architecture.OS), fmt.Sprintf("GOARCH=%v", architecture.Arch))
|
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)
|
utils.SetEnv(envVars)
|
||||||
|
|
||||||
buildOptions := []string{"build", "-trimpath"}
|
buildOptions := []string{"build", "-trimpath"}
|
||||||
|
|
||||||
if len(config.Output) > 0 {
|
if len(config.Output) > 0 {
|
||||||
fileExtension := ""
|
if len(config.Packages) > 1 {
|
||||||
if architecture.OS == "windows" {
|
binaries, outputDir, err := getOutputBinaries(config.Output, config.Packages, utils, architecture)
|
||||||
fileExtension = ".exe"
|
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...)
|
buildOptions = append(buildOptions, config.BuildFlags...)
|
||||||
if len(ldflags) > 0 {
|
if len(ldflags) > 0 {
|
||||||
@ -454,9 +465,10 @@ func runGolangBuildPerArchitecture(config *golangBuildOptions, utils golangBuild
|
|||||||
if err := utils.RunExecutable("go", buildOptions...); err != nil {
|
if err := utils.RunExecutable("go", buildOptions...); err != nil {
|
||||||
log.Entry().Debugf("buildOptions: %v", buildOptions)
|
log.Entry().Debugf("buildOptions: %v", buildOptions)
|
||||||
log.SetErrorCategory(log.ErrorBuild)
|
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
|
// 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)
|
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
|
||||||
|
}
|
||||||
|
@ -72,7 +72,7 @@ func newGolangBuildTestsUtils() *golangBuildMockUtils {
|
|||||||
utils := golangBuildMockUtils{
|
utils := golangBuildMockUtils{
|
||||||
ExecMockRunner: &mock.ExecMockRunner{},
|
ExecMockRunner: &mock.ExecMockRunner{},
|
||||||
FilesMock: &mock.FilesMock{},
|
FilesMock: &mock.FilesMock{},
|
||||||
//clientOptions: []piperhttp.ClientOptions{},
|
// clientOptions: []piperhttp.ClientOptions{},
|
||||||
fileUploads: map[string]string{},
|
fileUploads: map[string]string{},
|
||||||
}
|
}
|
||||||
return &utils
|
return &utils
|
||||||
@ -632,14 +632,15 @@ func TestRunGolangBuildPerArchitecture(t *testing.T) {
|
|||||||
ldflags := "-X test=test"
|
ldflags := "-X test=test"
|
||||||
architecture, _ := multiarch.ParsePlatformString("linux,amd64")
|
architecture, _ := multiarch.ParsePlatformString("linux,amd64")
|
||||||
|
|
||||||
binaryName, err := runGolangBuildPerArchitecture(&config, utils, ldflags, architecture)
|
binaryNames, err := runGolangBuildPerArchitecture(&config, utils, ldflags, architecture)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Contains(t, utils.Calls[0].Params, "-o")
|
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, "testBin-linux.amd64")
|
||||||
assert.Contains(t, utils.Calls[0].Params, "./test/..")
|
assert.Contains(t, utils.Calls[0].Params, "./test/..")
|
||||||
assert.Contains(t, utils.Calls[0].Params, "-ldflags")
|
assert.Contains(t, utils.Calls[0].Params, "-ldflags")
|
||||||
assert.Contains(t, utils.Calls[0].Params, "-X test=test")
|
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) {
|
t.Run("success - windows", func(t *testing.T) {
|
||||||
@ -649,11 +650,80 @@ func TestRunGolangBuildPerArchitecture(t *testing.T) {
|
|||||||
ldflags := ""
|
ldflags := ""
|
||||||
architecture, _ := multiarch.ParsePlatformString("windows,amd64")
|
architecture, _ := multiarch.ParsePlatformString("windows,amd64")
|
||||||
|
|
||||||
binaryName, err := runGolangBuildPerArchitecture(&config, utils, ldflags, architecture)
|
binaryNames, err := runGolangBuildPerArchitecture(&config, utils, ldflags, architecture)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Contains(t, utils.Calls[0].Params, "-o")
|
assert.Contains(t, utils.Calls[0].Params, "-o")
|
||||||
assert.Contains(t, utils.Calls[0].Params, "testBin-windows.amd64.exe")
|
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) {
|
t.Run("execution error", func(t *testing.T) {
|
||||||
|
Reference in New Issue
Block a user