Compare commits

..

11 Commits

Author SHA1 Message Date
Lea Anthony
68cd6a7f0e Warn for unsupported platforms 2020-02-23 07:06:55 +11:00
Lea Anthony
e00ffdbeea Merge latest develop 2020-02-23 06:32:14 +11:00
konez2k
4eebbfd22c Merge branch 'develop' into master 2020-02-22 12:46:00 +01:00
konez2k
1998736baa check if xgo and docker are installed 2020-02-20 16:46:52 +01:00
konez2k
89579db7fa create build directory if not exists 2020-02-20 16:46:18 +01:00
konez2k
3679114445 add cross-platform build and packaging 2020-02-19 13:44:30 +01:00
konez2k
9e5dd0bc86 add FindFile to FSHelper 2020-02-19 13:43:29 +01:00
konez2k
8c9ca5be95 add arch to project options 2020-02-19 13:42:09 +01:00
konez2k
526136099b add cross-compile flag to build command 2020-02-18 20:26:26 +01:00
konez2k
f490bf8bc9 embed assets internally using mewn 2020-02-18 12:46:25 +01:00
konez2k
3edca62a38 cleanup unused imports 2020-02-18 12:44:11 +01:00
31 changed files with 1729 additions and 2023 deletions

View File

@@ -23,7 +23,3 @@ Wails is what it is because of the time and effort given by these great people.
* [Jack Mordaunt](https://github.com/JackMordaunt) * [Jack Mordaunt](https://github.com/JackMordaunt)
* [Michael Hipp](https://github.com/MichaelHipp) * [Michael Hipp](https://github.com/MichaelHipp)
* [Travis McLane](https://github.com/tmclane) * [Travis McLane](https://github.com/tmclane)
* [Reuben Thomas-Davis](https://github.com/Rested)
* [Jarek](https://github.com/Jarek-SRT)
* [Konez2k](https://github.com/konez2k)
* [msms](https://github.com/sayuthisobri)

View File

@@ -57,7 +57,7 @@ _Ubuntu: 16.04, 18.04, 19.04_
_Also succesfully tested on: Zorin 15, Parrot 4.7, Linuxmint 19, Elementary 5, Kali, Neon_ _Also succesfully tested on: Zorin 15, Parrot 4.7, Linuxmint 19, Elementary 5, Kali, Neon_
#### Arch Linux / ArchLabs #### Arch Linux
`sudo pacman -S webkit2gtk gtk3` `sudo pacman -S webkit2gtk gtk3`

View File

@@ -1,4 +1,4 @@
// +build linux darwin !windows // +build +linux +darwin !windows
package wails package wails

File diff suppressed because one or more lines are too long

View File

@@ -117,10 +117,10 @@ func (fs *FSHelper) RemoveFile(filename string) error {
} }
// RemoveFiles removes the given filenames // RemoveFiles removes the given filenames
func (fs *FSHelper) RemoveFiles(files []string, continueOnError bool) error { func (fs *FSHelper) RemoveFiles(files []string) error {
for _, filename := range files { for _, filename := range files {
err := os.Remove(filename) err := os.Remove(filename)
if err != nil && !continueOnError { if err != nil {
return err return err
} }
} }

View File

@@ -5,10 +5,8 @@ import (
"io/ioutil" "io/ioutil"
"os" "os"
"os/exec" "os/exec"
"os/user"
"path/filepath" "path/filepath"
"runtime" "runtime"
"strconv"
"strings" "strings"
"time" "time"
@@ -83,118 +81,44 @@ func EmbedAssets() ([]string, error) {
return targetFiles, nil return targetFiles, nil
} }
func InitializeCrossCompilation(verbose bool) error { // BuildApplication will attempt to build the project based on the given inputs
// Check Docker func BuildApplication(binaryName string, forceRebuild bool, buildMode string, packageApp bool, projectOptions *ProjectOptions) error {
if err := CheckIfInstalled("docker"); err != nil {
return err
}
var packSpinner *spinner.Spinner if buildMode == BuildModeBridge && projectOptions.CrossCompile {
if !verbose {
packSpinner = spinner.New("Pulling wailsapp/xgo:latest docker image... (may take a while)")
packSpinner.SetSpinSpeed(50)
packSpinner.Start()
} else {
println("Pulling wailsapp/xgo:latest docker image... (may take a while)")
}
err := NewProgramHelper(verbose).RunCommandArray([]string{"docker",
"pull", "wailsapp/xgo:latest"})
if err != nil {
if packSpinner != nil {
packSpinner.Error()
}
return err
}
if packSpinner != nil {
packSpinner.Success()
}
return nil
}
// BuildDocker builds the project using the cross compiling wailsapp/xgo:latest container
func BuildDocker(binaryName string, buildMode string, projectOptions *ProjectOptions) error {
var packSpinner *spinner.Spinner
if buildMode == BuildModeBridge {
return fmt.Errorf("you cant serve the application in cross-compilation") return fmt.Errorf("you cant serve the application in cross-compilation")
} }
// Check build directory // Generate Windows assets if needed
buildDirectory := filepath.Join(fs.Cwd(), "build") if projectOptions.Platform == "windows" {
if !fs.DirExists(buildDirectory) { cleanUp := !packageApp
fs.MkDir(buildDirectory) err := NewPackageHelper(projectOptions.Platform).PackageWindows(projectOptions, cleanUp)
} if err != nil {
return err
buildCommand := slicer.String()
userid := 1000
user, _ := user.Current()
if i, err := strconv.Atoi(user.Uid); err == nil {
userid = i
}
for _, arg := range []string{
"docker",
"run",
"--rm",
"-v", fmt.Sprintf("%s:/build", filepath.Join(fs.Cwd(), "build")),
"-v", fmt.Sprintf("%s:/source", fs.Cwd()),
"-e", fmt.Sprintf("LOCAL_USER_ID=%v", userid),
"-e", fmt.Sprintf("FLAG_LDFLAGS=%s", ldFlags(projectOptions, buildMode)),
"-e", "FLAG_V=false",
"-e", "FLAG_X=false",
"-e", "FLAG_RACE=false",
"-e", "FLAG_BUILDMODE=default",
"-e", "FLAG_TRIMPATH=false",
"-e", fmt.Sprintf("TARGETS=%s", projectOptions.Platform+"/"+projectOptions.Architecture),
"-e", "GOPROXY=",
"-e", "GO111MODULE=on",
"wailsapp/xgo:latest",
".",
} {
buildCommand.Add(arg)
}
compileMessage := fmt.Sprintf(
"Packing + Compiling project for %s/%s using docker image wailsapp/xgo:latest",
projectOptions.Platform, projectOptions.Architecture)
if buildMode == BuildModeDebug {
compileMessage += " (Debug Mode)"
}
if !projectOptions.Verbose {
packSpinner = spinner.New(compileMessage + "...")
packSpinner.SetSpinSpeed(50)
packSpinner.Start()
} else {
println(compileMessage)
}
err := NewProgramHelper(projectOptions.Verbose).RunCommandArray(buildCommand.AsSlice())
if err != nil {
if packSpinner != nil {
packSpinner.Error()
} }
return err
}
if packSpinner != nil {
packSpinner.Success()
} }
return nil if projectOptions.CrossCompile {
} // Check build directory
buildDirectory := filepath.Join(fs.Cwd(), "build")
if !fs.DirExists(buildDirectory) {
fs.MkDir(buildDirectory)
}
// BuildNative builds on the target platform itself. // Check Docker
func BuildNative(binaryName string, forceRebuild bool, buildMode string, projectOptions *ProjectOptions) error { if err := CheckIfInstalled("docker"); err != nil {
return err
}
// Check Mewn is installed // Check xgo
if err := CheckMewn(projectOptions.Verbose); err != nil { if err := CheckIfInstalled("xgo"); err != nil {
return err return err
} }
} else {
if err := CheckWindres(); err != nil { // Check Mewn is installed
return err err := CheckMewn(projectOptions.Verbose)
if err != nil {
return err
}
} }
compileMessage := "Packing + Compiling project" compileMessage := "Packing + Compiling project"
@@ -212,18 +136,40 @@ func BuildNative(binaryName string, forceRebuild bool, buildMode string, project
println(compileMessage) println(compileMessage)
} }
buildCommand := slicer.String() // embed resources
buildCommand.Add("go") targetFiles, err := EmbedAssets()
if err != nil {
return err
}
// cleanup temporary embedded assets
defer func() {
for _, filename := range targetFiles {
if err := os.Remove(filename); err != nil {
fmt.Println(err)
}
}
}()
buildCommand := slicer.String()
if projectOptions.CrossCompile {
buildCommand.Add("xgo")
} else {
buildCommand.Add("mewn")
}
buildCommand.Add("build")
if buildMode == BuildModeBridge { if buildMode == BuildModeBridge {
// Ignore errors // Ignore errors
buildCommand.Add("-i") buildCommand.Add("-i")
} }
if binaryName != "" { if !projectOptions.CrossCompile {
buildCommand.Add("build")
}
if binaryName != "" && !projectOptions.CrossCompile {
// Alter binary name based on OS // Alter binary name based on OS
switch projectOptions.Platform { switch runtime.GOOS {
case "windows": case "windows":
if !strings.HasSuffix(binaryName, ".exe") { if !strings.HasSuffix(binaryName, ".exe") {
binaryName += ".exe" binaryName += ".exe"
@@ -233,21 +179,46 @@ func BuildNative(binaryName string, forceRebuild bool, buildMode string, project
binaryName = strings.TrimSuffix(binaryName, ".exe") binaryName = strings.TrimSuffix(binaryName, ".exe")
} }
} }
buildCommand.Add("-o", filepath.Join("build", binaryName)) buildCommand.Add("-o", binaryName)
} }
// If we are forcing a rebuild // If we are forcing a rebuild
if forceRebuild { if forceRebuild && !projectOptions.CrossCompile {
buildCommand.Add("-a") buildCommand.Add("-a")
} }
buildCommand.AddSlice([]string{"-ldflags", ldFlags(projectOptions, buildMode)}) // Setup ld flags
ldflags := "-w -s "
if projectOptions.Verbose { if buildMode == BuildModeDebug {
fmt.Printf("Command: %v\n", buildCommand.AsSlice()) ldflags = ""
} }
err := NewProgramHelper(projectOptions.Verbose).RunCommandArray(buildCommand.AsSlice()) // Add windows flags
if projectOptions.Platform == "windows" && buildMode == BuildModeProd {
ldflags += "-H windowsgui "
}
ldflags += "-X github.com/wailsapp/wails.BuildMode=" + buildMode
// If we wish to generate typescript
if projectOptions.typescriptDefsFilename != "" {
cwd, err := os.Getwd()
if err != nil {
return err
}
filename := filepath.Join(cwd, projectOptions.FrontEnd.Dir, projectOptions.typescriptDefsFilename)
ldflags += " -X github.com/wailsapp/wails/lib/binding.typescriptDefinitionFilename=" + filename
}
buildCommand.AddSlice([]string{"-ldflags", ldflags})
if projectOptions.CrossCompile {
buildCommand.Add("-targets", projectOptions.Platform+"/"+projectOptions.Architecture)
buildCommand.Add("-out", "build/"+binaryName)
buildCommand.Add("./")
}
err = NewProgramHelper(projectOptions.Verbose).RunCommandArray(buildCommand.AsSlice())
if err != nil { if err != nil {
if packSpinner != nil { if packSpinner != nil {
packSpinner.Error() packSpinner.Error()
@@ -258,55 +229,7 @@ func BuildNative(binaryName string, forceRebuild bool, buildMode string, project
packSpinner.Success() packSpinner.Success()
} }
return nil // packageApp
}
// BuildApplication will attempt to build the project based on the given inputs
func BuildApplication(binaryName string, forceRebuild bool, buildMode string, packageApp bool, projectOptions *ProjectOptions) error {
var err error
// embed resources
targetFiles, err := EmbedAssets()
if err != nil {
return err
}
if projectOptions.CrossCompile {
if err := InitializeCrossCompilation(projectOptions.Verbose); err != nil {
return err
}
}
helper := NewPackageHelper(projectOptions.Platform)
// Generate windows resources
if projectOptions.Platform == "windows" {
if err := helper.PackageWindows(projectOptions, false); err != nil {
return err
}
}
// cleanup temporary embedded assets
defer func() {
for _, filename := range targetFiles {
if err := os.Remove(filename); err != nil {
fmt.Println(err)
}
}
if projectOptions.Platform == "windows" {
helper.CleanWindows(projectOptions)
}
}()
if projectOptions.CrossCompile {
err = BuildDocker(binaryName, buildMode, projectOptions)
} else {
err = BuildNative(binaryName, forceRebuild, buildMode, projectOptions)
}
if err != nil {
return err
}
if packageApp { if packageApp {
err = PackageApplication(projectOptions) err = PackageApplication(projectOptions)
if err != nil { if err != nil {
@@ -319,11 +242,22 @@ func BuildApplication(binaryName string, forceRebuild bool, buildMode string, pa
// PackageApplication will attempt to package the application in a platform dependent way // PackageApplication will attempt to package the application in a platform dependent way
func PackageApplication(projectOptions *ProjectOptions) error { func PackageApplication(projectOptions *ProjectOptions) error {
// Package app
message := "Generating .app"
if projectOptions.Platform == "windows" {
err := CheckWindres()
if err != nil {
return err
}
message = "Generating resource bundle"
}
var packageSpinner *spinner.Spinner var packageSpinner *spinner.Spinner
if projectOptions.Verbose { if !projectOptions.Verbose {
packageSpinner = spinner.New("Packaging application...") packageSpinner = spinner.New(message)
packageSpinner.SetSpinSpeed(50) packageSpinner.SetSpinSpeed(50)
packageSpinner.Start() packageSpinner.Start()
} else {
println(message)
} }
err := NewPackageHelper(projectOptions.Platform).Package(projectOptions) err := NewPackageHelper(projectOptions.Platform).Package(projectOptions)
@@ -388,7 +322,7 @@ func CheckMewn(verbose bool) (err error) {
// CheckWindres checks if Windres is installed and if not, aborts // CheckWindres checks if Windres is installed and if not, aborts
func CheckWindres() (err error) { func CheckWindres() (err error) {
if runtime.GOOS != "windows" { // FIXME: Handle windows cross-compile for windows! if runtime.GOOS != "windows" {
return nil return nil
} }
programHelper := NewProgramHelper() programHelper := NewProgramHelper()
@@ -530,7 +464,7 @@ func ServeProject(projectOptions *ProjectOptions, logger *Logger) error {
time.Sleep(2 * time.Second) time.Sleep(2 * time.Second)
logger.Green(">>>>> To connect, you will need to run '" + projectOptions.FrontEnd.Serve + "' in the '" + projectOptions.FrontEnd.Dir + "' directory <<<<<") logger.Green(">>>>> To connect, you will need to run '" + projectOptions.FrontEnd.Serve + "' in the '" + projectOptions.FrontEnd.Dir + "' directory <<<<<")
}() }()
location, err := filepath.Abs(filepath.Join("build", projectOptions.BinaryName)) location, err := filepath.Abs(projectOptions.BinaryName)
if err != nil { if err != nil {
return err return err
} }
@@ -546,28 +480,3 @@ func ServeProject(projectOptions *ProjectOptions, logger *Logger) error {
return nil return nil
} }
func ldFlags(po *ProjectOptions, buildMode string) string {
// Setup ld flags
ldflags := "-w -s "
if buildMode == BuildModeDebug {
ldflags = ""
}
// Add windows flags
if po.Platform == "windows" && buildMode == BuildModeProd {
ldflags += "-H windowsgui "
}
ldflags += "-X github.com/wailsapp/wails.BuildMode=" + buildMode
// If we wish to generate typescript
if po.typescriptDefsFilename != "" {
cwd, err := os.Getwd()
if err == nil {
filename := filepath.Join(cwd, po.FrontEnd.Dir, po.typescriptDefsFilename)
ldflags += " -X github.com/wailsapp/wails/lib/binding.typescriptDefinitionFilename=" + filename
}
}
return ldflags
}

View File

@@ -53,12 +53,6 @@ const (
Deepin Deepin
// Raspbian distribution // Raspbian distribution
Raspbian Raspbian
// Tumbleweed (OpenSUSE) distribution
Tumbleweed
// Leap (OpenSUSE) distribution
Leap
// ArchLabs distribution
ArchLabs
) )
// DistroInfo contains all the information relating to a linux distribution // DistroInfo contains all the information relating to a linux distribution
@@ -116,15 +110,13 @@ func parseOsRelease(osRelease string) *DistroInfo {
} }
// Check distro name against list of distros // Check distro name against list of distros
switch strings.ToLower(osID) { switch osID {
case "fedora": case "fedora":
result.Distribution = Fedora result.Distribution = Fedora
case "centos": case "centos":
result.Distribution = CentOS result.Distribution = CentOS
case "arch": case "arch":
result.Distribution = Arch result.Distribution = Arch
case "archlabs":
result.Distribution = ArchLabs
case "debian": case "debian":
result.Distribution = Debian result.Distribution = Debian
case "ubuntu": case "ubuntu":
@@ -155,10 +147,6 @@ func parseOsRelease(osRelease string) *DistroInfo {
result.Distribution = Deepin result.Distribution = Deepin
case "raspbian": case "raspbian":
result.Distribution = Raspbian result.Distribution = Raspbian
case "opensuse-tumbleweed":
result.Distribution = Tumbleweed
case "opensuse-leap":
result.Distribution = Leap
default: default:
result.Distribution = Unknown result.Distribution = Unknown
} }

View File

@@ -22,25 +22,5 @@ UBUNTU_CODENAME=bionic
if result.Distribution != Ubuntu { if result.Distribution != Ubuntu {
t.Errorf("expected 'Ubuntu' ID but got '%d'", result.Distribution) t.Errorf("expected 'Ubuntu' ID but got '%d'", result.Distribution)
} }
}
func TestTumbleweedDetection(t *testing.T) {
osrelease := `
NAME="openSUSE Tumbleweed"
# VERSION="20200414"
ID="opensuse-tumbleweed"
ID_LIKE="opensuse suse"
VERSION_ID="20200414"
PRETTY_NAME="openSUSE Tumbleweed"
ANSI_COLOR="0;32"
CPE_NAME="cpe:/o:opensuse:tumbleweed:20200414"
BUG_REPORT_URL="https://bugs.opensuse.org"
HOME_URL="https://www.opensuse.org/"
LOGO="distributor-logo"
`
result := parseOsRelease(osrelease)
if result.Distribution != Tumbleweed {
t.Errorf("expected 'Tumbleweed' ID but got '%d'", result.Distribution)
}
} }

View File

@@ -176,15 +176,6 @@ distributions:
gccversioncommand: *gccdumpversion gccversioncommand: *gccdumpversion
programs: *archdefaultprograms programs: *archdefaultprograms
libraries: *archdefaultlibraries libraries: *archdefaultlibraries
archlabs:
id: archlabs
releases:
default:
version: default
name: ArchLabs
gccversioncommand: *gccdumpversion
programs: *archdefaultprograms
libraries: *archdefaultlibraries
manjaro: manjaro:
id: manjaro id: manjaro
releases: releases:
@@ -232,32 +223,3 @@ distributions:
gccversioncommand: *gccdumpfullversion gccversioncommand: *gccdumpfullversion
programs: *debiandefaultprograms programs: *debiandefaultprograms
libraries: *debiandefaultlibraries libraries: *debiandefaultlibraries
opensuse-tumbleweed:
id: opensuse-tumbleweed
releases:
default:
version: default
name: openSUSE Tumbleweed
gccversioncommand: *gccdumpfullversion
programs: &opensusedefaultprograms
- name: gcc
help: Please install with `sudo zypper in gcc-c++` and try again
- name: pkg-config
help: Please install with `sudo zypper in pkgconf-pkg-config` and try again
- name: npm
help: Please install `sudo zypper in nodejs` and try again
libraries: &opensusedefaultlibraries
- name: gtk3-devel
help: Please install with `sudo zypper in gtk3-devel` and try again
- name: webkit2gtk3-devel
help: Please install with `sudo zypper in webkit2gtk3-devel` and try again
opensuse-leap:
id: opensuse-leap
releases:
default:
version: default
name: openSUSE Leap
gccversioncommand: *gccdumpfullversion
programs: *opensusedefaultprograms
libraries: *opensusedefaultlibraries

View File

@@ -1,12 +1,9 @@
package cmd package cmd
import ( import (
"bufio"
"bytes" "bytes"
"encoding/binary"
"fmt" "fmt"
"image" "image"
"image/png"
"io/ioutil" "io/ioutil"
"os" "os"
"path" "path"
@@ -16,8 +13,6 @@ import (
"text/template" "text/template"
"time" "time"
"golang.org/x/image/draw"
"github.com/jackmordaunt/icns" "github.com/jackmordaunt/icns"
) )
@@ -60,87 +55,6 @@ func newPlistData(title, exe, packageID, version, author string) *plistData {
} }
} }
type windowsIconHeader struct {
_ uint16
imageType uint16
imageCount uint16
width uint8
height uint8
colours uint8
_ uint8
planes uint16
bpp uint16
size uint32
offset uint32
}
func generateWindowsIcon(pngFilename string, iconfile string) error {
header := &windowsIconHeader{
imageType: 1,
imageCount: 1,
bpp: 32,
planes: 1,
offset: 22,
width: 0,
height: 0,
}
// Load png
pngfile, err := os.Open(pngFilename)
if err != nil {
return err
}
defer pngfile.Close()
// Decode to internal image
pngdata, err := png.Decode(pngfile)
if err != nil {
return err
}
// Scale to 256*256
rect := image.Rect(0, 0, 255, 255)
rawdata := image.NewRGBA(rect)
scale := draw.ApproxBiLinear
scale.Scale(rawdata, rect, pngdata, pngdata.Bounds(), draw.Over, nil)
// Convert back to PNG
icondata := new(bytes.Buffer)
writer := bufio.NewWriter(icondata)
err = png.Encode(writer, rawdata)
if err != nil {
return err
}
err = writer.Flush()
if err != nil {
return err
}
// Save size of PNG data
header.size = uint32(len(icondata.Bytes()))
// Open icon file for writing
outfilename := filepath.Join(filepath.Dir(pngFilename), iconfile)
outfile, err := os.Create(outfilename)
if err != nil {
return err
}
defer outfile.Close()
// Write out the header
err = binary.Write(outfile, binary.LittleEndian, header)
if err != nil {
return err
}
// Write out the image data
_, err = outfile.Write(icondata.Bytes())
if err != nil {
return err
}
return nil
}
func defaultString(val string, defaultVal string) string { func defaultString(val string, defaultVal string) string {
if val != "" { if val != "" {
return val return val
@@ -158,23 +72,29 @@ func (b *PackageHelper) getPackageFileBaseDir() string {
func (b *PackageHelper) Package(po *ProjectOptions) error { func (b *PackageHelper) Package(po *ProjectOptions) error {
switch b.platform { switch b.platform {
case "darwin": case "darwin":
// Check we have the exe
if !b.fs.FileExists(po.BinaryName) {
// Check cross-compiled application
if b.platform == runtime.GOOS {
return fmt.Errorf("cannot bundle non-existent binary file '%s'. Please build with 'wails build' first", po.BinaryName)
}
if _, err := b.fs.FindFile(path.Join(b.fs.Cwd(), "build"), "darwin"); err != nil {
return fmt.Errorf("cannot bundle non-existent cross-compiled binary file '%s'. Please build with 'wails build -x darwin' first", po.BinaryName)
}
}
return b.packageOSX(po) return b.packageOSX(po)
case "windows": case "windows":
return b.PackageWindows(po, true) return b.PackageWindows(po, false)
case "linux": case "linux":
return b.packageLinux(po) return fmt.Errorf("linux is not supported at this time. Please see https://github.com/wailsapp/wails/issues/2")
default: default:
return fmt.Errorf("platform '%s' not supported for bundling yet", b.platform) return fmt.Errorf("platform '%s' not supported for bundling yet", b.platform)
} }
} }
func (b *PackageHelper) packageLinux(po *ProjectOptions) error {
return nil
}
// Package the application for OSX // Package the application for OSX
func (b *PackageHelper) packageOSX(po *ProjectOptions) error { func (b *PackageHelper) packageOSX(po *ProjectOptions) error {
build := path.Join(b.fs.Cwd(), "build")
system := NewSystemHelper() system := NewSystemHelper()
config, err := system.LoadConfig() config, err := system.LoadConfig()
@@ -191,25 +111,34 @@ func (b *PackageHelper) packageOSX(po *ProjectOptions) error {
appname := po.Name + ".app" appname := po.Name + ".app"
// Check binary exists // Check binary exists
source := path.Join(build, exe) source := path.Join(b.fs.Cwd(), exe)
if po.CrossCompile == true {
file, err := b.fs.FindFile(build, "darwin") if b.platform != runtime.GOOS {
file, err := b.fs.FindFile(path.Join(b.fs.Cwd(), "build"), "darwin")
if err != nil { if err != nil {
return err return err
} }
source = path.Join(build, file)
// rename to exe
if err := os.Rename(path.Join(b.fs.Cwd(), "build", file), path.Join(b.fs.Cwd(), "build", exe)); err != nil {
return err
}
source = path.Join(b.fs.Cwd(), "build", exe)
} }
if !b.fs.FileExists(source) { if !b.fs.FileExists(source) {
// We need to build! // We need to build!
return fmt.Errorf("Target '%s' not available. Has it been compiled yet?", source) return fmt.Errorf("Target '%s' not available. Has it been compiled yet?", exe)
} }
// Remove the existing package // Remove the existing package
os.RemoveAll(appname) os.RemoveAll(appname)
exeDir := path.Join(build, appname, "/Contents/MacOS") exeDir := path.Join(b.fs.Cwd(), appname, "/Contents/MacOS")
b.fs.MkDirs(exeDir, 0755) b.fs.MkDirs(exeDir, 0755)
resourceDir := path.Join(build, appname, "/Contents/Resources") resourceDir := path.Join(b.fs.Cwd(), appname, "/Contents/Resources")
b.fs.MkDirs(resourceDir, 0755) b.fs.MkDirs(resourceDir, 0755)
tmpl := template.New("infoPlist") tmpl := template.New("infoPlist")
plistFile := filepath.Join(b.getPackageFileBaseDir(), "info.plist") plistFile := filepath.Join(b.getPackageFileBaseDir(), "info.plist")
@@ -225,7 +154,7 @@ func (b *PackageHelper) packageOSX(po *ProjectOptions) error {
if err != nil { if err != nil {
return err return err
} }
filename := path.Join(build, appname, "Contents", "Info.plist") filename := path.Join(b.fs.Cwd(), appname, "Contents", "Info.plist")
err = ioutil.WriteFile(filename, tpl.Bytes(), 0644) err = ioutil.WriteFile(filename, tpl.Bytes(), 0644)
if err != nil { if err != nil {
return err return err
@@ -246,37 +175,22 @@ func (b *PackageHelper) packageOSX(po *ProjectOptions) error {
return err return err
} }
// CleanWindows removes any windows related files found in the directory
func (b *PackageHelper) CleanWindows(po *ProjectOptions) {
pdir := b.fs.Cwd()
basename := strings.TrimSuffix(po.BinaryName, ".exe")
exts := []string{".ico", ".exe.manifest", ".rc", "-res.syso"}
rsrcs := []string{}
for _, ext := range exts {
rsrcs = append(rsrcs, filepath.Join(pdir, basename+ext))
}
b.fs.RemoveFiles(rsrcs, true)
}
// PackageWindows packages the application for windows platforms // PackageWindows packages the application for windows platforms
func (b *PackageHelper) PackageWindows(po *ProjectOptions, cleanUp bool) error { func (b *PackageHelper) PackageWindows(po *ProjectOptions, cleanUp bool) error {
outputDir := b.fs.Cwd()
basename := strings.TrimSuffix(po.BinaryName, ".exe") basename := strings.TrimSuffix(po.BinaryName, ".exe")
// Copy default icon if needed // Copy icon
icon, err := b.copyIcon() tgtIconFile := filepath.Join(b.fs.Cwd(), basename+".ico")
if err != nil { if !b.fs.FileExists(tgtIconFile) {
return err srcIconfile := filepath.Join(b.getPackageFileBaseDir(), "wails.ico")
} err := b.fs.CopyFile(srcIconfile, tgtIconFile)
if err != nil {
// Generate icon from PNG return err
err = generateWindowsIcon(icon, po.BinaryName+".ico") }
if err != nil {
return err
} }
// Copy manifest // Copy manifest
tgtManifestFile := filepath.Join(outputDir, basename+".exe.manifest") tgtManifestFile := filepath.Join(b.fs.Cwd(), basename+".exe.manifest")
if !b.fs.FileExists(tgtManifestFile) { if !b.fs.FileExists(tgtManifestFile) {
srcManifestfile := filepath.Join(b.getPackageFileBaseDir(), "wails.exe.manifest") srcManifestfile := filepath.Join(b.getPackageFileBaseDir(), "wails.exe.manifest")
err := b.fs.CopyFile(srcManifestfile, tgtManifestFile) err := b.fs.CopyFile(srcManifestfile, tgtManifestFile)
@@ -286,7 +200,7 @@ func (b *PackageHelper) PackageWindows(po *ProjectOptions, cleanUp bool) error {
} }
// Copy rc file // Copy rc file
tgtRCFile := filepath.Join(outputDir, basename+".rc") tgtRCFile := filepath.Join(b.fs.Cwd(), basename+".rc")
if !b.fs.FileExists(tgtRCFile) { if !b.fs.FileExists(tgtRCFile) {
srcRCfile := filepath.Join(b.getPackageFileBaseDir(), "wails.rc") srcRCfile := filepath.Join(b.getPackageFileBaseDir(), "wails.rc")
rcfilebytes, err := ioutil.ReadFile(srcRCfile) rcfilebytes, err := ioutil.ReadFile(srcRCfile)
@@ -301,17 +215,23 @@ func (b *PackageHelper) PackageWindows(po *ProjectOptions, cleanUp bool) error {
} }
// Build syso // Build syso
sysofile := filepath.Join(outputDir, basename+"-res.syso") sysofile := filepath.Join(b.fs.Cwd(), basename+"-res.syso")
// cross-compile // cross-compile
if b.platform != runtime.GOOS { if b.platform != runtime.GOOS {
folder, err := os.Getwd()
if err != nil {
return err
}
args := []string{ args := []string{
"docker", "run", "--rm", "docker", "run", "--rm",
"-v", outputDir + ":/build", "-v", folder + ":/build",
"--entrypoint", "/bin/sh", "--entrypoint", "/bin/sh",
"wailsapp/xgo:latest", "techknowlogick/xgo",
"-c", "/usr/bin/x86_64-w64-mingw32-windres -o /build/" + basename + "-res.syso /build/" + basename + ".rc", "-c", "/usr/bin/x86_64-w64-mingw32-windres -o /build/" + basename + "-res.syso /build/" + basename + ".rc",
} }
if err := NewProgramHelper().RunCommandArray(args); err != nil { if err := NewProgramHelper().RunCommandArray(args); err != nil {
return err return err
} }
@@ -328,10 +248,20 @@ func (b *PackageHelper) PackageWindows(po *ProjectOptions, cleanUp bool) error {
return err return err
} }
} }
// clean up
if cleanUp {
filesToDelete := []string{tgtIconFile, tgtManifestFile, tgtRCFile, sysofile}
err := b.fs.RemoveFiles(filesToDelete)
if err != nil {
return err
}
}
return nil return nil
} }
func (b *PackageHelper) copyIcon() (string, error) { 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"
@@ -356,7 +286,7 @@ func (b *PackageHelper) copyIcon() (string, error) {
func (b *PackageHelper) packageIconOSX(resourceDir string) error { func (b *PackageHelper) packageIconOSX(resourceDir string) error {
srcIcon, err := b.copyIcon() srcIcon, err := b.copyIcon(resourceDir)
if err != nil { if err != nil {
return err return err
} }

View File

@@ -1,6 +1,5 @@
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0"><dict> <plist version="1.0"><dict>
<key>CFBundlePackageType</key><string>APPL</string>
<key>CFBundleName</key><string>{{.Title}}</string> <key>CFBundleName</key><string>{{.Title}}</string>
<key>CFBundleExecutable</key><string>{{.Exe}}</string> <key>CFBundleExecutable</key><string>{{.Exe}}</string>
<key>CFBundleIdentifier</key><string>{{.PackageID}}</string> <key>CFBundleIdentifier</key><string>{{.PackageID}}</string>

View File

@@ -138,11 +138,11 @@ func (p *ProgramHelper) RunCommand(command string) error {
// RunCommandArray runs the command specified in the array // RunCommandArray runs the command specified in the array
func (p *ProgramHelper) RunCommandArray(args []string, dir ...string) error { func (p *ProgramHelper) RunCommandArray(args []string, dir ...string) error {
programCommand := args[0] program := args[0]
// TODO: Run FindProgram here and get the full path to the exe // TODO: Run FindProgram here and get the full path to the exe
program, err := exec.LookPath(programCommand) program, err := exec.LookPath(program)
if err != nil { if err != nil {
fmt.Printf("ERROR: Looks like '%s' isn't installed. Please install and try again.", programCommand) fmt.Printf("ERROR: Looks like '%s' isn't installed. Please install and try again.", program)
return err return err
} }

View File

@@ -6,6 +6,7 @@ import (
"io/ioutil" "io/ioutil"
"os" "os"
"path/filepath" "path/filepath"
"runtime"
"sort" "sort"
"strings" "strings"
@@ -157,10 +158,10 @@ type ProjectOptions struct {
selectedTemplate *TemplateDetails selectedTemplate *TemplateDetails
WailsVersion string WailsVersion string
typescriptDefsFilename string typescriptDefsFilename string
Verbose bool `json:"-"` Verbose bool `json:"-"`
CrossCompile bool CrossCompile bool `json:"-"`
Platform string Platform string `json:"-"`
Architecture string Architecture string `json:"-"`
} }
// Defaults sets the default project template // Defaults sets the default project template
@@ -335,6 +336,11 @@ 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)
if runtime.GOOS == "windows" {
if !strings.HasSuffix(po.BinaryName, ".exe") {
po.BinaryName += ".exe"
}
}
} }
fmt.Println("Output binary Name: " + po.BinaryName) fmt.Println("Output binary Name: " + po.BinaryName)
} }

View File

@@ -276,9 +276,9 @@ func CheckDependencies(logger *Logger) (bool, error) {
switch distroInfo.Distribution { switch distroInfo.Distribution {
case Ubuntu, Debian, Zorin, Parrot, Linuxmint, Elementary, Kali, Neon, Deepin, Raspbian: case Ubuntu, Debian, Zorin, Parrot, Linuxmint, Elementary, Kali, Neon, Deepin, Raspbian:
libraryChecker = DpkgInstalled libraryChecker = DpkgInstalled
case Arch, ArcoLinux, ArchLabs, Manjaro, ManjaroARM: case Arch, ArcoLinux, Manjaro, ManjaroARM:
libraryChecker = PacmanInstalled libraryChecker = PacmanInstalled
case CentOS, Fedora, Tumbleweed, Leap: case CentOS, Fedora:
libraryChecker = RpmInstalled libraryChecker = RpmInstalled
case Gentoo: case Gentoo:
libraryChecker = EqueryInstalled libraryChecker = EqueryInstalled

View File

@@ -4,12 +4,12 @@
"version": "0.1.0", "version": "0.1.0",
"private": true, "private": true,
"dependencies": { "dependencies": {
"core-js": "^3.6.4", "core-js": "^3.1.4",
"react": "^16.13.0", "react": "^16.8.6",
"react-dom": "^16.13.0", "react-dom": "^16.8.6",
"wails-react-scripts": "3.0.1-2", "wails-react-scripts": "3.0.1-2",
"react-modal": "3.11.2", "react-modal": "3.8.1",
"@wailsapp/runtime": "^1.0.10" "@wailsapp/runtime": "^1.0.0"
}, },
"scripts": { "scripts": {
"start": "react-scripts start", "start": "react-scripts start",

View File

@@ -8,21 +8,21 @@
"lint": "vue-cli-service lint" "lint": "vue-cli-service lint"
}, },
"dependencies": { "dependencies": {
"core-js": "^3.6.4", "core-js": "^3.6.1",
"regenerator-runtime": "^0.13.3", "regenerator-runtime": "^0.13.3",
"vue": "^2.6.11", "vue": "^2.5.22",
"@wailsapp/runtime": "^1.0.10" "@wailsapp/runtime": "^1.0.0"
}, },
"devDependencies": { "devDependencies": {
"@vue/cli-plugin-babel": "^4.2.3", "@vue/cli-plugin-babel": "^3.4.0",
"@vue/cli-plugin-eslint": "^4.2.3", "@vue/cli-plugin-eslint": "^3.4.0",
"@vue/cli-service": "^4.2.3", "@vue/cli-service": "^3.4.0",
"babel-eslint": "^10.1.0", "babel-eslint": "^10.0.1",
"eslint": "^6.8.0", "eslint": "^5.8.0",
"eslint-plugin-vue": "^6.2.1", "eslint-plugin-vue": "^5.0.0",
"eventsource-polyfill": "^0.9.6", "eventsource-polyfill": "^0.9.6",
"vue-template-compiler": "^2.6.11", "vue-template-compiler": "^2.5.21",
"webpack-hot-middleware": "^2.25.0" "webpack-hot-middleware": "^2.24.3"
}, },
"eslintConfig": { "eslintConfig": {
"root": true, "root": true,

View File

@@ -7,24 +7,24 @@
"build": "vue-cli-service build", "build": "vue-cli-service build",
"lint": "vue-cli-service lint" "lint": "vue-cli-service lint"
}, },
"dependencies": { "dependencies": {
"core-js": "^3.6.4", "core-js": "^3.6.1",
"regenerator-runtime": "^0.13.3", "regenerator-runtime": "^0.13.3",
"material-design-icons-iconfont": "^5.0.1", "material-design-icons-iconfont": "^5.0.1",
"vue": "^2.5.22", "vue": "^2.5.22",
"vuetify": "^1.5.14", "vuetify": "^1.5.14",
"@wailsapp/runtime": "^1.0.10" "@wailsapp/runtime": "^1.0.0"
}, },
"devDependencies": { "devDependencies": {
"@vue/cli-plugin-babel": "^4.2.3", "@vue/cli-plugin-babel": "^3.4.0",
"@vue/cli-plugin-eslint": "^4.2.3", "@vue/cli-plugin-eslint": "^3.4.0",
"@vue/cli-service": "^4.2.3", "@vue/cli-service": "^3.4.0",
"babel-eslint": "^10.1.0", "babel-eslint": "^10.0.1",
"eslint": "^6.8.0", "eslint": "^5.8.0",
"eslint-plugin-vue": "^6.2.1", "eslint-plugin-vue": "^5.0.0",
"eventsource-polyfill": "^0.9.6", "eventsource-polyfill": "^0.9.6",
"vue-template-compiler": "^2.6.11", "vue-template-compiler": "^2.5.21",
"webpack-hot-middleware": "^2.25.0" "webpack-hot-middleware": "^2.24.3"
}, },
"eslintConfig": { "eslintConfig": {
"root": true, "root": true,

View File

@@ -8,23 +8,23 @@
"lint": "vue-cli-service lint" "lint": "vue-cli-service lint"
}, },
"dependencies": { "dependencies": {
"core-js": "^3.6.4", "core-js": "^3.6.1",
"regenerator-runtime": "^0.13.3", "regenerator-runtime": "^0.13.3",
"vue": "^2.6.11", "vue": "^2.5.22",
"vuetify": "^2.2.15", "vuetify": "^2.0.15",
"@wailsapp/runtime": "^1.0.10" "@wailsapp/runtime": "^1.0.0"
}, },
"devDependencies": { "devDependencies": {
"@mdi/font": "^4.9.95", "@mdi/font": "^4.3.95",
"@vue/cli-plugin-babel": "^4.2.3", "@vue/cli-plugin-babel": "^3.4.0",
"@vue/cli-plugin-eslint": "^4.2.3", "@vue/cli-plugin-eslint": "^3.4.0",
"@vue/cli-service": "^4.2.3", "@vue/cli-service": "^3.4.0",
"babel-eslint": "^10.1.0", "babel-eslint": "^10.0.1",
"eslint": "^6.8.0", "eslint": "^5.8.0",
"eslint-plugin-vue": "^6.2.1", "eslint-plugin-vue": "^5.0.0",
"eventsource-polyfill": "^0.9.6", "eventsource-polyfill": "^0.9.6",
"vue-template-compiler": "^2.6.11", "vue-template-compiler": "^2.5.21",
"webpack-hot-middleware": "^2.25.0" "webpack-hot-middleware": "^2.24.3"
}, },
"eslintConfig": { "eslintConfig": {
"root": true, "root": true,

View File

@@ -1,4 +1,4 @@
package cmd package cmd
// Version - Wails version // Version - Wails version
const Version = "v1.6.0-pre2" const Version = "v1.0.2"

View File

@@ -6,21 +6,11 @@ import (
"runtime" "runtime"
"strings" "strings"
"github.com/leaanthony/slicer"
"github.com/leaanthony/spinner" "github.com/leaanthony/spinner"
"github.com/wailsapp/wails/cmd" "github.com/wailsapp/wails/cmd"
) )
// getSupportedPlatforms returns a slice of platform/architecture
// targets that are buildable using the cross-platform 'x' option.
func getSupportedPlatforms() []string {
return []string{
"darwin/amd64",
"linux/amd64",
"linux/arm-7",
"windows/amd64",
}
}
func init() { func init() {
var packageApp = false var packageApp = false
@@ -40,15 +30,8 @@ func init() {
BoolFlag("f", "Force rebuild of application components", &forceRebuild). BoolFlag("f", "Force rebuild of application components", &forceRebuild).
BoolFlag("d", "Build in Debug mode", &debugMode). BoolFlag("d", "Build in Debug mode", &debugMode).
BoolFlag("verbose", "Verbose output", &verbose). BoolFlag("verbose", "Verbose output", &verbose).
StringFlag("t", "Generate Typescript definitions to given file (at runtime)", &typescriptFilename) StringFlag("t", "Generate Typescript definitions to given file (at runtime)", &typescriptFilename).
StringFlag("x", "Cross-compile application to specified platform via xgo", &platform)
var b strings.Builder
for _, plat := range getSupportedPlatforms() {
fmt.Fprintf(&b, " - %s\n", plat)
}
initCmd.StringFlag("x",
fmt.Sprintf("Cross-compile application to specified platform via xgo\n%s", b.String()),
&platform)
initCmd.Action(func() error { initCmd.Action(func() error {
@@ -74,25 +57,6 @@ func init() {
return fmt.Errorf("Unable to find 'project.json'. Please check you are in a Wails project directory") return fmt.Errorf("Unable to find 'project.json'. Please check you are in a Wails project directory")
} }
// Set cross-compile
projectOptions.Platform = runtime.GOOS
if len(platform) > 0 {
supported := false
for _, plat := range getSupportedPlatforms() {
if plat == platform {
supported = true
}
}
if !supported {
return fmt.Errorf("Unsupported platform '%s' specified.\nPlease run `wails build -h` to see the supported platform/architecture options.", platform)
}
projectOptions.CrossCompile = true
plat := strings.Split(platform, "/")
projectOptions.Platform = plat[0]
projectOptions.Architecture = plat[1]
}
// Validate config // Validate config
// Check if we have a frontend // Check if we have a frontend
err = cmd.ValidateFrontendConfig(projectOptions) err = cmd.ValidateFrontendConfig(projectOptions)
@@ -171,6 +135,29 @@ func init() {
buildSpinner.Success() buildSpinner.Success()
} }
// Set cross-compile
projectOptions.Platform = runtime.GOOS
if len(platform) > 0 {
projectOptions.CrossCompile = true
projectOptions.Platform = platform
projectOptions.Architecture = "amd64"
// check build architecture
if strings.Contains(platform, "/") {
p := strings.Split(platform, "/")
projectOptions.Platform = p[0]
projectOptions.Architecture = p[1]
}
// Check supported platforms
supportedPlatforms := slicer.String([]string{"linux/amd64", "linux/386", "windows/amd64", "windows/386", "darwin/amd64"})
targetPlatform := projectOptions.Platform + "/" + projectOptions.Architecture
if !supportedPlatforms.Contains(targetPlatform) {
println("\n*** WARNING: Unsupported target platform", targetPlatform+".", "Supported:", supportedPlatforms.Join(", "))
}
}
err = cmd.BuildApplication(projectOptions.BinaryName, forceRebuild, buildMode, packageApp, projectOptions) err = cmd.BuildApplication(projectOptions.BinaryName, forceRebuild, buildMode, packageApp, projectOptions)
if err != nil { if err != nil {
return err return err

View File

@@ -2,7 +2,6 @@ package main
import ( import (
"fmt" "fmt"
"runtime"
"github.com/leaanthony/spinner" "github.com/leaanthony/spinner"
"github.com/wailsapp/wails/cmd" "github.com/wailsapp/wails/cmd"
@@ -36,7 +35,6 @@ func init() {
// Project options // Project options
projectOptions := &cmd.ProjectOptions{} projectOptions := &cmd.ProjectOptions{}
projectOptions.Verbose = verbose projectOptions.Verbose = verbose
projectOptions.Platform = runtime.GOOS
// Check we are in project directory // Check we are in project directory
// Check project.json loads correctly // Check project.json loads correctly

3
go.mod
View File

@@ -10,7 +10,7 @@ require (
github.com/kennygrant/sanitize v1.2.4 github.com/kennygrant/sanitize v1.2.4
github.com/konsorten/go-windows-terminal-sequences v1.0.2 // indirect github.com/konsorten/go-windows-terminal-sequences v1.0.2 // indirect
github.com/leaanthony/mewn v0.10.7 github.com/leaanthony/mewn v0.10.7
github.com/leaanthony/slicer v1.4.0 github.com/leaanthony/slicer v1.4.1
github.com/leaanthony/spinner v0.5.3 github.com/leaanthony/spinner v0.5.3
github.com/mattn/go-colorable v0.1.1 // indirect github.com/mattn/go-colorable v0.1.1 // indirect
github.com/mattn/go-isatty v0.0.7 // indirect github.com/mattn/go-isatty v0.0.7 // indirect
@@ -22,7 +22,6 @@ require (
github.com/stretchr/testify v1.3.0 // indirect github.com/stretchr/testify v1.3.0 // indirect
github.com/syossan27/tebata v0.0.0-20180602121909-b283fe4bc5ba github.com/syossan27/tebata v0.0.0-20180602121909-b283fe4bc5ba
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529 // indirect golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529 // indirect
golang.org/x/image v0.0.0-20200430140353-33d19683fad8
golang.org/x/net v0.0.0-20190509222800-a4d6f7feada5 // indirect golang.org/x/net v0.0.0-20190509222800-a4d6f7feada5 // indirect
golang.org/x/sys v0.0.0-20190509141414-a5b02f93d862 golang.org/x/sys v0.0.0-20190509141414-a5b02f93d862
golang.org/x/text v0.3.0 golang.org/x/text v0.3.0

6
go.sum
View File

@@ -28,8 +28,8 @@ github.com/kr/pty v1.1.1 h1:VkoXIwSboBpnk99O/KFauAEILuNHv5DVFKZMBN/gUgw=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/leaanthony/mewn v0.10.7 h1:jCcNJyIUOpwj+I5SuATvCugDjHkoo+j6ubEOxxrxmPA= github.com/leaanthony/mewn v0.10.7 h1:jCcNJyIUOpwj+I5SuATvCugDjHkoo+j6ubEOxxrxmPA=
github.com/leaanthony/mewn v0.10.7/go.mod h1:CRkTx8unLiSSilu/Sd7i1LwrdaAL+3eQ3ses99qGMEQ= github.com/leaanthony/mewn v0.10.7/go.mod h1:CRkTx8unLiSSilu/Sd7i1LwrdaAL+3eQ3ses99qGMEQ=
github.com/leaanthony/slicer v1.4.0 h1:Q9u4w+UBU4WHjXnEDdz+eRLMKF/rnyosRBiqULnc1J8= github.com/leaanthony/slicer v1.4.1 h1:X/SmRIDhkUAolP79mSTO0jTcVX1k504PJBqvV6TwP0w=
github.com/leaanthony/slicer v1.4.0/go.mod h1:FwrApmf8gOrpzEWM2J/9Lh79tyq8KTX5AzRtwV7m4AY= github.com/leaanthony/slicer v1.4.1/go.mod h1:FwrApmf8gOrpzEWM2J/9Lh79tyq8KTX5AzRtwV7m4AY=
github.com/leaanthony/spinner v0.5.3 h1:IMTvgdQCec5QA4qRy0wil4XsRP+QcG1OwLWVK/LPZ5Y= github.com/leaanthony/spinner v0.5.3 h1:IMTvgdQCec5QA4qRy0wil4XsRP+QcG1OwLWVK/LPZ5Y=
github.com/leaanthony/spinner v0.5.3/go.mod h1:oHlrvWicr++CVV7ALWYi+qHk/XNA91D9IJ48IqmpVUo= github.com/leaanthony/spinner v0.5.3/go.mod h1:oHlrvWicr++CVV7ALWYi+qHk/XNA91D9IJ48IqmpVUo=
github.com/leaanthony/synx v0.1.0 h1:R0lmg2w6VMb8XcotOwAe5DLyzwjLrskNkwU7LLWsyL8= github.com/leaanthony/synx v0.1.0 h1:R0lmg2w6VMb8XcotOwAe5DLyzwjLrskNkwU7LLWsyL8=
@@ -70,8 +70,6 @@ golang.org/x/crypto v0.0.0-20190123085648-057139ce5d2b/go.mod h1:6SG95UA2DQfeDnf
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529 h1:iMGN4xG0cnqj3t+zOM8wUB0BiPKHEwSxEZCvzcbZuvk= golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529 h1:iMGN4xG0cnqj3t+zOM8wUB0BiPKHEwSxEZCvzcbZuvk=
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/image v0.0.0-20200430140353-33d19683fad8 h1:6WW6V3x1P/jokJBpRQYUJnMHRP6isStQwCozxnU7XQw=
golang.org/x/image v0.0.0-20200430140353-33d19683fad8/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190509222800-a4d6f7feada5 h1:6M3SDHlHHDCx2PcQw3S4KsR170vGqDhJDOmpVd4Hjak= golang.org/x/net v0.0.0-20190509222800-a4d6f7feada5 h1:6M3SDHlHHDCx2PcQw3S4KsR170vGqDhJDOmpVd4Hjak=
golang.org/x/net v0.0.0-20190509222800-a4d6f7feada5/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190509222800-a4d6f7feada5/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=

View File

@@ -42,12 +42,12 @@ type Bridge struct {
server *http.Server server *http.Server
lock sync.Mutex lock sync.Mutex
sessions map[string]*session sessions map[string]session
} }
// Initialise the Bridge Renderer // Initialise the Bridge Renderer
func (h *Bridge) Initialise(appConfig interfaces.AppConfig, ipcManager interfaces.IPCManager, eventManager interfaces.EventManager) error { func (h *Bridge) Initialise(appConfig interfaces.AppConfig, ipcManager interfaces.IPCManager, eventManager interfaces.EventManager) error {
h.sessions = map[string]*session{} h.sessions = map[string]session{}
h.ipcManager = ipcManager h.ipcManager = ipcManager
h.appConfig = appConfig h.appConfig = appConfig
h.eventManager = eventManager h.eventManager = eventManager
@@ -70,11 +70,13 @@ func (h *Bridge) wsBridgeHandler(w http.ResponseWriter, r *http.Request) {
} }
func (h *Bridge) startSession(conn *websocket.Conn) { func (h *Bridge) startSession(conn *websocket.Conn) {
s := newSession(conn, s := session{
h.bindingCache, conn: conn,
h.ipcManager, bindingCache: h.bindingCache,
logger.NewCustomLogger("BridgeSession"), ipc: h.ipcManager,
h.eventManager) log: h.log,
eventManager: h.eventManager,
}
conn.SetCloseHandler(func(int, string) error { conn.SetCloseHandler(func(int, string) error {
h.log.Infof("Connection dropped [%s].", s.Identifier()) h.log.Infof("Connection dropped [%s].", s.Identifier())
@@ -158,7 +160,7 @@ func (h *Bridge) NotifyEvent(event *messages.EventData) error {
} }
message := fmt.Sprintf("window.wails._.Notify('%s','%s')", event.Name, data) message := fmt.Sprintf("window.wails._.Notify('%s','%s')", event.Name, data)
dead := []*session{} dead := []session{}
for _, session := range h.sessions { for _, session := range h.sessions {
err := session.evalJS(message, notifyMessage) err := session.evalJS(message, notifyMessage)
if err != nil { if err != nil {
@@ -205,9 +207,6 @@ func (h *Bridge) SetTitle(title string) {
// for the Renderer interface // for the Renderer interface
func (h *Bridge) Close() { func (h *Bridge) Close() {
h.log.Debug("Shutting down") h.log.Debug("Shutting down")
for _, session := range h.sessions {
session.Shutdown()
}
err := h.server.Close() err := h.server.Close()
if err != nil { if err != nil {
h.log.Errorf(err.Error()) h.log.Errorf(err.Error())

View File

@@ -1,8 +1,7 @@
package renderer package renderer
import ( import (
"time" "sync"
"unsafe"
"github.com/gorilla/websocket" "github.com/gorilla/websocket"
"github.com/leaanthony/mewn" "github.com/leaanthony/mewn"
@@ -21,22 +20,7 @@ type session struct {
ipc interfaces.IPCManager ipc interfaces.IPCManager
// Mutex for writing to the socket // Mutex for writing to the socket
shutdown chan bool lock sync.Mutex
writeChan chan []byte
done bool
}
func newSession(conn *websocket.Conn, bindingCache []string, ipc interfaces.IPCManager, logger *logger.CustomLogger, eventMgr interfaces.EventManager) *session {
return &session{
conn: conn,
bindingCache: bindingCache,
ipc: ipc,
log: logger,
eventManager: eventMgr,
shutdown: make(chan bool),
writeChan: make(chan []byte, 100),
}
} }
// Identifier returns a string identifier for the remote connection. // Identifier returns a string identifier for the remote connection.
@@ -49,15 +33,19 @@ func (s *session) Identifier() string {
} }
func (s *session) sendMessage(msg string) error { func (s *session) sendMessage(msg string) error {
if !s.done { s.lock.Lock()
s.writeChan <- *(*[]byte)(unsafe.Pointer(&msg)) defer s.lock.Unlock()
if err := s.conn.WriteMessage(websocket.TextMessage, []byte(msg)); err != nil {
s.log.Debug(err.Error())
return err
} }
return nil return nil
} }
func (s *session) start(firstSession bool) { func (s *session) start(firstSession bool) {
s.log.Infof("Connected to frontend.") s.log.Infof("Connected to frontend.")
go s.writePump()
wailsRuntime := mewn.String("../../runtime/assets/wails.js") wailsRuntime := mewn.String("../../runtime/assets/wails.js")
s.evalJS(wailsRuntime, wailsRuntimeMessage) s.evalJS(wailsRuntime, wailsRuntimeMessage)
@@ -86,10 +74,6 @@ func (s *session) start(firstSession bool) {
s.log.Debugf("Got message: %#v\n", string(buffer)) s.log.Debugf("Got message: %#v\n", string(buffer))
s.ipc.Dispatch(string(buffer), s.Callback) s.ipc.Dispatch(string(buffer), s.Callback)
if s.done {
break
}
} }
} }
@@ -99,38 +83,8 @@ func (s *session) Callback(data string) error {
} }
func (s *session) evalJS(js string, mtype messageType) error { func (s *session) evalJS(js string, mtype messageType) error {
// Prepend message type to message // Prepend message type to message
return s.sendMessage(mtype.toString() + js) return s.sendMessage(mtype.toString() + js)
}
// Shutdown
func (s *session) Shutdown() {
s.done = true
s.shutdown <- true
s.log.Debugf("session %v exit", s.Identifier())
}
// writePump pulls messages from the writeChan and sends them to the client
// since it uses a channel to read the messages the socket is protected without locks
func (s *session) writePump() {
s.log.Debugf("Session %v - writePump start", s.Identifier())
for {
select {
case msg, ok := <-s.writeChan:
s.conn.SetWriteDeadline(time.Now().Add(1 * time.Second))
if !ok {
s.log.Debug("writeChan was closed!")
s.conn.WriteMessage(websocket.CloseMessage, []byte{})
return
}
if err := s.conn.WriteMessage(websocket.TextMessage, msg); err != nil {
s.log.Debug(err.Error())
return
}
case <-s.shutdown:
break
}
}
s.log.Debug("writePump exiting...")
} }

File diff suppressed because one or more lines are too long

View File

@@ -139,7 +139,7 @@ struct webview_priv
#define DEFAULT_URL \ #define DEFAULT_URL \
"data:text/" \ "data:text/" \
"html,%3C%21DOCTYPE%20html%3E%0A%3Chtml%20lang=%22en%22%3E%0A%3Chead%3E%" \ "html,%3C%21DOCTYPE%20html%3E%0A%3Chtml%20lang=%22en%22%3E%0A%3Chead%3E%" \
"3Cmeta%20charset=%22utf-8%22%3E%3Cmeta%20http-equiv=%22IE=edge%22%" \ "3Cmeta%20charset=%22utf-8%22%3E%3Cmeta%20http-equiv=%22IE=edge%22%" \
"20content=%22IE=edge%22%3E%3C%2Fhead%3E%0A%3Cbody%3E%3Cdiv%20id=%22app%22%" \ "20content=%22IE=edge%22%3E%3C%2Fhead%3E%0A%3Cbody%3E%3Cdiv%20id=%22app%22%" \
"3E%3C%2Fdiv%3E%3Cscript%20type=%22text%2Fjavascript%22%3E%3C%2Fscript%3E%" \ "3E%3C%2Fdiv%3E%3Cscript%20type=%22text%2Fjavascript%22%3E%3C%2Fscript%3E%" \
"3C%2Fbody%3E%0A%3C%2Fhtml%3E" "3C%2Fbody%3E%0A%3C%2Fhtml%3E"
@@ -1227,7 +1227,7 @@ struct webview_priv
} }
VariantInit(&myURL); VariantInit(&myURL);
myURL.vt = VT_BSTR; myURL.vt = VT_BSTR;
// #ifndef UNICODE // #ifndef UNICODE
{ {
wchar_t *buffer = webview_to_utf16(webPageName); wchar_t *buffer = webview_to_utf16(webPageName);
if (buffer == NULL) if (buffer == NULL)
@@ -1237,9 +1237,9 @@ struct webview_priv
myURL.bstrVal = SysAllocString(buffer); myURL.bstrVal = SysAllocString(buffer);
GlobalFree(buffer); GlobalFree(buffer);
} }
// #else // #else
// myURL.bstrVal = SysAllocString(webPageName); // myURL.bstrVal = SysAllocString(webPageName);
// #endif // #endif
if (!myURL.bstrVal) if (!myURL.bstrVal)
{ {
badalloc: badalloc:
@@ -1277,7 +1277,7 @@ struct webview_priv
if (!SafeArrayAccessData(sfArray, (void **)&pVar)) if (!SafeArrayAccessData(sfArray, (void **)&pVar))
{ {
pVar->vt = VT_BSTR; pVar->vt = VT_BSTR;
// #ifndef UNICODE // #ifndef UNICODE
{ {
wchar_t *buffer = webview_to_utf16(url); wchar_t *buffer = webview_to_utf16(url);
if (buffer == NULL) if (buffer == NULL)
@@ -1287,9 +1287,9 @@ struct webview_priv
bstr = SysAllocString(buffer); bstr = SysAllocString(buffer);
GlobalFree(buffer); GlobalFree(buffer);
} }
// #else // #else
// bstr = SysAllocString(url); // bstr = SysAllocString(url);
// #endif // #endif
if ((pVar->bstrVal = bstr)) if ((pVar->bstrVal = bstr))
{ {
htmlDoc2->lpVtbl->write(htmlDoc2, sfArray); htmlDoc2->lpVtbl->write(htmlDoc2, sfArray);
@@ -1444,7 +1444,7 @@ struct webview_priv
rect.right - rect.left, rect.bottom - rect.top, rect.right - rect.left, rect.bottom - rect.top,
HWND_DESKTOP, NULL, hInstance, (void *)w); HWND_DESKTOP, NULL, hInstance, (void *)w);
#else #else
w->priv.hwnd = w->priv.hwnd =
CreateWindowEx(0, classname, w->title, style, rect.left, rect.top, CreateWindowEx(0, classname, w->title, style, rect.left, rect.top,
rect.right - rect.left, rect.bottom - rect.top, rect.right - rect.left, rect.bottom - rect.top,
HWND_DESKTOP, NULL, hInstance, (void *)w); HWND_DESKTOP, NULL, hInstance, (void *)w);
@@ -1467,6 +1467,7 @@ struct webview_priv
SetWindowText(w->priv.hwnd, w->title); SetWindowText(w->priv.hwnd, w->title);
#endif #endif
ShowWindow(w->priv.hwnd, SW_SHOWDEFAULT); ShowWindow(w->priv.hwnd, SW_SHOWDEFAULT);
UpdateWindow(w->priv.hwnd); UpdateWindow(w->priv.hwnd);
SetFocus(w->priv.hwnd); SetFocus(w->priv.hwnd);
@@ -1493,11 +1494,6 @@ struct webview_priv
case WM_KEYDOWN: case WM_KEYDOWN:
case WM_KEYUP: case WM_KEYUP:
{ {
// Disable refresh when pressing F5 on windows
if (msg.wParam == VK_F5)
{
break;
}
HRESULT r = S_OK; HRESULT r = S_OK;
IWebBrowser2 *webBrowser2; IWebBrowser2 *webBrowser2;
IOleObject *browser = *w->priv.browser; IOleObject *browser = *w->priv.browser;
@@ -1607,7 +1603,7 @@ struct webview_priv
WEBVIEW_API void webview_set_title(struct webview *w, const char *title) WEBVIEW_API void webview_set_title(struct webview *w, const char *title)
{ {
#ifdef UNICODE #ifdef UNICODE
wchar_t *u16title = webview_to_utf16(title); wchar_t *u16title = webview_to_utf16(title);
if (u16title == NULL) if (u16title == NULL)
{ {
@@ -1615,11 +1611,12 @@ struct webview_priv
} }
SetWindowText(w->priv.hwnd, u16title); SetWindowText(w->priv.hwnd, u16title);
GlobalFree(u16title); GlobalFree(u16title);
#else #else
SetWindowText(w->priv.hwnd, title); SetWindowText(w->priv.hwnd, title);
#endif #endif
} }
WEBVIEW_API void webview_set_fullscreen(struct webview *w, int fullscreen) WEBVIEW_API void webview_set_fullscreen(struct webview *w, int fullscreen)
{ {
if (w->priv.is_fullscreen == !!fullscreen) if (w->priv.is_fullscreen == !!fullscreen)
@@ -1930,26 +1927,22 @@ struct webview_priv
[script setValue:self forKey:@"external"]; [script setValue:self forKey:@"external"];
} }
static void webview_run_input_open_panel(id self, SEL cmd, id webview, static void webview_run_input_open_panel(id self, SEL cmd, id webview,
id listener, BOOL allowMultiple) id listener, BOOL allowMultiple) {
{
char filename[256] = ""; char filename[256] = "";
struct webview *w = struct webview *w =
(struct webview *)objc_getAssociatedObject(self, "webview"); (struct webview *)objc_getAssociatedObject(self, "webview");
webview_dialog(w, WEBVIEW_DIALOG_TYPE_OPEN, WEBVIEW_DIALOG_FLAG_FILE, "", "", webview_dialog(w, WEBVIEW_DIALOG_TYPE_OPEN, WEBVIEW_DIALOG_FLAG_FILE, "", "",
filename, 255); filename, 255);
filename[255] = '\0'; if (strlen(filename)) {
if (strlen(filename) > 0)
{
[listener chooseFilename:[NSString stringWithUTF8String:filename]]; [listener chooseFilename:[NSString stringWithUTF8String:filename]];
} } else {
else
{
[listener cancel]; [listener cancel];
} }
} }
static void webview_external_invoke(id self, SEL cmd, id arg) static void webview_external_invoke(id self, SEL cmd, id arg)
{ {
struct webview *w = struct webview *w =
@@ -1962,7 +1955,7 @@ struct webview_priv
{ {
return; return;
} }
w->external_invoke_cb(w, [(NSString *)(arg) UTF8String]); w->external_invoke_cb(w, [(NSString *)(arg)UTF8String]);
} }
WEBVIEW_API int webview_init(struct webview *w) WEBVIEW_API int webview_init(struct webview *w)

File diff suppressed because it is too large Load Diff

View File

@@ -1,6 +1,6 @@
{ {
"name": "@wailsapp/runtime", "name": "@wailsapp/runtime",
"version": "1.0.11", "version": "1.0.10",
"description": "Wails Javascript runtime library", "description": "Wails Javascript runtime library",
"main": "main.js", "main": "main.js",
"types": "runtime.d.ts", "types": "runtime.d.ts",

View File

@@ -7,11 +7,11 @@ declare const wailsapp__runtime: {
}; };
Events: { Events: {
Acknowledge(eventName: string): void; Acknowledge(eventName: string): void;
Emit(eventName: string, data?: any): void; Emit(eventName: string): void;
Heartbeat(eventName: string, timeInMilliseconds: number, callback: (data?: any) => void): void; Heartbeat(eventName: string, timeInMilliseconds: number, callback: () => void): void;
On(eventName: string, callback: (data?: any) => void): void; On(eventName: string, callback: () => void): void;
OnMultiple(eventName: string, callback: (data?: any) => void, maxCallbacks: number): void; OnMultiple(eventName: string, callback: () => void, maxCallbacks: number): void;
Once(eventName: string, callback: (data?: any) => void): void; Once(eventName: string, callback: () => void): void;
}; };
Init(callback: () => void): void; Init(callback: () => void): void;
Log: { Log: {

View File

@@ -1,13 +1,5 @@
#!/usr/bin/env bash #!/usr/bin/env bash
echo "**** Checking if Wails passes unit tests ****"
if ! go test ./...
then
echo ""
echo "ERROR: Unit tests failed!"
exit 1;
fi
# Build runtime # Build runtime
echo "**** Building Runtime ****" echo "**** Building Runtime ****"
cd runtime/js cd runtime/js
@@ -23,23 +15,9 @@ cd lib/renderer
mewn mewn
cd ../.. cd ../..
cd cmd/wails
echo "**** Checking if Wails compiles ****"
if ! go build .
then
echo ""
echo "ERROR: Build failed!"
exit 1;
fi
echo "**** Installing Wails locally ****" echo "**** Installing Wails locally ****"
if ! go install cd cmd/wails
then go install
echo ""
echo "ERROR: Install failed!"
exit 1;
fi
cd ../.. cd ../..
echo "**** Tidying the mods! ****" echo "**** Tidying the mods! ****"