Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Beats] Build darwin/arm64 and universal binary #29585

Merged
merged 2 commits into from
Mar 23, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .ci/packaging.groovy
Original file line number Diff line number Diff line change
Expand Up @@ -211,6 +211,8 @@ def linuxPlatforms() {
'windows/amd64',
'windows/386',
'darwin/amd64'
// TODO(AndersonQ): comment in after the tests pass
// 'darwin/arm64'
Comment on lines +214 to +215
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is the plan here? Comment it in once CI is green and merge or do it in another PR?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I ended up testing it on the elastic-agent repo, didn't do what I needed. Besides in order to unblock the release team, better to merge it now and adjust the CI later. But good catch anyway :)

].join(' ')
}

Expand Down
8 changes: 8 additions & 0 deletions auditbeat/magefile.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ import (

auditbeat "github.com/elastic/beats/v7/auditbeat/scripts/mage"
devtools "github.com/elastic/beats/v7/dev-tools/mage"
"github.com/elastic/beats/v7/dev-tools/mage/target/build"

// mage:import
"github.com/elastic/beats/v7/dev-tools/mage/target/common"
Expand Down Expand Up @@ -74,6 +75,13 @@ func CrossBuildGoDaemon() error {
return devtools.CrossBuildGoDaemon()
}

// AssembleDarwinUniversal merges the darwin/amd64 and darwin/arm64 into a single
// universal binary using `lipo`. It assumes the darwin/amd64 and darwin/arm64
// were built and only performs the merge.
Comment on lines +78 to +80
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not use mage dependencies to ensure the binaries are built instead of failing?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Mage dependencies will ensure a function was called, here I need more than just that. I need the build to have been invoked with the right environment variables, it has happened 2 times (once for each architecture). Thus it isn't really a solution here.

func AssembleDarwinUniversal() error {
return build.AssembleDarwinUniversal()
}

// Package packages the Beat for distribution.
// Use SNAPSHOT=true to build snapshots.
// Use PLATFORMS to control the target platforms.
Expand Down
21 changes: 20 additions & 1 deletion dev-tools/mage/common.go
Original file line number Diff line number Diff line change
Expand Up @@ -235,6 +235,25 @@ func HaveKubectl() error {
return nil
}

// IsDarwinUniversal indicates whether ot not the darwin/universal should be
// assembled. If both platforms darwin/adm64 and darwin/arm64 are listed, then
// IsDarwinUniversal returns true.
// Note: Platforms might be edited at different moments, therefore it's necessary
// to perform this check on the fly.
func IsDarwinUniversal() bool {
var darwinAMD64, darwinARM64 bool
for _, p := range Platforms {
if p.Name == "darwin/arm64" {
darwinARM64 = true
}
if p.Name == "darwin/amd64" {
darwinAMD64 = true
}
}

return darwinAMD64 && darwinARM64
}

// FindReplace reads a file, performs a find/replace operation, then writes the
// output to the same file path.
func FindReplace(file string, re *regexp.Regexp, repl string) error {
Expand Down Expand Up @@ -586,7 +605,7 @@ func ParallelCtx(ctx context.Context, fns ...interface{}) {
// Parallel runs the given functions in parallel with an upper limit set based
// on GOMAXPROCS.
func Parallel(fns ...interface{}) {
ParallelCtx(context.Background(), fns...)
ParallelCtx(context.TODO(), fns...)
}

// funcTypeWrap wraps a valid FuncType to FuncContextError
Expand Down
31 changes: 30 additions & 1 deletion dev-tools/mage/crossbuild.go
Original file line number Diff line number Diff line change
Expand Up @@ -172,7 +172,7 @@ func CrossBuild(options ...CrossBuildOption) error {
mg.Deps(func() error { return gotool.Mod.Download() })
}

// Build the magefile for Linux so we can run it inside the container.
// Build the magefile for Linux, so we can run it inside the container.
mg.Deps(buildMage)

log.Println("crossBuild: Platform list =", params.Platforms)
Expand All @@ -194,6 +194,33 @@ func CrossBuild(options ...CrossBuildOption) error {

// Each build runs in parallel.
Parallel(deps...)

// It needs to run after all the builds, as it needs the darwin binaries.
if err := assembleDarwinUniversal(params); err != nil {
return err
}

return nil
}

// assembleDarwinUniversal checks if darwin/amd64 and darwin/arm64 were build,
// if so, it generates a darwin/universal binary that is the merge fo them two.
func assembleDarwinUniversal(params crossBuildParams) error {
if IsDarwinUniversal() {
builder := GolangCrossBuilder{
// the docker image for darwin/arm64 is the one capable of merging the binaries.
Platform: "darwin/arm64",
Target: "assembleDarwinUniversal",
InDir: params.InDir,
ImageSelector: params.ImageSelector}
if err := builder.Build(); err != nil {
return errors.Wrapf(err,
"failed merging darwin/amd64 and darwin/arm64 into darwin/universal target=%v for platform=%v",
builder.Target,
builder.Platform)
}
}

return nil
}

Expand Down Expand Up @@ -223,6 +250,8 @@ func CrossBuildImage(platform string) (string, error) {
tagSuffix = "darwin-debian10"
case platform == "darwin/arm64":
tagSuffix = "darwin-arm64-debian10"
case platform == "darwin/universal":
tagSuffix = "darwin-arm64-debian10"
case platform == "linux/arm64":
tagSuffix = "arm"
// when it runs on a ARM64 host/worker.
Expand Down
18 changes: 17 additions & 1 deletion dev-tools/mage/pkg.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,8 +43,10 @@ func Package() error {
"UseCommunityBeatPackaging, UseElasticBeatPackaging or USeElasticBeatWithoutXPackPackaging first.")
}

platforms := updateWithDarwinUniversal(Platforms)

var tasks []interface{}
for _, target := range Platforms {
for _, target := range platforms {
for _, pkg := range Packages {
if pkg.OS != target.GOOS() || pkg.Arch != "" && pkg.Arch != target.Arch() {
continue
Expand Down Expand Up @@ -112,6 +114,20 @@ func Package() error {
return nil
}

// updateWithDarwinUniversal checks if darwin/amd64 and darwin/arm64, are listed
// if so, the universal binary was built, then we need to package it as well.
func updateWithDarwinUniversal(platforms BuildPlatformList) BuildPlatformList {
if IsDarwinUniversal() {
platforms = append(platforms,
BuildPlatform{
Name: "darwin/universal",
Flags: CGOSupported | CrossBuildSupported | Default,
})
}

return platforms
}

// isPackageTypeSelected returns true if SelectedPackageTypes is empty or if
// pkgType is present on SelectedPackageTypes. It returns false otherwise.
func isPackageTypeSelected(pkgType PackageType) bool {
Expand Down
43 changes: 37 additions & 6 deletions dev-tools/mage/pkgtypes.go
Original file line number Diff line number Diff line change
Expand Up @@ -121,8 +121,10 @@ var OSArchNames = map[string]map[PackageType]map[string]string{
},
"darwin": map[PackageType]map[string]string{
TarGz: map[string]string{
"386": "x86",
"amd64": "x86_64",
"386": "x86",
"amd64": "x86_64",
"arm64": "aarch64",
"universal": "universal",
},
},
"linux": map[PackageType]map[string]string{
Expand Down Expand Up @@ -423,12 +425,12 @@ func (s PackageSpec) Evaluate(args ...map[string]interface{}) PackageSpec {
}

f.Source = filepath.Join(s.packageDir, filepath.Base(f.Target))
if err = ioutil.WriteFile(createDir(f.Source), []byte(content), 0644); err != nil {
if err = ioutil.WriteFile(CreateDir(f.Source), []byte(content), 0644); err != nil {
panic(errors.Wrapf(err, "failed to write file containing content for target=%v", target))
}
case f.Template != "":
f.Source = filepath.Join(s.packageDir, filepath.Base(f.Template))
if err := s.ExpandFile(f.Template, createDir(f.Source)); err != nil {
if err := s.ExpandFile(f.Template, CreateDir(f.Source)); err != nil {
panic(errors.Wrapf(err, "failed to expand template file for target=%v", target))
}
default:
Expand Down Expand Up @@ -566,7 +568,7 @@ func PackageZip(spec PackageSpec) error {
spec.OutputFile = Zip.AddFileExtension(spec.OutputFile)

// Write the zip file.
if err := ioutil.WriteFile(createDir(spec.OutputFile), buf.Bytes(), 0644); err != nil {
if err := ioutil.WriteFile(CreateDir(spec.OutputFile), buf.Bytes(), 0644); err != nil {
return errors.Wrap(err, "failed to write zip file")
}

Expand All @@ -588,6 +590,27 @@ func PackageTarGz(spec PackageSpec) error {
w := tar.NewWriter(buf)
baseDir := spec.rootDir()

// Replace the darwin-universal by darwin-x86_64 and darwin-arm64. Also
// keep the other files.
if spec.Name == "elastic-agent" && spec.OS == "darwin" && spec.Arch == "universal" {
newFiles := map[string]PackageFile{}
for filename, pkgFile := range spec.Files {
if strings.Contains(pkgFile.Target, "darwin-universal") &&
strings.Contains(pkgFile.Target, "downloads") {

amdFilename, amdpkgFile := replaceFileArch(filename, pkgFile, "x86_64")
armFilename, armpkgFile := replaceFileArch(filename, pkgFile, "aarch64")

newFiles[amdFilename] = amdpkgFile
newFiles[armFilename] = armpkgFile
} else {
newFiles[filename] = pkgFile
}
}

spec.Files = newFiles
}

// Add files to tar.
for _, pkgFile := range spec.Files {
if pkgFile.Symlink {
Expand Down Expand Up @@ -632,7 +655,7 @@ func PackageTarGz(spec PackageSpec) error {

// Open the output file.
log.Println("Creating output file at", spec.OutputFile)
outFile, err := os.Create(createDir(spec.OutputFile))
outFile, err := os.Create(CreateDir(spec.OutputFile))
if err != nil {
return err
}
Expand All @@ -658,6 +681,14 @@ func PackageTarGz(spec PackageSpec) error {
return errors.Wrap(CreateSHA512File(spec.OutputFile), "failed to create .sha512 file")
}

func replaceFileArch(filename string, pkgFile PackageFile, arch string) (string, PackageFile) {
filename = strings.ReplaceAll(filename, "universal", arch)
pkgFile.Source = strings.ReplaceAll(pkgFile.Source, "universal", arch)
pkgFile.Target = strings.ReplaceAll(pkgFile.Target, "universal", arch)

return filename, pkgFile
}

// PackageDeb packages a deb file. This requires Docker to execute FPM.
func PackageDeb(spec PackageSpec) error {
return runFPM(spec, Deb)
Expand Down
6 changes: 3 additions & 3 deletions dev-tools/mage/platforms.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ var BuildPlatforms = BuildPlatformList{
{"darwin/386", CGOSupported | CrossBuildSupported},
{"darwin/amd64", CGOSupported | CrossBuildSupported | Default},
{"darwin/arm", CGOSupported},
{"darwin/arm64", CGOSupported},
{"darwin/arm64", CGOSupported | CrossBuildSupported | Default},
{"dragonfly/amd64", CGOSupported},
{"freebsd/386", CGOSupported},
{"freebsd/amd64", CGOSupported},
Expand Down Expand Up @@ -326,13 +326,13 @@ func newPlatformExpression(expr string) (*platformExpression, error) {

// NewPlatformList returns a new BuildPlatformList based on given expression.
//
// By default the initial set include only the platforms designated as defaults.
// By default, the initial set include only the platforms designated as defaults.
// To add additional platforms to list use an addition term that is designated
// with a plug sign (e.g. "+netbsd" or "+linux/armv7"). Or you may use "+all"
// to change the initial set to include all possible platforms then filter
// from there (e.g. "+all linux windows").
//
// The expression can consists of selections (e.g. "linux") and/or
// The expression can consist of selections (e.g. "linux") and/or
// removals (e.g."!windows"). Each term can be valid GOOS or a valid GOOS/Arch
// pair.
//
Expand Down
4 changes: 2 additions & 2 deletions dev-tools/mage/settings.go
Original file line number Diff line number Diff line change
Expand Up @@ -63,8 +63,8 @@ var (
PLATFORMS = EnvOr("PLATFORMS", "")
PACKAGES = EnvOr("PACKAGES", "")

// CrossBuildMountModcache, if true, mounts $GOPATH/pkg/mod into
// the crossbuild images at /go/pkg/mod, read-only.
// CrossBuildMountModcache mounts $GOPATH/pkg/mod into
// the crossbuild images at /go/pkg/mod, read-only, when set to true.
CrossBuildMountModcache = true

BeatName = EnvOr("BEAT_NAME", filepath.Base(CWD()))
Expand Down
30 changes: 30 additions & 0 deletions dev-tools/mage/target/build/build.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,11 @@
package build

import (
"fmt"
"os/exec"

"github.com/magefile/mage/sh"

devtools "github.com/elastic/beats/v7/dev-tools/mage"
)

Expand Down Expand Up @@ -46,3 +51,28 @@ func CrossBuild() error {
func CrossBuildGoDaemon() error {
return devtools.CrossBuildGoDaemon()
}

// AssembleDarwinUniversal merges the darwin/amd64 and darwin/arm64 into a single
// universal binary using `lipo`. It's automatically invoked by CrossBuild whenever
// the darwin/amd64 and darwin/arm64 are present.
func AssembleDarwinUniversal() error {
cmd := "lipo"

if _, err := exec.LookPath(cmd); err != nil {
return fmt.Errorf("'%s' is required to assemble the universal binary: %w",
cmd, err)
}

var lipoArgs []string
args := []string{
"build/golang-crossbuild/%s-darwin-universal",
"build/golang-crossbuild/%s-darwin-arm64",
"build/golang-crossbuild/%s-darwin-amd64"}

for _, arg := range args {
lipoArgs = append(lipoArgs, fmt.Sprintf(arg, devtools.BeatName))
}

lipo := sh.RunCmd(cmd, "-create", "-output")
return lipo(lipoArgs...)
}
8 changes: 8 additions & 0 deletions filebeat/magefile.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ import (
"github.com/magefile/mage/mg"

devtools "github.com/elastic/beats/v7/dev-tools/mage"
"github.com/elastic/beats/v7/dev-tools/mage/target/build"
filebeat "github.com/elastic/beats/v7/filebeat/scripts/mage"

// mage:import
Expand Down Expand Up @@ -73,6 +74,13 @@ func CrossBuildGoDaemon() error {
return devtools.CrossBuildGoDaemon()
}

// AssembleDarwinUniversal merges the darwin/amd64 and darwin/arm64 into a single
// universal binary using `lipo`. It assumes the darwin/amd64 and darwin/arm64
// were built and only performs the merge.
func AssembleDarwinUniversal() error {
return build.AssembleDarwinUniversal()
}

// Package packages the Beat for distribution.
// Use SNAPSHOT=true to build snapshots.
// Use PLATFORMS to control the target platforms.
Expand Down
8 changes: 8 additions & 0 deletions libbeat/magefile.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ package main

import (
devtools "github.com/elastic/beats/v7/dev-tools/mage"
"github.com/elastic/beats/v7/dev-tools/mage/target/build"

// mage:import
_ "github.com/elastic/beats/v7/dev-tools/mage/target/common"
Expand Down Expand Up @@ -53,3 +54,10 @@ func Fields() error {
func Config() error {
return devtools.Config(devtools.ShortConfigType|devtools.ReferenceConfigType, devtools.DefaultConfigFileParams(), ".")
}

// AssembleDarwinUniversal merges the darwin/amd64 and darwin/arm64 into a single
// universal binary using `lipo`. It assumes the darwin/amd64 and darwin/arm64
// were built and only performs the merge.
func AssembleDarwinUniversal() error {
return build.AssembleDarwinUniversal()
}
8 changes: 8 additions & 0 deletions packetbeat/magefile.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import (
"github.com/magefile/mage/mg"

devtools "github.com/elastic/beats/v7/dev-tools/mage"
"github.com/elastic/beats/v7/dev-tools/mage/target/build"
packetbeat "github.com/elastic/beats/v7/packetbeat/scripts/mage"

// mage:import
Expand Down Expand Up @@ -72,6 +73,13 @@ func CrossBuildGoDaemon() error {
return devtools.CrossBuildGoDaemon()
}

// AssembleDarwinUniversal merges the darwin/amd64 and darwin/arm64 into a single
// universal binary using `lipo`. It assumes the darwin/amd64 and darwin/arm64
// were built and only performs the merge.
func AssembleDarwinUniversal() error {
return build.AssembleDarwinUniversal()
}

// Package packages the Beat for distribution.
// Use SNAPSHOT=true to build snapshots.
// Use PLATFORMS to control the target platforms.
Expand Down
Loading