5
0
mirror of https://github.com/wailsapp/wails.git synced 2025-05-04 01:19:12 +08:00

Merge branch 'develop'

This commit is contained in:
Lea Anthony 2020-04-26 16:10:56 +10:00
commit a8ecc1e872
No known key found for this signature in database
GPG Key ID: 33DAF7BB90A58405
33 changed files with 2131 additions and 1680 deletions

View File

@ -22,4 +22,7 @@ Wails is what it is because of the time and effort given by these great people.
* [Kris Raney](https://github.com/kraney) * [Kris Raney](https://github.com/kraney)
* [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)

View File

@ -147,3 +147,11 @@ This project was mainly coded to the following albums:
## Licensing ## Licensing
[![FOSSA Status](https://app.fossa.io/api/projects/git%2Bgithub.com%2Fwailsapp%2Fwails.svg?type=large)](https://app.fossa.io/projects/git%2Bgithub.com%2Fwailsapp%2Fwails?ref=badge_large) [![FOSSA Status](https://app.fossa.io/api/projects/git%2Bgithub.com%2Fwailsapp%2Fwails.svg?type=large)](https://app.fossa.io/projects/git%2Bgithub.com%2Fwailsapp%2Fwails?ref=badge_large)
## Special Thank You
<p align="center" style="text-align: center">
A special thank you to JetBrains for donating licenses to us!<br/><br/>
Please click the logo to let them know your appreciation!<br/><br/>
<a href="https://www.jetbrains.com?from=Wails"><img src="jetbrains-grayscale.png" width="30%"></a>
</p>

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

@ -12,6 +12,7 @@ import (
"path" "path"
"path/filepath" "path/filepath"
"runtime" "runtime"
"strings"
"github.com/leaanthony/slicer" "github.com/leaanthony/slicer"
) )
@ -47,6 +48,22 @@ func (fs *FSHelper) FileExists(path string) bool {
return fi.Mode().IsRegular() return fi.Mode().IsRegular()
} }
// FindFile returns the first occurrence of match inside path.
func (fs *FSHelper) FindFile(path, match string) (string, error) {
files, err := ioutil.ReadDir(path)
if err != nil {
return "", err
}
for _, f := range files {
if !f.IsDir() && strings.Contains(f.Name(), match) {
return f.Name(), nil
}
}
return "", fmt.Errorf("file not found")
}
// CreateFile creates a file at the given filename location with the contents // CreateFile creates a file at the given filename location with the contents
// set to the given data. It will create intermediary directories if needed. // set to the given data. It will create intermediary directories if needed.
func (fs *FSHelper) CreateFile(filename string, data []byte) error { func (fs *FSHelper) CreateFile(filename string, data []byte) error {
@ -100,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) error { func (fs *FSHelper) RemoveFiles(files []string, continueOnError bool) error {
for _, filename := range files { for _, filename := range files {
err := os.Remove(filename) err := os.Remove(filename)
if err != nil { if err != nil && !continueOnError {
return err return err
} }
} }

View File

@ -5,12 +5,15 @@ import (
"io/ioutil" "io/ioutil"
"os" "os"
"os/exec" "os/exec"
"os/user"
"path/filepath" "path/filepath"
"runtime" "runtime"
"strconv"
"strings" "strings"
"time" "time"
"github.com/leaanthony/mewn" "github.com/leaanthony/mewn"
"github.com/leaanthony/mewn/lib"
"github.com/leaanthony/slicer" "github.com/leaanthony/slicer"
"github.com/leaanthony/spinner" "github.com/leaanthony/spinner"
) )
@ -36,34 +39,161 @@ func ValidateFrontendConfig(projectOptions *ProjectOptions) error {
} }
// InstallGoDependencies will run go get in the current directory // InstallGoDependencies will run go get in the current directory
func InstallGoDependencies() error { func InstallGoDependencies(verbose bool) error {
depSpinner := spinner.New("Ensuring Dependencies are up to date...") var depSpinner *spinner.Spinner
depSpinner.SetSpinSpeed(50) if !verbose {
depSpinner.Start() depSpinner = spinner.New("Ensuring Dependencies are up to date...")
err := NewProgramHelper().RunCommand("go get") depSpinner.SetSpinSpeed(50)
depSpinner.Start()
}
err := NewProgramHelper(verbose).RunCommand("go get")
if err != nil { if err != nil {
depSpinner.Error() if !verbose {
depSpinner.Error()
}
return err return err
} }
depSpinner.Success() if !verbose {
depSpinner.Success()
}
return nil return nil
} }
// BuildApplication will attempt to build the project based on the given inputs // EmbedAssets will embed the built frontend assets via mewn.
func BuildApplication(binaryName string, forceRebuild bool, buildMode string, packageApp bool, projectOptions *ProjectOptions) error { func EmbedAssets() ([]string, error) {
mewnFiles := lib.GetMewnFiles([]string{}, false)
// Generate Windows assets if needed referencedAssets, err := lib.GetReferencedAssets(mewnFiles)
if runtime.GOOS == "windows" { if err != nil {
cleanUp := !packageApp return []string{}, err
err := NewPackageHelper().PackageWindows(projectOptions, cleanUp)
if err != nil {
return err
}
} }
// Check Mewn is installed targetFiles := []string{}
err := CheckMewn()
for _, referencedAsset := range referencedAssets {
packfileData, err := lib.GeneratePackFileString(referencedAsset, false)
if err != nil {
return []string{}, err
}
targetFile := filepath.Join(referencedAsset.BaseDir, referencedAsset.PackageName+"-mewn.go")
targetFiles = append(targetFiles, targetFile)
ioutil.WriteFile(targetFile, []byte(packfileData), 0644)
}
return targetFiles, nil
}
func InitializeCrossCompilation(verbose bool) error {
// Check Docker
if err := CheckIfInstalled("docker"); err != nil {
return err
}
var packSpinner *spinner.Spinner
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 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")
}
// Check build directory
buildDirectory := filepath.Join(fs.Cwd(), "build")
if !fs.DirExists(buildDirectory) {
fs.MkDir(buildDirectory)
}
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
}
// BuildNative builds on the target platform itself.
func BuildNative(binaryName string, forceRebuild bool, buildMode string, projectOptions *ProjectOptions) error {
// Check Mewn is installed
if err := CheckMewn(projectOptions.Verbose); err != nil {
return err
}
if err := CheckWindres(); err != nil {
return err return err
} }
@ -73,23 +203,27 @@ func BuildApplication(binaryName string, forceRebuild bool, buildMode string, pa
compileMessage += " (Debug Mode)" compileMessage += " (Debug Mode)"
} }
packSpinner := spinner.New(compileMessage + "...") var packSpinner *spinner.Spinner
packSpinner.SetSpinSpeed(50) if !projectOptions.Verbose {
packSpinner.Start() packSpinner = spinner.New(compileMessage + "...")
packSpinner.SetSpinSpeed(50)
packSpinner.Start()
} else {
println(compileMessage)
}
buildCommand := slicer.String() buildCommand := slicer.String()
buildCommand.Add("mewn") buildCommand.Add("go")
buildCommand.Add("build")
if buildMode == BuildModeBridge { if buildMode == BuildModeBridge {
// Ignore errors // Ignore errors
buildCommand.Add("-i") buildCommand.Add("-i")
} }
buildCommand.Add("build")
if binaryName != "" { if binaryName != "" {
// Alter binary name based on OS // Alter binary name based on OS
switch runtime.GOOS { switch projectOptions.Platform {
case "windows": case "windows":
if !strings.HasSuffix(binaryName, ".exe") { if !strings.HasSuffix(binaryName, ".exe") {
binaryName += ".exe" binaryName += ".exe"
@ -99,7 +233,7 @@ func BuildApplication(binaryName string, forceRebuild bool, buildMode string, pa
binaryName = strings.TrimSuffix(binaryName, ".exe") binaryName = strings.TrimSuffix(binaryName, ".exe")
} }
} }
buildCommand.Add("-o", binaryName) buildCommand.Add("-o", filepath.Join("build", binaryName))
} }
// If we are forcing a rebuild // If we are forcing a rebuild
@ -107,38 +241,72 @@ func BuildApplication(binaryName string, forceRebuild bool, buildMode string, pa
buildCommand.Add("-a") buildCommand.Add("-a")
} }
// Setup ld flags buildCommand.AddSlice([]string{"-ldflags", ldFlags(projectOptions, buildMode)})
ldflags := "-w -s "
if buildMode == BuildModeDebug { if projectOptions.Verbose {
ldflags = "" fmt.Printf("Command: %v\n", buildCommand.AsSlice())
} }
// Add windows flags err := NewProgramHelper(projectOptions.Verbose).RunCommandArray(buildCommand.AsSlice())
if runtime.GOOS == "windows" && buildMode == BuildModeProd { if err != nil {
ldflags += "-H windowsgui " if packSpinner != nil {
} packSpinner.Error()
}
ldflags += "-X github.com/wailsapp/wails.BuildMode=" + buildMode return err
}
// If we wish to generate typescript if packSpinner != nil {
if projectOptions.typescriptDefsFilename != "" { packSpinner.Success()
cwd, err := os.Getwd() }
if err != nil {
return err return nil
} }
filename := filepath.Join(cwd, projectOptions.FrontEnd.Dir, projectOptions.typescriptDefsFilename)
ldflags += " -X github.com/wailsapp/wails/lib/binding.typescriptDefinitionFilename=" + filename // 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
buildCommand.AddSlice([]string{"-ldflags", ldflags})
err = NewProgramHelper().RunCommandArray(buildCommand.AsSlice()) // 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 { if err != nil {
packSpinner.Error()
return err return err
} }
packSpinner.Success()
// packageApp
if packageApp { if packageApp {
err = PackageApplication(projectOptions) err = PackageApplication(projectOptions)
if err != nil { if err != nil {
@ -151,61 +319,76 @@ 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 var packageSpinner *spinner.Spinner
message := "Generating .app" if projectOptions.Verbose {
if runtime.GOOS == "windows" { packageSpinner = spinner.New("Packaging application...")
err := CheckWindres() packageSpinner.SetSpinSpeed(50)
if err != nil { packageSpinner.Start()
return err
}
message = "Generating resource bundle"
} }
packageSpinner := spinner.New(message)
packageSpinner.SetSpinSpeed(50) err := NewPackageHelper(projectOptions.Platform).Package(projectOptions)
packageSpinner.Start()
err := NewPackageHelper().Package(projectOptions)
if err != nil { if err != nil {
packageSpinner.Error() if packageSpinner != nil {
packageSpinner.Error()
}
return err return err
} }
packageSpinner.Success() if packageSpinner != nil {
packageSpinner.Success()
}
return nil return nil
} }
// BuildFrontend runs the given build command // BuildFrontend runs the given build command
func BuildFrontend(buildCommand string) error { func BuildFrontend(projectOptions *ProjectOptions) error {
buildFESpinner := spinner.New("Building frontend...") var buildFESpinner *spinner.Spinner
buildFESpinner.SetSpinSpeed(50) if !projectOptions.Verbose {
buildFESpinner.Start() buildFESpinner = spinner.New("Building frontend...")
err := NewProgramHelper().RunCommand(buildCommand) buildFESpinner.SetSpinSpeed(50)
buildFESpinner.Start()
} else {
println("Building frontend...")
}
err := NewProgramHelper(projectOptions.Verbose).RunCommand(projectOptions.FrontEnd.Build)
if err != nil { if err != nil {
buildFESpinner.Error() if buildFESpinner != nil {
buildFESpinner.Error()
}
return err return err
} }
buildFESpinner.Success() if buildFESpinner != nil {
buildFESpinner.Success()
}
return nil return nil
} }
// CheckMewn checks if mewn is installed and if not, attempts to fetch it // CheckMewn checks if mewn is installed and if not, attempts to fetch it
func CheckMewn() (err error) { func CheckMewn(verbose bool) (err error) {
programHelper := NewProgramHelper() programHelper := NewProgramHelper(verbose)
if !programHelper.IsInstalled("mewn") { if !programHelper.IsInstalled("mewn") {
buildSpinner := spinner.New() var buildSpinner *spinner.Spinner
buildSpinner.SetSpinSpeed(50) if !verbose {
buildSpinner.Start("Installing Mewn asset packer...") buildSpinner = spinner.New()
buildSpinner.SetSpinSpeed(50)
buildSpinner.Start("Installing Mewn asset packer...")
}
err := programHelper.InstallGoPackage("github.com/leaanthony/mewn/cmd/mewn") err := programHelper.InstallGoPackage("github.com/leaanthony/mewn/cmd/mewn")
if err != nil { if err != nil {
buildSpinner.Error() if buildSpinner != nil {
buildSpinner.Error()
}
return err return err
} }
buildSpinner.Success() if buildSpinner != nil {
buildSpinner.Success()
}
} }
return nil return nil
} }
// 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" { if runtime.GOOS != "windows" { // FIXME: Handle windows cross-compile for windows!
return nil return nil
} }
programHelper := NewProgramHelper() programHelper := NewProgramHelper()
@ -215,6 +398,15 @@ func CheckWindres() (err error) {
return nil return nil
} }
// CheckIfInstalled returns if application is installed
func CheckIfInstalled(application string) (err error) {
programHelper := NewProgramHelper()
if !programHelper.IsInstalled(application) {
return fmt.Errorf("%s not installed. Ensure you have installed %s correctly", application, application)
}
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 {
@ -225,9 +417,14 @@ func InstallFrontendDeps(projectDir string, projectOptions *ProjectOptions, forc
} }
// Check if frontend deps have been updated // Check if frontend deps have been updated
feSpinner := spinner.New("Ensuring frontend dependencies are up to date (This may take a while)") var feSpinner *spinner.Spinner
feSpinner.SetSpinSpeed(50) if !projectOptions.Verbose {
feSpinner.Start() feSpinner = spinner.New("Ensuring frontend dependencies are up to date (This may take a while)")
feSpinner.SetSpinSpeed(50)
feSpinner.Start()
} else {
println("Ensuring frontend dependencies are up to date (This may take a while)")
}
requiresNPMInstall := true requiresNPMInstall := true
@ -259,7 +456,11 @@ func InstallFrontendDeps(projectDir string, projectOptions *ProjectOptions, forc
if savedMD5sum == packageJSONMD5 { if savedMD5sum == packageJSONMD5 {
// Same - no need for reinstall // Same - no need for reinstall
requiresNPMInstall = false requiresNPMInstall = false
feSpinner.Success("Skipped frontend dependencies (-f to force rebuild)") if feSpinner != nil {
feSpinner.Success("Skipped frontend dependencies (-f to force rebuild)")
} else {
println("Skipped frontend dependencies (-f to force rebuild)")
}
} }
} }
} }
@ -268,12 +469,16 @@ func InstallFrontendDeps(projectDir string, projectOptions *ProjectOptions, forc
// Different? Build // Different? Build
if requiresNPMInstall || forceRebuild { if requiresNPMInstall || forceRebuild {
// Install dependencies // Install dependencies
err = NewProgramHelper().RunCommand(projectOptions.FrontEnd.Install) err = NewProgramHelper(projectOptions.Verbose).RunCommand(projectOptions.FrontEnd.Install)
if err != nil { if err != nil {
feSpinner.Error() if feSpinner != nil {
feSpinner.Error()
}
return err return err
} }
feSpinner.Success() if feSpinner != nil {
feSpinner.Success()
}
// Update md5sum file // Update md5sum file
ioutil.WriteFile(md5sumFile, []byte(packageJSONMD5), 0644) ioutil.WriteFile(md5sumFile, []byte(packageJSONMD5), 0644)
@ -286,7 +491,7 @@ func InstallFrontendDeps(projectDir string, projectOptions *ProjectOptions, forc
} }
// Build frontend // Build frontend
err = BuildFrontend(projectOptions.FrontEnd.Build) err = BuildFrontend(projectOptions)
if err != nil { if err != nil {
return err return err
} }
@ -325,7 +530,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(projectOptions.BinaryName) location, err := filepath.Abs(filepath.Join("build", projectOptions.BinaryName))
if err != nil { if err != nil {
return err return err
} }
@ -341,3 +546,28 @@ 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,6 +53,10 @@ const (
Deepin Deepin
// Raspbian distribution // Raspbian distribution
Raspbian Raspbian
// openSUSE Tumbleweed distribution
Tumbleweed
// openSUSE Leap distribution
Leap
) )
// DistroInfo contains all the information relating to a linux distribution // DistroInfo contains all the information relating to a linux distribution
@ -147,6 +151,10 @@ 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,5 +22,25 @@ 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

@ -223,3 +223,32 @@ 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

@ -18,17 +18,19 @@ import (
// PackageHelper helps with the 'wails package' command // PackageHelper helps with the 'wails package' command
type PackageHelper struct { type PackageHelper struct {
fs *FSHelper platform string
log *Logger fs *FSHelper
system *SystemHelper log *Logger
system *SystemHelper
} }
// NewPackageHelper creates a new PackageHelper! // NewPackageHelper creates a new PackageHelper!
func NewPackageHelper() *PackageHelper { func NewPackageHelper(platform string) *PackageHelper {
return &PackageHelper{ return &PackageHelper{
fs: NewFSHelper(), platform: platform,
log: NewLogger(), fs: NewFSHelper(),
system: NewSystemHelper(), log: NewLogger(),
system: NewSystemHelper(),
} }
} }
@ -63,29 +65,30 @@ func defaultString(val string, defaultVal string) string {
func (b *PackageHelper) getPackageFileBaseDir() string { func (b *PackageHelper) getPackageFileBaseDir() string {
// Calculate template base dir // Calculate template base dir
_, filename, _, _ := runtime.Caller(1) _, filename, _, _ := runtime.Caller(1)
return filepath.Join(path.Dir(filename), "packages", runtime.GOOS) return filepath.Join(path.Dir(filename), "packages", b.platform)
} }
// 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 {
switch runtime.GOOS { switch b.platform {
case "darwin": case "darwin":
// Check we have the exe
if !b.fs.FileExists(po.BinaryName) {
return fmt.Errorf("cannot bundle non-existent binary file '%s'. Please build with 'wails build' first", po.BinaryName)
}
return b.packageOSX(po) return b.packageOSX(po)
case "windows": case "windows":
return b.PackageWindows(po, false) return b.PackageWindows(po, true)
case "linux": case "linux":
return fmt.Errorf("linux is not supported at this time. Please see https://github.com/wailsapp/wails/issues/2") return b.packageLinux(po)
default: default:
return fmt.Errorf("platform '%s' not supported for bundling yet", runtime.GOOS) 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()
@ -102,18 +105,25 @@ func (b *PackageHelper) packageOSX(po *ProjectOptions) error {
appname := po.Name + ".app" appname := po.Name + ".app"
// Check binary exists // Check binary exists
source := path.Join(b.fs.Cwd(), exe) source := path.Join(build, exe)
if !b.fs.FileExists(source) { if po.CrossCompile == true {
// We need to build! file, err := b.fs.FindFile(build, "darwin")
return fmt.Errorf("Target '%s' not available. Has it been compiled yet?", exe) if err != nil {
return err
}
source = path.Join(build, file)
} }
if !b.fs.FileExists(source) {
// We need to build!
return fmt.Errorf("Target '%s' not available. Has it been compiled yet?", source)
}
// Remove the existing package // Remove the existing package
os.RemoveAll(appname) os.RemoveAll(appname)
exeDir := path.Join(b.fs.Cwd(), appname, "/Contents/MacOS") exeDir := path.Join(build, appname, "/Contents/MacOS")
b.fs.MkDirs(exeDir, 0755) b.fs.MkDirs(exeDir, 0755)
resourceDir := path.Join(b.fs.Cwd(), appname, "/Contents/Resources") resourceDir := path.Join(build, 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")
@ -129,7 +139,7 @@ func (b *PackageHelper) packageOSX(po *ProjectOptions) error {
if err != nil { if err != nil {
return err return err
} }
filename := path.Join(b.fs.Cwd(), appname, "Contents", "Info.plist") filename := path.Join(build, 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
@ -150,12 +160,25 @@ 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 icon // Copy icon
tgtIconFile := filepath.Join(b.fs.Cwd(), basename+".ico") tgtIconFile := filepath.Join(outputDir, basename+".ico")
if !b.fs.FileExists(tgtIconFile) { if !b.fs.FileExists(tgtIconFile) {
srcIconfile := filepath.Join(b.getPackageFileBaseDir(), "wails.ico") srcIconfile := filepath.Join(b.getPackageFileBaseDir(), "wails.ico")
err := b.fs.CopyFile(srcIconfile, tgtIconFile) err := b.fs.CopyFile(srcIconfile, tgtIconFile)
@ -165,7 +188,7 @@ func (b *PackageHelper) PackageWindows(po *ProjectOptions, cleanUp bool) error {
} }
// Copy manifest // Copy manifest
tgtManifestFile := filepath.Join(b.fs.Cwd(), basename+".exe.manifest") tgtManifestFile := filepath.Join(outputDir, 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)
@ -175,7 +198,7 @@ func (b *PackageHelper) PackageWindows(po *ProjectOptions, cleanUp bool) error {
} }
// Copy rc file // Copy rc file
tgtRCFile := filepath.Join(b.fs.Cwd(), basename+".rc") tgtRCFile := filepath.Join(outputDir, 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)
@ -190,29 +213,33 @@ func (b *PackageHelper) PackageWindows(po *ProjectOptions, cleanUp bool) error {
} }
// Build syso // Build syso
sysofile := filepath.Join(b.fs.Cwd(), basename+"-res.syso") sysofile := filepath.Join(outputDir, basename+"-res.syso")
batfile, err := fs.LocalDir(".") // cross-compile
if err != nil { if b.platform != runtime.GOOS {
return err args := []string{
} "docker", "run", "--rm",
"-v", outputDir + ":/build",
"--entrypoint", "/bin/sh",
"wailsapp/xgo:latest",
"-c", "/usr/bin/x86_64-w64-mingw32-windres -o /build/" + basename + "-res.syso /build/" + basename + ".rc",
}
if err := NewProgramHelper().RunCommandArray(args); err != nil {
return err
}
} else {
batfile, err := fs.LocalDir(".")
if err != nil {
return err
}
windresBatFile := filepath.Join(batfile.fullPath, "windres.bat") windresBatFile := filepath.Join(batfile.fullPath, "windres.bat")
windresCommand := []string{windresBatFile, sysofile, tgtRCFile} windresCommand := []string{windresBatFile, sysofile, tgtRCFile}
err = NewProgramHelper().RunCommandArray(windresCommand) err = NewProgramHelper().RunCommandArray(windresCommand)
if err != nil {
return err
}
// clean up
if cleanUp {
filesToDelete := []string{tgtIconFile, tgtManifestFile, tgtRCFile, sysofile}
err := b.fs.RemoveFiles(filesToDelete)
if err != nil { if err != nil {
return err return err
} }
} }
return nil return nil
} }

View File

@ -1,5 +1,6 @@
<!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

@ -3,6 +3,7 @@ package cmd
import ( import (
"bytes" "bytes"
"fmt" "fmt"
"os"
"os/exec" "os/exec"
"path/filepath" "path/filepath"
"strings" "strings"
@ -11,14 +12,22 @@ import (
// ProgramHelper - Utility functions around installed applications // ProgramHelper - Utility functions around installed applications
type ProgramHelper struct { type ProgramHelper struct {
shell *ShellHelper shell *ShellHelper
verbose bool
} }
// NewProgramHelper - Creates a new ProgramHelper // NewProgramHelper - Creates a new ProgramHelper
func NewProgramHelper() *ProgramHelper { func NewProgramHelper(verbose ...bool) *ProgramHelper {
return &ProgramHelper{ result := &ProgramHelper{
shell: NewShellHelper(), shell: NewShellHelper(),
} }
if len(verbose) > 0 {
result.verbose = verbose[0]
if result.verbose {
result.shell.SetVerbose()
}
}
return result
} }
// IsInstalled tries to determine if the given binary name is installed // IsInstalled tries to determine if the given binary name is installed
@ -29,8 +38,9 @@ func (p *ProgramHelper) IsInstalled(programName string) bool {
// Program - A struct to define an installed application/binary // Program - A struct to define an installed application/binary
type Program struct { type Program struct {
Name string `json:"name"` Name string `json:"name"`
Path string `json:"path"` Path string `json:"path"`
verbose bool
} }
// FindProgram attempts to find the given program on the system.FindProgram // FindProgram attempts to find the given program on the system.FindProgram
@ -45,8 +55,9 @@ func (p *ProgramHelper) FindProgram(programName string) *Program {
return nil return nil
} }
return &Program{ return &Program{
Name: programName, Name: programName,
Path: path, Path: path,
verbose: p.verbose,
} }
} }
@ -63,12 +74,18 @@ func (p *Program) Run(vars ...string) (stdout, stderr string, exitCode int, err
return "", "", 1, err return "", "", 1, err
} }
cmd := exec.Command(command, vars...) cmd := exec.Command(command, vars...)
var stdo, stde bytes.Buffer if !p.verbose {
cmd.Stdout = &stdo var stdo, stde bytes.Buffer
cmd.Stderr = &stde cmd.Stdout = &stdo
err = cmd.Run() cmd.Stderr = &stde
stdout = string(stdo.Bytes()) err = cmd.Run()
stderr = string(stde.Bytes()) stdout = string(stdo.Bytes())
stderr = string(stde.Bytes())
} else {
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
err = cmd.Run()
}
// https://stackoverflow.com/questions/10385551/get-exit-code-go // https://stackoverflow.com/questions/10385551/get-exit-code-go
if err != nil { if err != nil {
@ -121,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 {
program := args[0] programCommand := 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(program) program, err := exec.LookPath(programCommand)
if err != nil { if err != nil {
fmt.Printf("ERROR: Looks like '%s' isn't installed. Please install and try again.", program) fmt.Printf("ERROR: Looks like '%s' isn't installed. Please install and try again.", programCommand)
return err return err
} }

View File

@ -6,7 +6,6 @@ import (
"io/ioutil" "io/ioutil"
"os" "os"
"path/filepath" "path/filepath"
"runtime"
"sort" "sort"
"strings" "strings"
@ -158,6 +157,10 @@ type ProjectOptions struct {
selectedTemplate *TemplateDetails selectedTemplate *TemplateDetails
WailsVersion string WailsVersion string
typescriptDefsFilename string typescriptDefsFilename string
Verbose bool `json:"-"`
CrossCompile bool
Platform string
Architecture string
} }
// Defaults sets the default project template // Defaults sets the default project template
@ -332,11 +335,6 @@ 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

@ -8,6 +8,7 @@ import (
// ShellHelper helps with Shell commands // ShellHelper helps with Shell commands
type ShellHelper struct { type ShellHelper struct {
verbose bool
} }
// NewShellHelper creates a new ShellHelper! // NewShellHelper creates a new ShellHelper!
@ -15,16 +16,27 @@ func NewShellHelper() *ShellHelper {
return &ShellHelper{} return &ShellHelper{}
} }
// SetVerbose sets the verbose flag
func (sh *ShellHelper) SetVerbose() {
sh.verbose = true
}
// Run the given command // Run the given command
func (sh *ShellHelper) Run(command string, vars ...string) (stdout, stderr string, err error) { func (sh *ShellHelper) Run(command string, vars ...string) (stdout, stderr string, err error) {
cmd := exec.Command(command, vars...) cmd := exec.Command(command, vars...)
cmd.Env = append(os.Environ(), "GO111MODULE=on") cmd.Env = append(os.Environ(), "GO111MODULE=on")
var stdo, stde bytes.Buffer if !sh.verbose {
cmd.Stdout = &stdo var stdo, stde bytes.Buffer
cmd.Stderr = &stde cmd.Stdout = &stdo
err = cmd.Run() cmd.Stderr = &stde
stdout = string(stdo.Bytes()) err = cmd.Run()
stderr = string(stde.Bytes()) stdout = string(stdo.Bytes())
stderr = string(stde.Bytes())
} else {
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
err = cmd.Run()
}
return return
} }
@ -33,11 +45,17 @@ func (sh *ShellHelper) RunInDirectory(dir string, command string, vars ...string
cmd := exec.Command(command, vars...) cmd := exec.Command(command, vars...)
cmd.Dir = dir cmd.Dir = dir
cmd.Env = append(os.Environ(), "GO111MODULE=on") cmd.Env = append(os.Environ(), "GO111MODULE=on")
var stdo, stde bytes.Buffer if !sh.verbose {
cmd.Stdout = &stdo var stdo, stde bytes.Buffer
cmd.Stderr = &stde cmd.Stdout = &stdo
err = cmd.Run() cmd.Stderr = &stde
stdout = string(stdo.Bytes()) err = cmd.Run()
stderr = string(stde.Bytes()) stdout = string(stdo.Bytes())
stderr = string(stde.Bytes())
} else {
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
err = cmd.Run()
}
return return
} }

View File

@ -278,7 +278,7 @@ func CheckDependencies(logger *Logger) (bool, error) {
libraryChecker = DpkgInstalled libraryChecker = DpkgInstalled
case Arch, ArcoLinux, Manjaro, ManjaroARM: case Arch, ArcoLinux, Manjaro, ManjaroARM:
libraryChecker = PacmanInstalled libraryChecker = PacmanInstalled
case CentOS, Fedora: case CentOS, Fedora, Tumbleweed, Leap:
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.1.4", "core-js": "^3.6.4",
"react": "^16.8.6", "react": "^16.13.0",
"react-dom": "^16.8.6", "react-dom": "^16.13.0",
"wails-react-scripts": "3.0.1-2", "wails-react-scripts": "3.0.1-2",
"react-modal": "3.8.1", "react-modal": "3.11.2",
"@wailsapp/runtime": "^1.0.0" "@wailsapp/runtime": "^1.0.10"
}, },
"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.1", "core-js": "^3.6.4",
"regenerator-runtime": "^0.13.3", "regenerator-runtime": "^0.13.3",
"vue": "^2.5.22", "vue": "^2.6.11",
"@wailsapp/runtime": "^1.0.0" "@wailsapp/runtime": "^1.0.10"
}, },
"devDependencies": { "devDependencies": {
"@vue/cli-plugin-babel": "^3.4.0", "@vue/cli-plugin-babel": "^4.2.3",
"@vue/cli-plugin-eslint": "^3.4.0", "@vue/cli-plugin-eslint": "^4.2.3",
"@vue/cli-service": "^3.4.0", "@vue/cli-service": "^4.2.3",
"babel-eslint": "^10.0.1", "babel-eslint": "^10.1.0",
"eslint": "^5.8.0", "eslint": "^6.8.0",
"eslint-plugin-vue": "^5.0.0", "eslint-plugin-vue": "^6.2.1",
"eventsource-polyfill": "^0.9.6", "eventsource-polyfill": "^0.9.6",
"vue-template-compiler": "^2.5.21", "vue-template-compiler": "^2.6.11",
"webpack-hot-middleware": "^2.24.3" "webpack-hot-middleware": "^2.25.0"
}, },
"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.1", "core-js": "^3.6.4",
"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.0" "@wailsapp/runtime": "^1.0.10"
}, },
"devDependencies": { "devDependencies": {
"@vue/cli-plugin-babel": "^3.4.0", "@vue/cli-plugin-babel": "^4.2.3",
"@vue/cli-plugin-eslint": "^3.4.0", "@vue/cli-plugin-eslint": "^4.2.3",
"@vue/cli-service": "^3.4.0", "@vue/cli-service": "^4.2.3",
"babel-eslint": "^10.0.1", "babel-eslint": "^10.1.0",
"eslint": "^5.8.0", "eslint": "^6.8.0",
"eslint-plugin-vue": "^5.0.0", "eslint-plugin-vue": "^6.2.1",
"eventsource-polyfill": "^0.9.6", "eventsource-polyfill": "^0.9.6",
"vue-template-compiler": "^2.5.21", "vue-template-compiler": "^2.6.11",
"webpack-hot-middleware": "^2.24.3" "webpack-hot-middleware": "^2.25.0"
}, },
"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.1", "core-js": "^3.6.4",
"regenerator-runtime": "^0.13.3", "regenerator-runtime": "^0.13.3",
"vue": "^2.5.22", "vue": "^2.6.11",
"vuetify": "^2.0.15", "vuetify": "^2.2.15",
"@wailsapp/runtime": "^1.0.0" "@wailsapp/runtime": "^1.0.10"
}, },
"devDependencies": { "devDependencies": {
"@mdi/font": "^4.3.95", "@mdi/font": "^4.9.95",
"@vue/cli-plugin-babel": "^3.4.0", "@vue/cli-plugin-babel": "^4.2.3",
"@vue/cli-plugin-eslint": "^3.4.0", "@vue/cli-plugin-eslint": "^4.2.3",
"@vue/cli-service": "^3.4.0", "@vue/cli-service": "^4.2.3",
"babel-eslint": "^10.0.1", "babel-eslint": "^10.1.0",
"eslint": "^5.8.0", "eslint": "^6.8.0",
"eslint-plugin-vue": "^5.0.0", "eslint-plugin-vue": "^6.2.1",
"eventsource-polyfill": "^0.9.6", "eventsource-polyfill": "^0.9.6",
"vue-template-compiler": "^2.5.21", "vue-template-compiler": "^2.6.11",
"webpack-hot-middleware": "^2.24.3" "webpack-hot-middleware": "^2.25.0"
}, },
"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.0.2" const Version = "v1.5.0"

View File

@ -40,7 +40,7 @@ Create your first project by running 'wails init'.`
} }
// Check Mewn // Check Mewn
err = cmd.CheckMewn() err = cmd.CheckMewn(false)
if err != nil { if err != nil {
return err return err
} }

View File

@ -3,17 +3,32 @@ package main
import ( import (
"fmt" "fmt"
"os" "os"
"runtime"
"strings"
"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
var forceRebuild = false var forceRebuild = false
var debugMode = false var debugMode = false
var typescriptFilename = "" var typescriptFilename = ""
var verbose = false
var platform = ""
buildSpinner := spinner.NewSpinner() buildSpinner := spinner.NewSpinner()
buildSpinner.SetSpinSpeed(50) buildSpinner.SetSpinSpeed(50)
@ -24,8 +39,17 @@ func init() {
BoolFlag("p", "Package application on successful build", &packageApp). BoolFlag("p", "Package application on successful build", &packageApp).
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).
StringFlag("t", "Generate Typescript definitions to given file (at runtime)", &typescriptFilename) StringFlag("t", "Generate Typescript definitions to given file (at runtime)", &typescriptFilename)
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 {
message := "Building Application" message := "Building Application"
@ -40,6 +64,7 @@ func init() {
// Project options // Project options
projectOptions := &cmd.ProjectOptions{} projectOptions := &cmd.ProjectOptions{}
projectOptions.Verbose = verbose
// Check we are in project directory // Check we are in project directory
// Check project.json loads correctly // Check project.json loads correctly
@ -49,6 +74,25 @@ 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)
@ -90,7 +134,7 @@ func init() {
} }
// Install dependencies // Install dependencies
err = cmd.InstallGoDependencies() err = cmd.InstallGoDependencies(projectOptions.Verbose)
if err != nil { if err != nil {
return err return err
} }

View File

@ -10,12 +10,14 @@ import (
func init() { func init() {
var forceRebuild = false var forceRebuild = false
var verbose = false
buildSpinner := spinner.NewSpinner() buildSpinner := spinner.NewSpinner()
buildSpinner.SetSpinSpeed(50) buildSpinner.SetSpinSpeed(50)
commandDescription := `This command builds then serves your application in bridge mode. Useful for developing your app in a browser.` commandDescription := `This command builds then serves your application in bridge mode. Useful for developing your app in a browser.`
initCmd := app.Command("serve", "Run your Wails project in bridge mode"). initCmd := app.Command("serve", "Run your Wails project in bridge mode").
LongDescription(commandDescription). LongDescription(commandDescription).
BoolFlag("verbose", "Verbose output", &verbose).
BoolFlag("f", "Force rebuild of application components", &forceRebuild) BoolFlag("f", "Force rebuild of application components", &forceRebuild)
initCmd.Action(func() error { initCmd.Action(func() error {
@ -25,13 +27,14 @@ func init() {
fmt.Println() fmt.Println()
// Check Mewn is installed // Check Mewn is installed
err := cmd.CheckMewn() err := cmd.CheckMewn(verbose)
if err != nil { if err != nil {
return err return err
} }
// Project options // Project options
projectOptions := &cmd.ProjectOptions{} projectOptions := &cmd.ProjectOptions{}
projectOptions.Verbose = verbose
// Check we are in project directory // Check we are in project directory
// Check project.json loads correctly // Check project.json loads correctly
@ -51,7 +54,7 @@ func init() {
} }
// Install dependencies // Install dependencies
err = cmd.InstallGoDependencies() err = cmd.InstallGoDependencies(projectOptions.Verbose)
if err != nil { if err != nil {
return err return err
} }

3
go.mod
View File

@ -3,9 +3,6 @@ module github.com/wailsapp/wails
require ( require (
github.com/Masterminds/semver v1.4.2 github.com/Masterminds/semver v1.4.2
github.com/abadojack/whatlanggo v1.0.1 github.com/abadojack/whatlanggo v1.0.1
github.com/dchest/cssmin v0.0.0-20151210170030-fb8d9b44afdc // indirect
github.com/dchest/htmlmin v0.0.0-20150526090704-e254725e81ac
github.com/dchest/jsmin v0.0.0-20160823214000-faeced883947 // indirect
github.com/fatih/color v1.7.0 github.com/fatih/color v1.7.0
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

8
go.sum
View File

@ -7,12 +7,6 @@ github.com/abadojack/whatlanggo v1.0.1/go.mod h1:66WiQbSbJBIlOZMsvbKe5m6pzQovxCH
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/dchest/cssmin v0.0.0-20151210170030-fb8d9b44afdc h1:VBS1z48BFEe00G81z8MKOtwX7f/ISkuH38NscT8iVPw=
github.com/dchest/cssmin v0.0.0-20151210170030-fb8d9b44afdc/go.mod h1:ABJPuor7YlcsHmvJ1QxX38e2NcufLY3hm0yXv+cy9sI=
github.com/dchest/htmlmin v0.0.0-20150526090704-e254725e81ac h1:DpMwFluHWoZpV9ex5XjkWO4HyCz5HLVI8XbHw0FhHi4=
github.com/dchest/htmlmin v0.0.0-20150526090704-e254725e81ac/go.mod h1:XsAE+b4rOZc8gvgsgF+wU75mNBvBcyED1wdd9PBLlJ0=
github.com/dchest/jsmin v0.0.0-20160823214000-faeced883947 h1:Fm10/KNuoAyBm2P5P5H91Xy21hGcZnBdjR+cMdytv1M=
github.com/dchest/jsmin v0.0.0-20160823214000-faeced883947/go.mod h1:Dv9D0NUlAsaQcGQZa5kc5mqR9ua72SmA8VXi4cd+cBw=
github.com/fatih/color v1.7.0 h1:DkWD4oS2D8LGGgTQ6IvwJJXSL5Vp2ffcQg58nFV38Ys= github.com/fatih/color v1.7.0 h1:DkWD4oS2D8LGGgTQ6IvwJJXSL5Vp2ffcQg58nFV38Ys=
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
github.com/go-playground/colors v1.2.0 h1:0EdjTXKrr2g1L/LQTYtIqabeHpZuGZz1U4osS1T8+5M= github.com/go-playground/colors v1.2.0 h1:0EdjTXKrr2g1L/LQTYtIqabeHpZuGZz1U4osS1T8+5M=
@ -34,8 +28,6 @@ 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.3.2 h1:kGWWFoyaY5WzwGrUsHXMmGbssuYthP4qYBNlkNpNAB8=
github.com/leaanthony/slicer v1.3.2/go.mod h1:VMB/HGvr3uR3MRpFWHWAm0w+DHQLzPHYe2pKfpFlQIQ=
github.com/leaanthony/slicer v1.4.0 h1:Q9u4w+UBU4WHjXnEDdz+eRLMKF/rnyosRBiqULnc1J8= github.com/leaanthony/slicer v1.4.0 h1:Q9u4w+UBU4WHjXnEDdz+eRLMKF/rnyosRBiqULnc1J8=
github.com/leaanthony/slicer v1.4.0/go.mod h1:FwrApmf8gOrpzEWM2J/9Lh79tyq8KTX5AzRtwV7m4AY= github.com/leaanthony/slicer v1.4.0/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=

BIN
jetbrains-grayscale.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 103 KiB

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,13 +70,11 @@ 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 := session{ s := newSession(conn,
conn: conn, h.bindingCache,
bindingCache: h.bindingCache, h.ipcManager,
ipc: h.ipcManager, logger.NewCustomLogger("BridgeSession"),
log: h.log, h.eventManager)
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())
@ -160,7 +158,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 {
@ -207,6 +205,9 @@ 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,7 +1,8 @@
package renderer package renderer
import ( import (
"sync" "time"
"unsafe"
"github.com/gorilla/websocket" "github.com/gorilla/websocket"
"github.com/leaanthony/mewn" "github.com/leaanthony/mewn"
@ -20,7 +21,22 @@ type session struct {
ipc interfaces.IPCManager ipc interfaces.IPCManager
// Mutex for writing to the socket // Mutex for writing to the socket
lock sync.Mutex shutdown chan bool
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.
@ -33,19 +49,15 @@ func (s *session) Identifier() string {
} }
func (s *session) sendMessage(msg string) error { func (s *session) sendMessage(msg string) error {
s.lock.Lock() if !s.done {
defer s.lock.Unlock() s.writeChan <- *(*[]byte)(unsafe.Pointer(&msg))
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)
@ -74,6 +86,10 @@ 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
}
} }
} }
@ -83,8 +99,38 @@ 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

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.10", "version": "1.0.11",
"description": "Wails Javascript runtime library", "description": "Wails Javascript runtime library",
"main": "main.js", "main": "main.js",
"types": "runtime.d.ts", "types": "runtime.d.ts",
@ -25,4 +25,4 @@
"devDependencies": { "devDependencies": {
"dts-gen": "^0.5.8" "dts-gen": "^0.5.8"
} }
} }

View File

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

View File

@ -1,5 +1,13 @@
#!/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
@ -15,9 +23,23 @@ cd lib/renderer
mewn mewn
cd ../.. cd ../..
echo "**** Installing Wails locally ****"
cd cmd/wails cd cmd/wails
go install echo "**** Checking if Wails compiles ****"
if ! go build .
then
echo ""
echo "ERROR: Build failed!"
exit 1;
fi
echo "**** Installing Wails locally ****"
if ! go install
then
echo ""
echo "ERROR: Install failed!"
exit 1;
fi
cd ../.. cd ../..
echo "**** Tidying the mods! ****" echo "**** Tidying the mods! ****"