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 {
ldflags = ""
}
// Add windows flags
if runtime.GOOS == "windows" {
ldflags += "-H windowsgui "
}
ldflags += "-X github.com/wailsapp/wails.BuildMode=" + buildMode
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
func PackageApplication(projectOptions *ProjectOptions) error {
// 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.Start()
err := NewPackageHelper().Package(projectOptions)
@@ -134,6 +144,18 @@ func CheckMewn() (err error) {
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
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
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 {
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)
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":
return fmt.Errorf("linux is not supported at this time. Please see https://github.com/wailsapp/wails/issues/2")
default:
@@ -146,15 +146,63 @@ func (b *PackageHelper) packageOSX(po *ProjectOptions) error {
if err != nil {
return err
}
err = b.packageIcon(resourceDir)
err = b.packageIconOSX(resourceDir)
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
const appIconFilename = "appicon.png"
srcIcon := path.Join(b.fs.Cwd(), appIconFilename)
// Check if appicon.png exists
@@ -164,14 +212,22 @@ func (b *PackageHelper) packageIcon(resourceDir string) error {
iconfile := filepath.Join(b.getPackageFileBaseDir(), "icon.png")
iconData, err := ioutil.ReadFile(iconfile)
if err != nil {
return err
return "", err
}
err = ioutil.WriteFile(srcIcon, iconData, 0644)
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")
imageFile, err := os.Open(srcIcon)
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"
"io/ioutil"
"path/filepath"
"runtime"
"strings"
)
@@ -82,11 +83,27 @@ func (ph *ProjectHelper) GenerateProject(projectOptions *ProjectOptions) error {
if err != nil {
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("To compile the project, run 'wails build' in the project directory.")
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
func (ph *ProjectHelper) LoadProjectConfig(dir string) (*ProjectOptions, error) {
po := ph.NewProjectOptions()
@@ -245,38 +262,41 @@ func computeBinaryName(projectName string) string {
func processOutputDirectory(po *ProjectOptions) error {
// 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 {
if po.OutputDirectory == "" {
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
}
func processProjectName(po *ProjectOptions) {
if po.Name == "" {
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) {
if po.BinaryName == "" {
var binaryNameComputed = computeBinaryName(po.Name)
po.BinaryName = Prompt("The output binary name", binaryNameComputed)
} else {
fmt.Println("Output binary Name: " + po.BinaryName)
if runtime.GOOS == "windows" {
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 {

View File

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

View File

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