mirror of
https://github.com/wavetermdev/wails.git
synced 2026-08-05 13:53:43 -07:00
Refactored build command (#2123)
* Refactored build command * Update v2/cmd/wails/build.go Co-authored-by: stffabi <stffabi@users.noreply.github.com> * WIP * Refactor `wails doctor` * Refactor `wails dev` * Refactor `wails dev` * Fix merge conflict * Fix test * Update build_and_test.yml Co-authored-by: stffabi <stffabi@users.noreply.github.com>
This commit is contained in:
@@ -29,6 +29,7 @@ jobs:
|
||||
go-version: ${{ matrix.go-version }}
|
||||
|
||||
- name: Run tests
|
||||
working-directory: ./v2
|
||||
run: go test -v ./...
|
||||
|
||||
test_templates:
|
||||
|
||||
@@ -0,0 +1,254 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/leaanthony/slicer"
|
||||
"github.com/pterm/pterm"
|
||||
"github.com/wailsapp/wails/v2/cmd/wails/flags"
|
||||
"github.com/wailsapp/wails/v2/cmd/wails/internal/gomod"
|
||||
"github.com/wailsapp/wails/v2/internal/colour"
|
||||
"github.com/wailsapp/wails/v2/internal/project"
|
||||
"github.com/wailsapp/wails/v2/pkg/clilogger"
|
||||
"github.com/wailsapp/wails/v2/pkg/commands/build"
|
||||
"os"
|
||||
"runtime"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
func buildApplication(f *flags.Build) error {
|
||||
|
||||
if f.NoColour {
|
||||
pterm.DisableColor()
|
||||
colour.ColourEnabled = false
|
||||
}
|
||||
|
||||
quiet := f.Verbosity == flags.Quiet
|
||||
|
||||
// Create logger
|
||||
logger := clilogger.New(os.Stdout)
|
||||
logger.Mute(quiet)
|
||||
|
||||
if quiet {
|
||||
pterm.DisableOutput()
|
||||
} else {
|
||||
app.PrintBanner()
|
||||
}
|
||||
|
||||
err := f.Process()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
cwd, err := os.Getwd()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
projectOptions, err := project.Load(cwd)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Create BuildOptions
|
||||
buildOptions := &build.Options{
|
||||
Logger: logger,
|
||||
OutputType: "desktop",
|
||||
OutputFile: f.OutputFilename,
|
||||
CleanBinDirectory: f.Clean,
|
||||
Mode: f.GetBuildMode(),
|
||||
Pack: !f.NoPackage,
|
||||
LDFlags: f.LdFlags,
|
||||
Compiler: f.Compiler,
|
||||
SkipModTidy: f.SkipModTidy,
|
||||
Verbosity: f.Verbosity,
|
||||
ForceBuild: f.ForceBuild,
|
||||
IgnoreFrontend: f.SkipFrontend,
|
||||
Compress: f.Upx,
|
||||
CompressFlags: f.UpxFlags,
|
||||
UserTags: f.GetTags(),
|
||||
WebView2Strategy: f.GetWebView2Strategy(),
|
||||
TrimPath: f.TrimPath,
|
||||
RaceDetector: f.RaceDetector,
|
||||
WindowsConsole: f.WindowsConsole,
|
||||
Obfuscated: f.Obfuscated,
|
||||
GarbleArgs: f.GarbleArgs,
|
||||
SkipBindings: f.SkipBindings,
|
||||
ProjectData: projectOptions,
|
||||
}
|
||||
|
||||
tableData := pterm.TableData{
|
||||
{"Platform(s)", f.Platform},
|
||||
{"Compiler", f.GetCompilerPath()},
|
||||
{"Skip Bindings", bool2Str(f.SkipBindings)},
|
||||
{"Build Mode", f.GetBuildModeAsString()},
|
||||
{"Frontend Directory", projectOptions.GetFrontendDir()},
|
||||
{"Obfuscated", bool2Str(f.Obfuscated)},
|
||||
}
|
||||
if f.Obfuscated {
|
||||
tableData = append(tableData, []string{"Garble Args", f.GarbleArgs})
|
||||
}
|
||||
tableData = append(tableData, pterm.TableData{
|
||||
{"Skip Frontend", bool2Str(f.SkipFrontend)},
|
||||
{"Compress", bool2Str(f.Upx)},
|
||||
{"Package", bool2Str(!f.NoPackage)},
|
||||
{"Clean Bin Dir", bool2Str(f.Clean)},
|
||||
{"LDFlags", f.LdFlags},
|
||||
{"Tags", "[" + strings.Join(f.GetTags(), ",") + "]"},
|
||||
{"Race Detector", bool2Str(f.RaceDetector)},
|
||||
}...)
|
||||
if len(buildOptions.OutputFile) > 0 && f.GetTargets().Length() == 1 {
|
||||
tableData = append(tableData, []string{"Output File", f.OutputFilename})
|
||||
}
|
||||
pterm.DefaultSection.Println("Build Options")
|
||||
|
||||
err = pterm.DefaultTable.WithData(tableData).Render()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = gomod.SyncGoMod(logger, f.UpdateWailsVersionGoMod)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Check platform
|
||||
validPlatformArch := slicer.String([]string{
|
||||
"darwin",
|
||||
"darwin/amd64",
|
||||
"darwin/arm64",
|
||||
"darwin/universal",
|
||||
"linux",
|
||||
"linux/amd64",
|
||||
"linux/arm64",
|
||||
"linux/arm",
|
||||
"windows",
|
||||
"windows/amd64",
|
||||
"windows/arm64",
|
||||
"windows/386",
|
||||
})
|
||||
|
||||
outputBinaries := map[string]string{}
|
||||
|
||||
// Allows cancelling the build after the first error. It would be nice if targets.Each would support funcs
|
||||
// returning an error.
|
||||
var targetErr error
|
||||
targets := f.GetTargets()
|
||||
targets.Each(func(platform string) {
|
||||
if targetErr != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if !validPlatformArch.Contains(platform) {
|
||||
buildOptions.Logger.Println("platform '%s' is not supported - skipping. Supported platforms: %s", platform, validPlatformArch.Join(","))
|
||||
return
|
||||
}
|
||||
|
||||
desiredFilename := projectOptions.OutputFilename
|
||||
if desiredFilename == "" {
|
||||
desiredFilename = projectOptions.Name
|
||||
}
|
||||
desiredFilename = strings.TrimSuffix(desiredFilename, ".exe")
|
||||
|
||||
// Calculate platform and arch
|
||||
platformSplit := strings.Split(platform, "/")
|
||||
buildOptions.Platform = platformSplit[0]
|
||||
buildOptions.Arch = f.GetDefaultArch()
|
||||
if len(platformSplit) > 1 {
|
||||
buildOptions.Arch = platformSplit[1]
|
||||
}
|
||||
banner := "Building target: " + buildOptions.Platform + "/" + buildOptions.Arch
|
||||
pterm.DefaultSection.Println(banner)
|
||||
|
||||
if f.Upx && platform == "darwin/universal" {
|
||||
pterm.Warning.Println("Warning: compress flag unsupported for universal binaries. Ignoring.")
|
||||
f.Upx = false
|
||||
}
|
||||
|
||||
switch buildOptions.Platform {
|
||||
case "linux":
|
||||
if runtime.GOOS != "linux" {
|
||||
pterm.Warning.Println("Crosscompiling to Linux not currently supported.\n")
|
||||
return
|
||||
}
|
||||
case "darwin":
|
||||
if runtime.GOOS != "darwin" {
|
||||
pterm.Warning.Println("Crosscompiling to Mac not currently supported.\n")
|
||||
return
|
||||
}
|
||||
macTargets := targets.Filter(func(platform string) bool {
|
||||
return strings.HasPrefix(platform, "darwin")
|
||||
})
|
||||
if macTargets.Length() == 2 {
|
||||
buildOptions.BundleName = fmt.Sprintf("%s-%s.app", desiredFilename, buildOptions.Arch)
|
||||
}
|
||||
}
|
||||
|
||||
if targets.Length() > 1 {
|
||||
// target filename
|
||||
switch buildOptions.Platform {
|
||||
case "windows":
|
||||
desiredFilename = fmt.Sprintf("%s-%s", desiredFilename, buildOptions.Arch)
|
||||
case "linux", "darwin":
|
||||
desiredFilename = fmt.Sprintf("%s-%s-%s", desiredFilename, buildOptions.Platform, buildOptions.Arch)
|
||||
}
|
||||
}
|
||||
if buildOptions.Platform == "windows" {
|
||||
desiredFilename += ".exe"
|
||||
}
|
||||
buildOptions.OutputFile = desiredFilename
|
||||
|
||||
if f.OutputFilename != "" {
|
||||
buildOptions.OutputFile = f.OutputFilename
|
||||
}
|
||||
|
||||
if f.Obfuscated && f.SkipBindings {
|
||||
pterm.Warning.Println("obfuscated flag overrides skipbindings flag.")
|
||||
buildOptions.SkipBindings = false
|
||||
}
|
||||
|
||||
if !f.DryRun {
|
||||
// Start Time
|
||||
start := time.Now()
|
||||
|
||||
compiledBinary, err := build.Build(buildOptions)
|
||||
if err != nil {
|
||||
pterm.Error.Println(err.Error())
|
||||
targetErr = err
|
||||
return
|
||||
}
|
||||
|
||||
buildOptions.IgnoreFrontend = true
|
||||
buildOptions.CleanBinDirectory = false
|
||||
|
||||
// Output stats
|
||||
buildOptions.Logger.Println(fmt.Sprintf("Built '%s' in %s.\n", compiledBinary, time.Since(start).Round(time.Millisecond).String()))
|
||||
|
||||
outputBinaries[buildOptions.Platform+"/"+buildOptions.Arch] = compiledBinary
|
||||
} else {
|
||||
pterm.Info.Println("Dry run: skipped build.")
|
||||
}
|
||||
})
|
||||
|
||||
if targetErr != nil {
|
||||
return targetErr
|
||||
}
|
||||
|
||||
if f.DryRun {
|
||||
return nil
|
||||
}
|
||||
|
||||
if f.NSIS {
|
||||
amd64Binary := outputBinaries["windows/amd64"]
|
||||
arm64Binary := outputBinaries["windows/arm64"]
|
||||
if amd64Binary == "" && arm64Binary == "" {
|
||||
return fmt.Errorf("cannot build nsis installer - no windows targets")
|
||||
}
|
||||
|
||||
if err := build.GenerateNSISInstaller(buildOptions, amd64Binary, arm64Binary); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"github.com/pterm/pterm"
|
||||
"github.com/wailsapp/wails/v2/cmd/wails/flags"
|
||||
"github.com/wailsapp/wails/v2/cmd/wails/internal/dev"
|
||||
"github.com/wailsapp/wails/v2/internal/colour"
|
||||
"github.com/wailsapp/wails/v2/pkg/clilogger"
|
||||
"os"
|
||||
)
|
||||
|
||||
func devApplication(f *flags.Dev) error {
|
||||
|
||||
if f.NoColour {
|
||||
pterm.DisableColor()
|
||||
colour.ColourEnabled = false
|
||||
}
|
||||
|
||||
quiet := f.Verbosity == flags.Quiet
|
||||
|
||||
// Create logger
|
||||
logger := clilogger.New(os.Stdout)
|
||||
logger.Mute(quiet)
|
||||
|
||||
if quiet {
|
||||
pterm.DisableOutput()
|
||||
} else {
|
||||
app.PrintBanner()
|
||||
}
|
||||
|
||||
err := f.Process()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return dev.Application(f, logger)
|
||||
|
||||
}
|
||||
@@ -0,0 +1,165 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"github.com/pterm/pterm"
|
||||
"github.com/wailsapp/wails/v2/cmd/wails/flags"
|
||||
"github.com/wailsapp/wails/v2/internal/colour"
|
||||
"github.com/wailsapp/wails/v2/internal/system"
|
||||
"github.com/wailsapp/wails/v2/internal/system/packagemanager"
|
||||
"runtime"
|
||||
"runtime/debug"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func diagnoseEnvironment(f *flags.Doctor) error {
|
||||
|
||||
if f.NoColour {
|
||||
pterm.DisableColor()
|
||||
colour.ColourEnabled = false
|
||||
}
|
||||
|
||||
app.PrintBanner()
|
||||
|
||||
pterm.Print("Scanning system - Please wait (this may take a long time)...")
|
||||
|
||||
// Get system info
|
||||
info, err := system.GetInfo()
|
||||
if err != nil {
|
||||
pterm.Println("Failed.")
|
||||
return err
|
||||
}
|
||||
pterm.Println("Done.")
|
||||
|
||||
pterm.DefaultSection.Println("System")
|
||||
|
||||
systemTabledata := [][]string{
|
||||
{"OS", info.OS.Name},
|
||||
{"Version", info.OS.Version},
|
||||
{"ID", info.OS.ID},
|
||||
{"Go Version", runtime.Version()},
|
||||
{"Platform", runtime.GOOS},
|
||||
{"Architecture", runtime.GOARCH},
|
||||
}
|
||||
|
||||
err = pterm.DefaultTable.WithData(systemTabledata).Render()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
pterm.DefaultSection.Println("Wails")
|
||||
|
||||
wailsTableData := [][]string{
|
||||
{"Version", app.Version()},
|
||||
}
|
||||
|
||||
if buildInfo, _ := debug.ReadBuildInfo(); buildInfo != nil {
|
||||
buildSettingToName := map[string]string{
|
||||
"vcs.revision": "Revision",
|
||||
"vcs.modified": "Modified",
|
||||
}
|
||||
for _, buildSetting := range buildInfo.Settings {
|
||||
name := buildSettingToName[buildSetting.Key]
|
||||
if name == "" {
|
||||
continue
|
||||
}
|
||||
wailsTableData = append(wailsTableData, []string{name, buildSetting.Value})
|
||||
}
|
||||
}
|
||||
|
||||
// Exit early if PM not found
|
||||
if info.PM != nil {
|
||||
wailsTableData = append(wailsTableData, []string{"Package Manager", info.PM.Name()})
|
||||
}
|
||||
|
||||
err = pterm.DefaultTable.WithData(wailsTableData).Render()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
pterm.DefaultSection.Println("Dependencies")
|
||||
|
||||
// Output Dependencies Status
|
||||
var dependenciesMissing = []string{}
|
||||
var externalPackages = []*packagemanager.Dependency{}
|
||||
var dependenciesAvailableRequired = 0
|
||||
var dependenciesAvailableOptional = 0
|
||||
|
||||
dependenciesTableData := [][]string{
|
||||
{"Dependency", "Package Name", "Status", "Version"},
|
||||
}
|
||||
|
||||
hasOptionalDependencies := false
|
||||
// Loop over dependencies
|
||||
for _, dependency := range info.Dependencies {
|
||||
|
||||
name := dependency.Name
|
||||
if dependency.Optional {
|
||||
name = "*" + name
|
||||
hasOptionalDependencies = true
|
||||
}
|
||||
packageName := "Unknown"
|
||||
status := "Not Found"
|
||||
|
||||
// If we found the package
|
||||
if dependency.PackageName != "" {
|
||||
|
||||
packageName = dependency.PackageName
|
||||
|
||||
// If it's installed, update the status
|
||||
if dependency.Installed {
|
||||
status = "Installed"
|
||||
} else {
|
||||
// Generate meaningful status text
|
||||
status = "Available"
|
||||
|
||||
if dependency.Optional {
|
||||
dependenciesAvailableOptional++
|
||||
} else {
|
||||
dependenciesAvailableRequired++
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if !dependency.Optional {
|
||||
dependenciesMissing = append(dependenciesMissing, dependency.Name)
|
||||
}
|
||||
|
||||
if dependency.External {
|
||||
externalPackages = append(externalPackages, dependency)
|
||||
}
|
||||
}
|
||||
|
||||
dependenciesTableData = append(dependenciesTableData, []string{name, packageName, status, dependency.Version})
|
||||
}
|
||||
|
||||
err = pterm.DefaultTable.WithHasHeader(true).WithData(dependenciesTableData).Render()
|
||||
|
||||
if hasOptionalDependencies {
|
||||
pterm.Println("* - Optional Dependency")
|
||||
}
|
||||
|
||||
pterm.DefaultSection.Println("Diagnosis")
|
||||
|
||||
// Generate an appropriate diagnosis
|
||||
|
||||
if len(dependenciesMissing) == 0 && dependenciesAvailableRequired == 0 {
|
||||
pterm.Println("Your system is ready for Wails development!")
|
||||
} else {
|
||||
pterm.Println("Your system has missing dependencies!\n")
|
||||
}
|
||||
|
||||
if dependenciesAvailableRequired != 0 {
|
||||
pterm.Println("Required package(s) installation details: \n" + info.Dependencies.InstallAllRequiredCommand())
|
||||
}
|
||||
|
||||
if dependenciesAvailableOptional != 0 {
|
||||
pterm.Println("Optional package(s) installation details: \n" + info.Dependencies.InstallAllOptionalCommand())
|
||||
}
|
||||
|
||||
if len(dependenciesMissing) != 0 {
|
||||
pterm.Println("Fatal:")
|
||||
pterm.Println("Required dependencies missing: " + strings.Join(dependenciesMissing, " "))
|
||||
pterm.Println("Please read this article on how to resolve this: https://wails.io/guides/resolving-missing-packages")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,166 @@
|
||||
package flags
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/leaanthony/slicer"
|
||||
"github.com/wailsapp/wails/v2/internal/system"
|
||||
"github.com/wailsapp/wails/v2/pkg/commands/build"
|
||||
"github.com/wailsapp/wails/v2/pkg/commands/buildtags"
|
||||
"os"
|
||||
"os/exec"
|
||||
"runtime"
|
||||
"strings"
|
||||
)
|
||||
|
||||
const (
|
||||
Quiet int = 0
|
||||
Normal int = 1
|
||||
Verbose int = 2
|
||||
)
|
||||
|
||||
// TODO: unify this and `build.Options`
|
||||
type Build struct {
|
||||
Common
|
||||
BuildCommon
|
||||
|
||||
NoPackage bool `name:"noPackage" description:"Skips platform specific packaging"`
|
||||
SkipModTidy bool `name:"m" description:"Skip mod tidy before compile"`
|
||||
Upx bool `description:"Compress final binary with UPX (if installed)"`
|
||||
UpxFlags string `description:"Flags to pass to upx"`
|
||||
Platform string `description:"Platform to target. Comma separate multiple platforms"`
|
||||
OutputFilename string `name:"o" description:"Output filename"`
|
||||
Clean bool `description:"Clean the bin directory before building"`
|
||||
WebView2 string `description:"WebView2 installer strategy: download,embed,browser,error"`
|
||||
ForceBuild bool `name:"f" description:"Force build of application"`
|
||||
UpdateWailsVersionGoMod bool `name:"u" description:"Updates go.mod to use the same Wails version as the CLI"`
|
||||
Debug bool `description:"Builds the application in debug mode"`
|
||||
NSIS bool `description:"Generate NSIS installer for Windows"`
|
||||
TrimPath bool `description:"Remove all file system paths from the resulting executable"`
|
||||
WindowsConsole bool `description:"Keep the console when building for Windows"`
|
||||
Obfuscated bool `description:"Code obfuscation of bound Wails methods"`
|
||||
GarbleArgs string `description:"Arguments to pass to garble"`
|
||||
DryRun bool `description:"Prints the build command without executing it"`
|
||||
|
||||
// Build Specific
|
||||
|
||||
// Internal state
|
||||
compilerPath string
|
||||
userTags []string
|
||||
wv2rtstrategy string // WebView2 runtime strategy
|
||||
defaultArch string // Default architecture
|
||||
}
|
||||
|
||||
func (b *Build) Default() *Build {
|
||||
|
||||
defaultPlatform := os.Getenv("GOOS")
|
||||
if defaultPlatform == "" {
|
||||
defaultPlatform = runtime.GOOS
|
||||
}
|
||||
defaultArch := os.Getenv("GOARCH")
|
||||
if defaultArch == "" {
|
||||
if system.IsAppleSilicon {
|
||||
defaultArch = "arm64"
|
||||
} else {
|
||||
defaultArch = runtime.GOARCH
|
||||
}
|
||||
}
|
||||
b.defaultArch = defaultArch
|
||||
platform := defaultPlatform + "/" + defaultArch
|
||||
|
||||
result := &Build{
|
||||
Platform: platform,
|
||||
WebView2: "download",
|
||||
GarbleArgs: "-literals -tiny -seed=random",
|
||||
}
|
||||
result.BuildCommon = result.BuildCommon.Default()
|
||||
return result
|
||||
}
|
||||
|
||||
func (b *Build) GetBuildMode() build.Mode {
|
||||
if b.Debug {
|
||||
return build.Debug
|
||||
}
|
||||
return build.Production
|
||||
}
|
||||
|
||||
func (b *Build) GetWebView2Strategy() string {
|
||||
return b.wv2rtstrategy
|
||||
}
|
||||
|
||||
func (b *Build) GetTargets() *slicer.StringSlicer {
|
||||
var targets slicer.StringSlicer
|
||||
targets.AddSlice(strings.Split(b.Platform, ","))
|
||||
targets.Deduplicate()
|
||||
return &targets
|
||||
}
|
||||
|
||||
func (b *Build) GetCompilerPath() string {
|
||||
return b.compilerPath
|
||||
}
|
||||
|
||||
func (b *Build) GetTags() []string {
|
||||
return b.userTags
|
||||
}
|
||||
|
||||
func (b *Build) Process() error {
|
||||
// Lookup compiler path
|
||||
var err error
|
||||
b.compilerPath, err = exec.LookPath(b.Compiler)
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to find compiler: %s", b.Compiler)
|
||||
}
|
||||
|
||||
// Process User Tags
|
||||
b.userTags, err = buildtags.Parse(b.Tags)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// WebView2 installer strategy (download by default)
|
||||
b.WebView2 = strings.ToLower(b.WebView2)
|
||||
if b.WebView2 != "" {
|
||||
validWV2Runtime := slicer.String([]string{"download", "embed", "browser", "error"})
|
||||
if !validWV2Runtime.Contains(b.WebView2) {
|
||||
return fmt.Errorf("invalid option for flag 'webview2': %s", b.WebView2)
|
||||
}
|
||||
b.wv2rtstrategy = "wv2runtime." + b.WebView2
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func bool2Str(b bool) string {
|
||||
if b {
|
||||
return "true"
|
||||
}
|
||||
return "false"
|
||||
}
|
||||
|
||||
func (b *Build) GetBuildModeAsString() string {
|
||||
if b.Debug {
|
||||
return "debug"
|
||||
}
|
||||
return "production"
|
||||
}
|
||||
|
||||
func (b *Build) GetDefaultArch() string {
|
||||
return b.defaultArch
|
||||
}
|
||||
|
||||
/*
|
||||
_, _ = fmt.Fprintf(w, "Frontend Directory: \t%s\n", projectOptions.GetFrontendDir())
|
||||
_, _ = fmt.Fprintf(w, "Obfuscated: \t%t\n", buildOptions.Obfuscated)
|
||||
if buildOptions.Obfuscated {
|
||||
_, _ = fmt.Fprintf(w, "Garble Args: \t%s\n", buildOptions.GarbleArgs)
|
||||
}
|
||||
_, _ = fmt.Fprintf(w, "Skip Frontend: \t%t\n", skipFrontend)
|
||||
_, _ = fmt.Fprintf(w, "Compress: \t%t\n", buildOptions.Compress)
|
||||
_, _ = fmt.Fprintf(w, "Package: \t%t\n", buildOptions.Pack)
|
||||
_, _ = fmt.Fprintf(w, "Clean Bin Dir: \t%t\n", buildOptions.CleanBinDirectory)
|
||||
_, _ = fmt.Fprintf(w, "LDFlags: \t\"%s\"\n", buildOptions.LDFlags)
|
||||
_, _ = fmt.Fprintf(w, "Tags: \t[%s]\n", strings.Join(buildOptions.UserTags, ","))
|
||||
_, _ = fmt.Fprintf(w, "Race Detector: \t%t\n", buildOptions.RaceDetector)
|
||||
if len(buildOptions.OutputFile) > 0 && targets.Length() == 1 {
|
||||
_, _ = fmt.Fprintf(w, "Output File: \t%s\n", buildOptions.OutputFile)
|
||||
}
|
||||
*/
|
||||
@@ -0,0 +1,18 @@
|
||||
package flags
|
||||
|
||||
type BuildCommon struct {
|
||||
LdFlags string `description:"Additional ldflags to pass to the compiler"`
|
||||
Compiler string `description:"Use a different go compiler to build, eg go1.15beta1"`
|
||||
SkipBindings bool `description:"Skips generation of bindings"`
|
||||
RaceDetector bool `name:"race" description:"Build with Go's race detector"`
|
||||
SkipFrontend bool `name:"s" description:"Skips building the frontend"`
|
||||
Verbosity int `name:"v" description:"Verbosity level (0 = quiet, 1 = normal, 2 = verbose)"`
|
||||
Tags string `description:"Build tags to pass to Go compiler. Must be quoted. Space or comma (but not both) separated"`
|
||||
}
|
||||
|
||||
func (c BuildCommon) Default() BuildCommon {
|
||||
return BuildCommon{
|
||||
Compiler: "go",
|
||||
Verbosity: 1,
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
package flags
|
||||
|
||||
type Common struct {
|
||||
NoColour bool `description:"Disable colour in output"`
|
||||
}
|
||||
@@ -0,0 +1,144 @@
|
||||
package flags
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/samber/lo"
|
||||
"github.com/wailsapp/wails/v2/internal/project"
|
||||
"github.com/wailsapp/wails/v2/pkg/commands/build"
|
||||
"net"
|
||||
"net/url"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
)
|
||||
|
||||
type Dev struct {
|
||||
BuildCommon
|
||||
|
||||
AssetDir string `flag:"assetdir" description:"Serve assets from the given directory instead of using the provided asset FS"`
|
||||
Extensions string `flag:"e" description:"Extensions to trigger rebuilds (comma separated) eg go"`
|
||||
ReloadDirs string `flag:"reloaddirs" description:"Additional directories to trigger reloads (comma separated)"`
|
||||
Browser bool `flag:"browser" description:"Open the application in a browser"`
|
||||
NoReload bool `flag:"noreload" description:"Disable reload on asset change"`
|
||||
NoColour bool `flag:"nocolor" description:"Disable colour in output"`
|
||||
WailsJSDir string `flag:"wailsjsdir" description:"Directory to generate the Wails JS modules"`
|
||||
LogLevel string `flag:"loglevel" description:"LogLevel to use - Trace, Debug, Info, Warning, Error)"`
|
||||
ForceBuild bool `flag:"f" description:"Force build of application"`
|
||||
Debounce int `flag:"debounce" description:"The amount of time to wait to trigger a reload on change"`
|
||||
DevServer string `flag:"devserver" description:"The address of the wails dev server"`
|
||||
AppArgs string `flag:"appargs" description:"arguments to pass to the underlying app (quoted and space separated)"`
|
||||
Save bool `flag:"save" description:"Save the given flags as defaults"`
|
||||
FrontendDevServerURL string `flag:"frontenddevserverurl" description:"The url of the external frontend dev server to use"`
|
||||
|
||||
// Internal state
|
||||
devServerURL *url.URL
|
||||
projectConfig *project.Project
|
||||
}
|
||||
|
||||
func (*Dev) Default() *Dev {
|
||||
result := &Dev{
|
||||
Extensions: "go",
|
||||
Debounce: 100,
|
||||
}
|
||||
result.BuildCommon = result.BuildCommon.Default()
|
||||
return result
|
||||
}
|
||||
|
||||
func (d *Dev) Process() error {
|
||||
|
||||
var err error
|
||||
err = d.loadAndMergeProjectConfig()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if _, _, err := net.SplitHostPort(d.DevServer); err != nil {
|
||||
return fmt.Errorf("DevServer is not of the form 'host:port', please check your wails.json")
|
||||
}
|
||||
|
||||
d.devServerURL, err = url.Parse("http://" + d.DevServer)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *Dev) loadAndMergeProjectConfig() error {
|
||||
var err error
|
||||
cwd, err := os.Getwd()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
d.projectConfig, err = project.Load(cwd)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
d.AssetDir, _ = lo.Coalesce(d.AssetDir, d.projectConfig.AssetDirectory)
|
||||
d.projectConfig.AssetDirectory = filepath.ToSlash(d.AssetDir)
|
||||
if d.AssetDir != "" {
|
||||
d.AssetDir, err = filepath.Abs(d.AssetDir)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
d.ReloadDirs, _ = lo.Coalesce(d.ReloadDirs, d.projectConfig.ReloadDirectories)
|
||||
d.projectConfig.ReloadDirectories = filepath.ToSlash(d.ReloadDirs)
|
||||
d.DevServer, _ = lo.Coalesce(d.DevServer, d.projectConfig.DevServer)
|
||||
d.projectConfig.DevServer = d.DevServer
|
||||
d.FrontendDevServerURL, _ = lo.Coalesce(d.FrontendDevServerURL, d.projectConfig.FrontendDevServerURL)
|
||||
d.projectConfig.FrontendDevServerURL = d.FrontendDevServerURL
|
||||
d.WailsJSDir, _ = lo.Coalesce(d.WailsJSDir, d.projectConfig.GetWailsJSDir(), d.projectConfig.GetFrontendDir())
|
||||
d.projectConfig.WailsJSDir = filepath.ToSlash(d.WailsJSDir)
|
||||
|
||||
if d.Debounce == 100 && d.projectConfig.DebounceMS != 100 {
|
||||
if d.projectConfig.DebounceMS == 0 {
|
||||
d.projectConfig.DebounceMS = 100
|
||||
}
|
||||
d.Debounce = d.projectConfig.DebounceMS
|
||||
}
|
||||
d.projectConfig.DebounceMS = d.Debounce
|
||||
|
||||
d.AppArgs, _ = lo.Coalesce(d.AppArgs, d.projectConfig.AppArgs)
|
||||
|
||||
if d.Save {
|
||||
err = d.projectConfig.Save()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
|
||||
}
|
||||
|
||||
// GenerateBuildOptions creates a build.Options using the flags
|
||||
func (d *Dev) GenerateBuildOptions() *build.Options {
|
||||
result := &build.Options{
|
||||
OutputType: "dev",
|
||||
Mode: build.Dev,
|
||||
Arch: runtime.GOARCH,
|
||||
Pack: true,
|
||||
Platform: runtime.GOOS,
|
||||
LDFlags: d.LdFlags,
|
||||
Compiler: d.Compiler,
|
||||
ForceBuild: d.ForceBuild,
|
||||
IgnoreFrontend: d.SkipFrontend,
|
||||
Verbosity: d.Verbosity,
|
||||
WailsJSDir: d.WailsJSDir,
|
||||
RaceDetector: d.RaceDetector,
|
||||
ProjectData: d.projectConfig,
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
func (d *Dev) ProjectConfig() *project.Project {
|
||||
return d.projectConfig
|
||||
}
|
||||
|
||||
func (d *Dev) DevServerURL() *url.URL {
|
||||
return d.devServerURL
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
package flags
|
||||
|
||||
type Doctor struct {
|
||||
Common
|
||||
}
|
||||
|
||||
func (b *Doctor) Default() *Doctor {
|
||||
return &Doctor{}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
package flags
|
||||
|
||||
type GenerateModule struct {
|
||||
Common
|
||||
Tags string `description:"Build tags to pass to Go compiler. Must be quoted. Space or comma (but not both) separated"`
|
||||
Verbosity int `name:"v" description:"Verbosity level (0 = quiet, 1 = normal, 2 = verbose)"`
|
||||
}
|
||||
|
||||
type GenerateTemplate struct {
|
||||
Common
|
||||
Name string `description:"Name of the template to generate"`
|
||||
Frontend string `description:"Frontend to use for the template"`
|
||||
Quiet bool `description:"Suppress output"`
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
package flags
|
||||
|
||||
type Init struct {
|
||||
Common
|
||||
|
||||
TemplateName string `name:"t" description:"Name of built-in template to use, path to template or template url"`
|
||||
ProjectName string `name:"n" description:"Name of project"`
|
||||
CIMode bool `name:"ci" description:"CI Mode"`
|
||||
ProjectDir string `name:"d" description:"Project directory"`
|
||||
Quiet bool `name:"q" description:"Suppress output to console"`
|
||||
InitGit bool `name:"g" description:"Initialise git repository"`
|
||||
IDE string `name:"ide" description:"Generate IDE project files"`
|
||||
List bool `name:"l" description:"List templates"`
|
||||
}
|
||||
|
||||
func (i *Init) Default() *Init {
|
||||
|
||||
result := &Init{
|
||||
TemplateName: "vanilla",
|
||||
}
|
||||
return result
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
package flags
|
||||
|
||||
type ShowReleaseNotes struct {
|
||||
Common
|
||||
Version string `description:"The version to show the release notes for"`
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
package flags
|
||||
|
||||
type Update struct {
|
||||
Common
|
||||
Version string `description:"The version to update to"`
|
||||
PreRelease bool `name:"pre" description:"Update to latest pre-release"`
|
||||
}
|
||||
@@ -0,0 +1,245 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/leaanthony/debme"
|
||||
"github.com/leaanthony/gosod"
|
||||
"github.com/pterm/pterm"
|
||||
"github.com/tidwall/sjson"
|
||||
"github.com/wailsapp/wails/v2/cmd/wails/flags"
|
||||
"github.com/wailsapp/wails/v2/cmd/wails/internal/template"
|
||||
"github.com/wailsapp/wails/v2/internal/colour"
|
||||
"github.com/wailsapp/wails/v2/internal/fs"
|
||||
"github.com/wailsapp/wails/v2/internal/project"
|
||||
"github.com/wailsapp/wails/v2/pkg/clilogger"
|
||||
"github.com/wailsapp/wails/v2/pkg/commands/bindings"
|
||||
"github.com/wailsapp/wails/v2/pkg/commands/buildtags"
|
||||
"os"
|
||||
"path/filepath"
|
||||
)
|
||||
|
||||
func generateModule(f *flags.GenerateModule) error {
|
||||
|
||||
if f.NoColour {
|
||||
pterm.DisableColor()
|
||||
colour.ColourEnabled = false
|
||||
}
|
||||
|
||||
quiet := f.Verbosity == flags.Quiet
|
||||
logger := clilogger.New(os.Stdout)
|
||||
logger.Mute(quiet)
|
||||
|
||||
buildTags, err := buildtags.Parse(f.Tags)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
cwd, err := os.Getwd()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
projectConfig, err := project.Load(cwd)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_, err = bindings.GenerateBindings(bindings.Options{
|
||||
Tags: buildTags,
|
||||
TsPrefix: projectConfig.Bindings.TsGeneration.Prefix,
|
||||
TsSuffix: projectConfig.Bindings.TsGeneration.Suffix,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func generateTemplate(f *flags.GenerateTemplate) error {
|
||||
|
||||
if f.NoColour {
|
||||
pterm.DisableColor()
|
||||
colour.ColourEnabled = false
|
||||
}
|
||||
|
||||
quiet := f.Quiet
|
||||
logger := clilogger.New(os.Stdout)
|
||||
logger.Mute(quiet)
|
||||
|
||||
// name is mandatory
|
||||
if f.Name == "" {
|
||||
return fmt.Errorf("please provide a template name using the -name flag")
|
||||
}
|
||||
|
||||
// If the current directory is not empty, we create a new directory
|
||||
cwd, err := os.Getwd()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
templateDir := filepath.Join(cwd, f.Name)
|
||||
if !fs.DirExists(templateDir) {
|
||||
err := os.MkdirAll(templateDir, 0755)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
empty, err := fs.DirIsEmpty(templateDir)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
pterm.DefaultSection.Println("Generating template")
|
||||
|
||||
if !empty {
|
||||
templateDir = filepath.Join(cwd, f.Name)
|
||||
printBulletPoint("Creating new template directory:", f.Name)
|
||||
err = fs.Mkdir(templateDir)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// Create base template
|
||||
baseTemplate, err := debme.FS(template.Base, "base")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
g := gosod.New(baseTemplate)
|
||||
g.SetTemplateFilters([]string{".template"})
|
||||
|
||||
err = os.Chdir(templateDir)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
type templateData struct {
|
||||
Name string
|
||||
Description string
|
||||
TemplateDir string
|
||||
WailsVersion string
|
||||
}
|
||||
|
||||
printBulletPoint("Extracting base template files...")
|
||||
|
||||
err = g.Extract(templateDir, &templateData{
|
||||
Name: f.Name,
|
||||
TemplateDir: templateDir,
|
||||
WailsVersion: app.Version(),
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = os.Chdir(cwd)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// If we aren't migrating the files, just exit
|
||||
if f.Frontend == "" {
|
||||
pterm.Println()
|
||||
pterm.Println()
|
||||
pterm.Info.Println("No frontend specified to migrate. Template created.")
|
||||
pterm.Println()
|
||||
return nil
|
||||
}
|
||||
|
||||
// Remove frontend directory
|
||||
frontendDir := filepath.Join(templateDir, "frontend")
|
||||
err = os.RemoveAll(frontendDir)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Copy the files into a new frontend directory
|
||||
printBulletPoint("Migrating existing project files to frontend directory...")
|
||||
|
||||
sourceDir, err := filepath.Abs(f.Frontend)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
newFrontendDir := filepath.Join(templateDir, "frontend")
|
||||
err = fs.CopyDirExtended(sourceDir, newFrontendDir, []string{f.Name, "node_modules"})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Process package.json
|
||||
err = processPackageJSON(frontendDir)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Process package-lock.json
|
||||
err = processPackageLockJSON(frontendDir)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Remove node_modules - ignore error, eg it doesn't exist
|
||||
_ = os.RemoveAll(filepath.Join(frontendDir, "node_modules"))
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func processPackageJSON(frontendDir string) error {
|
||||
var err error
|
||||
|
||||
packageJSON := filepath.Join(frontendDir, "package.json")
|
||||
if !fs.FileExists(packageJSON) {
|
||||
return fmt.Errorf("no package.json found - cannot process")
|
||||
}
|
||||
|
||||
json, err := os.ReadFile(packageJSON)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// We will ignore these errors - it's not critical
|
||||
printBulletPoint("Updating package.json data...")
|
||||
json, _ = sjson.SetBytes(json, "name", "{{.ProjectName}}")
|
||||
json, _ = sjson.SetBytes(json, "author", "{{.AuthorName}}")
|
||||
|
||||
err = os.WriteFile(packageJSON, json, 0644)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
baseDir := filepath.Dir(packageJSON)
|
||||
printBulletPoint("Renaming package.json -> package.tmpl.json...")
|
||||
err = os.Rename(packageJSON, filepath.Join(baseDir, "package.tmpl.json"))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func processPackageLockJSON(frontendDir string) error {
|
||||
var err error
|
||||
|
||||
filename := filepath.Join(frontendDir, "package-lock.json")
|
||||
if !fs.FileExists(filename) {
|
||||
return fmt.Errorf("no package-lock.json found - cannot process")
|
||||
}
|
||||
|
||||
data, err := os.ReadFile(filename)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
json := string(data)
|
||||
|
||||
// We will ignore these errors - it's not critical
|
||||
printBulletPoint("Updating package-lock.json data...")
|
||||
json, _ = sjson.Set(json, "name", "{{.ProjectName}}")
|
||||
|
||||
err = os.WriteFile(filename, []byte(json), 0644)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
baseDir := filepath.Dir(filename)
|
||||
printBulletPoint("Renaming package-lock.json -> package-lock.tmpl.json...")
|
||||
err = os.Rename(filename, filepath.Join(baseDir, "package-lock.tmpl.json"))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,278 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"fmt"
|
||||
"github.com/flytam/filenamify"
|
||||
"github.com/leaanthony/slicer"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/pterm/pterm"
|
||||
"github.com/wailsapp/wails/v2/cmd/wails/flags"
|
||||
"github.com/wailsapp/wails/v2/internal/colour"
|
||||
"github.com/wailsapp/wails/v2/pkg/buildassets"
|
||||
"github.com/wailsapp/wails/v2/pkg/clilogger"
|
||||
"github.com/wailsapp/wails/v2/pkg/git"
|
||||
"github.com/wailsapp/wails/v2/pkg/templates"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
func initProject(f *flags.Init) error {
|
||||
|
||||
if f.NoColour {
|
||||
pterm.DisableColor()
|
||||
colour.ColourEnabled = false
|
||||
}
|
||||
|
||||
quiet := f.Quiet
|
||||
|
||||
// Create logger
|
||||
logger := clilogger.New(os.Stdout)
|
||||
logger.Mute(quiet)
|
||||
|
||||
// Are we listing templates?
|
||||
if f.List {
|
||||
app.PrintBanner()
|
||||
templateList, err := templates.List()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
pterm.DefaultSection.Println("Available templates")
|
||||
|
||||
table := pterm.TableData{{"Template", "Short Name", "Description"}}
|
||||
for _, template := range templateList {
|
||||
table = append(table, []string{template.Name, template.ShortName, template.Description})
|
||||
}
|
||||
err = pterm.DefaultTable.WithHasHeader(true).WithBoxed(true).WithData(table).Render()
|
||||
pterm.Println()
|
||||
return err
|
||||
}
|
||||
|
||||
// Validate name
|
||||
if len(f.ProjectName) == 0 {
|
||||
return fmt.Errorf("please provide a project name using the -n flag")
|
||||
}
|
||||
|
||||
// Validate IDE option
|
||||
supportedIDEs := slicer.String([]string{"vscode", "goland"})
|
||||
ide := strings.ToLower(f.IDE)
|
||||
if ide != "" {
|
||||
if !supportedIDEs.Contains(ide) {
|
||||
return fmt.Errorf("ide '%s' not supported. Valid values: %s", ide, supportedIDEs.Join(" "))
|
||||
}
|
||||
}
|
||||
|
||||
if !quiet {
|
||||
app.PrintBanner()
|
||||
}
|
||||
|
||||
pterm.DefaultSection.Printf("Initialising Project '%s'", f.ProjectName)
|
||||
|
||||
projectFilename, err := filenamify.Filenamify(f.ProjectName, filenamify.Options{
|
||||
Replacement: "_",
|
||||
MaxLength: 255,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
goBinary, err := exec.LookPath("go")
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to find Go compiler. Please download and install Go: https://golang.org/dl/")
|
||||
}
|
||||
|
||||
// Get base path and convert to forward slashes
|
||||
goPath := filepath.ToSlash(filepath.Dir(goBinary))
|
||||
// Trim bin directory
|
||||
goSDKPath := strings.TrimSuffix(goPath, "/bin")
|
||||
|
||||
// Create Template Options
|
||||
options := &templates.Options{
|
||||
ProjectName: f.ProjectName,
|
||||
TargetDir: f.ProjectDir,
|
||||
TemplateName: f.TemplateName,
|
||||
Logger: logger,
|
||||
IDE: ide,
|
||||
InitGit: f.InitGit,
|
||||
ProjectNameFilename: projectFilename,
|
||||
WailsVersion: app.Version(),
|
||||
GoSDKPath: goSDKPath,
|
||||
}
|
||||
|
||||
// Try to discover author details from git config
|
||||
findAuthorDetails(options)
|
||||
|
||||
// Start Time
|
||||
start := time.Now()
|
||||
|
||||
// Install the template
|
||||
remote, template, err := templates.Install(options)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Install the default assets
|
||||
err = buildassets.Install(options.TargetDir)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = os.Chdir(options.TargetDir)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if !f.CIMode {
|
||||
// Run `go mod tidy` to ensure `go.sum` is up to date
|
||||
cmd := exec.Command("go", "mod", "tidy")
|
||||
cmd.Dir = options.TargetDir
|
||||
cmd.Stderr = os.Stderr
|
||||
if !quiet {
|
||||
cmd.Stdout = os.Stdout
|
||||
}
|
||||
err = cmd.Run()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
// Update go mod
|
||||
workspace := os.Getenv("GITHUB_WORKSPACE")
|
||||
pterm.Println("GitHub workspace:", workspace)
|
||||
if workspace == "" {
|
||||
os.Exit(1)
|
||||
}
|
||||
updateReplaceLine(workspace)
|
||||
}
|
||||
|
||||
// Remove the `.git`` directory in the template project
|
||||
err = os.RemoveAll(".git")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if options.InitGit {
|
||||
err = initGit(options)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if quiet {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Output stats
|
||||
elapsed := time.Since(start)
|
||||
|
||||
// Create pterm table
|
||||
table := pterm.TableData{
|
||||
{"Project Name", options.ProjectName},
|
||||
{"Project Directory", options.TargetDir},
|
||||
{"Template", template.Name},
|
||||
{"Template Source", template.HelpURL},
|
||||
}
|
||||
err = pterm.DefaultTable.WithData(table).Render()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// IDE message
|
||||
switch options.IDE {
|
||||
case "vscode":
|
||||
pterm.Println()
|
||||
pterm.Info.Println("VSCode config files generated.")
|
||||
case "goland":
|
||||
pterm.Println()
|
||||
pterm.Info.Println("Goland config files generated.")
|
||||
}
|
||||
|
||||
if options.InitGit {
|
||||
pterm.Info.Println("Git repository initialised.")
|
||||
}
|
||||
|
||||
if remote {
|
||||
pterm.Warning.Println("NOTE: You have created a project using a remote template. The Wails project takes no responsibility for 3rd party templates. Only use remote templates that you trust.")
|
||||
}
|
||||
|
||||
pterm.Println("")
|
||||
pterm.Printf("Initialised project '%s' in %s.\n", options.ProjectName, elapsed.Round(time.Millisecond).String())
|
||||
pterm.Println("")
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func initGit(options *templates.Options) error {
|
||||
err := git.InitRepo(options.TargetDir)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "Unable to initialise git repository:")
|
||||
}
|
||||
|
||||
ignore := []string{
|
||||
"build/bin",
|
||||
"frontend/dist",
|
||||
"frontend/node_modules",
|
||||
}
|
||||
err = os.WriteFile(filepath.Join(options.TargetDir, ".gitignore"), []byte(strings.Join(ignore, "\n")), 0644)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "Unable to create gitignore")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// findAuthorDetails tries to find the user's name and email
|
||||
// from gitconfig. If it finds them, it stores them in the project options
|
||||
func findAuthorDetails(options *templates.Options) {
|
||||
if git.IsInstalled() {
|
||||
name, err := git.Name()
|
||||
if err == nil {
|
||||
options.AuthorName = strings.TrimSpace(name)
|
||||
}
|
||||
|
||||
email, err := git.Email()
|
||||
if err == nil {
|
||||
options.AuthorEmail = strings.TrimSpace(email)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func updateReplaceLine(targetPath string) {
|
||||
file, err := os.Open("go.mod")
|
||||
if err != nil {
|
||||
fatal(err.Error())
|
||||
}
|
||||
|
||||
var lines []string
|
||||
scanner := bufio.NewScanner(file)
|
||||
for scanner.Scan() {
|
||||
lines = append(lines, scanner.Text())
|
||||
}
|
||||
|
||||
err = file.Close()
|
||||
if err != nil {
|
||||
fatal(err.Error())
|
||||
}
|
||||
|
||||
if err := scanner.Err(); err != nil {
|
||||
fatal(err.Error())
|
||||
}
|
||||
|
||||
for i, line := range lines {
|
||||
println(line)
|
||||
if strings.HasPrefix(line, "// replace") {
|
||||
pterm.Println("Found replace line")
|
||||
splitLine := strings.Split(line, " ")
|
||||
splitLine[5] = targetPath + "/v2"
|
||||
lines[i] = strings.Join(splitLine[1:], " ")
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
err = os.WriteFile("go.mod", []byte(strings.Join(lines, "\n")), 0644)
|
||||
if err != nil {
|
||||
fatal(err.Error())
|
||||
}
|
||||
}
|
||||
@@ -1,46 +0,0 @@
|
||||
# Build
|
||||
|
||||
The build command processes the Wails project and generates an application binary.
|
||||
|
||||
## Usage
|
||||
|
||||
`wails build <flags>`
|
||||
|
||||
### Flags
|
||||
|
||||
| Flag | Details | Default |
|
||||
| :------------- | :----------- | :------ |
|
||||
| -clean | Clean the bin directory before building | |
|
||||
| -compiler path/to/compiler | Use a different go compiler, eg go1.15beta1 | go |
|
||||
| -ldflags "custom ld flags" | Use given ldflags | |
|
||||
| -o path/to/binary | Compile to given path/filename | |
|
||||
| -k | Keep generated assets | |
|
||||
| -tags | Build tags to pass to Go compiler (quoted and space separated) | |
|
||||
| -upx | Compress final binary with UPX (if installed) | |
|
||||
| -upxflags "custom flags" | Flags to pass to upx | |
|
||||
| -v int | Verbosity level (0 - silent, 1 - default, 2 - verbose) | 1 |
|
||||
| -delve | If true, runs delve on the compiled binary | false |
|
||||
|
||||
## The Build Process
|
||||
|
||||
The build process is as follows:
|
||||
|
||||
- The flags are processed, and an Options struct built containing the build context.
|
||||
- The type of target is determined, and a custom build process is followed for target.
|
||||
|
||||
### Desktop Target
|
||||
|
||||
- The frontend dependencies are installed. The command is read from the project file `wails.json` under the key `frontend:install` and executed in the `frontend` directory. If this is not defined, it is ignored.
|
||||
- The frontend is then built. This command is read from the project file `wails.json` under the key `frontend:install` and executed in the `frontend` directory. If this is not defined, it is ignored.
|
||||
- The project directory is checked to see if the `build` directory exists. If not, it is created and default project assets are copied to it.
|
||||
- An asset bundle is then created by reading the `html` key from `wails.json` and loading the referenced file. This is then parsed, looking for local Javascript and CSS references. Those files are in turn loaded into memory, converted to C data and saved into the asset bundle located at `build/assets.h`, which also includes the original HTML.
|
||||
- The application icon is then processed: if there is no `build/appicon.png`, a default icon is copied. On Windows,
|
||||
an `app.ico` file is generated from this png. On Mac, `icons.icns` is generated.
|
||||
- The platform assets in the `build/<platform>` directory are processed: manifest + icons compiled to a `.syso` file (
|
||||
deleted after compilation), `info.plist` copied to `.app` on Mac.
|
||||
- If we are building a universal binary for Mac, the application is compiled for both `arm64` and `amd64`. The `lipo`
|
||||
tool is then executed to create the universal binary.
|
||||
- If we are not building a universal binary for Mac, the application is built using `go build`, using build tags to indicate type of application and build mode (debug/production).
|
||||
- If the `-upx` flag was provided, `upx` is invoked to compress the binary. Custom flags may be provided using the `-upxflags` flag.
|
||||
|
||||
|
||||
@@ -1,401 +0,0 @@
|
||||
package build
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"os/exec"
|
||||
"runtime"
|
||||
"strings"
|
||||
"text/tabwriter"
|
||||
"time"
|
||||
|
||||
"github.com/wailsapp/wails/v2/pkg/commands/buildtags"
|
||||
|
||||
"github.com/wailsapp/wails/v2/internal/colour"
|
||||
"github.com/wailsapp/wails/v2/internal/project"
|
||||
"github.com/wailsapp/wails/v2/internal/system"
|
||||
|
||||
"github.com/leaanthony/clir"
|
||||
"github.com/leaanthony/slicer"
|
||||
"github.com/wailsapp/wails/v2/pkg/clilogger"
|
||||
"github.com/wailsapp/wails/v2/pkg/commands/build"
|
||||
)
|
||||
|
||||
// AddBuildSubcommand adds the `build` command for the Wails application
|
||||
func AddBuildSubcommand(app *clir.Cli, w io.Writer) {
|
||||
|
||||
outputType := "desktop"
|
||||
|
||||
validTargetTypes := slicer.String([]string{"desktop", "hybrid", "server"})
|
||||
|
||||
command := app.NewSubCommand("build", "Builds the application")
|
||||
|
||||
// Setup noPackage flag
|
||||
noPackage := false
|
||||
command.BoolFlag("noPackage", "Skips platform specific packaging", &noPackage)
|
||||
|
||||
compilerCommand := "go"
|
||||
command.StringFlag("compiler", "Use a different go compiler to build, eg go1.15beta1", &compilerCommand)
|
||||
|
||||
skipModTidy := false
|
||||
command.BoolFlag("m", "Skip mod tidy before compile", &skipModTidy)
|
||||
|
||||
compress := false
|
||||
command.BoolFlag("upx", "Compress final binary with UPX (if installed)", &compress)
|
||||
|
||||
compressFlags := ""
|
||||
command.StringFlag("upxflags", "Flags to pass to upx", &compressFlags)
|
||||
|
||||
defaultPlatform := os.Getenv("GOOS")
|
||||
if defaultPlatform == "" {
|
||||
defaultPlatform = runtime.GOOS
|
||||
}
|
||||
defaultArch := os.Getenv("GOARCH")
|
||||
if defaultArch == "" {
|
||||
if system.IsAppleSilicon {
|
||||
defaultArch = "arm64"
|
||||
} else {
|
||||
defaultArch = runtime.GOARCH
|
||||
}
|
||||
}
|
||||
platform := defaultPlatform + "/" + defaultArch
|
||||
|
||||
command.StringFlag("platform", "Platform to target. Comma separate multiple platforms", &platform)
|
||||
|
||||
// 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)
|
||||
|
||||
// tags to pass to `go`
|
||||
tags := ""
|
||||
command.StringFlag("tags", "Build tags to pass to Go compiler. Must be quoted. Space or comma (but not both) separated", &tags)
|
||||
|
||||
outputFilename := ""
|
||||
command.StringFlag("o", "Output filename", &outputFilename)
|
||||
|
||||
// Clean bin directory
|
||||
cleanBinDirectory := false
|
||||
command.BoolFlag("clean", "Clean the bin directory before building", &cleanBinDirectory)
|
||||
|
||||
webview2 := "download"
|
||||
command.StringFlag("webview2", "WebView2 installer strategy: download,embed,browser,error.", &webview2)
|
||||
|
||||
skipFrontend := false
|
||||
command.BoolFlag("s", "Skips building the frontend", &skipFrontend)
|
||||
|
||||
forceBuild := false
|
||||
command.BoolFlag("f", "Force build application", &forceBuild)
|
||||
|
||||
updateGoModWailsVersion := false
|
||||
command.BoolFlag("u", "Updates go.mod to use the same Wails version as the CLI", &updateGoModWailsVersion)
|
||||
|
||||
debug := false
|
||||
command.BoolFlag("debug", "Retains debug data in the compiled application", &debug)
|
||||
|
||||
nsis := false
|
||||
command.BoolFlag("nsis", "Generate NSIS installer for Windows", &nsis)
|
||||
|
||||
trimpath := false
|
||||
command.BoolFlag("trimpath", "Remove all file system paths from the resulting executable", &trimpath)
|
||||
|
||||
raceDetector := false
|
||||
command.BoolFlag("race", "Build with Go's race detector", &raceDetector)
|
||||
|
||||
windowsConsole := false
|
||||
command.BoolFlag("windowsconsole", "Keep the console when building for Windows", &windowsConsole)
|
||||
|
||||
obfuscated := false
|
||||
command.BoolFlag("obfuscated", "Code obfuscation of bound Wails methods", &obfuscated)
|
||||
|
||||
garbleargs := "-literals -tiny -seed=random"
|
||||
command.StringFlag("garbleargs", "Arguments to pass to garble", &garbleargs)
|
||||
|
||||
dryRun := false
|
||||
command.BoolFlag("dryrun", "Dry run, prints the config for the command that would be executed", &dryRun)
|
||||
|
||||
skipBindings := false
|
||||
command.BoolFlag("skipbindings", "Skips generation of bindings", &skipBindings)
|
||||
|
||||
command.Action(func() error {
|
||||
|
||||
quiet := verbosity == 0
|
||||
|
||||
// Create logger
|
||||
logger := clilogger.New(w)
|
||||
logger.Mute(quiet)
|
||||
|
||||
// Validate output type
|
||||
if !validTargetTypes.Contains(outputType) {
|
||||
return fmt.Errorf("output type '%s' is not valid", outputType)
|
||||
}
|
||||
|
||||
if !quiet {
|
||||
app.PrintBanner()
|
||||
}
|
||||
|
||||
// Lookup compiler path
|
||||
compilerPath, err := exec.LookPath(compilerCommand)
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to find compiler: %s", compilerCommand)
|
||||
}
|
||||
|
||||
// Process User Tags
|
||||
userTags, err := buildtags.Parse(tags)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Webview2 installer strategy (download by default)
|
||||
wv2rtstrategy := ""
|
||||
webview2 = strings.ToLower(webview2)
|
||||
if webview2 != "" {
|
||||
validWV2Runtime := slicer.String([]string{"download", "embed", "browser", "error"})
|
||||
if !validWV2Runtime.Contains(webview2) {
|
||||
return fmt.Errorf("invalid option for flag 'webview2': %s", webview2)
|
||||
}
|
||||
// These are the build tags associated with the strategies
|
||||
switch webview2 {
|
||||
case "embed":
|
||||
wv2rtstrategy = "wv2runtime.embed"
|
||||
case "error":
|
||||
wv2rtstrategy = "wv2runtime.error"
|
||||
case "browser":
|
||||
wv2rtstrategy = "wv2runtime.browser"
|
||||
}
|
||||
}
|
||||
|
||||
mode := build.Production
|
||||
modeString := "Production"
|
||||
if debug {
|
||||
mode = build.Debug
|
||||
modeString = "Debug"
|
||||
}
|
||||
|
||||
var targets slicer.StringSlicer
|
||||
targets.AddSlice(strings.Split(platform, ","))
|
||||
targets.Deduplicate()
|
||||
|
||||
cwd, err := os.Getwd()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
projectOptions, err := project.Load(cwd)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Create BuildOptions
|
||||
buildOptions := &build.Options{
|
||||
Logger: logger,
|
||||
OutputType: outputType,
|
||||
OutputFile: outputFilename,
|
||||
CleanBinDirectory: cleanBinDirectory,
|
||||
Mode: mode,
|
||||
Pack: !noPackage,
|
||||
LDFlags: ldflags,
|
||||
Compiler: compilerCommand,
|
||||
SkipModTidy: skipModTidy,
|
||||
Verbosity: verbosity,
|
||||
ForceBuild: forceBuild,
|
||||
IgnoreFrontend: skipFrontend,
|
||||
Compress: compress,
|
||||
CompressFlags: compressFlags,
|
||||
UserTags: userTags,
|
||||
WebView2Strategy: wv2rtstrategy,
|
||||
TrimPath: trimpath,
|
||||
RaceDetector: raceDetector,
|
||||
WindowsConsole: windowsConsole,
|
||||
Obfuscated: obfuscated,
|
||||
GarbleArgs: garbleargs,
|
||||
SkipBindings: skipBindings,
|
||||
ProjectData: projectOptions,
|
||||
}
|
||||
|
||||
// Start a new tabwriter
|
||||
if !quiet {
|
||||
w := new(tabwriter.Writer)
|
||||
w.Init(os.Stdout, 8, 8, 0, '\t', 0)
|
||||
|
||||
// Write out the system information
|
||||
_, _ = fmt.Fprintf(w, "App Type: \t%s\n", buildOptions.OutputType)
|
||||
_, _ = fmt.Fprintf(w, "Platforms: \t%s\n", platform)
|
||||
_, _ = fmt.Fprintf(w, "Compiler: \t%s\n", compilerPath)
|
||||
_, _ = fmt.Fprintf(w, "Skip Bindings: \t%t\n", skipBindings)
|
||||
_, _ = fmt.Fprintf(w, "Build Mode: \t%s\n", modeString)
|
||||
_, _ = fmt.Fprintf(w, "Frontend Directory: \t%s\n", projectOptions.GetFrontendDir())
|
||||
_, _ = fmt.Fprintf(w, "Obfuscated: \t%t\n", buildOptions.Obfuscated)
|
||||
if buildOptions.Obfuscated {
|
||||
_, _ = fmt.Fprintf(w, "Garble Args: \t%s\n", buildOptions.GarbleArgs)
|
||||
}
|
||||
_, _ = fmt.Fprintf(w, "Skip Frontend: \t%t\n", skipFrontend)
|
||||
_, _ = fmt.Fprintf(w, "Compress: \t%t\n", buildOptions.Compress)
|
||||
_, _ = fmt.Fprintf(w, "Package: \t%t\n", buildOptions.Pack)
|
||||
_, _ = fmt.Fprintf(w, "Clean Bin Dir: \t%t\n", buildOptions.CleanBinDirectory)
|
||||
_, _ = fmt.Fprintf(w, "LDFlags: \t\"%s\"\n", buildOptions.LDFlags)
|
||||
_, _ = fmt.Fprintf(w, "Tags: \t[%s]\n", strings.Join(buildOptions.UserTags, ","))
|
||||
_, _ = fmt.Fprintf(w, "Race Detector: \t%t\n", buildOptions.RaceDetector)
|
||||
if len(buildOptions.OutputFile) > 0 && targets.Length() == 1 {
|
||||
_, _ = fmt.Fprintf(w, "Output File: \t%s\n", buildOptions.OutputFile)
|
||||
}
|
||||
_, _ = fmt.Fprintf(w, "\n")
|
||||
err = w.Flush()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
err = SyncGoMod(logger, updateGoModWailsVersion)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Check platform
|
||||
validPlatformArch := slicer.String([]string{
|
||||
"darwin",
|
||||
"darwin/amd64",
|
||||
"darwin/arm64",
|
||||
"darwin/universal",
|
||||
"linux",
|
||||
"linux/amd64",
|
||||
"linux/arm64",
|
||||
"linux/arm",
|
||||
"windows",
|
||||
"windows/amd64",
|
||||
"windows/arm64",
|
||||
"windows/386",
|
||||
})
|
||||
|
||||
outputBinaries := map[string]string{}
|
||||
|
||||
// Allows cancelling the build after the first error. It would be nice if targets.Each would support funcs
|
||||
// returning an error.
|
||||
var targetErr error
|
||||
targets.Each(func(platform string) {
|
||||
if targetErr != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if !validPlatformArch.Contains(platform) {
|
||||
buildOptions.Logger.Println("platform '%s' is not supported - skipping. Supported platforms: %s", platform, validPlatformArch.Join(","))
|
||||
return
|
||||
}
|
||||
|
||||
desiredFilename := projectOptions.OutputFilename
|
||||
if desiredFilename == "" {
|
||||
desiredFilename = projectOptions.Name
|
||||
}
|
||||
desiredFilename = strings.TrimSuffix(desiredFilename, ".exe")
|
||||
|
||||
// Calculate platform and arch
|
||||
platformSplit := strings.Split(platform, "/")
|
||||
buildOptions.Platform = platformSplit[0]
|
||||
buildOptions.Arch = defaultArch
|
||||
if len(platformSplit) > 1 {
|
||||
buildOptions.Arch = platformSplit[1]
|
||||
}
|
||||
banner := "Building target: " + buildOptions.Platform + "/" + buildOptions.Arch
|
||||
logger.Println(banner)
|
||||
logger.Println(strings.Repeat("-", len(banner)))
|
||||
|
||||
if compress && platform == "darwin/universal" {
|
||||
logger.Println("Warning: compress flag unsupported for universal binaries. Ignoring.")
|
||||
compress = false
|
||||
}
|
||||
|
||||
switch buildOptions.Platform {
|
||||
case "linux":
|
||||
if runtime.GOOS != "linux" {
|
||||
logger.Println("Crosscompiling to Linux not currently supported.\n")
|
||||
return
|
||||
}
|
||||
case "darwin":
|
||||
if runtime.GOOS != "darwin" {
|
||||
logger.Println("Crosscompiling to Mac not currently supported.\n")
|
||||
return
|
||||
}
|
||||
macTargets := targets.Filter(func(platform string) bool {
|
||||
return strings.HasPrefix(platform, "darwin")
|
||||
})
|
||||
if macTargets.Length() == 2 {
|
||||
buildOptions.BundleName = fmt.Sprintf("%s-%s.app", desiredFilename, buildOptions.Arch)
|
||||
}
|
||||
}
|
||||
|
||||
if targets.Length() > 1 {
|
||||
// target filename
|
||||
switch buildOptions.Platform {
|
||||
case "windows":
|
||||
desiredFilename = fmt.Sprintf("%s-%s", desiredFilename, buildOptions.Arch)
|
||||
case "linux", "darwin":
|
||||
desiredFilename = fmt.Sprintf("%s-%s-%s", desiredFilename, buildOptions.Platform, buildOptions.Arch)
|
||||
}
|
||||
}
|
||||
if buildOptions.Platform == "windows" {
|
||||
desiredFilename += ".exe"
|
||||
}
|
||||
buildOptions.OutputFile = desiredFilename
|
||||
|
||||
if outputFilename != "" {
|
||||
buildOptions.OutputFile = outputFilename
|
||||
}
|
||||
|
||||
if obfuscated && skipBindings {
|
||||
logger.Println("Warning: obfuscated flag overrides skipbindings flag.")
|
||||
buildOptions.SkipBindings = false
|
||||
}
|
||||
|
||||
if !dryRun {
|
||||
// Start Time
|
||||
start := time.Now()
|
||||
|
||||
compiledBinary, err := build.Build(buildOptions)
|
||||
if err != nil {
|
||||
logger.Println("Error: %s", err.Error())
|
||||
targetErr = err
|
||||
return
|
||||
}
|
||||
|
||||
buildOptions.IgnoreFrontend = true
|
||||
buildOptions.CleanBinDirectory = false
|
||||
|
||||
// Output stats
|
||||
buildOptions.Logger.Println(fmt.Sprintf("Built '%s' in %s.\n", compiledBinary, time.Since(start).Round(time.Millisecond).String()))
|
||||
|
||||
outputBinaries[buildOptions.Platform+"/"+buildOptions.Arch] = compiledBinary
|
||||
} else {
|
||||
logger.Println("Dry run: skipped build.")
|
||||
}
|
||||
})
|
||||
|
||||
if targetErr != nil {
|
||||
return targetErr
|
||||
}
|
||||
|
||||
if dryRun {
|
||||
return nil
|
||||
}
|
||||
|
||||
if nsis {
|
||||
amd64Binary := outputBinaries["windows/amd64"]
|
||||
arm64Binary := outputBinaries["windows/arm64"]
|
||||
if amd64Binary == "" && arm64Binary == "" {
|
||||
return fmt.Errorf("cannot build nsis installer - no windows targets")
|
||||
}
|
||||
|
||||
if err := build.GenerateNSISInstaller(buildOptions, amd64Binary, arm64Binary); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
func LogGreen(message string, args ...interface{}) {
|
||||
text := fmt.Sprintf(message, args...)
|
||||
println(colour.Green(text))
|
||||
}
|
||||
@@ -1,22 +0,0 @@
|
||||
# Dev
|
||||
|
||||
The dev command allows you to develop your application through a standard browser.
|
||||
|
||||
## Usage
|
||||
|
||||
`wails dev <flags>`
|
||||
|
||||
### Flags
|
||||
|
||||
| Flag | Details | Default |
|
||||
| :------------- | :----------- | :------ |
|
||||
| -compiler path/to/compiler | Use a different go compiler, eg go1.15beta1 | go |
|
||||
| -ldflags "custom ld flags" | Use given ldflags | |
|
||||
| -e list,of,extensions | File extensions to trigger rebuilds | go |
|
||||
| -w | Show warnings | false |
|
||||
| -v int | Verbosity level (0 - silent, 1 - default, 2 - verbose) | 1 |
|
||||
| -loglevel | Loglevel to pass to the application - Trace, Debug, Info, Warning, Error | Debug |
|
||||
|
||||
## How it works
|
||||
|
||||
The project is built using a special mode that starts a webserver and starts listening to port 34115. When the frontend project is run independently, so long as the JS is wrapped with the runtime method `ready`, then the frontend will connect to the backend code via websockets. The interface should be present in your browser, and you should be able to interact with the backend as you would in a desktop app.
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,174 +0,0 @@
|
||||
package doctor
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"runtime"
|
||||
"runtime/debug"
|
||||
"strings"
|
||||
"text/tabwriter"
|
||||
|
||||
"github.com/leaanthony/clir"
|
||||
"github.com/wailsapp/wails/v2/internal/system"
|
||||
"github.com/wailsapp/wails/v2/internal/system/packagemanager"
|
||||
"github.com/wailsapp/wails/v2/pkg/clilogger"
|
||||
)
|
||||
|
||||
// AddSubcommand adds the `doctor` command for the Wails application
|
||||
func AddSubcommand(app *clir.Cli, w io.Writer) error {
|
||||
|
||||
command := app.NewSubCommand("doctor", "Diagnose your environment")
|
||||
|
||||
command.Action(func() error {
|
||||
|
||||
logger := clilogger.New(w)
|
||||
|
||||
app.PrintBanner()
|
||||
|
||||
logger.Print("Scanning system - Please wait (this may take a long time)...")
|
||||
|
||||
// Get system info
|
||||
info, err := system.GetInfo()
|
||||
if err != nil {
|
||||
logger.Println("Failed.")
|
||||
return err
|
||||
}
|
||||
logger.Println("Done.")
|
||||
|
||||
logger.Println("")
|
||||
|
||||
// Start a new tabwriter
|
||||
w := new(tabwriter.Writer)
|
||||
w.Init(os.Stdout, 8, 8, 0, '\t', 0)
|
||||
|
||||
// Write out the system information
|
||||
fmt.Fprintf(w, "System\n")
|
||||
fmt.Fprintf(w, "------\n")
|
||||
fmt.Fprintf(w, "%s\t%s\n", "OS:", info.OS.Name)
|
||||
fmt.Fprintf(w, "%s\t%s\n", "Version: ", info.OS.Version)
|
||||
fmt.Fprintf(w, "%s\t%s\n", "ID:", info.OS.ID)
|
||||
|
||||
// Output Go Information
|
||||
fmt.Fprintf(w, "%s\t%s\n", "Go Version:", runtime.Version())
|
||||
fmt.Fprintf(w, "%s\t%s\n", "Platform:", runtime.GOOS)
|
||||
fmt.Fprintf(w, "%s\t%s\n", "Architecture:", runtime.GOARCH)
|
||||
|
||||
// Write out the wails information
|
||||
fmt.Fprintf(w, "\n")
|
||||
fmt.Fprintf(w, "Wails\n")
|
||||
fmt.Fprintf(w, "------\n")
|
||||
fmt.Fprintf(w, "%s\t%s\n", "Version: ", app.Version())
|
||||
|
||||
printBuildSettings(w)
|
||||
|
||||
// Exit early if PM not found
|
||||
if info.PM != nil {
|
||||
fmt.Fprintf(w, "%s\t%s\n", "Package Manager: ", info.PM.Name())
|
||||
}
|
||||
|
||||
// Output Dependencies Status
|
||||
var dependenciesMissing = []string{}
|
||||
var externalPackages = []*packagemanager.Dependency{}
|
||||
var dependenciesAvailableRequired = 0
|
||||
var dependenciesAvailableOptional = 0
|
||||
fmt.Fprintf(w, "\n")
|
||||
fmt.Fprintf(w, "Dependency\tPackage Name\tStatus\tVersion\n")
|
||||
fmt.Fprintf(w, "----------\t------------\t------\t-------\n")
|
||||
|
||||
hasOptionalDependencies := false
|
||||
// Loop over dependencies
|
||||
for _, dependency := range info.Dependencies {
|
||||
|
||||
name := dependency.Name
|
||||
if dependency.Optional {
|
||||
name = "*" + name
|
||||
hasOptionalDependencies = true
|
||||
}
|
||||
packageName := "Unknown"
|
||||
status := "Not Found"
|
||||
|
||||
// If we found the package
|
||||
if dependency.PackageName != "" {
|
||||
|
||||
packageName = dependency.PackageName
|
||||
|
||||
// If it's installed, update the status
|
||||
if dependency.Installed {
|
||||
status = "Installed"
|
||||
} else {
|
||||
// Generate meaningful status text
|
||||
status = "Available"
|
||||
|
||||
if dependency.Optional {
|
||||
dependenciesAvailableOptional++
|
||||
} else {
|
||||
dependenciesAvailableRequired++
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if !dependency.Optional {
|
||||
dependenciesMissing = append(dependenciesMissing, dependency.Name)
|
||||
}
|
||||
|
||||
if dependency.External {
|
||||
externalPackages = append(externalPackages, dependency)
|
||||
}
|
||||
}
|
||||
|
||||
fmt.Fprintf(w, "%s \t%s \t%s \t%s\n", name, packageName, status, dependency.Version)
|
||||
}
|
||||
if hasOptionalDependencies {
|
||||
fmt.Fprintf(w, "\n")
|
||||
fmt.Fprintf(w, "* - Optional Dependency\n")
|
||||
}
|
||||
w.Flush()
|
||||
logger.Println("")
|
||||
logger.Println("Diagnosis")
|
||||
logger.Println("---------")
|
||||
|
||||
// Generate an appropriate diagnosis
|
||||
|
||||
if len(dependenciesMissing) == 0 && dependenciesAvailableRequired == 0 {
|
||||
logger.Println("Your system is ready for Wails development!")
|
||||
} else {
|
||||
logger.Println("Your system has missing dependencies!\n")
|
||||
}
|
||||
|
||||
if dependenciesAvailableRequired != 0 {
|
||||
logger.Println("Required package(s) installation details: \n" + info.Dependencies.InstallAllRequiredCommand())
|
||||
}
|
||||
|
||||
if dependenciesAvailableOptional != 0 {
|
||||
logger.Println("Optional package(s) installation details: \n" + info.Dependencies.InstallAllOptionalCommand())
|
||||
}
|
||||
|
||||
if len(dependenciesMissing) != 0 {
|
||||
logger.Println("Fatal:")
|
||||
logger.Println("Required dependencies missing: " + strings.Join(dependenciesMissing, " "))
|
||||
logger.Println("Please read this article on how to resolve this: https://wails.io/guides/resolving-missing-packages")
|
||||
}
|
||||
|
||||
logger.Println("")
|
||||
return nil
|
||||
})
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func printBuildSettings(w *tabwriter.Writer) {
|
||||
if buildInfo, _ := debug.ReadBuildInfo(); buildInfo != nil {
|
||||
buildSettingToName := map[string]string{
|
||||
"vcs.revision": "Revision",
|
||||
"vcs.modified": "Modified",
|
||||
}
|
||||
for _, buildSetting := range buildInfo.Settings {
|
||||
name := buildSettingToName[buildSetting.Key]
|
||||
if name == "" {
|
||||
continue
|
||||
}
|
||||
|
||||
_, _ = fmt.Fprintf(w, "%s:\t%s\n", name, buildSetting.Value)
|
||||
}
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user