mirror of
https://github.com/taigrr/wails.git
synced 2026-04-13 18:38:11 -07:00
Support building arm64 & universal binaries.
This commit is contained in:
@@ -3,8 +3,10 @@ package build
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"runtime"
|
||||
"strings"
|
||||
"text/tabwriter"
|
||||
"time"
|
||||
|
||||
"github.com/leaanthony/clir"
|
||||
@@ -23,8 +25,8 @@ func AddBuildSubcommand(app *clir.Cli, w io.Writer) {
|
||||
command := app.NewSubCommand("build", "Builds the application")
|
||||
|
||||
// Setup target type flag
|
||||
description := "Type of application to build. Valid types: " + validTargetTypes.Join(",")
|
||||
command.StringFlag("t", description, &outputType)
|
||||
//description := "Type of application to build. Valid types: " + validTargetTypes.Join(",")
|
||||
//command.StringFlag("t", description, &outputType)
|
||||
|
||||
// Setup production flag
|
||||
production := false
|
||||
@@ -41,22 +43,26 @@ func AddBuildSubcommand(app *clir.Cli, w io.Writer) {
|
||||
platform := runtime.GOOS
|
||||
command.StringFlag("platform", "Platform to target", &platform)
|
||||
|
||||
// Quiet Build
|
||||
quiet := false
|
||||
command.BoolFlag("q", "Suppress output to console", &quiet)
|
||||
// Verbosity
|
||||
verbosity := 1
|
||||
command.IntFlag("v", "Verbosity level (0 - silent, 1 - default, 2 - verbose)", &verbosity)
|
||||
|
||||
// ldflags to pass to `go`
|
||||
ldflags := ""
|
||||
command.StringFlag("ldflags", "optional ldflags", &ldflags)
|
||||
|
||||
// Log to file
|
||||
logFile := ""
|
||||
command.StringFlag("l", "Log to file", &logFile)
|
||||
//logFile := ""
|
||||
//command.StringFlag("l", "Log to file", &logFile)
|
||||
|
||||
// Retain assets
|
||||
keepAssets := false
|
||||
command.BoolFlag("k", "Keep generated assets", &keepAssets)
|
||||
|
||||
// Retain assets
|
||||
outputFilename := ""
|
||||
command.StringFlag("o", "Output filename", &outputFilename)
|
||||
|
||||
appleIdentity := ""
|
||||
if runtime.GOOS == "darwin" {
|
||||
command.StringFlag("sign", "Signs your app with the given identity.", &appleIdentity)
|
||||
@@ -64,6 +70,8 @@ func AddBuildSubcommand(app *clir.Cli, w io.Writer) {
|
||||
|
||||
command.Action(func() error {
|
||||
|
||||
quiet := verbosity == 0
|
||||
|
||||
// Create logger
|
||||
logger := clilogger.New(w)
|
||||
logger.Mute(quiet)
|
||||
@@ -82,29 +90,72 @@ func AddBuildSubcommand(app *clir.Cli, w io.Writer) {
|
||||
return fmt.Errorf("must use `-package` flag when using `-sign`")
|
||||
}
|
||||
|
||||
task := fmt.Sprintf("Building %s Application", strings.Title(outputType))
|
||||
logger.Println(task)
|
||||
logger.Println(strings.Repeat("-", len(task)))
|
||||
|
||||
// Setup mode
|
||||
mode := build.Debug
|
||||
if production {
|
||||
mode = build.Production
|
||||
}
|
||||
|
||||
// Check platform
|
||||
validPlatformArch := slicer.String([]string{
|
||||
"darwin",
|
||||
"darwin/amd64",
|
||||
"darwin/arm64",
|
||||
"darwin/universal",
|
||||
//"linux/amd64",
|
||||
//"linux/arm-7",
|
||||
//"windows/amd64",
|
||||
})
|
||||
if !validPlatformArch.Contains(platform) {
|
||||
return fmt.Errorf("platform %s is not supported", platform)
|
||||
}
|
||||
|
||||
// Create BuildOptions
|
||||
buildOptions := &build.Options{
|
||||
Logger: logger,
|
||||
OutputType: outputType,
|
||||
OutputFile: outputFilename,
|
||||
Mode: mode,
|
||||
Pack: pack,
|
||||
Platform: platform,
|
||||
LDFlags: ldflags,
|
||||
Compiler: compilerCommand,
|
||||
KeepAssets: keepAssets,
|
||||
AppleIdentity: appleIdentity,
|
||||
Verbosity: verbosity,
|
||||
}
|
||||
|
||||
// Calculate platform and arch
|
||||
platformSplit := strings.Split(platform, "/")
|
||||
buildOptions.Platform = platformSplit[0]
|
||||
buildOptions.Arch = runtime.GOARCH
|
||||
if len(platformSplit) == 2 {
|
||||
buildOptions.Arch = platformSplit[1]
|
||||
}
|
||||
|
||||
// Start a new tabwriter
|
||||
w := new(tabwriter.Writer)
|
||||
w.Init(os.Stdout, 8, 8, 0, '\t', 0)
|
||||
|
||||
buildModeText := "debug"
|
||||
if production {
|
||||
buildModeText = "production"
|
||||
}
|
||||
|
||||
// Write out the system information
|
||||
fmt.Fprintf(w, "App Type: \t%s\n", buildOptions.OutputType)
|
||||
fmt.Fprintf(w, "Platform: \t%s\n", buildOptions.Platform)
|
||||
fmt.Fprintf(w, "Arch: \t%s\n", buildOptions.Arch)
|
||||
fmt.Fprintf(w, "Compiler: \t%s\n", buildOptions.Compiler)
|
||||
fmt.Fprintf(w, "Build Mode: \t%s\n", buildModeText)
|
||||
fmt.Fprintf(w, "Package: \t%t\n", buildOptions.Pack)
|
||||
fmt.Fprintf(w, "KeepAssets: \t%t\n", buildOptions.KeepAssets)
|
||||
fmt.Fprintf(w, "LDFlags: \t\"%s\"\n", buildOptions.LDFlags)
|
||||
if len(buildOptions.OutputFile) > 0 {
|
||||
fmt.Fprintf(w, "Output File: \t%s\n", buildOptions.OutputFile)
|
||||
}
|
||||
fmt.Fprintf(w, "\n")
|
||||
w.Flush()
|
||||
|
||||
return doBuild(buildOptions)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -19,6 +19,10 @@ import (
|
||||
"github.com/wailsapp/wails/v2/pkg/clilogger"
|
||||
)
|
||||
|
||||
const (
|
||||
VERBOSE int = 2
|
||||
)
|
||||
|
||||
// BaseBuilder is the common builder struct
|
||||
type BaseBuilder struct {
|
||||
filesToDelete slicer.StringSlicer
|
||||
@@ -142,11 +146,23 @@ func (b *BaseBuilder) CleanUp() {
|
||||
})
|
||||
}
|
||||
|
||||
func (b *BaseBuilder) OutputFilename(options *Options) string {
|
||||
outputFile := options.OutputFile
|
||||
if outputFile == "" {
|
||||
outputFile = b.projectData.OutputFilename
|
||||
}
|
||||
return outputFile
|
||||
}
|
||||
|
||||
// CompileProject compiles the project
|
||||
func (b *BaseBuilder) CompileProject(options *Options) error {
|
||||
|
||||
// Run go mod tidy first
|
||||
cmd := exec.Command(options.Compiler, "mod", "tidy")
|
||||
if options.Verbosity == VERBOSE {
|
||||
cmd.Stdout = os.Stdout
|
||||
cmd.Stderr = os.Stderr
|
||||
}
|
||||
err := cmd.Run()
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -167,6 +183,7 @@ func (b *BaseBuilder) CompileProject(options *Options) error {
|
||||
// potentially try and see if the assets have changed but will
|
||||
// this take as much time as a `-a` build?
|
||||
commands.Add("-a")
|
||||
commands.Add("-x")
|
||||
|
||||
var tags slicer.StringSlicer
|
||||
tags.Add(options.OutputType)
|
||||
@@ -195,10 +212,10 @@ func (b *BaseBuilder) CompileProject(options *Options) error {
|
||||
|
||||
// Get application build directory
|
||||
appDir := options.BuildDirectory
|
||||
err = cleanBuildDirectory(options)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
//err = cleanBuildDirectory(options)
|
||||
//if err != nil {
|
||||
// return err
|
||||
//}
|
||||
|
||||
if options.LDFlags != "" {
|
||||
commands.Add("-ldflags")
|
||||
@@ -206,10 +223,7 @@ func (b *BaseBuilder) CompileProject(options *Options) error {
|
||||
}
|
||||
|
||||
// Set up output filename
|
||||
outputFile := options.OutputFile
|
||||
if outputFile == "" {
|
||||
outputFile = b.projectData.OutputFilename
|
||||
}
|
||||
outputFile := b.OutputFilename(options)
|
||||
compiledBinary := filepath.Join(appDir, outputFile)
|
||||
commands.Add("-o")
|
||||
commands.Add(compiledBinary)
|
||||
@@ -219,7 +233,10 @@ func (b *BaseBuilder) CompileProject(options *Options) error {
|
||||
|
||||
// Create the command
|
||||
cmd = exec.Command(options.Compiler, commands.AsSlice()...)
|
||||
|
||||
if options.Verbosity == VERBOSE {
|
||||
cmd.Stdout = os.Stdout
|
||||
cmd.Stderr = os.Stderr
|
||||
}
|
||||
// Set the directory
|
||||
cmd.Dir = b.projectData.Path
|
||||
|
||||
@@ -241,29 +258,36 @@ func (b *BaseBuilder) CompileProject(options *Options) error {
|
||||
return v
|
||||
})
|
||||
|
||||
// Setup buffers
|
||||
var stdo, stde bytes.Buffer
|
||||
cmd.Stdout = &stdo
|
||||
cmd.Stderr = &stde
|
||||
cmd.Env = upsertEnv(cmd.Env, "GOOS", func(v string) string {
|
||||
return options.Platform
|
||||
})
|
||||
|
||||
cmd.Env = upsertEnv(cmd.Env, "GOARCH", func(v string) string {
|
||||
return options.Arch
|
||||
})
|
||||
|
||||
cmd.Env = upsertEnv(cmd.Env, "CGO_ENABLED", func(v string) string {
|
||||
return "1"
|
||||
})
|
||||
|
||||
// Run command
|
||||
err = cmd.Run()
|
||||
|
||||
// Format error if we have one
|
||||
if err != nil {
|
||||
return fmt.Errorf("%s\n%s", err, string(stde.Bytes()))
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// NpmInstall runs "npm install" in the given directory
|
||||
func (b *BaseBuilder) NpmInstall(sourceDir string) error {
|
||||
return b.NpmInstallUsingCommand(sourceDir, "npm install")
|
||||
func (b *BaseBuilder) NpmInstall(sourceDir string, verbose bool) error {
|
||||
return b.NpmInstallUsingCommand(sourceDir, "npm install", verbose)
|
||||
}
|
||||
|
||||
// NpmInstallUsingCommand runs the given install command in the specified npm project directory
|
||||
func (b *BaseBuilder) NpmInstallUsingCommand(sourceDir string, installCommand string) error {
|
||||
func (b *BaseBuilder) NpmInstallUsingCommand(sourceDir string, installCommand string, verbose bool) error {
|
||||
|
||||
packageJSON := filepath.Join(sourceDir, "package.json")
|
||||
|
||||
@@ -305,7 +329,7 @@ func (b *BaseBuilder) NpmInstallUsingCommand(sourceDir string, installCommand st
|
||||
// Split up the InstallCommand and execute it
|
||||
cmd := strings.Split(installCommand, " ")
|
||||
stdout, stderr, err := shell.RunCommand(sourceDir, cmd[0], cmd[1:]...)
|
||||
if err != nil {
|
||||
if verbose || err != nil {
|
||||
for _, l := range strings.Split(stdout, "\n") {
|
||||
fmt.Printf(" %s\n", l)
|
||||
}
|
||||
@@ -354,31 +378,40 @@ func (b *BaseBuilder) NpmRunWithEnvironment(projectDir, buildTarget string, verb
|
||||
func (b *BaseBuilder) BuildFrontend(outputLogger *clilogger.CLILogger) error {
|
||||
|
||||
// TODO: Fix this up from the CLI
|
||||
verbose := false
|
||||
verbose := b.options.Verbosity == VERBOSE
|
||||
|
||||
frontendDir := filepath.Join(b.projectData.Path, "frontend")
|
||||
|
||||
// Check there is an 'InstallCommand' provided in wails.json
|
||||
if b.projectData.InstallCommand == "" {
|
||||
// No - don't install
|
||||
outputLogger.Println(" - No Install command. Skipping.")
|
||||
outputLogger.Println("No Install command. Skipping.")
|
||||
} else {
|
||||
// Do install if needed
|
||||
outputLogger.Println(" - Installing dependencies...")
|
||||
if err := b.NpmInstallUsingCommand(frontendDir, b.projectData.InstallCommand); err != nil {
|
||||
outputLogger.Print("Installing frontend dependencies: ")
|
||||
if verbose {
|
||||
outputLogger.Println("")
|
||||
outputLogger.Println("\tCommand: " + b.projectData.InstallCommand)
|
||||
}
|
||||
if err := b.NpmInstallUsingCommand(frontendDir, b.projectData.InstallCommand, verbose); err != nil {
|
||||
return err
|
||||
}
|
||||
outputLogger.Println("Done.")
|
||||
}
|
||||
|
||||
// Check if there is a build command
|
||||
if b.projectData.BuildCommand == "" {
|
||||
outputLogger.Println(" - No Build command. Skipping.")
|
||||
outputLogger.Println("No Build command. Skipping.")
|
||||
// No - ignore
|
||||
return nil
|
||||
}
|
||||
|
||||
outputLogger.Println(" - Compiling Frontend Project")
|
||||
outputLogger.Print("Compiling frontend: ")
|
||||
cmd := strings.Split(b.projectData.BuildCommand, " ")
|
||||
if verbose {
|
||||
outputLogger.Println("")
|
||||
outputLogger.Println("\tCommand: '" + strings.Join(cmd, " ") + "'")
|
||||
}
|
||||
stdout, stderr, err := shell.RunCommand(frontendDir, cmd[0], cmd[1:]...)
|
||||
if verbose || err != nil {
|
||||
for _, l := range strings.Split(stdout, "\n") {
|
||||
@@ -388,7 +421,12 @@ func (b *BaseBuilder) BuildFrontend(outputLogger *clilogger.CLILogger) error {
|
||||
fmt.Printf(" %s\n", l)
|
||||
}
|
||||
}
|
||||
return err
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
outputLogger.Println("Done.")
|
||||
return nil
|
||||
}
|
||||
|
||||
// ExtractAssets gets the assets from the index.html file
|
||||
|
||||
@@ -6,7 +6,10 @@ import (
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
|
||||
"github.com/leaanthony/slicer"
|
||||
"github.com/wailsapp/wails/v2/internal/fs"
|
||||
|
||||
"github.com/wailsapp/wails/v2/internal/shell"
|
||||
|
||||
"github.com/wailsapp/wails/v2/internal/project"
|
||||
"github.com/wailsapp/wails/v2/pkg/clilogger"
|
||||
)
|
||||
@@ -32,12 +35,14 @@ type Options struct {
|
||||
ProjectData *project.Project // The project data
|
||||
Pack bool // Create a package for the app after building
|
||||
Platform string // The platform to build for
|
||||
Arch string // The architecture to build for
|
||||
Compiler string // The compiler command to use
|
||||
IgnoreFrontend bool // Indicates if the frontend does not need building
|
||||
OutputFile string // Override the output filename
|
||||
BuildDirectory string // Directory to use for building the application
|
||||
CompiledBinary string // Fully qualified path to the compiled binary
|
||||
KeepAssets bool // /Keep the generated assets/files
|
||||
Verbosity int // Verbosity level (0 - silent, 1 - default, 2 - verbose)
|
||||
AppleIdentity string
|
||||
}
|
||||
|
||||
@@ -58,12 +63,6 @@ func Build(options *Options) (string, error) {
|
||||
return "", err
|
||||
}
|
||||
|
||||
// Check platform
|
||||
validPlatforms := slicer.String([]string{"linux", "darwin", "windows"})
|
||||
if !validPlatforms.Contains(options.Platform) {
|
||||
return "", fmt.Errorf("platform %s is not supported", options.Platform)
|
||||
}
|
||||
|
||||
// Load project
|
||||
projectData, err := project.Load(cwd)
|
||||
if err != nil {
|
||||
@@ -71,7 +70,7 @@ func Build(options *Options) (string, error) {
|
||||
}
|
||||
options.ProjectData = projectData
|
||||
|
||||
// Calculate build dir
|
||||
// Set build directory
|
||||
options.BuildDirectory = filepath.Join(options.ProjectData.Path, "build", options.Platform, options.OutputType)
|
||||
|
||||
// Save the project type
|
||||
@@ -107,7 +106,6 @@ func Build(options *Options) (string, error) {
|
||||
// return "", err
|
||||
// }
|
||||
if !options.IgnoreFrontend {
|
||||
outputLogger.Println(" - Building Project Frontend")
|
||||
err = builder.BuildFrontend(outputLogger)
|
||||
if err != nil {
|
||||
return "", err
|
||||
@@ -115,30 +113,61 @@ func Build(options *Options) (string, error) {
|
||||
}
|
||||
|
||||
// Build the base assets
|
||||
outputLogger.Println(" - Compiling Assets")
|
||||
err = builder.BuildAssets(options)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
// Compile the application
|
||||
outputLogger.Print(" - Compiling Application in " + GetModeAsString(options.Mode) + " mode...")
|
||||
err = builder.CompileProject(options)
|
||||
if err != nil {
|
||||
return "", err
|
||||
outputLogger.Print("Compiling application: ")
|
||||
|
||||
if options.Platform == "darwin" && options.Arch == "universal" {
|
||||
outputFile := builder.OutputFilename(options)
|
||||
amd64Filename := outputFile + "-amd64"
|
||||
arm64Filename := outputFile + "-arm64"
|
||||
|
||||
// Build amd64 first
|
||||
options.Arch = "amd64"
|
||||
options.OutputFile = amd64Filename
|
||||
err = builder.CompileProject(options)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
// Build arm64
|
||||
options.Arch = "arm64"
|
||||
options.OutputFile = arm64Filename
|
||||
err = builder.CompileProject(options)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
// Run lipo
|
||||
_, stderr, err := shell.RunCommand(options.BuildDirectory, "lipo", "-create", "-output", outputFile, amd64Filename, arm64Filename)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("%s - %s", err.Error(), stderr)
|
||||
}
|
||||
// Remove temp binaries
|
||||
fs.DeleteFile(filepath.Join(options.BuildDirectory, amd64Filename))
|
||||
fs.DeleteFile(filepath.Join(options.BuildDirectory, arm64Filename))
|
||||
projectData.OutputFilename = outputFile
|
||||
} else {
|
||||
err = builder.CompileProject(options)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
}
|
||||
outputLogger.Println("done.")
|
||||
outputLogger.Println("Done.")
|
||||
|
||||
// Do we need to pack the app?
|
||||
if options.Pack {
|
||||
|
||||
outputLogger.Println(" - Packaging Application")
|
||||
outputLogger.Print("Packaging application: ")
|
||||
|
||||
// TODO: Allow cross platform build
|
||||
err = packageProject(options, runtime.GOOS)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
outputLogger.Println("Done.")
|
||||
}
|
||||
|
||||
return projectData.OutputFilename, nil
|
||||
|
||||
@@ -12,5 +12,6 @@ type Builder interface {
|
||||
BuildFrontend(*clilogger.CLILogger) error
|
||||
BuildRuntime(*Options) error
|
||||
CompileProject(*Options) error
|
||||
OutputFilename(*Options) string
|
||||
CleanUp()
|
||||
}
|
||||
|
||||
@@ -55,7 +55,7 @@ func (d *DesktopBuilder) BuildBaseAssets(assets *html.AssetBundle, options *Opti
|
||||
var err error
|
||||
|
||||
outputLogger := options.Logger
|
||||
outputLogger.Print(" - Embedding Assets...")
|
||||
outputLogger.Print("Building assets: ")
|
||||
|
||||
// Get target asset directory
|
||||
assetDir, err := fs.RelativeToCwd("build")
|
||||
@@ -96,7 +96,7 @@ func (d *DesktopBuilder) BuildBaseAssets(assets *html.AssetBundle, options *Opti
|
||||
return err
|
||||
}
|
||||
|
||||
outputLogger.Println("done.")
|
||||
outputLogger.Println("Done.")
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -125,11 +125,11 @@ func (d *DesktopBuilder) BuildRuntime(options *Options) error {
|
||||
|
||||
sourceDir := fs.RelativePath("../../../internal/runtime/js")
|
||||
|
||||
if err := d.NpmInstall(sourceDir); err != nil {
|
||||
if err := d.NpmInstall(sourceDir, options.Verbosity == VERBOSE); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
outputLogger.Print(" - Embedding Runtime...")
|
||||
outputLogger.Print("Embedding Runtime: ")
|
||||
envvars := []string{"WAILSPLATFORM=" + options.Platform}
|
||||
if err := d.NpmRunWithEnvironment(sourceDir, "build:desktop", false, envvars); err != nil {
|
||||
return err
|
||||
|
||||
@@ -98,7 +98,7 @@ func (s *ServerBuilder) BuildBaseAssets(assets *html.AssetBundle) error {
|
||||
func (s *ServerBuilder) BuildRuntime(options *Options) error {
|
||||
|
||||
sourceDir := fs.RelativePath("../../../internal/runtime/js")
|
||||
if err := s.NpmInstall(sourceDir); err != nil {
|
||||
if err := s.NpmInstall(sourceDir, options.Verbosity == VERBOSE); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user