5
0
mirror of https://github.com/wailsapp/wails.git synced 2025-05-02 22:13:36 +08:00

Significant support for Windows builds

This commit is contained in:
Lea Anthony 2019-02-20 22:24:47 +11:00
parent 3025a94a77
commit bdcf98fc15
12 changed files with 163 additions and 30 deletions

View File

@ -76,6 +76,12 @@ func BuildApplication(binaryName string, forceRebuild bool, buildMode string) er
if buildMode == BuildModeDebug { if buildMode == BuildModeDebug {
ldflags = "" ldflags = ""
} }
// Add windows flags
if runtime.GOOS == "windows" {
ldflags += "-H windowsgui "
}
ldflags += "-X github.com/wailsapp/wails.BuildMode=" + buildMode ldflags += "-X github.com/wailsapp/wails.BuildMode=" + buildMode
buildCommand.AddSlice([]string{"-ldflags", ldflags}) buildCommand.AddSlice([]string{"-ldflags", ldflags})
@ -91,7 +97,11 @@ func BuildApplication(binaryName string, forceRebuild bool, buildMode string) er
// PackageApplication will attempt to package the application in a pltform dependent way // PackageApplication will attempt to package the application in a pltform dependent way
func PackageApplication(projectOptions *ProjectOptions) error { func PackageApplication(projectOptions *ProjectOptions) error {
// Package app // Package app
packageSpinner := spinner.New("Packaging Application") message := "Generating .app"
if runtime.GOOS == "windows" {
message = "Generating resource bundle"
}
packageSpinner := spinner.New(message)
packageSpinner.SetSpinSpeed(50) packageSpinner.SetSpinSpeed(50)
packageSpinner.Start() packageSpinner.Start()
err := NewPackageHelper().Package(projectOptions) err := NewPackageHelper().Package(projectOptions)
@ -134,6 +144,18 @@ func CheckMewn() (err error) {
return nil return nil
} }
// CheckWindres checks if Windres is installed and if not, aborts
func CheckWindres() (err error) {
if runtime.GOOS != "windows" {
return nil
}
programHelper := NewProgramHelper()
if !programHelper.IsInstalled("windres") {
return fmt.Errorf("windres not installed. It comes by default with mingw. Ensure you have installed mingw correctly.")
}
return nil
}
// InstallFrontendDeps attempts to install the frontend dependencies based on the given options // InstallFrontendDeps attempts to install the frontend dependencies based on the given options
func InstallFrontendDeps(projectDir string, projectOptions *ProjectOptions, forceRebuild bool, caller string) error { func InstallFrontendDeps(projectDir string, projectOptions *ProjectOptions, forceRebuild bool, caller string) error {

View File

@ -68,15 +68,15 @@ func (b *PackageHelper) getPackageFileBaseDir() string {
// Package the application into a platform specific package // Package the application into a platform specific package
func (b *PackageHelper) Package(po *ProjectOptions) error { func (b *PackageHelper) Package(po *ProjectOptions) error {
// Check we have the exe
if !b.fs.FileExists(po.BinaryName) {
return fmt.Errorf("cannot bundle non-existant binary file '%s'. Please build with 'wails build' first", po.BinaryName)
}
switch runtime.GOOS { switch runtime.GOOS {
case "darwin": case "darwin":
// Check we have the exe
if !b.fs.FileExists(po.BinaryName) {
return fmt.Errorf("cannot bundle non-existant binary file '%s'. Please build with 'wails build' first", po.BinaryName)
}
return b.packageOSX(po) return b.packageOSX(po)
case "windows": case "windows":
return fmt.Errorf("windows is not supported at this time. Please see https://github.com/wailsapp/wails/issues/3") return b.packageWindows(po)
case "linux": case "linux":
return fmt.Errorf("linux is not supported at this time. Please see https://github.com/wailsapp/wails/issues/2") return fmt.Errorf("linux is not supported at this time. Please see https://github.com/wailsapp/wails/issues/2")
default: default:
@ -146,15 +146,63 @@ func (b *PackageHelper) packageOSX(po *ProjectOptions) error {
if err != nil { if err != nil {
return err return err
} }
err = b.packageIcon(resourceDir) err = b.packageIconOSX(resourceDir)
return err return err
} }
func (b *PackageHelper) packageIcon(resourceDir string) error { func (b *PackageHelper) packageWindows(po *ProjectOptions) error {
basename := strings.TrimSuffix(po.BinaryName, ".exe")
// Copy icon
tgtIconFile := filepath.Join(b.fs.Cwd(), basename+".ico")
if !b.fs.FileExists(tgtIconFile) {
srcIconfile := filepath.Join(b.getPackageFileBaseDir(), "wails.ico")
err := b.fs.CopyFile(srcIconfile, tgtIconFile)
if err != nil {
return err
}
}
// Copy manifest
tgtManifestFile := filepath.Join(b.fs.Cwd(), basename+".exe.manifest")
if !b.fs.FileExists(tgtManifestFile) {
srcManifestfile := filepath.Join(b.getPackageFileBaseDir(), "wails.exe.manifest")
err := b.fs.CopyFile(srcManifestfile, tgtManifestFile)
if err != nil {
return err
}
}
// Copy rc file
tgtRCFile := filepath.Join(b.fs.Cwd(), basename+".rc")
if !b.fs.FileExists(tgtRCFile) {
srcRCfile := filepath.Join(b.getPackageFileBaseDir(), "wails.rc")
rcfilebytes, err := ioutil.ReadFile(srcRCfile)
if err != nil {
return err
}
rcfiledata := strings.Replace(string(rcfilebytes), "$NAME$", basename, -1)
err = ioutil.WriteFile(tgtRCFile, []byte(rcfiledata), 0755)
if err != nil {
return err
}
}
// Build syso
sysofile := filepath.Join(b.fs.Cwd(), basename+"-res.syso")
windresCommand := []string{"windres", "-o", sysofile, tgtRCFile}
err := NewProgramHelper().RunCommandArray(windresCommand)
if err != nil {
return err
}
return nil
}
func (b *PackageHelper) copyIcon(resourceDir string) (string, error) {
// TODO: Read this from project.json // TODO: Read this from project.json
const appIconFilename = "appicon.png" const appIconFilename = "appicon.png"
srcIcon := path.Join(b.fs.Cwd(), appIconFilename) srcIcon := path.Join(b.fs.Cwd(), appIconFilename)
// Check if appicon.png exists // Check if appicon.png exists
@ -164,14 +212,22 @@ func (b *PackageHelper) packageIcon(resourceDir string) error {
iconfile := filepath.Join(b.getPackageFileBaseDir(), "icon.png") iconfile := filepath.Join(b.getPackageFileBaseDir(), "icon.png")
iconData, err := ioutil.ReadFile(iconfile) iconData, err := ioutil.ReadFile(iconfile)
if err != nil { if err != nil {
return err return "", err
} }
err = ioutil.WriteFile(srcIcon, iconData, 0644) err = ioutil.WriteFile(srcIcon, iconData, 0644)
if err != nil { if err != nil {
return err return "", err
} }
} }
return srcIcon, nil
}
func (b *PackageHelper) packageIconOSX(resourceDir string) error {
srcIcon, err := b.copyIcon(resourceDir)
if err != nil {
return err
}
tgtBundle := path.Join(resourceDir, "iconfile.icns") tgtBundle := path.Join(resourceDir, "iconfile.icns")
imageFile, err := os.Open(srcIcon) imageFile, err := os.Open(srcIcon)
if err != nil { if err != nil {

Binary file not shown.

After

Width:  |  Height:  |  Size: 106 KiB

View File

@ -0,0 +1,13 @@
<!-- HUGE thanks to tambre: https://stackoverflow.com/a/44989306 -->
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly manifestVersion="1.0" xmlns="urn:schemas-microsoft-com:asm.v1" xmlns:asmv3="urn:schemas-microsoft-com:asm.v3">
<assemblyIdentity type="win32" name="MyApplication" version="1.0.0.0" processorArchitecture="amd64"/>
<asmv3:application>
<asmv3:windowsSettings>
<dpiAware xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">true/pm</dpiAware> <!-- fallback for Windows 7 and 8 -->
<dpiAwareness xmlns="http://schemas.microsoft.com/SMI/2016/WindowsSettings">permonitorv2,permonitor</dpiAwareness> <!-- falls back to per-monitor if per-monitor v2 is not supported -->
<gdiScaling xmlns="http://schemas.microsoft.com/SMI/2017/WindowsSettings">true</gdiScaling> <!-- enables GDI DPI scaling -->
</asmv3:windowsSettings>
</asmv3:application>
</assembly>

Binary file not shown.

After

Width:  |  Height:  |  Size: 315 KiB

View File

@ -0,0 +1,2 @@
100 ICON "$NAME$.ico"
100 24 "$NAME$.exe.manifest"

View File

@ -5,6 +5,7 @@ import (
"fmt" "fmt"
"io/ioutil" "io/ioutil"
"path/filepath" "path/filepath"
"runtime"
"strings" "strings"
) )
@ -82,11 +83,27 @@ func (ph *ProjectHelper) GenerateProject(projectOptions *ProjectOptions) error {
if err != nil { if err != nil {
return err return err
} }
// // If we are on windows, dump a windows_resource.json
// if runtime.GOOS == "windows" {
// ph.GenerateWindowsResourceConfig(projectOptions)
// }
ph.log.Yellow("Project '%s' generated in directory '%s'!", projectOptions.Name, projectOptions.OutputDirectory) ph.log.Yellow("Project '%s' generated in directory '%s'!", projectOptions.Name, projectOptions.OutputDirectory)
ph.log.Yellow("To compile the project, run 'wails build' in the project directory.") ph.log.Yellow("To compile the project, run 'wails build' in the project directory.")
return nil return nil
} }
// // GenerateWindowsResourceConfig generates the default windows resource file
// func (ph *ProjectHelper) GenerateWindowsResourceConfig(po *ProjectOptions) {
// fmt.Println(buffer.String())
// // vi.Build()
// // vi.Walk()
// // err := vi.WriteSyso(outPath, runtime.GOARCH)
// }
// LoadProjectConfig loads the project config from the given directory // LoadProjectConfig loads the project config from the given directory
func (ph *ProjectHelper) LoadProjectConfig(dir string) (*ProjectOptions, error) { func (ph *ProjectHelper) LoadProjectConfig(dir string) (*ProjectOptions, error) {
po := ph.NewProjectOptions() po := ph.NewProjectOptions()
@ -245,38 +262,41 @@ func computeBinaryName(projectName string) string {
func processOutputDirectory(po *ProjectOptions) error { func processOutputDirectory(po *ProjectOptions) error {
// po.OutputDirectory // po.OutputDirectory
if po.OutputDirectory != "" { if po.OutputDirectory == "" {
projectPath, err := filepath.Abs(po.OutputDirectory)
if err != nil {
return err
}
if NewFSHelper().DirExists(projectPath) {
return fmt.Errorf("directory '%s' already exists", projectPath)
}
fmt.Println("Project Directory: " + po.OutputDirectory)
} else {
po.OutputDirectory = PromptRequired("Project directory name") po.OutputDirectory = PromptRequired("Project directory name")
} }
projectPath, err := filepath.Abs(po.OutputDirectory)
if err != nil {
return err
}
if NewFSHelper().DirExists(projectPath) {
return fmt.Errorf("directory '%s' already exists", projectPath)
}
fmt.Println("Project Directory: " + po.OutputDirectory)
return nil return nil
} }
func processProjectName(po *ProjectOptions) { func processProjectName(po *ProjectOptions) {
if po.Name == "" { if po.Name == "" {
po.Name = Prompt("The name of the project", "My Project") po.Name = Prompt("The name of the project", "My Project")
} else {
fmt.Println("Project Name: " + po.Name)
} }
fmt.Println("Project Name: " + po.Name)
} }
func processBinaryName(po *ProjectOptions) { func processBinaryName(po *ProjectOptions) {
if po.BinaryName == "" { if po.BinaryName == "" {
var binaryNameComputed = computeBinaryName(po.Name) var binaryNameComputed = computeBinaryName(po.Name)
po.BinaryName = Prompt("The output binary name", binaryNameComputed) po.BinaryName = Prompt("The output binary name", binaryNameComputed)
} else { if runtime.GOOS == "windows" {
fmt.Println("Output binary Name: " + po.BinaryName) if !strings.HasSuffix(po.BinaryName, ".exe") {
po.BinaryName += ".exe"
}
}
} }
fmt.Println("Output binary Name: " + po.BinaryName)
} }
func processTemplateMetadata(templateMetadata map[string]interface{}, po *ProjectOptions) error { func processTemplateMetadata(templateMetadata map[string]interface{}, po *ProjectOptions) error {

View File

@ -18,7 +18,6 @@ func Prompt(question string, defaultValue ...string) string {
if haveDefault { if haveDefault {
if len(answer) == 0 { if len(answer) == 0 {
answer = defaultValue[0] answer = defaultValue[0]
fmt.Println(" -> " + answer)
} }
} }
return answer return answer

View File

@ -3,6 +3,7 @@ package main
import ( import (
"fmt" "fmt"
"os" "os"
"runtime"
"github.com/leaanthony/spinner" "github.com/leaanthony/spinner"
"github.com/wailsapp/wails/cmd" "github.com/wailsapp/wails/cmd"
@ -93,13 +94,26 @@ func init() {
if debugMode { if debugMode {
buildMode = cmd.BuildModeDebug buildMode = cmd.BuildModeDebug
} }
if runtime.GOOS == "windows" {
if packageApp {
err = cmd.CheckWindres()
if err != nil {
return err
}
err = cmd.PackageApplication(projectOptions)
if err != nil {
return err
}
}
}
err = cmd.BuildApplication(projectOptions.BinaryName, forceRebuild, buildMode) err = cmd.BuildApplication(projectOptions.BinaryName, forceRebuild, buildMode)
if err != nil { if err != nil {
return err return err
} }
// Package application // Package application
if packageApp { if runtime.GOOS != "windows" && packageApp {
err = cmd.PackageApplication(projectOptions) err = cmd.PackageApplication(projectOptions)
if err != nil { if err != nil {
return err return err

2
go.mod
View File

@ -1,6 +1,7 @@
module github.com/wailsapp/wails module github.com/wailsapp/wails
require ( require (
github.com/akavel/rsrc v0.0.0-20170831122431-f6a15ece2cfd // indirect
github.com/dchest/cssmin v0.0.0-20151210170030-fb8d9b44afdc // indirect github.com/dchest/cssmin v0.0.0-20151210170030-fb8d9b44afdc // indirect
github.com/dchest/htmlmin v0.0.0-20150526090704-e254725e81ac github.com/dchest/htmlmin v0.0.0-20150526090704-e254725e81ac
github.com/dchest/jsmin v0.0.0-20160823214000-faeced883947 // indirect github.com/dchest/jsmin v0.0.0-20160823214000-faeced883947 // indirect
@ -8,6 +9,7 @@ require (
github.com/go-playground/colors v1.2.0 github.com/go-playground/colors v1.2.0
github.com/gorilla/websocket v1.4.0 github.com/gorilla/websocket v1.4.0
github.com/jackmordaunt/icns v1.0.0 github.com/jackmordaunt/icns v1.0.0
github.com/josephspurrier/goversioninfo v0.0.0-20190209210621-63e6d1acd3dd // indirect
github.com/leaanthony/mewn v0.9.0 github.com/leaanthony/mewn v0.9.0
github.com/leaanthony/slicer v1.0.0 github.com/leaanthony/slicer v1.0.0
github.com/leaanthony/spinner v0.5.0 github.com/leaanthony/spinner v0.5.0

5
go.sum
View File

@ -3,6 +3,8 @@ github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03
github.com/Masterminds/semver v1.4.2/go.mod h1:MB6lktGJrhw8PrUyiEoblNEGEQ+RzHPF078ddwwvV3Y= github.com/Masterminds/semver v1.4.2/go.mod h1:MB6lktGJrhw8PrUyiEoblNEGEQ+RzHPF078ddwwvV3Y=
github.com/Netflix/go-expect v0.0.0-20180615182759-c93bf25de8e8/go.mod h1:oX5x61PbNXchhh0oikYAH+4Pcfw5LKv21+Jnpr6r6Pc= github.com/Netflix/go-expect v0.0.0-20180615182759-c93bf25de8e8/go.mod h1:oX5x61PbNXchhh0oikYAH+4Pcfw5LKv21+Jnpr6r6Pc=
github.com/ajg/form v0.0.0-20160822230020-523a5da1a92f/go.mod h1:uL1WgH+h2mgNtvBq0339dVnzXdBETtL2LeUXaIv25UY= github.com/ajg/form v0.0.0-20160822230020-523a5da1a92f/go.mod h1:uL1WgH+h2mgNtvBq0339dVnzXdBETtL2LeUXaIv25UY=
github.com/akavel/rsrc v0.0.0-20170831122431-f6a15ece2cfd h1:yumR8733CaQ3P76MFbIbBKdrJmy4EqnQ5DIhqq8gq2Q=
github.com/akavel/rsrc v0.0.0-20170831122431-f6a15ece2cfd/go.mod h1:2+aQMrY0hBFBaIr2xxnZ/ctfwnYmMRMbTczYLAC34v4=
github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8=
github.com/cockroachdb/apd v1.1.0/go.mod h1:8Sl8LxpKi29FqWXR16WEFZRNSz3SoPzUzeMeY4+DwBQ= github.com/cockroachdb/apd v1.1.0/go.mod h1:8Sl8LxpKi29FqWXR16WEFZRNSz3SoPzUzeMeY4+DwBQ=
github.com/cockroachdb/cockroach-go v0.0.0-20181001143604-e0a95dfd547c/go.mod h1:XGLbWH/ujMcbPbhZq52Nv6UrCghb1yGn//133kEsvDk= github.com/cockroachdb/cockroach-go v0.0.0-20181001143604-e0a95dfd547c/go.mod h1:XGLbWH/ujMcbPbhZq52Nv6UrCghb1yGn//133kEsvDk=
@ -241,6 +243,8 @@ github.com/jmoiron/sqlx v0.0.0-20180614180643-0dae4fefe7c0/go.mod h1:IiEW3SEiiEr
github.com/joho/godotenv v1.2.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg= github.com/joho/godotenv v1.2.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg=
github.com/joho/godotenv v1.3.0 h1:Zjp+RcGpHhGlrMbJzXTrZZPrWj+1vfm90La1wgB6Bhc= github.com/joho/godotenv v1.3.0 h1:Zjp+RcGpHhGlrMbJzXTrZZPrWj+1vfm90La1wgB6Bhc=
github.com/joho/godotenv v1.3.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg= github.com/joho/godotenv v1.3.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg=
github.com/josephspurrier/goversioninfo v0.0.0-20190209210621-63e6d1acd3dd h1:KikNiFwUO3QLyeKyN4k9yBH9Pcu/gU/yficWi61cJIw=
github.com/josephspurrier/goversioninfo v0.0.0-20190209210621-63e6d1acd3dd/go.mod h1:eJTEwMjXb7kZ633hO3Ln9mBUCOjX2+FlTljvpl9SYdE=
github.com/karrick/godirwalk v1.7.5/go.mod h1:2c9FRhkDxdIbgkOnCEvnSWs71Bhugbl46shStcFDJ34= github.com/karrick/godirwalk v1.7.5/go.mod h1:2c9FRhkDxdIbgkOnCEvnSWs71Bhugbl46shStcFDJ34=
github.com/karrick/godirwalk v1.7.7/go.mod h1:2c9FRhkDxdIbgkOnCEvnSWs71Bhugbl46shStcFDJ34= github.com/karrick/godirwalk v1.7.7/go.mod h1:2c9FRhkDxdIbgkOnCEvnSWs71Bhugbl46shStcFDJ34=
github.com/karrick/godirwalk v1.7.8 h1:VfG72pyIxgtC7+3X9CMHI0AOl4LwyRAg98WAgsvffi8= github.com/karrick/godirwalk v1.7.8 h1:VfG72pyIxgtC7+3X9CMHI0AOl4LwyRAg98WAgsvffi8=
@ -261,6 +265,7 @@ github.com/leaanthony/spinner v0.5.0 h1:HQykt/iTy7fmINEREtRbWrt+8j4MxC8dtvWBxEWM
github.com/leaanthony/spinner v0.5.0/go.mod h1:8TSFz9SL1AUC4XSbEFYE6SfN5Mlus51qYluVGrie9ww= github.com/leaanthony/spinner v0.5.0/go.mod h1:8TSFz9SL1AUC4XSbEFYE6SfN5Mlus51qYluVGrie9ww=
github.com/leaanthony/synx v0.1.0 h1:R0lmg2w6VMb8XcotOwAe5DLyzwjLrskNkwU7LLWsyL8= github.com/leaanthony/synx v0.1.0 h1:R0lmg2w6VMb8XcotOwAe5DLyzwjLrskNkwU7LLWsyL8=
github.com/leaanthony/synx v0.1.0/go.mod h1:Iz7eybeeG8bdq640iR+CwYb8p+9EOsgMWghkSRyZcqs= github.com/leaanthony/synx v0.1.0/go.mod h1:Iz7eybeeG8bdq640iR+CwYb8p+9EOsgMWghkSRyZcqs=
github.com/leaanthony/wincursor v0.1.0 h1:Dsyp68QcF5cCs65AMBmxoYNEm0n8K7mMchG6a8fYxf8=
github.com/leaanthony/wincursor v0.1.0/go.mod h1:7TVwwrzSH/2Y9gLOGH+VhA+bZhoWXBRgbGNTMk+yimE= github.com/leaanthony/wincursor v0.1.0/go.mod h1:7TVwwrzSH/2Y9gLOGH+VhA+bZhoWXBRgbGNTMk+yimE=
github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=

File diff suppressed because one or more lines are too long