Compare commits

..

103 Commits

Author SHA1 Message Date
Lea Anthony
d0908470ea Don't clean up windows files 2020-07-27 21:08:06 +10:00
Lea Anthony
7b6bb5e259 Update Contributors 2020-07-16 19:41:41 +10:00
konez2k
77bd34d601 refactor: use proper user config directory
replace os.UserHomeDir with os.UserConfigDir which manages properly the config folder based on platform.
2020-07-13 20:00:59 +10:00
konez2k
d750077a90 Update minimum go version to 1.13 2020-07-13 20:00:59 +10:00
konez2k
71df64087b refactor homedir to stdlib os.UserHomeDir (go1.12)
- remove mitchellh/go-homedir dependency
2020-07-13 20:00:59 +10:00
Lea Anthony
5565f8ba94 v1.7.1 2020-07-03 20:05:01 +10:00
Lea Anthony
3bc91f20c5 Fix contributors 2020-07-03 13:54:02 +10:00
Lea Anthony
0a15cbaa1d v1.7.0-pre2 2020-07-03 13:52:51 +10:00
Lea Anthony
c650671265 fix: vanilla template for windows 2020-07-03 13:48:27 +10:00
Lea Anthony
7106c338df fix: windows icon name 2020-07-03 06:05:47 +10:00
Lea Anthony
be2cef4a63 free filter memory 2020-06-29 06:41:24 +10:00
Lea Anthony
ee93f3c1e6 v1.7.0-pre1 2020-06-22 20:14:10 +10:00
Lea Anthony
b9ffe53732 Add ldflags option to build 2020-06-22 20:13:15 +10:00
Lea Anthony
bbc16fe03a Lint fixes 2020-06-19 09:38:01 +10:00
Lea Anthony
bdfc3ca631 Release v1.6.0 2020-06-19 09:26:19 +10:00
Lea Anthony
490d66cf77 Update Contributors 2020-06-19 09:11:34 +10:00
Lea Anthony
bce686d779 lint fixes 2020-06-19 08:52:44 +10:00
Lea Anthony
c0b0ef0200 Initial implementation of vanilla template 2020-06-19 08:52:44 +10:00
Florian
8e869baed7 fix: trim whitespaces from filters 2020-06-18 20:25:40 +10:00
Florian Didron
7522428d5c feat: add dialog filter for Cocoa 2020-06-18 20:25:40 +10:00
Florian Didron
bc570999e8 feat: file dialog filters for win32 2020-06-18 20:25:40 +10:00
Florian
393a4fceb2 feat: adds file filtering to gtk select/save dialogs 2020-06-16 18:57:25 +10:00
Lea Anthony
da20bcc8d2 v1.6.0-pre6 2020-06-06 14:42:59 +10:00
Lea Anthony
b091baa16f fix: Project options getting overwritten 2020-06-06 14:42:59 +10:00
Lea Anthony
ecaaafa9d9 v1.6.0-pre5 2020-05-27 12:49:14 +10:00
Lea Anthony
0009da9585 fix: ignore case of linux distro id 2020-05-27 12:48:28 +10:00
Lea Anthony
6235e83677 v1.6.0-pre4 2020-05-27 10:38:40 +10:00
Lea Anthony
9f93e7d979 fix default libs identifier for solus 2020-05-27 10:35:42 +10:00
Lea Anthony
949bc40317 Fix package detection for solus 2020-05-27 10:35:42 +10:00
Lea Anthony
81777f29d8 update package names 2020-05-27 10:35:42 +10:00
Lea Anthony
5d35dd3105 Initial support for solus 2020-05-27 10:35:42 +10:00
Lea Anthony
ad034d3950 fix: popos ID 2020-05-27 08:28:23 +10:00
Lea Anthony
98337df92d Support Pop!_OS
Added link to guide in request process
2020-05-27 08:28:23 +10:00
Dario Emerson
9cc417cf04 Add myself as contributor 2020-05-25 20:48:48 +10:00
Dario Emerson
030b954971 Correctly add icon to window on Windows 2020-05-25 20:48:48 +10:00
Dario Emerson
7a3ab27977 Changed icon scaling algorithm to CatmullRom 2020-05-25 17:32:49 +10:00
Dario Emerson
0e6265a9d7 Moved ico file writing later 2020-05-25 17:32:49 +10:00
Dario Emerson
b003a080b0 Improved Windows .ico generation 2020-05-25 17:32:49 +10:00
Lea Anthony
376ba743f4 Release v1.6.0-pre3 2020-05-22 07:10:58 +10:00
Lea Anthony
aa8ffff68d fix! 2020-05-21 22:05:17 +01:00
Lea Anthony
613a44af5e fix: icon size 2020-05-21 22:05:17 +01:00
Lea Anthony
2e15c4e045 intial support for .ico generation 2020-05-21 22:05:17 +01:00
Lea Anthony
421c13805d Merge branch 'dedo1911-408-default-serve-platform' into develop 2020-05-22 07:02:55 +10:00
Dario Emerson
cc204ab1f7 Fix missing import 2020-05-21 01:35:34 +02:00
Dario Emerson
a94a1a9fcb Merge branch 'develop' into 408-default-serve-platform 2020-05-21 01:33:22 +02:00
Dario Emerson
e860bd06ec Default Platform for serve command 2020-05-20 14:36:52 +02:00
Lea Anthony
6da02e6b44 v1.6.0-pre2 2020-05-17 20:17:36 +10:00
Lea Anthony
7cd78df1cd Support ArchLabs 2020-05-17 20:17:00 +10:00
Lea Anthony
bcecd854bc v1.6.0-pre1 2020-05-16 07:21:26 +10:00
Lea Anthony
6339f230f9 Fix CWE-126 2020-05-15 22:18:50 +01:00
Lea Anthony
131fd973cd lint fix 2020-05-15 22:18:50 +01:00
Lea Anthony
f1d16a03ec Disable F5 key on windows 2020-05-15 22:18:50 +01:00
Lea Anthony
cc99dcce80 Update contributors 2020-05-07 06:30:53 +10:00
Lea Anthony
a8ecc1e872 Merge branch 'develop' 2020-04-26 16:10:56 +10:00
Lea Anthony
6fc419ab48 Version v1.5.0 2020-04-26 16:00:39 +10:00
Lea Anthony
400bb37c03 Updated contributors 2020-04-26 14:39:11 +10:00
Lea Anthony
10fc7d762b fixes to OSX cross compile 2020-04-26 14:29:46 +10:00
Lea Anthony
61b9e50b8e Rapply -o suffix handling 2020-04-26 14:29:46 +10:00
Travis McLane
47916216de fixup 2020-04-26 14:29:46 +10:00
Travis McLane
a2b066decb wip: attempting to remove '.exe' from binary name 2020-04-26 14:29:46 +10:00
Travis McLane
8d1238289f correct issues with exe naming on windows 2020-04-26 14:29:46 +10:00
Travis McLane
b232b608fa remove extraneous println 2020-04-26 14:29:46 +10:00
Travis McLane
3b034ceadb correct issue with wails serve 2020-04-26 14:29:46 +10:00
Travis McLane
75be8f698a handle windows resources + cleanup when done
Moved the file cleanup to a CleanWindows function which simply deletes
all the files a Windows compilation generates for the icon resources.
Windows resource files MUST be in the same directory as the `go` source or
it will not be included in the resulting binary.
package.PackageWindows should be renamed to `GenerateWindowsResources`
since it doesn't actually package anything but rather generates the
various resources that are used during compilation.
2020-04-26 14:29:46 +10:00
Travis McLane
a78acbb247 continueOnError for RemoveFiles
Allow the purge to continue even if one of the files fails to be removed.
2020-04-26 14:29:46 +10:00
Travis McLane
6e29d1b087 docker process updates
- inform user we are starting to pull image and it may take a while
- inform user the target platform being built using docker image
2020-04-26 14:29:46 +10:00
Travis McLane
8171b644ca use 'build' dir for windows resources + cleanup 2020-04-26 14:29:46 +10:00
Travis McLane
a691ef0580 ensure OS X App is built inside 'build' dir 2020-04-26 14:29:46 +10:00
Travis McLane
151b4bff06 generate mewn files + cleanup 2020-04-26 14:29:46 +10:00
Travis McLane
fb093c58d2 cleanup: build native vs docker
- BuildDocker: uses wailsapp/xgo:latest docker container to build
- BuildNative: builds on the host for the host platform
2020-04-26 14:29:46 +10:00
Travis McLane
b68c69f4eb run tests first to fail fast 2020-04-26 14:29:46 +10:00
Travis McLane
8405ce37f9 remove "'" around ldflags
The single quote is needed for running the command in the shell directly.
When run inside the Go command 'os/exec' it is not removed and is
preventing the flags from being processed correctly inside the Docker image.
2020-04-26 14:29:46 +10:00
Travis McLane
76edc7976e use userid=1000 if user.Current() is not an integer 2020-04-26 14:29:46 +10:00
Travis McLane
2ab8fc37ab remove linux/i386 (buggy) 2020-04-26 14:29:46 +10:00
Travis McLane
c66e9cba5a cross-platform builds w/ wailsapp/xgo:latest
- use docker directly instead of `xgo` to call docker.
- use custom xgo docker image
2020-04-26 14:29:46 +10:00
Travis McLane
b33794c883 use wailsapp/xgo:latest for windres call 2020-04-26 14:29:46 +10:00
Travis McLane
5d719685d3 cross compilation command + help 2020-04-26 14:29:46 +10:00
Travis McLane
5b2e5b1d85 chore: correct build tag on app_other 2020-04-26 14:29:46 +10:00
Travis McLane
773bad45f6 build.sh improvements
- run `go test ./...` and STOP on error
- run `go build` and STOP on error
- run `go install` and STOP on error
2020-04-26 14:29:46 +10:00
Travis McLane
404cd7d14e linux packaging (noop) + list platforms 2020-04-26 14:29:46 +10:00
Travis McLane
ea703acfed generated files 2020-04-26 14:29:46 +10:00
konez2k
4750ee5d22 update image version 2020-04-26 14:29:46 +10:00
konez2k
1bd9408e9e add docker version pinning 2020-04-26 14:29:46 +10:00
konez2k
34202f1a2f add docker webkit/gtk support 2020-04-26 14:29:46 +10:00
konez2k
9d6d77f768 initial docker support 2020-04-26 14:29:46 +10:00
konez2k
fbff822ea9 check if xgo and docker are installed 2020-04-26 14:29:46 +10:00
konez2k
da28683ddc create build directory if not exists 2020-04-26 14:29:46 +10:00
konez2k
b7f0155c3b add cross-platform build and packaging 2020-04-26 14:29:46 +10:00
konez2k
f220e0e5bd add FindFile to FSHelper 2020-04-26 14:29:46 +10:00
konez2k
b5bad14124 add arch to project options 2020-04-26 14:29:46 +10:00
konez2k
0f36ee7030 add cross-compile flag to build command 2020-04-26 14:29:46 +10:00
konez2k
58761ccff4 embed assets internally using mewn 2020-04-26 14:29:46 +10:00
Lea Anthony
059cbbfd9a doc: updated contributors 2020-04-26 07:00:05 +10:00
Jarek Cora
339b0ecbaf feat: added openSUSE support 2020-04-26 06:56:53 +10:00
Travis McLane
615cc55b31 bump channel capacity to 100 2020-03-26 05:21:35 +11:00
Travis McLane
577b59aa89 writer channel to protect websocket 2020-03-26 05:21:35 +11:00
Travis McLane
45a507673e custom logger for Bridge.session 2020-03-26 05:21:35 +11:00
Lea Anthony
eb8a1f2303 Add Reuben to Contributors 2020-03-24 22:04:48 +11:00
Reuben
566914a9c2 fix RunCommandArray bug where if exec.LookPath returns an error program is an empty string in the log. 2020-03-24 22:02:56 +11:00
Lea Anthony
0866a633a3 fix optional callback data in definition 2020-03-21 14:32:17 +11:00
konez2k
0e7981cf87 Macos notarize fix (#362)
* Develop (#343)

* Support Distribution 'ArcoLinux' #310 (#312)

* Support Distribution 'ArcoLinux' #310

* Vuetify2 support (resurrected from git@github.com:MichaelHipp/wails.git) (#315)

* Initial create of vuetify2-basic folder

* Change template descr of vuetify-basic to say Vuetify 1.5

* Get vuetify2 template installing vuetify v2.0 (but with styling probs)

* Update App.vue, HelloWorld.vue for Vuetify v2

* Remove babel-polyfill, add mdi/font

* fix: codacy corrections

* fix: babel -> core-js, regenerator-runtime

Co-authored-by: Michael Hipp <michael@redmule.com>
Co-authored-by: Lea Anthony <lea.anthony@gmail.com>

* Update Contributors

* v1.0.2-pre1

* [313-remote-conn] allow remote connections to the websocket bridge (#314)

* [313-remote-conn] feat: compute wsURL based on window.location

* [313-remote-conn] feat: allow any host to connect to vue server

removing the 'host: "localhost"' specification causes the development
server to listen on all interfaces.

* [313-remote-conn] feat: allow any host to connect to angular dev server

* test: reinject tabs

Co-authored-by: Lea Anthony <lea.anthony@gmail.com>

* fix: disable host check for vuetify 2 template

* v1.0.2-pre2

* fix: shutdown ipcmanager

* use channel to trigger shutdown

* load linuxdb from relative path

* Feat manjaro arm & deepin (#324)

* feat: new distros: manjaroARM & Deepin

* v1.0.2-pre3

* [326-platform-raspbian] feat: implement raspbian support (#327)

* fix: emit arguments (#306)

* v1.0.2-pre4 Raspbarian support

* Initial support for Typescript decl file (#330)

* v1.0.2-pre5

* revert to Go 1.12

* New CI (#331)

* prepare

* new CI/github actions

* Rename later-pre.yml to latest-pre.yml

* Update latest-pre.yml

* Update README.md

* Ensure version in go.mod is up to date (#339)

* release v1.0.2-pre6

* Fix typescript generation

* Release v1.0.2-pre7

* 316-multi-bridge-conn (#317)

* [316-multi-bridge-conn] feat: use callback func for bridge response

* [316-multi-bridge-conn] feat: implement multiple session support

* split client handling portion into 'session'
* keep track of sessions by remote address (ip & port)
* notify each of the sessions anytime an event comes across the bus

* [316-multi-bridge-conn] chore: move bridge files to package

* [316-multi-bridge-conn] chore: remove deprecated Callback function

The Callback function is no longer needed for the operation of
the frontend callback since the ipc.Dispatch function now requires
a callback function to be provided as an argument.
This function can be a private function since it is passed by reference.

* [316-multi-bridge-conn] chore: make webview.Callback private

* [316-multi-bridge-conn] chore: remove unused injectCSS function

I believe a slightly better method of doing this might need to be devised
if it is needed in the future. I presume it should collect the values
into a cache and then inject it into each sesssion as it appears.

* [316-multi-bridge-conn] ensure wails:ready event is emitted

Event is only emitted for the first session created from the Bridge.

* [316-multi-bridge-conn] emit events for session lifecycle

Emit an event for each session started and ended.

* [316-multi-bridge-conn] fix: session handling fixes

Co-authored-by: Lea Anthony <lea.anthony@gmail.com>

* Release v1.0.2-pre8

* Release v1.0.2

Co-authored-by: Byron <ktc@protonmail.com>
Co-authored-by: Travis McLane <tmclane@gmail.com>
Co-authored-by: Michael Hipp <michael@redmule.com>

* Macos notarize fix

Co-authored-by: Lea Anthony <lea.anthony@gmail.com>
Co-authored-by: Byron <ktc@protonmail.com>
Co-authored-by: Travis McLane <tmclane@gmail.com>
Co-authored-by: Michael Hipp <michael@redmule.com>
2020-03-17 23:29:43 +11:00
Lea Anthony
ce3bd8f56f Update dependencies (#354)
* update vuetify2 dependencies

* update eslint

* update vuetify1 deps

* update vuebasic deps

* update react deps

* bump version
2020-03-04 06:39:34 +10:00
Lea Anthony
a3cc1de0a2 v1.0.3-pre1 2020-02-23 15:10:31 +11:00
50 changed files with 2459 additions and 1790 deletions

View File

@@ -13,10 +13,10 @@ jobs:
os: [ubuntu-latest, windows-latest, macOS-latest] os: [ubuntu-latest, windows-latest, macOS-latest]
steps: steps:
- name: Set up Go 1.12 - name: Set up Go 1.13
uses: actions/setup-go@v1 uses: actions/setup-go@v1
with: with:
go-version: 1.12 go-version: 1.13
id: go id: go
- name: Check out code into the Go module directory - name: Check out code into the Go module directory

View File

@@ -13,10 +13,10 @@ jobs:
os: [ubuntu-latest, windows-latest, macOS-latest] os: [ubuntu-latest, windows-latest, macOS-latest]
steps: steps:
- name: Set up Go 1.12 - name: Set up Go 1.13
uses: actions/setup-go@v1 uses: actions/setup-go@v1
with: with:
go-version: 1.12 go-version: 1.13
id: go id: go
- name: Check out code into the Go module directory - name: Check out code into the Go module directory

View File

@@ -15,10 +15,10 @@ jobs:
os: [ubuntu-latest, windows-latest, macOS-latest] os: [ubuntu-latest, windows-latest, macOS-latest]
steps: steps:
- name: Set up Go 1.12 - name: Set up Go 1.13
uses: actions/setup-go@v1 uses: actions/setup-go@v1
with: with:
go-version: 1.12 go-version: 1.13
id: go id: go
- name: Check out code into the Go module directory - name: Check out code into the Go module directory

View File

@@ -15,7 +15,6 @@ Wails is what it is because of the time and effort given by these great people.
* [admin_3.exe](https://github.com/bh90210) * [admin_3.exe](https://github.com/bh90210)
* [iceleo-com](https://github.com/iceleo-com) * [iceleo-com](https://github.com/iceleo-com)
* [fallendusk](https://github.com/fallendusk) * [fallendusk](https://github.com/fallendusk)
* [Florian Didran](https://github.com/fdidron)
* [Nikolai Zimmermann](https://github.com/Chronophylos) * [Nikolai Zimmermann](https://github.com/Chronophylos)
* [Toyam Cox](https://github.com/Vaelatern) * [Toyam Cox](https://github.com/Vaelatern)
* [Robin Eklind](https://github.com/mewmew) * [Robin Eklind](https://github.com/mewmew)
@@ -23,3 +22,10 @@ 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)
* [dedo1911](https://github.com/dedo1911)
* [Florian Didron](https://github.com/fdidron)
* [Christopher Murphy](https://github.com/Splode)

View File

@@ -36,7 +36,7 @@ The official docs can be found at [https://wails.app](https://wails.app).
Wails uses cgo to bind to the native rendering engines so a number of platform dependent libraries are needed as well as an installation of Go. The basic requirements are: Wails uses cgo to bind to the native rendering engines so a number of platform dependent libraries are needed as well as an installation of Go. The basic requirements are:
- Go 1.12 - Go 1.13
- npm - npm
### MacOS ### MacOS
@@ -55,9 +55,9 @@ _Debian: 8, 9, 10_
_Ubuntu: 16.04, 18.04, 19.04_ _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_, Pop!_OS
#### Arch Linux #### Arch Linux / ArchLabs
`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) 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,8 +5,10 @@ import (
"io/ioutil" "io/ioutil"
"os" "os"
"os/exec" "os/exec"
"os/user"
"path/filepath" "path/filepath"
"runtime" "runtime"
"strconv"
"strings" "strings"
"time" "time"
@@ -81,45 +83,119 @@ func EmbedAssets() ([]string, error) {
return targetFiles, nil return targetFiles, nil
} }
// BuildApplication will attempt to build the project based on the given inputs func InitializeCrossCompilation(verbose bool) error {
func BuildApplication(binaryName string, forceRebuild bool, buildMode string, packageApp bool, projectOptions *ProjectOptions) error { // Check Docker
if err := CheckIfInstalled("docker"); err != nil {
return err
}
if buildMode == BuildModeBridge && projectOptions.CrossCompile { 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 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")
} }
// Generate Windows assets if needed
if projectOptions.Platform == "windows" {
cleanUp := !packageApp
err := NewPackageHelper(projectOptions.Platform).PackageWindows(projectOptions, cleanUp)
if err != nil {
return err
}
}
if projectOptions.CrossCompile {
// Check build directory // Check build directory
buildDirectory := filepath.Join(fs.Cwd(), "build") buildDirectory := filepath.Join(fs.Cwd(), "build")
if !fs.DirExists(buildDirectory) { if !fs.DirExists(buildDirectory) {
fs.MkDir(buildDirectory) fs.MkDir(buildDirectory)
} }
// Check Docker buildCommand := slicer.String()
if err := CheckIfInstalled("docker"); err != nil { 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 return err
} }
// Check xgo if err := CheckWindres(); err != nil {
if err := CheckIfInstalled("xgo"); err != nil {
return err return err
} }
} else {
// Check Mewn is installed
err := CheckMewn(projectOptions.Verbose)
if err != nil {
return err
}
}
compileMessage := "Packing + Compiling project" compileMessage := "Packing + Compiling project"
@@ -136,40 +212,18 @@ func BuildApplication(binaryName string, forceRebuild bool, buildMode string, pa
println(compileMessage) println(compileMessage)
} }
// embed resources
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() buildCommand := slicer.String()
if projectOptions.CrossCompile { buildCommand.Add("go")
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 !projectOptions.CrossCompile { if binaryName != "" {
buildCommand.Add("build")
}
if binaryName != "" && !projectOptions.CrossCompile {
// 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"
@@ -179,46 +233,21 @@ 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
if forceRebuild && !projectOptions.CrossCompile { if forceRebuild {
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 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()
@@ -229,7 +258,57 @@ func BuildApplication(binaryName string, forceRebuild bool, buildMode string, pa
packSpinner.Success() packSpinner.Success()
} }
// packageApp return nil
}
// 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)
}
}
// Removed by popular demand
// TODO: Potentially add a flag to cleanup
// 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 {
@@ -242,22 +321,11 @@ 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(message) packageSpinner = spinner.New("Packaging application...")
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)
@@ -322,7 +390,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" { if runtime.GOOS != "windows" { // FIXME: Handle windows cross-compile for windows!
return nil return nil
} }
programHelper := NewProgramHelper() programHelper := NewProgramHelper()
@@ -464,7 +532,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
} }
@@ -480,3 +548,33 @@ 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
// Add additional ldflags passed in via the `ldflags` cli flag
if len(po.LdFlags) > 0 {
ldflags += " " + po.LdFlags
}
// 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,16 @@ const (
Deepin Deepin
// Raspbian distribution // Raspbian distribution
Raspbian Raspbian
// Tumbleweed (OpenSUSE) distribution
Tumbleweed
// Leap (OpenSUSE) distribution
Leap
// ArchLabs distribution
ArchLabs
// PopOS distribution
PopOS
// Solus distribution
Solus
) )
// DistroInfo contains all the information relating to a linux distribution // DistroInfo contains all the information relating to a linux distribution
@@ -101,7 +111,7 @@ func parseOsRelease(osRelease string) *DistroInfo {
} }
switch splitLine[0] { switch splitLine[0] {
case "ID": case "ID":
osID = strings.Trim(splitLine[1], "\"") osID = strings.ToLower(strings.Trim(splitLine[1], "\""))
case "NAME": case "NAME":
osNAME = strings.Trim(splitLine[1], "\"") osNAME = strings.Trim(splitLine[1], "\"")
case "VERSION_ID": case "VERSION_ID":
@@ -117,6 +127,8 @@ func parseOsRelease(osRelease string) *DistroInfo {
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":
@@ -147,6 +159,14 @@ 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
case "pop":
result.Distribution = PopOS
case "solus":
result.Distribution = Solus
default: default:
result.Distribution = Unknown result.Distribution = Unknown
} }
@@ -183,6 +203,17 @@ func DpkgInstalled(packageName string) (bool, error) {
return exitCode == 0, nil return exitCode == 0, nil
} }
// EOpkgInstalled uses dpkg to see if a package is installed
func EOpkgInstalled(packageName string) (bool, error) {
program := NewProgramHelper()
eopkg := program.FindProgram("eopkg")
if eopkg == nil {
return false, fmt.Errorf("cannot check dependencies: eopkg not found")
}
stdout, _, _, _ := eopkg.Run("info", packageName)
return strings.HasPrefix(stdout, "Installed"), nil
}
// PacmanInstalled uses pacman to see if a package is installed. // PacmanInstalled uses pacman to see if a package is installed.
func PacmanInstalled(packageName string) (bool, error) { func PacmanInstalled(packageName string) (bool, error) {
program := NewProgramHelper() program := NewProgramHelper()
@@ -254,5 +285,9 @@ func RequestSupportForDistribution(distroInfo *DistroInfo) error {
fmt.Println("Opening browser to file request.") fmt.Println("Opening browser to file request.")
browser.OpenURL(fullURL + url.PathEscape(params)) browser.OpenURL(fullURL + url.PathEscape(params))
result = Prompt("We have a guide for adding support for your distribution. Would you like to view it?", "yes")
if strings.ToLower(result) == "yes" {
browser.OpenURL("https://wails.app/guides/distro/")
}
return nil return nil
} }

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

@@ -28,6 +28,15 @@ distributions:
gccversioncommand: &gccdumpfullversion -dumpfullversion gccversioncommand: &gccdumpfullversion -dumpfullversion
programs: *debiandefaultprograms programs: *debiandefaultprograms
libraries: *debiandefaultlibraries libraries: *debiandefaultlibraries
pop:
id: pop
releases:
default:
version: default
name: Pop!_OS
gccversioncommand: &gccdumpfullversion -dumpfullversion
programs: *debiandefaultprograms
libraries: *debiandefaultlibraries
kali: kali:
id: kali id: kali
releases: releases:
@@ -176,6 +185,15 @@ 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:
@@ -223,3 +241,51 @@ distributions:
gccversioncommand: *gccdumpfullversion gccversioncommand: *gccdumpfullversion
programs: *debiandefaultprograms programs: *debiandefaultprograms
libraries: *debiandefaultlibraries libraries: *debiandefaultlibraries
solus:
id: solus
releases:
default:
version: default
name: Solus
gccversioncommand: *gccdumpfullversion
programs: &solusdefaultprograms
- name: gcc
help: Please install with `sudo eopkg it -c system.devel` and try again
- name: pkg-config
help: Please install with `sudo eopkg it -c system.devel` and try again
- name: npm
help: Please install with `sudo eopkg it nodejs` and try again
libraries: &solusdefaultlibraries
- name: libgtk-3-devel
help: Please install with `sudo eopkg it libgtk-3-devel` and try again
- name: libwebkit-gtk-devel
help: Please install with `sudo eopkg it libwebkit-gtk-devel` and try again
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,9 +1,12 @@
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"
@@ -14,6 +17,7 @@ import (
"time" "time"
"github.com/jackmordaunt/icns" "github.com/jackmordaunt/icns"
"golang.org/x/image/draw"
) )
// PackageHelper helps with the 'wails package' command // PackageHelper helps with the 'wails package' command
@@ -55,6 +59,111 @@ func newPlistData(title, exe, packageID, version, author string) *plistData {
} }
} }
type windowsIcoHeader struct {
_ uint16
imageType uint16
imageCount uint16
}
type windowsIcoDescriptor struct {
width uint8
height uint8
colours uint8
_ uint8
planes uint16
bpp uint16
size uint32
offset uint32
}
type windowsIcoContainer struct {
Header windowsIcoDescriptor
Data []byte
}
func generateWindowsIcon(pngFilename string, iconfile string) error {
sizes := []int{256, 128, 64, 48, 32, 16}
pngfile, err := os.Open(pngFilename)
if err != nil {
return err
}
defer pngfile.Close()
pngdata, err := png.Decode(pngfile)
if err != nil {
return err
}
icons := []windowsIcoContainer{}
for _, size := range sizes {
rect := image.Rect(0, 0, int(size), int(size))
rawdata := image.NewRGBA(rect)
scale := draw.CatmullRom
scale.Scale(rawdata, rect, pngdata, pngdata.Bounds(), draw.Over, nil)
icondata := new(bytes.Buffer)
writer := bufio.NewWriter(icondata)
err = png.Encode(writer, rawdata)
if err != nil {
return err
}
writer.Flush()
imgSize := size
if imgSize >= 256 {
imgSize = 0
}
data := icondata.Bytes()
icn := windowsIcoContainer{
Header: windowsIcoDescriptor{
width: uint8(imgSize),
height: uint8(imgSize),
planes: 1,
bpp: 32,
size: uint32(len(data)),
},
Data: data,
}
icons = append(icons, icn)
}
outfile, err := os.Create(iconfile)
if err != nil {
return err
}
defer outfile.Close()
ico := windowsIcoHeader{
imageType: 1,
imageCount: uint16(len(sizes)),
}
err = binary.Write(outfile, binary.LittleEndian, ico)
if err != nil {
return err
}
offset := uint32(6 + 16*len(sizes))
for _, icon := range icons {
icon.Header.offset = offset
err = binary.Write(outfile, binary.LittleEndian, icon.Header)
if err != nil {
return err
}
offset += icon.Header.size
}
for _, icon := range icons {
_, err = outfile.Write(icon.Data)
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
@@ -72,29 +181,23 @@ 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, 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", 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()
@@ -111,34 +214,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 po.CrossCompile == true {
if b.platform != runtime.GOOS { file, err := b.fs.FindFile(build, "darwin")
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?", exe) 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")
@@ -154,7 +248,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
@@ -175,22 +269,37 @@ 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 default icon if needed
tgtIconFile := filepath.Join(b.fs.Cwd(), basename+".ico") icon, err := b.copyIcon()
if !b.fs.FileExists(tgtIconFile) {
srcIconfile := filepath.Join(b.getPackageFileBaseDir(), "wails.ico")
err := b.fs.CopyFile(srcIconfile, tgtIconFile)
if err != nil { if err != nil {
return err return err
} }
// Generate icon from PNG
err = generateWindowsIcon(icon, basename+".ico")
if err != nil {
return err
} }
// 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)
@@ -200,7 +309,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)
@@ -215,23 +324,17 @@ 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")
// 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", folder + ":/build", "-v", outputDir + ":/build",
"--entrypoint", "/bin/sh", "--entrypoint", "/bin/sh",
"techknowlogick/xgo", "wailsapp/xgo:latest",
"-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
} }
@@ -248,20 +351,10 @@ 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(resourceDir string) (string, error) { func (b *PackageHelper) copyIcon() (string, error) {
// TODO: Read this from project.json // TODO: Read this from project.json
const appIconFilename = "appicon.png" const appIconFilename = "appicon.png"
@@ -286,7 +379,7 @@ func (b *PackageHelper) copyIcon(resourceDir string) (string, error) {
func (b *PackageHelper) packageIconOSX(resourceDir string) error { func (b *PackageHelper) packageIconOSX(resourceDir string) error {
srcIcon, err := b.copyIcon(resourceDir) srcIcon, err := b.copyIcon()
if err != nil { if err != nil {
return err return err
} }

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

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

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 {
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"
@@ -159,9 +158,10 @@ type ProjectOptions struct {
WailsVersion string WailsVersion string
typescriptDefsFilename string typescriptDefsFilename string
Verbose bool `json:"-"` Verbose bool `json:"-"`
CrossCompile bool `json:"-"` CrossCompile bool
Platform string `json:"-"` Platform string
Architecture string `json:"-"` Architecture string
LdFlags string
} }
// Defaults sets the default project template // Defaults sets the default project template
@@ -336,11 +336,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

@@ -5,12 +5,11 @@ import (
"fmt" "fmt"
"io/ioutil" "io/ioutil"
"log" "log"
"os"
"path/filepath" "path/filepath"
"runtime" "runtime"
"strconv" "strconv"
"time" "time"
homedir "github.com/mitchellh/go-homedir"
) )
// SystemHelper - Defines everything related to the system // SystemHelper - Defines everything related to the system
@@ -38,12 +37,13 @@ func NewSystemHelper() *SystemHelper {
// setSystemDirs calculates the system directories it is interested in // setSystemDirs calculates the system directories it is interested in
func (s *SystemHelper) setSystemDirs() { func (s *SystemHelper) setSystemDirs() {
var err error var err error
s.homeDir, err = homedir.Dir() s.homeDir, err = os.UserConfigDir()
if err != nil { if err != nil {
log.Fatal("Cannot find home directory! Please file a bug report!") log.Fatal("Cannot find home directory! Please file a bug report!")
} }
// TODO: A better config system // TODO: A better config system
s.wailsSystemDir = filepath.Join(s.homeDir, ".wails") s.wailsSystemDir = filepath.Join(s.homeDir, "wails")
s.wailsSystemConfig = filepath.Join(s.wailsSystemDir, s.configFilename) s.wailsSystemConfig = filepath.Join(s.wailsSystemDir, s.configFilename)
} }
@@ -274,16 +274,18 @@ func CheckDependencies(logger *Logger) (bool, error) {
distroInfo := GetLinuxDistroInfo() distroInfo := GetLinuxDistroInfo()
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, PopOS:
libraryChecker = DpkgInstalled libraryChecker = DpkgInstalled
case Arch, ArcoLinux, Manjaro, ManjaroARM: case Arch, ArcoLinux, ArchLabs, 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
case VoidLinux: case VoidLinux:
libraryChecker = XbpsInstalled libraryChecker = XbpsInstalled
case Solus:
libraryChecker = EOpkgInstalled
default: default:
return false, RequestSupportForDistribution(distroInfo) return false, RequestSupportForDistribution(distroInfo)
} }

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

@@ -0,0 +1,5 @@
# README
This is an experimental template for vanilla HTML/JS/CSS.
The webpack rules may need to be adjusted to correctly embed all assets. Babel may also need to be setup correctly.

View File

@@ -0,0 +1,42 @@
{
"name": "vanilla",
"author": "Lea<l>",
"private": true,
"scripts": {
"serve": "webpack-dev-server",
"build": "npx webpack"
},
"dependencies": {
"core-js": "^3.6.4",
"regenerator-runtime": "^0.13.3",
"@wailsapp/runtime": "^1.0.10"
},
"devDependencies": {
"babel-eslint": "^10.1.0",
"copy-webpack-plugin": "^6.0.2",
"eslint": "^6.8.0",
"eventsource-polyfill": "^0.9.6",
"webpack": "^4.43.0",
"webpack-cli": "^3.3.11",
"webpack-dev-server": "^3.11.0",
"webpack-hot-middleware": "^2.25.0"
},
"eslintConfig": {
"root": true,
"env": {
"node": true
},
"extends": [
"eslint:recommended"
],
"rules": {},
"parserOptions": {
"parser": "babel-eslint"
}
},
"browserslist": [
"> 1%",
"last 2 versions",
"not ie <= 8"
]
}

View File

@@ -0,0 +1,9 @@
<html>
<head>
<link rel="stylesheet" type="text/css" href="main.css">
</head>
<body>
<div id="app"></div>
<script src="main.js"></script>
</body>
</html>

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,30 @@
import 'core-js/stable';
const runtime = require('@wailsapp/runtime');
// Main entry point
function start() {
// Ensure the default app div is 100% wide/high
var app = document.getElementById('app');
app.style.width = '100%';
app.style.height = '100%';
// Inject html
app.innerHTML = `
<div class='logo'></div>
<div class='container'>
<button id='button'>Click Me!</button>
<div id='result'/>
</div>
`;
// Connect button to Go method
document.getElementById('button').onclick = function() {
window.backend.basic().then( function(result) {
document.getElementById('result').innerText = result;
});
};
};
// We provide our entrypoint as a callback for runtime.Init
runtime.Init(start);

View File

@@ -0,0 +1,56 @@
const path = require('path');
const CopyWebpackPlugin = require('copy-webpack-plugin');
let imageSizeLimit = 9007199254740991; // Number.MAX_SAFE_INTEGER
let sourceDir = path.resolve(__dirname, 'src');
let buildDir = path.resolve(__dirname, 'build');
module.exports = {
entry: {
index: path.resolve(sourceDir, 'main.js')
},
output: {
path: buildDir,
filename: 'main.js'
},
optimization: {
splitChunks: false
},
devServer: {
disableHostCheck: true,
contentBase: path.join(__dirname, 'src'),
compress: true,
open: true,
port: 8090
},
mode: 'production',
module: {
rules: [
{
test: /\.(png|gif|jpg|woff2?|eot|ttf|otf|svg)(\?.*)?$/i,
use: [
{
loader: 'url-loader',
options: {
limit: imageSizeLimit
}
}
],
}
]
},
plugins: [
new CopyWebpackPlugin({
patterns: [
{
from: path.resolve(sourceDir, 'main.css'),
to: path.resolve(buildDir, 'main.css')
},
{
from: path.resolve(sourceDir, 'index.html'),
to: path.resolve(buildDir, 'index.html')
},
]
})
]
};

View File

@@ -0,0 +1,5 @@
module {{.BinaryName}}
require (
github.com/wailsapp/wails {{.WailsVersion}}
)

View File

@@ -0,0 +1,27 @@
package main
import (
"github.com/leaanthony/mewn"
"github.com/wailsapp/wails"
)
func basic() string {
return "Hello World!"
}
func main() {
js := mewn.String("./frontend/build/main.js")
css := mewn.String("./frontend/build/main.css")
app := wails.CreateApp(&wails.AppConfig{
Width: 1024,
Height: 768,
Title: "{{.Name}}",
JS: js,
CSS: css,
Colour: "#131313",
})
app.Bind(basic)
app.Run()
}

View File

@@ -0,0 +1,12 @@
{
"name": "Vanilla",
"shortdescription": "A Vanilla HTML/JS template",
"description": "A basic template using plain html/js and bundled using Webpack 4",
"author": "Lea Anthony<lea.anthony@gmail.com>",
"created": "2020-06-14",
"frontenddir": "frontend",
"install": "npm install",
"build": "npm run build",
"serve": "npm run serve",
"bridge": "src"
}

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.7.1"

View File

@@ -6,11 +6,21 @@ 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
@@ -19,6 +29,7 @@ func init() {
var typescriptFilename = "" var typescriptFilename = ""
var verbose = false var verbose = false
var platform = "" var platform = ""
var ldflags = ""
buildSpinner := spinner.NewSpinner() buildSpinner := spinner.NewSpinner()
buildSpinner.SetSpinSpeed(50) buildSpinner.SetSpinSpeed(50)
@@ -31,7 +42,15 @@ func init() {
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) StringFlag("ldflags", "Extra options for -ldflags", &ldflags)
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 {
@@ -57,6 +76,28 @@ 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]
}
// Add ldflags
projectOptions.LdFlags = ldflags
// Validate config // Validate config
// Check if we have a frontend // Check if we have a frontend
err = cmd.ValidateFrontendConfig(projectOptions) err = cmd.ValidateFrontendConfig(projectOptions)
@@ -135,29 +176,6 @@ 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,6 +2,7 @@ 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"
@@ -34,7 +35,6 @@ 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
@@ -44,6 +44,10 @@ func init() {
return err return err
} }
// Set project options
projectOptions.Verbose = verbose
projectOptions.Platform = runtime.GOOS
// Save project directory // Save project directory
projectDir := fs.Cwd() projectDir := fs.Cwd()

View File

@@ -3,9 +3,9 @@ package main
import ( import (
"fmt" "fmt"
"log" "log"
"os"
"github.com/leaanthony/spinner" "github.com/leaanthony/spinner"
"github.com/mitchellh/go-homedir"
"github.com/wailsapp/wails/cmd" "github.com/wailsapp/wails/cmd"
) )
@@ -146,7 +146,7 @@ func updateToVersion(targetVersion *cmd.SemanticVersion, force bool) error {
updateSpinner.Start("Installing Wails " + desiredVersion) updateSpinner.Start("Installing Wails " + desiredVersion)
// Run command in non module directory // Run command in non module directory
homeDir, err := homedir.Dir() homeDir, err := os.UserHomeDir()
if err != nil { if err != nil {
log.Fatal("Cannot find home directory! Please file a bug report!") log.Fatal("Cannot find home directory! Please file a bug report!")
} }

11
go.mod
View File

@@ -10,23 +10,22 @@ 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.1 github.com/leaanthony/slicer v1.4.0
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
github.com/mitchellh/go-homedir v1.1.0
github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646 // indirect github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646 // indirect
github.com/pkg/browser v0.0.0-20180916011732-0a3d74bf9ce4 github.com/pkg/browser v0.0.0-20180916011732-0a3d74bf9ce4
github.com/pkg/errors v0.8.1 // indirect github.com/pkg/errors v0.8.1 // indirect
github.com/sirupsen/logrus v1.4.1 github.com/sirupsen/logrus v1.4.1
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/image v0.0.0-20200430140353-33d19683fad8
golang.org/x/net v0.0.0-20190509222800-a4d6f7feada5 // indirect golang.org/x/net v0.0.0-20200625001655-4c5254603344 // indirect
golang.org/x/sys v0.0.0-20190509141414-a5b02f93d862 golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd
golang.org/x/text v0.3.0 golang.org/x/text v0.3.0
gopkg.in/AlecAivazis/survey.v1 v1.8.4 gopkg.in/AlecAivazis/survey.v1 v1.8.4
gopkg.in/yaml.v3 v3.0.0-20190709130402-674ba3eaed22 gopkg.in/yaml.v3 v3.0.0-20190709130402-674ba3eaed22
) )
go 1.12 go 1.13

20
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.1 h1:X/SmRIDhkUAolP79mSTO0jTcVX1k504PJBqvV6TwP0w= github.com/leaanthony/slicer v1.4.0 h1:Q9u4w+UBU4WHjXnEDdz+eRLMKF/rnyosRBiqULnc1J8=
github.com/leaanthony/slicer v1.4.1/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=
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=
@@ -46,8 +46,6 @@ github.com/mattn/go-isatty v0.0.7 h1:UvyT9uN+3r7yLEYSlJsbQGdsaB/a0DlgWP3pql6iwOc
github.com/mattn/go-isatty v0.0.7/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= github.com/mattn/go-isatty v0.0.7/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b h1:j7+1HpAFS1zy5+Q4qx1fWh90gTKwiN4QCGoY9TWyyO4= github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b h1:j7+1HpAFS1zy5+Q4qx1fWh90gTKwiN4QCGoY9TWyyO4=
github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE= github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE=
github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y=
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646 h1:zYyBkD/k9seD2A7fsi6Oo2LfFZAehjjQMERAvZLEDnQ= github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646 h1:zYyBkD/k9seD2A7fsi6Oo2LfFZAehjjQMERAvZLEDnQ=
github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646/go.mod h1:jpp1/29i3P1S/RLdc7JQKbRpFeM1dOBd8T9ki5s+AY8= github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646/go.mod h1:jpp1/29i3P1S/RLdc7JQKbRpFeM1dOBd8T9ki5s+AY8=
github.com/pkg/browser v0.0.0-20180916011732-0a3d74bf9ce4 h1:49lOXmGaUpV9Fz3gd7TFZY106KVlPVa5jcYD1gaQf98= github.com/pkg/browser v0.0.0-20180916011732-0a3d74bf9ce4 h1:49lOXmGaUpV9Fz3gd7TFZY106KVlPVa5jcYD1gaQf98=
@@ -68,19 +66,21 @@ github.com/syossan27/tebata v0.0.0-20180602121909-b283fe4bc5ba h1:2DHfQOxcpWdGf5
github.com/syossan27/tebata v0.0.0-20180602121909-b283fe4bc5ba/go.mod h1:iLnlXG2Pakcii2CU0cbY07DRCSvpWNa7nFxtevhOChk= github.com/syossan27/tebata v0.0.0-20180602121909-b283fe4bc5ba/go.mod h1:iLnlXG2Pakcii2CU0cbY07DRCSvpWNa7nFxtevhOChk=
golang.org/x/crypto v0.0.0-20190123085648-057139ce5d2b/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190123085648-057139ce5d2b/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
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-20200622213623-75b288015ac9 h1:psW17arqaxU48Z5kZ0CQnkZWQJsqcURM6tKiBApRjXI=
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
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-20200625001655-4c5254603344 h1:vGXIOMxbNfDTk/aXCmfdLgkrSV+Z2tcbze+pEc3v5W4=
golang.org/x/net v0.0.0-20190509222800-a4d6f7feada5/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
golang.org/x/sys v0.0.0-20180606202747-9527bec2660b/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180606202747-9527bec2660b/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181228144115-9a3f9b0469bb/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181228144115-9a3f9b0469bb/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190509141414-a5b02f93d862 h1:rM0ROo5vb9AdYJi1110yjWGMej9ITfKddS89P3Fkhug= golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd h1:xhmwyvizuTgC2qz7ZlMluP20uW+C3Rm0FD/WLDX8884=
golang.org/x/sys v0.0.0-20190509141414-a5b02f93d862/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg= golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
gopkg.in/AlecAivazis/survey.v1 v1.8.4 h1:10xXXN3wgIhPheb5NI58zFgZv32Ana7P3Tl4shW+0Qc= gopkg.in/AlecAivazis/survey.v1 v1.8.4 h1:10xXXN3wgIhPheb5NI58zFgZv32Ana7P3Tl4shW+0Qc=

View File

@@ -17,9 +17,9 @@ type Renderer interface {
NotifyEvent(eventData *messages.EventData) error NotifyEvent(eventData *messages.EventData) error
// Dialog Runtime // Dialog Runtime
SelectFile() string SelectFile(title string, filter string) string
SelectDirectory() string SelectDirectory() string
SelectSaveFile() string SelectSaveFile(title string, filter string) string
// Window Runtime // Window Runtime
SetColour(string) error SetColour(string) error

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())
@@ -116,7 +114,7 @@ func (h *Bridge) NewBinding(methodName string) error {
// SelectFile is unsupported for Bridge but required // SelectFile is unsupported for Bridge but required
// for the Renderer interface // for the Renderer interface
func (h *Bridge) SelectFile() string { func (h *Bridge) SelectFile(title string, filter string) string {
h.log.Warn("SelectFile() unsupported in bridge mode") h.log.Warn("SelectFile() unsupported in bridge mode")
return "" return ""
} }
@@ -130,7 +128,7 @@ func (h *Bridge) SelectDirectory() string {
// SelectSaveFile is unsupported for Bridge but required // SelectSaveFile is unsupported for Bridge but required
// for the Renderer interface // for the Renderer interface
func (h *Bridge) SelectSaveFile() string { func (h *Bridge) SelectSaveFile(title string, filter string) string {
h.log.Warn("SelectSaveFile() unsupported in bridge mode") h.log.Warn("SelectSaveFile() unsupported in bridge mode")
return "" return ""
} }
@@ -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

View File

@@ -245,7 +245,7 @@ func (w *WebView) NewBinding(methodName string) error {
} }
// SelectFile opens a dialog that allows the user to select a file // SelectFile opens a dialog that allows the user to select a file
func (w *WebView) SelectFile() string { func (w *WebView) SelectFile(title string, filter string) string {
var result string var result string
// We need to run this on the main thread, however Dispatch is // We need to run this on the main thread, however Dispatch is
@@ -255,7 +255,7 @@ func (w *WebView) SelectFile() string {
wg.Add(1) wg.Add(1)
go func() { go func() {
w.window.Dispatch(func() { w.window.Dispatch(func() {
result = w.window.Dialog(wv.DialogTypeOpen, 0, "Select File", "") result = w.window.Dialog(wv.DialogTypeOpen, 0, title, "", filter)
wg.Done() wg.Done()
}) })
}() }()
@@ -273,7 +273,7 @@ func (w *WebView) SelectDirectory() string {
wg.Add(1) wg.Add(1)
go func() { go func() {
w.window.Dispatch(func() { w.window.Dispatch(func() {
result = w.window.Dialog(wv.DialogTypeOpen, wv.DialogFlagDirectory, "Select Directory", "") result = w.window.Dialog(wv.DialogTypeOpen, wv.DialogFlagDirectory, "Select Directory", "", "")
wg.Done() wg.Done()
}) })
}() }()
@@ -282,7 +282,7 @@ func (w *WebView) SelectDirectory() string {
} }
// SelectSaveFile opens a dialog that allows the user to select a file to save // SelectSaveFile opens a dialog that allows the user to select a file to save
func (w *WebView) SelectSaveFile() string { func (w *WebView) SelectSaveFile(title string, filter string) string {
var result string var result string
// We need to run this on the main thread, however Dispatch is // We need to run this on the main thread, however Dispatch is
// non-blocking so we launch this in a goroutine and wait for // non-blocking so we launch this in a goroutine and wait for
@@ -291,7 +291,7 @@ func (w *WebView) SelectSaveFile() string {
wg.Add(1) wg.Add(1)
go func() { go func() {
w.window.Dispatch(func() { w.window.Dispatch(func() {
result = w.window.Dialog(wv.DialogTypeSave, 0, "Save file", "") result = w.window.Dialog(wv.DialogTypeSave, 0, title, "", filter)
wg.Done() wg.Done()
}) })
}() }()

View File

@@ -74,9 +74,9 @@ static inline void CgoWebViewSetColor(void *w, uint8_t r, uint8_t g, uint8_t b,
} }
static inline void CgoDialog(void *w, int dlgtype, int flags, static inline void CgoDialog(void *w, int dlgtype, int flags,
char *title, char *arg, char *res, size_t ressz) { char *title, char *arg, char *res, size_t ressz, char *filter) {
webview_dialog(w, dlgtype, flags, webview_dialog(w, dlgtype, flags,
(const char*)title, (const char*) arg, res, ressz); (const char*)title, (const char*) arg, res, ressz, filter);
} }
static inline int CgoWebViewEval(void *w, char *js) { static inline int CgoWebViewEval(void *w, char *js) {
@@ -186,7 +186,7 @@ type WebView interface {
// Dialog() opens a system dialog of the given type and title. String // Dialog() opens a system dialog of the given type and title. String
// argument can be provided for certain dialogs, such as alert boxes. For // argument can be provided for certain dialogs, such as alert boxes. For
// alert boxes argument is a message inside the dialog box. // alert boxes argument is a message inside the dialog box.
Dialog(dlgType DialogType, flags int, title string, arg string) string Dialog(dlgType DialogType, flags int, title string, arg string, filter string) string
// Terminate() breaks the main UI loop. This method must be called from the main thread // Terminate() breaks the main UI loop. This method must be called from the main thread
// only. See Dispatch() for more details. // only. See Dispatch() for more details.
Terminate() Terminate()
@@ -311,7 +311,7 @@ func (w *webview) SetFullscreen(fullscreen bool) {
C.CgoWebViewSetFullscreen(w.w, C.int(boolToInt(fullscreen))) C.CgoWebViewSetFullscreen(w.w, C.int(boolToInt(fullscreen)))
} }
func (w *webview) Dialog(dlgType DialogType, flags int, title string, arg string) string { func (w *webview) Dialog(dlgType DialogType, flags int, title string, arg string, filter string) string {
const maxPath = 4096 const maxPath = 4096
titlePtr := C.CString(title) titlePtr := C.CString(title)
defer C.free(unsafe.Pointer(titlePtr)) defer C.free(unsafe.Pointer(titlePtr))
@@ -319,8 +319,10 @@ func (w *webview) Dialog(dlgType DialogType, flags int, title string, arg string
defer C.free(unsafe.Pointer(argPtr)) defer C.free(unsafe.Pointer(argPtr))
resultPtr := (*C.char)(C.calloc((C.size_t)(unsafe.Sizeof((*C.char)(nil))), (C.size_t)(maxPath))) resultPtr := (*C.char)(C.calloc((C.size_t)(unsafe.Sizeof((*C.char)(nil))), (C.size_t)(maxPath)))
defer C.free(unsafe.Pointer(resultPtr)) defer C.free(unsafe.Pointer(resultPtr))
filterPtr := C.CString(filter)
defer C.free(unsafe.Pointer(filterPtr))
C.CgoDialog(w.w, C.int(dlgType), C.int(flags), titlePtr, C.CgoDialog(w.w, C.int(dlgType), C.int(flags), titlePtr,
argPtr, resultPtr, C.size_t(maxPath)) argPtr, resultPtr, C.size_t(maxPath), filterPtr)
return C.GoString(resultPtr) return C.GoString(resultPtr)
} }

View File

@@ -174,7 +174,7 @@ struct webview_priv
WEBVIEW_API void webview_dialog(struct webview *w, WEBVIEW_API void webview_dialog(struct webview *w,
enum webview_dialog_type dlgtype, int flags, enum webview_dialog_type dlgtype, int flags,
const char *title, const char *arg, const char *title, const char *arg,
char *result, size_t resultsz); char *result, size_t resultsz, char *filter);
WEBVIEW_API void webview_dispatch(struct webview *w, webview_dispatch_fn fn, WEBVIEW_API void webview_dispatch(struct webview *w, webview_dispatch_fn fn,
void *arg); void *arg);
WEBVIEW_API void webview_terminate(struct webview *w); WEBVIEW_API void webview_terminate(struct webview *w);
@@ -418,7 +418,7 @@ struct webview_priv
WEBVIEW_API void webview_dialog(struct webview *w, WEBVIEW_API void webview_dialog(struct webview *w,
enum webview_dialog_type dlgtype, int flags, enum webview_dialog_type dlgtype, int flags,
const char *title, const char *arg, const char *title, const char *arg,
char *result, size_t resultsz) char *result, size_t resultsz, char *filter)
{ {
GtkWidget *dlg; GtkWidget *dlg;
if (result != NULL) if (result != NULL)
@@ -438,6 +438,17 @@ struct webview_priv
"_Cancel", GTK_RESPONSE_CANCEL, "_Cancel", GTK_RESPONSE_CANCEL,
(dlgtype == WEBVIEW_DIALOG_TYPE_OPEN ? "_Open" : "_Save"), (dlgtype == WEBVIEW_DIALOG_TYPE_OPEN ? "_Open" : "_Save"),
GTK_RESPONSE_ACCEPT, NULL); GTK_RESPONSE_ACCEPT, NULL);
if (filter[0] != '\0') {
GtkFileFilter *file_filter = gtk_file_filter_new();
gchar **filters = g_strsplit(filter, ",", -1);
gint i;
for(i = 0; filters && filters[i]; i++) {
gtk_file_filter_add_pattern(file_filter, filters[i]);
}
gtk_file_filter_set_name(file_filter, filter);
gtk_file_chooser_add_filter(GTK_FILE_CHOOSER(dlg), file_filter);
g_strfreev(filters);
}
gtk_file_chooser_set_local_only(GTK_FILE_CHOOSER(dlg), FALSE); gtk_file_chooser_set_local_only(GTK_FILE_CHOOSER(dlg), FALSE);
gtk_file_chooser_set_select_multiple(GTK_FILE_CHOOSER(dlg), FALSE); gtk_file_chooser_set_select_multiple(GTK_FILE_CHOOSER(dlg), FALSE);
gtk_file_chooser_set_show_hidden(GTK_FILE_CHOOSER(dlg), TRUE); gtk_file_chooser_set_show_hidden(GTK_FILE_CHOOSER(dlg), TRUE);
@@ -1227,7 +1238,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 +1248,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 +1288,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 +1298,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);
@@ -1410,6 +1421,8 @@ struct webview_priv
wc.hInstance = hInstance; wc.hInstance = hInstance;
wc.lpfnWndProc = wndproc; wc.lpfnWndProc = wndproc;
wc.lpszClassName = classname; wc.lpszClassName = classname;
wc.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(100));
wc.hIconSm = LoadIcon(hInstance, MAKEINTRESOURCE(100));
RegisterClassEx(&wc); RegisterClassEx(&wc);
style = WS_OVERLAPPEDWINDOW; style = WS_OVERLAPPEDWINDOW;
@@ -1467,7 +1480,6 @@ 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);
@@ -1494,6 +1506,11 @@ 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;
@@ -1603,7 +1620,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)
{ {
@@ -1611,12 +1628,11 @@ 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)
@@ -1777,7 +1793,7 @@ struct webview_priv
WEBVIEW_API void webview_dialog(struct webview *w, WEBVIEW_API void webview_dialog(struct webview *w,
enum webview_dialog_type dlgtype, int flags, enum webview_dialog_type dlgtype, int flags,
const char *title, const char *arg, const char *title, const char *arg,
char *result, size_t resultsz) char *result, size_t resultsz, char *filter)
{ {
if (dlgtype == WEBVIEW_DIALOG_TYPE_OPEN || if (dlgtype == WEBVIEW_DIALOG_TYPE_OPEN ||
dlgtype == WEBVIEW_DIALOG_TYPE_SAVE) dlgtype == WEBVIEW_DIALOG_TYPE_SAVE)
@@ -1817,6 +1833,32 @@ struct webview_priv
FOS_NOTESTFILECREATE | FOS_NODEREFERENCELINKS | FOS_NOTESTFILECREATE | FOS_NODEREFERENCELINKS |
FOS_FORCESHOWHIDDEN | FOS_DEFAULTNOMINIMODE; FOS_FORCESHOWHIDDEN | FOS_DEFAULTNOMINIMODE;
} }
if (filter[0] != '\0')
{
int count;
int i=0;
char* token;
char* filter_dup = strdup(filter);
for (count=1; filter[count]; filter[count]==',' ? count++ : *filter++);
COMDLG_FILTERSPEC rgSpec[count];
char* filters[count];
token = strtok(filter_dup, ",");
while(token != NULL)
{
filters[i] = token;
token = strtok(NULL, ",");
i++;
}
for (int i=0; i < count; i++) {
wchar_t *wFilter = (wchar_t *)malloc(4096);
MultiByteToWideChar(CP_ACP, 0, filters[i], -1, wFilter, 4096);
rgSpec[i].pszName = wFilter;
rgSpec[i].pszSpec = wFilter;
}
if (dlg->lpVtbl->SetFileTypes(dlg, count, rgSpec) != S_OK) {
goto error_dlg;
}
}
if (dlg->lpVtbl->GetOptions(dlg, &opts) != S_OK) if (dlg->lpVtbl->GetOptions(dlg, &opts) != S_OK)
{ {
goto error_dlg; goto error_dlg;
@@ -1927,22 +1969,26 @@ 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, "");
if (strlen(filename)) { filename[255] = '\0';
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 =
@@ -1955,7 +2001,7 @@ static void webview_run_input_open_panel(id self, SEL cmd, id webview,
{ {
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)
@@ -2194,12 +2240,16 @@ static void webview_run_input_open_panel(id self, SEL cmd, id webview,
WEBVIEW_API void webview_dialog(struct webview *w, WEBVIEW_API void webview_dialog(struct webview *w,
enum webview_dialog_type dlgtype, int flags, enum webview_dialog_type dlgtype, int flags,
const char *title, const char *arg, const char *title, const char *arg,
char *result, size_t resultsz) char *result, size_t resultsz, char *filter)
{ {
if (dlgtype == WEBVIEW_DIALOG_TYPE_OPEN || if (dlgtype == WEBVIEW_DIALOG_TYPE_OPEN ||
dlgtype == WEBVIEW_DIALOG_TYPE_SAVE) dlgtype == WEBVIEW_DIALOG_TYPE_SAVE)
{ {
NSSavePanel *panel; NSSavePanel *panel;
NSString *filter_str = [NSString stringWithUTF8String:filter];
filter_str = [filter_str stringByReplacingOccurrencesOfString:@"*."
withString:@""];
NSArray *fileTypes = [filter_str componentsSeparatedByString:@","];
if (dlgtype == WEBVIEW_DIALOG_TYPE_OPEN) if (dlgtype == WEBVIEW_DIALOG_TYPE_OPEN)
{ {
NSOpenPanel *openPanel = [NSOpenPanel openPanel]; NSOpenPanel *openPanel = [NSOpenPanel openPanel];
@@ -2212,6 +2262,10 @@ static void webview_run_input_open_panel(id self, SEL cmd, id webview,
{ {
[openPanel setCanChooseFiles:YES]; [openPanel setCanChooseFiles:YES];
[openPanel setCanChooseDirectories:NO]; [openPanel setCanChooseDirectories:NO];
if(filter[0] != NULL)
{
[openPanel setAllowedFileTypes:fileTypes];
}
} }
[openPanel setResolvesAliases:NO]; [openPanel setResolvesAliases:NO];
[openPanel setAllowsMultipleSelection:NO]; [openPanel setAllowsMultipleSelection:NO];
@@ -2225,6 +2279,10 @@ static void webview_run_input_open_panel(id self, SEL cmd, id webview,
[panel setShowsHiddenFiles:YES]; [panel setShowsHiddenFiles:YES];
[panel setExtensionHidden:NO]; [panel setExtensionHidden:NO];
[panel setCanSelectHiddenExtension:NO]; [panel setCanSelectHiddenExtension:NO];
if(filter[0] != NULL)
{
[panel setAllowedFileTypes:fileTypes];
}
[panel setTreatsFilePackagesAsDirectories:YES]; [panel setTreatsFilePackagesAsDirectories:YES];
[panel beginSheetModalForWindow:w->priv.window [panel beginSheetModalForWindow:w->priv.window
completionHandler:^(NSInteger result) { completionHandler:^(NSInteger result) {

View File

@@ -1,6 +1,7 @@
package runtime package runtime
import "github.com/wailsapp/wails/lib/interfaces" import "github.com/wailsapp/wails/lib/interfaces"
import "strings"
// Dialog exposes an interface to native dialogs // Dialog exposes an interface to native dialogs
type Dialog struct { type Dialog struct {
@@ -15,8 +16,16 @@ func NewDialog(renderer interfaces.Renderer) *Dialog {
} }
// SelectFile prompts the user to select a file // SelectFile prompts the user to select a file
func (r *Dialog) SelectFile() string { func (r *Dialog) SelectFile(params ...string) string {
return r.renderer.SelectFile() title := "Select File"
filter := ""
if len(params) > 0 {
title = params[0]
}
if len(params) > 1 {
filter = strings.Replace(params[1], " ", "", -1)
}
return r.renderer.SelectFile(title, filter)
} }
// SelectDirectory prompts the user to select a directory // SelectDirectory prompts the user to select a directory
@@ -25,6 +34,14 @@ func (r *Dialog) SelectDirectory() string {
} }
// SelectSaveFile prompts the user to select a file for saving // SelectSaveFile prompts the user to select a file for saving
func (r *Dialog) SelectSaveFile() string { func (r *Dialog) SelectSaveFile(params ...string) string {
return r.renderer.SelectSaveFile() title := "Select Save"
filter := ""
if len(params) > 0 {
title = params[0]
}
if len(params) > 1 {
filter = strings.Replace(params[1], " ", "", -1)
}
return r.renderer.SelectSaveFile(title, filter)
} }

View File

@@ -1,9 +1,9 @@
package runtime package runtime
import homedir "github.com/mitchellh/go-homedir" import "os"
// FileSystem exposes file system utilities to the runtime // FileSystem exposes file system utilities to the runtime
type FileSystem struct {} type FileSystem struct{}
// NewFileSystem creates a new FileSystem struct // NewFileSystem creates a new FileSystem struct
func NewFileSystem() *FileSystem { func NewFileSystem() *FileSystem {
@@ -12,5 +12,5 @@ func NewFileSystem() *FileSystem {
// HomeDir returns the user's home directory // HomeDir returns the user's home directory
func (r *FileSystem) HomeDir() (string, error) { func (r *FileSystem) HomeDir() (string, error) {
return homedir.Dir() return os.UserHomeDir()
} }

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",

View File

@@ -7,11 +7,11 @@ declare const wailsapp__runtime: {
}; };
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! ****"