From b7f0155c3ba80c73224ff44435f4e9eafa1375c5 Mon Sep 17 00:00:00 2001 From: konez2k Date: Wed, 19 Feb 2020 13:44:30 +0100 Subject: [PATCH] add cross-platform build and packaging --- cmd/helpers.go | 36 ++++++++++++++----- cmd/package.go | 85 +++++++++++++++++++++++++++++++++----------- cmd/wails/4_build.go | 16 ++++++++- 3 files changed, 107 insertions(+), 30 deletions(-) diff --git a/cmd/helpers.go b/cmd/helpers.go index fa8b280f..e9f0abfb 100644 --- a/cmd/helpers.go +++ b/cmd/helpers.go @@ -84,10 +84,14 @@ func EmbedAssets() ([]string, error) { // BuildApplication will attempt to build the project based on the given inputs func BuildApplication(binaryName string, forceRebuild bool, buildMode string, packageApp bool, projectOptions *ProjectOptions) error { + if buildMode == BuildModeBridge && projectOptions.CrossCompile { + return fmt.Errorf("you cant serve the application in cross-compilation") + } + // Generate Windows assets if needed - if runtime.GOOS == "windows" { + if projectOptions.Platform == "windows" { cleanUp := !packageApp - err := NewPackageHelper().PackageWindows(projectOptions, cleanUp) + err := NewPackageHelper(projectOptions.Platform).PackageWindows(projectOptions, cleanUp) if err != nil { return err } @@ -130,16 +134,22 @@ func BuildApplication(binaryName string, forceRebuild bool, buildMode string, pa }() buildCommand := slicer.String() - buildCommand.Add("go") + if projectOptions.CrossCompile { + buildCommand.Add("xgo") + } else { + buildCommand.Add("mewn") + } if buildMode == BuildModeBridge { // Ignore errors buildCommand.Add("-i") } - buildCommand.Add("build") + if !projectOptions.CrossCompile { + buildCommand.Add("build") + } - if binaryName != "" { + if binaryName != "" && !projectOptions.CrossCompile { // Alter binary name based on OS switch runtime.GOOS { case "windows": @@ -155,7 +165,7 @@ func BuildApplication(binaryName string, forceRebuild bool, buildMode string, pa } // If we are forcing a rebuild - if forceRebuild { + if forceRebuild && !projectOptions.CrossCompile { buildCommand.Add("-a") } @@ -166,7 +176,7 @@ func BuildApplication(binaryName string, forceRebuild bool, buildMode string, pa } // Add windows flags - if runtime.GOOS == "windows" && buildMode == BuildModeProd { + if projectOptions.Platform == "windows" && buildMode == BuildModeProd { ldflags += "-H windowsgui " } @@ -183,6 +193,13 @@ func BuildApplication(binaryName string, forceRebuild bool, buildMode string, pa } buildCommand.AddSlice([]string{"-ldflags", ldflags}) + + if projectOptions.CrossCompile { + buildCommand.Add("-targets", projectOptions.Platform+"/"+projectOptions.Architecture) + buildCommand.Add("-out", "build/"+binaryName) + buildCommand.Add("./") + } + err = NewProgramHelper(projectOptions.Verbose).RunCommandArray(buildCommand.AsSlice()) if err != nil { if packSpinner != nil { @@ -209,20 +226,21 @@ func BuildApplication(binaryName string, forceRebuild bool, buildMode string, pa func PackageApplication(projectOptions *ProjectOptions) error { // Package app message := "Generating .app" - if runtime.GOOS == "windows" { + if projectOptions.Platform == "windows" { err := CheckWindres() if err != nil { return err } message = "Generating resource bundle" } + var packageSpinner *spinner.Spinner if projectOptions.Verbose { packageSpinner = spinner.New(message) packageSpinner.SetSpinSpeed(50) packageSpinner.Start() } - err := NewPackageHelper().Package(projectOptions) + err := NewPackageHelper(projectOptions.Platform).Package(projectOptions) if err != nil { if packageSpinner != nil { packageSpinner.Error() diff --git a/cmd/package.go b/cmd/package.go index c9b0ef83..2787b54e 100644 --- a/cmd/package.go +++ b/cmd/package.go @@ -18,17 +18,19 @@ import ( // PackageHelper helps with the 'wails package' command type PackageHelper struct { - fs *FSHelper - log *Logger - system *SystemHelper + platform string + fs *FSHelper + log *Logger + system *SystemHelper } // NewPackageHelper creates a new PackageHelper! -func NewPackageHelper() *PackageHelper { +func NewPackageHelper(platform string) *PackageHelper { return &PackageHelper{ - fs: NewFSHelper(), - log: NewLogger(), - system: NewSystemHelper(), + platform: platform, + fs: NewFSHelper(), + log: NewLogger(), + system: NewSystemHelper(), } } @@ -63,16 +65,23 @@ func defaultString(val string, defaultVal string) string { func (b *PackageHelper) getPackageFileBaseDir() string { // Calculate template base dir _, filename, _, _ := runtime.Caller(1) - return filepath.Join(path.Dir(filename), "packages", runtime.GOOS) + return filepath.Join(path.Dir(filename), "packages", b.platform) } // Package the application into a platform specific package func (b *PackageHelper) Package(po *ProjectOptions) error { - switch runtime.GOOS { + switch b.platform { case "darwin": // Check we have the exe if !b.fs.FileExists(po.BinaryName) { - return fmt.Errorf("cannot bundle non-existent binary file '%s'. Please build with 'wails build' first", po.BinaryName) + // Check cross-compiled application + if b.platform == runtime.GOOS { + return fmt.Errorf("cannot bundle non-existent binary file '%s'. Please build with 'wails build' first", po.BinaryName) + } + + if _, err := b.fs.FindFile(path.Join(b.fs.Cwd(), "build"), "darwin"); err != nil { + return fmt.Errorf("cannot bundle non-existent cross-compiled binary file '%s'. Please build with 'wails build -x darwin' first", po.BinaryName) + } } return b.packageOSX(po) case "windows": @@ -80,7 +89,7 @@ func (b *PackageHelper) Package(po *ProjectOptions) error { case "linux": return fmt.Errorf("linux is not supported at this time. Please see https://github.com/wailsapp/wails/issues/2") default: - return fmt.Errorf("platform '%s' not supported for bundling yet", runtime.GOOS) + return fmt.Errorf("platform '%s' not supported for bundling yet", b.platform) } } @@ -103,6 +112,22 @@ func (b *PackageHelper) packageOSX(po *ProjectOptions) error { // Check binary exists source := path.Join(b.fs.Cwd(), exe) + + if b.platform != runtime.GOOS { + + file, err := b.fs.FindFile(path.Join(b.fs.Cwd(), "build"), "darwin") + if err != nil { + return err + } + + // rename to exe + if err := os.Rename(path.Join(b.fs.Cwd(), "build", file), path.Join(b.fs.Cwd(), "build", exe)); err != nil { + return err + } + + source = path.Join(b.fs.Cwd(), "build", exe) + } + if !b.fs.FileExists(source) { // We need to build! return fmt.Errorf("Target '%s' not available. Has it been compiled yet?", exe) @@ -192,16 +217,36 @@ func (b *PackageHelper) PackageWindows(po *ProjectOptions, cleanUp bool) error { // Build syso sysofile := filepath.Join(b.fs.Cwd(), basename+"-res.syso") - batfile, err := fs.LocalDir(".") - if err != nil { - return err - } + // cross-compile + if b.platform != runtime.GOOS { + folder, err := os.Getwd() + if err != nil { + return err + } - windresBatFile := filepath.Join(batfile.fullPath, "windres.bat") - windresCommand := []string{windresBatFile, sysofile, tgtRCFile} - err = NewProgramHelper().RunCommandArray(windresCommand) - if err != nil { - return err + args := []string{ + "docker", "run", "--rm", + "-v", folder + ":/build", + "--entrypoint", "/bin/sh", + "techknowlogick/xgo", + "-c", "/usr/bin/x86_64-w64-mingw32-windres -o /build/" + basename + "-res.syso /build/" + basename + ".rc", + } + + if err := NewProgramHelper().RunCommandArray(args); err != nil { + return err + } + } else { + batfile, err := fs.LocalDir(".") + if err != nil { + return err + } + + windresBatFile := filepath.Join(batfile.fullPath, "windres.bat") + windresCommand := []string{windresBatFile, sysofile, tgtRCFile} + err = NewProgramHelper().RunCommandArray(windresCommand) + if err != nil { + return err + } } // clean up diff --git a/cmd/wails/4_build.go b/cmd/wails/4_build.go index 30dc9f67..8a362316 100644 --- a/cmd/wails/4_build.go +++ b/cmd/wails/4_build.go @@ -3,6 +3,8 @@ package main import ( "fmt" "os" + "runtime" + "strings" "github.com/leaanthony/spinner" "github.com/wailsapp/wails/cmd" @@ -133,7 +135,19 @@ func init() { } // Set cross-compile - projectOptions.Platform = platform + projectOptions.Platform = runtime.GOOS + if len(platform) > 0 { + projectOptions.CrossCompile = true + projectOptions.Platform = platform + projectOptions.Architecture = "amd64" + + // check build architecture + if strings.Contains(platform, "/") { + p := strings.Split(platform, "/") + projectOptions.Platform = p[0] + projectOptions.Architecture = p[1] + } + } err = cmd.BuildApplication(projectOptions.BinaryName, forceRebuild, buildMode, packageApp, projectOptions) if err != nil {