Add run command for running tasks in a Taskfile.yml

Add `generate` command for generating assets
Add build example
This commit is contained in:
Lea Anthony 2023-01-04 20:01:13 +11:00
commit 174a31b49a
No known key found for this signature in database
GPG key ID: 33DAF7BB90A58405
31 changed files with 693 additions and 113 deletions

View file

@ -5,9 +5,25 @@ There are a number of commands related to tooling, such as icon generation and a
## Commands
### icon
### run
The `icon` command generates icons for your project. It takes a single argument which is the path to the icon file.
The run command is for running tasks defined in `Taskfile.yml`.
| Flag | Type | Description | Default |
|--------------------|--------|------------------------------------------------------|-----------------------|
| `-t` | string | The name of the task to run | |
### generate
The `generate` command is used to generate resources and assets for your Wails project.
It can be used to generate many things including:
- application icons,
- resource files for Windows applications
- Info.plist files for macOS deployments
#### icon
The `icon` command generates icons for your project.
| Flag | Type | Description | Default |
|--------------------|--------|------------------------------------------------------|-----------------------|
@ -18,18 +34,18 @@ The `icon` command generates icons for your project. It takes a single argument
| `-macFilename` | string | The output filename for the Mac icon bundle | icons.icns |
```bash
wails icon -input myicon.png -sizes "32,64,128" -windowsFilename myicon.ico -macFilename myicon.icns
wails generate icon -input myicon.png -sizes "32,64,128" -windowsFilename myicon.ico -macFilename myicon.icns
```
This will generate icons for mac and windows and save them in the current directory as `myicon.ico`
and `myicons.icns`.
### syso
#### syso
The `syso` command generates a Windows resource file (aka `.syso`).
```bash
wails syso <options>
wails generate syso <options>
```
| Flag | Type | Description | Default |
@ -53,4 +69,11 @@ information in the syso file.
NOTE: We use [winres](https://github.com/tc-hib/winres) to generate the syso file. Please
refer to the winres documentation for more information.
NOTE: Whilst the tool will work for 32-bit Windows, it is not supported. Please use 64-bit.
NOTE: Whilst the tool will work for 32-bit Windows, it is not supported. Please use 64-bit.
#### defaults
```bash
wails icon -input myicon.png -sizes "32,64,128" -windowsFilename myicon.ico -macFilename myicon.icns
```
This will generate all the default assets and resources in the current directory. I

View file

@ -8,12 +8,12 @@ import (
)
func main() {
app := clir.NewCli("wails", "The Wails CLI", "v3")
app.NewSubCommandFunction("init", "Initialise a new project", commands.Init)
app.NewSubCommandFunction("build", "Build the project", commands.Build)
app.NewSubCommandFunction("run", "Run a task", commands.Run)
generate := app.NewSubCommand("generate", "Generation tools")
generate.NewSubCommandFunction("icon", "Generate icons", commands.GenerateIcon)
generate.NewSubCommandFunction("defaults", "Generate default build assets", commands.Defaults)
generate.NewSubCommandFunction("icons", "Generate icons", commands.GenerateIcons)
generate.NewSubCommandFunction("syso", "Generate Windows .syso file", commands.GenerateSyso)
err := app.Run()
if err != nil {

View file

@ -0,0 +1,15 @@
# Build
Wails has adopted [Taskfile](https://taskfile.dev) as its build tool. This is optional
and any build tool can be used. However, Taskfile is a great tool, and we recommend it.
The Wails CLI has built-in integration with Taskfile so the standalone version is not a
requirement.
## Building
To build the example, run:
```bash
wails run -t build
```

View file

@ -0,0 +1,31 @@
version: '3'
tasks:
build:
cmds:
- go build -gcflags=all="-N -l" -o bin/testapp main.go
vars:
BIN_NAME: $NAME
env:
CGO_CFLAGS: "-mmacosx-version-min=10.13"
CGO_LDFLAGS: "-mmacosx-version-min=10.13"
generate-icons:
dir: build
cmds:
# Generates both .ico and .icns files
- wails generate icon -input appicon.png
build-prod:
cmds:
- go build -tags production -ldflags="-w -s" -o bin/testapp
package-darwin:
deps:
- build-prod
- generate-icons
cmds:
- mkdir -p buildtest.app/Contents/{MacOS,Resources}
- cp build/icons.icns buildtest.app/Contents/Resources
- cp bin/testapp buildtest.app/Contents/MacOS
- cp build/Info.plist buildtest.app/Contents

View file

@ -0,0 +1,35 @@
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleName</key>
<string>My App</string>
<key>CFBundleExecutable</key>
<string>app</string>
<key>CFBundleIdentifier</key>
<string>com.wails.app</string>
<key>CFBundleVersion</key>
<string>v1.0.0</string>
<key>CFBundleGetInfoString</key>
<string>The ultimate thing</string>
<key>CFBundleShortVersionString</key>
<string>v1</string>
<key>CFBundleIconFile</key>
<string>icons</string>
<key>LSMinimumSystemVersion</key>
<string>10.13.0</string>
<key>NSHighResolutionCapable</key>
<string>true</string>
<key>NSHumanReadableCopyright</key>
<string>(c) Me</string>
<key>NSAppTransportSecurity</key>
<dict>
<key>NSAllowsLocalNetworking</key>
<true/>
</dict>
</dict>
</plist>
</dict>
</plist>

View file

@ -0,0 +1,27 @@
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleName</key>
<string>My App</string>
<key>CFBundleExecutable</key>
<string>app</string>
<key>CFBundleIdentifier</key>
<string>com.wails.app</string>
<key>CFBundleVersion</key>
<string>v1.0.0</string>
<key>CFBundleGetInfoString</key>
<string>The ultimate thing</string>
<key>CFBundleShortVersionString</key>
<string>v1</string>
<key>CFBundleIconFile</key>
<string>icons</string>
<key>LSMinimumSystemVersion</key>
<string>10.13.0</string>
<key>NSHighResolutionCapable</key>
<string>true</string>
<key>NSHumanReadableCopyright</key>
<string>(c) Me</string>
</dict>
</plist>

View file

Before

Width:  |  Height:  |  Size: 130 KiB

After

Width:  |  Height:  |  Size: 130 KiB

Before After
Before After

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

View file

@ -0,0 +1,27 @@
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleName</key>
<string>My App</string>
<key>CFBundleExecutable</key>
<string>app</string>
<key>CFBundleIdentifier</key>
<string>com.wails.app</string>
<key>CFBundleVersion</key>
<string>v1.0.0</string>
<key>CFBundleGetInfoString</key>
<string>The ultimate thing</string>
<key>CFBundleShortVersionString</key>
<string>v1</string>
<key>CFBundleIconFile</key>
<string>icons</string>
<key>LSMinimumSystemVersion</key>
<string>10.13.0</string>
<key>NSHighResolutionCapable</key>
<string>true</string>
<key>NSHumanReadableCopyright</key>
<string>(c) Me</string>
</dict>
</plist>

275
exp/examples/build/main.go Executable file
View file

@ -0,0 +1,275 @@
package main
import (
_ "embed"
"fmt"
"log"
"math/rand"
"runtime"
"strconv"
"time"
"github.com/wailsapp/wails/exp/pkg/options"
"github.com/wailsapp/wails/exp/pkg/events"
"github.com/wailsapp/wails/exp/pkg/application"
)
func main() {
app := application.New(options.Application{
Name: "WebviewWindow Demo",
Description: "A demo of the WebviewWindow API",
Mac: options.Mac{
ApplicationShouldTerminateAfterLastWindowClosed: true,
},
})
app.On(events.Mac.ApplicationDidFinishLaunching, func() {
log.Println("ApplicationDidFinishLaunching")
})
currentWindow := func(fn func(window *application.WebviewWindow)) {
if app.CurrentWindow() != nil {
fn(app.CurrentWindow())
} else {
println("Current WebviewWindow is nil")
}
}
// Create a custom menu
menu := app.NewMenu()
menu.AddRole(application.AppMenu)
windowCounter := 1
// Let's make a "Demo" menu
myMenu := menu.AddSubmenu("New")
myMenu.Add("New WebviewWindow").
SetAccelerator("CmdOrCtrl+N").
OnClick(func(ctx *application.Context) {
app.NewWebviewWindow().
SetTitle("WebviewWindow "+strconv.Itoa(windowCounter)).
SetPosition(rand.Intn(1000), rand.Intn(800)).
SetURL("https://wails.io").
Show()
windowCounter++
})
myMenu.Add("New Frameless WebviewWindow").
SetAccelerator("CmdOrCtrl+F").
OnClick(func(ctx *application.Context) {
app.NewWebviewWindow().
SetTitle("WebviewWindow "+strconv.Itoa(windowCounter)).
SetPosition(rand.Intn(1000), rand.Intn(800)).
SetURL("https://wails.io").
SetFrameless(true).
Show()
windowCounter++
})
if runtime.GOOS == "darwin" {
myMenu.Add("New WebviewWindow (TitleBarHiddenInset)").
OnClick(func(ctx *application.Context) {
app.NewWebviewWindowWithOptions(&options.WebviewWindow{
Mac: options.MacWindow{
TitleBar: options.TitleBarHiddenInset,
InvisibleTitleBarHeight: 25,
},
}).
SetTitle("WebviewWindow "+strconv.Itoa(windowCounter)).
SetPosition(rand.Intn(1000), rand.Intn(800)).
SetHTML("<br/><br/><p>A TitleBarHiddenInset WebviewWindow example</p>").
Show()
windowCounter++
})
myMenu.Add("New WebviewWindow (TitleBarHiddenInsetUnified)").
OnClick(func(ctx *application.Context) {
app.NewWebviewWindowWithOptions(&options.WebviewWindow{
Mac: options.MacWindow{
TitleBar: options.TitleBarHiddenInsetUnified,
InvisibleTitleBarHeight: 50,
},
}).
SetTitle("WebviewWindow "+strconv.Itoa(windowCounter)).
SetPosition(rand.Intn(1000), rand.Intn(800)).
SetHTML("<br/><br/><p>A TitleBarHiddenInsetUnified WebviewWindow example</p>").
Show()
windowCounter++
})
myMenu.Add("New WebviewWindow (TitleBarHidden)").
OnClick(func(ctx *application.Context) {
app.NewWebviewWindowWithOptions(&options.WebviewWindow{
Mac: options.MacWindow{
TitleBar: options.TitleBarHidden,
InvisibleTitleBarHeight: 25,
},
}).
SetTitle("WebviewWindow "+strconv.Itoa(windowCounter)).
SetPosition(rand.Intn(1000), rand.Intn(800)).
SetHTML("<br/><br/><p>A TitleBarHidden WebviewWindow example</p>").
Show()
windowCounter++
})
}
sizeMenu := menu.AddSubmenu("Size")
sizeMenu.Add("Set Size (800,600)").OnClick(func(ctx *application.Context) {
currentWindow(func(w *application.WebviewWindow) {
w.SetSize(800, 600)
})
})
sizeMenu.Add("Set Size (Random)").OnClick(func(ctx *application.Context) {
currentWindow(func(w *application.WebviewWindow) {
w.SetSize(rand.Intn(800)+200, rand.Intn(600)+200)
})
})
sizeMenu.Add("Set Min Size (200,200)").OnClick(func(ctx *application.Context) {
currentWindow(func(w *application.WebviewWindow) {
w.SetMinSize(200, 200)
})
})
sizeMenu.Add("Set Max Size (600,600)").OnClick(func(ctx *application.Context) {
currentWindow(func(w *application.WebviewWindow) {
w.SetFullscreenButtonEnabled(false)
w.SetMaxSize(600, 600)
})
})
sizeMenu.Add("Get Current WebviewWindow Size").OnClick(func(ctx *application.Context) {
currentWindow(func(w *application.WebviewWindow) {
width, height := w.Size()
app.NewInfoDialog().SetTitle("Current WebviewWindow Size").SetMessage("Width: " + strconv.Itoa(width) + " Height: " + strconv.Itoa(height)).Show()
})
})
sizeMenu.Add("Reset Min Size").OnClick(func(ctx *application.Context) {
currentWindow(func(w *application.WebviewWindow) {
w.SetMinSize(0, 0)
})
})
sizeMenu.Add("Reset Max Size").OnClick(func(ctx *application.Context) {
currentWindow(func(w *application.WebviewWindow) {
w.SetMaxSize(0, 0)
w.SetFullscreenButtonEnabled(true)
})
})
positionMenu := menu.AddSubmenu("Position")
positionMenu.Add("Set Position (0,0)").OnClick(func(ctx *application.Context) {
currentWindow(func(w *application.WebviewWindow) {
w.SetPosition(0, 0)
})
})
positionMenu.Add("Set Position (Random)").OnClick(func(ctx *application.Context) {
currentWindow(func(w *application.WebviewWindow) {
w.SetPosition(rand.Intn(1000), rand.Intn(800))
})
})
positionMenu.Add("Get Position").OnClick(func(ctx *application.Context) {
currentWindow(func(w *application.WebviewWindow) {
x, y := w.Position()
app.NewInfoDialog().SetTitle("Current WebviewWindow Position").SetMessage("X: " + strconv.Itoa(x) + " Y: " + strconv.Itoa(y)).Show()
})
})
positionMenu.Add("Center").OnClick(func(ctx *application.Context) {
currentWindow(func(w *application.WebviewWindow) {
w.Center()
})
})
stateMenu := menu.AddSubmenu("State")
stateMenu.Add("Minimise (for 2 secs)").OnClick(func(ctx *application.Context) {
currentWindow(func(w *application.WebviewWindow) {
w.Minimise()
time.Sleep(2 * time.Second)
w.Restore()
})
})
stateMenu.Add("Maximise").OnClick(func(ctx *application.Context) {
currentWindow(func(w *application.WebviewWindow) {
w.Maximise()
})
})
stateMenu.Add("Fullscreen").OnClick(func(ctx *application.Context) {
currentWindow(func(w *application.WebviewWindow) {
w.Fullscreen()
})
})
stateMenu.Add("UnFullscreen").OnClick(func(ctx *application.Context) {
currentWindow(func(w *application.WebviewWindow) {
w.UnFullscreen()
})
})
stateMenu.Add("Restore").OnClick(func(ctx *application.Context) {
currentWindow(func(w *application.WebviewWindow) {
w.Restore()
})
})
stateMenu.Add("Hide (for 2 seconds)").OnClick(func(ctx *application.Context) {
currentWindow(func(w *application.WebviewWindow) {
w.Hide()
time.Sleep(2 * time.Second)
w.Show()
})
})
stateMenu.Add("Always on Top").OnClick(func(ctx *application.Context) {
currentWindow(func(w *application.WebviewWindow) {
w.SetAlwaysOnTop(true)
})
})
stateMenu.Add("Not always on Top").OnClick(func(ctx *application.Context) {
currentWindow(func(w *application.WebviewWindow) {
w.SetAlwaysOnTop(false)
})
})
stateMenu.Add("Google.com").OnClick(func(ctx *application.Context) {
currentWindow(func(w *application.WebviewWindow) {
w.SetURL("https://google.com")
})
})
stateMenu.Add("wails.io").OnClick(func(ctx *application.Context) {
currentWindow(func(w *application.WebviewWindow) {
w.SetURL("https://wails.io")
})
})
stateMenu.Add("Get Primary Screen").OnClick(func(ctx *application.Context) {
screen, err := app.GetPrimaryScreen()
if err != nil {
app.NewErrorDialog().SetTitle("Error").SetMessage(err.Error()).Show()
return
}
msg := fmt.Sprintf("Screen: %+v", screen)
app.NewInfoDialog().SetTitle("Primary Screen").SetMessage(msg).Show()
})
stateMenu.Add("Get Screens").OnClick(func(ctx *application.Context) {
screens, err := app.GetScreens()
if err != nil {
app.NewErrorDialog().SetTitle("Error").SetMessage(err.Error()).Show()
return
}
for _, screen := range screens {
msg := fmt.Sprintf("Screen: %+v", screen)
app.NewInfoDialog().SetTitle(fmt.Sprintf("Screen %s", screen.ID)).SetMessage(msg).Show()
}
})
stateMenu.Add("Get Screen for WebviewWindow").OnClick(func(ctx *application.Context) {
currentWindow(func(w *application.WebviewWindow) {
screen, err := w.GetScreen()
if err != nil {
app.NewErrorDialog().SetTitle("Error").SetMessage(err.Error()).Show()
return
}
msg := fmt.Sprintf("Screen: %+v", screen)
app.NewInfoDialog().SetTitle(fmt.Sprintf("Screen %s", screen.ID)).SetMessage(msg).Show()
})
})
app.NewWebviewWindow()
app.SetMenu(menu)
err := app.Run()
if err != nil {
log.Fatal(err)
}
}

View file

@ -3,7 +3,7 @@ module github.com/wailsapp/wails/exp
go 1.19
require (
github.com/go-task/task/v3 v3.19.0
github.com/go-task/task/v3 v3.19.1
github.com/jackmordaunt/icns/v2 v2.2.1
github.com/leaanthony/clir v1.3.0
github.com/leaanthony/winicon v1.0.0
@ -24,10 +24,12 @@ require (
github.com/sajari/fuzzy v1.0.0 // indirect
golang.org/x/exp v0.0.0-20220930202632-ec3f01382ef9 // indirect
golang.org/x/image v0.0.0-20201208152932-35266b937fa6 // indirect
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c // indirect
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f // indirect
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 // indirect
golang.org/x/sync v0.1.0 // indirect
golang.org/x/sys v0.3.0 // indirect
golang.org/x/term v0.3.0 // indirect
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
mvdan.cc/sh/v3 v3.6.0-0.dev.0.20220704111049-a6e3029cd899 // indirect
mvdan.cc/sh/v3 v3.6.0 // indirect
)
replace github.com/go-task/task/v3 v3.19.1 => github.com/wailsapp/task/v3 v3.19.1

View file

@ -1,21 +1,19 @@
github.com/creack/pty v1.1.17 h1:QeVUsEDNrLBW4tMgZHvxy18sKtr6VI492kBhUfhDJNI=
github.com/creack/pty v1.1.18 h1:n56/Zwd5o6whRC5PMGretI4IdRLlmBXYNjScPaBgsbY=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/fatih/color v1.13.0 h1:8LOYc1KYPPmyKMuN8QV2DNRWNbLo6LZ0iLs8+mlH53w=
github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk=
github.com/frankban/quicktest v1.14.0 h1:+cqqvzZV87b4adx/5ayVOaYZ2CrvM4ejQvUdBzPPUss=
github.com/frankban/quicktest v1.14.4 h1:g2rn0vABPOOXmZUj+vbmUp0lPoXEMuhTpIluN0XL9UY=
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 h1:p104kn46Q8WdvHunIJ9dAyjPVtrBPhSr3KT2yUst43I=
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE=
github.com/go-task/task/v3 v3.19.0 h1:jS1s2fHoSgYySw+kHEtbSxS6emE620unBsDDcNqdRbc=
github.com/go-task/task/v3 v3.19.0/go.mod h1:AjwL1xWWs8EwSZ0enL496mwqSTeIgXbPOXi+5of3MR4=
github.com/google/go-cmp v0.5.8 h1:e6P7q2lk1O+qJJb4BtCQXlK8vWEO8V1ZeuEdJNOqZyg=
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
github.com/jackmordaunt/icns/v2 v2.2.1 h1:MGklwYP2yohKn2Bw7XxlF69LZe98S1vUfl5OvAulPwg=
github.com/jackmordaunt/icns/v2 v2.2.1/go.mod h1:6aYIB9eSzyfHHMKqDf17Xrs1zetQPReAkiUSHzdw4cI=
github.com/joho/godotenv v1.4.0 h1:3l4+N6zfMWnkbPEXKng2o2/MR5mSwTrBih4ZEkkz1lg=
github.com/joho/godotenv v1.4.0/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4=
github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg=
github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0=
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
@ -44,7 +42,7 @@ github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZb
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/radovskyb/watcher v1.0.7 h1:AYePLih6dpmS32vlHfhCeli8127LzkIgwJGcwwe8tUE=
github.com/radovskyb/watcher v1.0.7/go.mod h1:78okwvY5wPdzcb1UYnip1pvrZNIVEIh/Cm+ZuvsUYIg=
github.com/rogpeppe/go-internal v1.8.1 h1:geMPLpDpQOgVyCg5z5GoRwLHepNdb71NXb67XFkP+Eg=
github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8=
github.com/sajari/fuzzy v1.0.0 h1:+FmwVvJErsd0d0hAPlj4CxqxUtQY/fOoY0DwX4ykpRY=
github.com/sajari/fuzzy v1.0.0/go.mod h1:OjYR6KxoWOe9+dOlXeiCJd4dIbED4Oo8wpS89o0pwOo=
github.com/spf13/afero v1.6.0/go.mod h1:Ai8FlHk4v/PARR026UzYexafAt9roJ7LcLMAmO6Z93I=
@ -55,6 +53,8 @@ github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5
github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk=
github.com/tc-hib/winres v0.1.6 h1:qgsYHze+BxQPEYilxIz/KCQGaClvI2+yLBAZs+3+0B8=
github.com/tc-hib/winres v0.1.6/go.mod h1:pe6dOR40VOrGz8PkzreVKNvEKnlE8t4yR8A8naL+t7A=
github.com/wailsapp/task/v3 v3.19.1 h1:syDKYaPBXgrXKKSJVEWcOEoSFtZzpvxqlHf90YRukRc=
github.com/wailsapp/task/v3 v3.19.1/go.mod h1:y7rWakbLR5gFElGgo6rA2dyr6vU/zNIDVfn3S4Of6OI=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/exp v0.0.0-20220930202632-ec3f01382ef9 h1:RjggHMcaTVp0LOVZcW0bo8alwHrOaCrGUDgfWUHhnN4=
@ -63,18 +63,18 @@ golang.org/x/image v0.0.0-20200430140353-33d19683fad8/go.mod h1:FeLwcggjj3mMvU+o
golang.org/x/image v0.0.0-20201208152932-35266b937fa6 h1:nfeHNc1nAqecKCy2FCy4HY+soOOe5sDLJ/gZLbx6GYI=
golang.org/x/image v0.0.0-20201208152932-35266b937fa6/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c h1:5KslGYwFpkhGh+Q16bwMP3cOontH8FOep7tGV86Y7SQ=
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o=
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/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-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f h1:v4INt8xihDGvnrfjMDVXGxw9wrfxYyCjk0KbXjhR55s=
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 h1:JGgROgKl9N8DuW20oFS5gxc+lE67/N3FcwmBPMe7ArY=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/sys v0.3.0 h1:w8ZOecv6NaNa/zC8944JTU3vz4u6Lagfk4RPQxv92NQ=
golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.3.0 h1:qoo4akIqOcDME5bhc/NgxUdovd6BSS2uMsVjB56q1xI=
golang.org/x/term v0.3.0/go.mod h1:q750SLmJuPmVoN1blW3UFBPREJfb1KmY3vwxfr+nFDA=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
@ -85,5 +85,5 @@ gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
mvdan.cc/sh/v3 v3.6.0-0.dev.0.20220704111049-a6e3029cd899 h1:nwm4t5PtLlFd/H342GP50CtYf7vyMCOZkPx3g9shO0c=
mvdan.cc/sh/v3 v3.6.0-0.dev.0.20220704111049-a6e3029cd899/go.mod h1:1JcoyAKm1lZw/2bZje/iYKWicU/KMd0rsyJeKHnsK4E=
mvdan.cc/sh/v3 v3.6.0 h1:gtva4EXJ0dFNvl5bHjcUEvws+KRcDslT8VKheTYkbGU=
mvdan.cc/sh/v3 v3.6.0/go.mod h1:U4mhtBLZ32iWhif5/lD+ygy1zrgaQhUu+XFy7C8+TTA=

View file

@ -1,30 +0,0 @@
package commands
import "testing"
func TestBuild(t *testing.T) {
type args struct {
options *BuildOptions
}
tests := []struct {
name string
args args
wantErr bool
}{
// TODO: Add test cases.
{
name: "TestBuild",
args: args{
options: &BuildOptions{},
},
wantErr: false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if err := Build(tt.args.options); (err != nil) != tt.wantErr {
t.Errorf("Build() error = %v, wantErr %v", err, tt.wantErr)
}
})
}
}

View file

@ -0,0 +1,61 @@
package commands
import (
_ "embed"
"os"
)
//go:embed defaults/info.json
var Info []byte
//go:embed defaults/wails.exe.manifest
var Manifest []byte
//go:embed defaults/appicon.png
var AppIcon []byte
//go:embed defaults/icons.ico
var IconsIco []byte
//go:embed defaults/Info.plist
var InfoPlist []byte
//go:embed defaults/Info.dev.plist
var InfoDevPlist []byte
//go:embed defaults/icons.icns
var IconsIcns []byte
var AllAssets = map[string][]byte{
"info.json": Info,
"wails.exe.manifest": Manifest,
"appicon.png": AppIcon,
"icons.ico": IconsIco,
"Info.plist": InfoPlist,
"Info.dev.plist": InfoDevPlist,
"icons.icns": IconsIcns,
}
type DefaultsOptions struct {
Dir string `description:"The directory to generate the files into"`
}
func Defaults(options *DefaultsOptions) error {
dir := options.Dir
if dir == "" {
dir = "."
}
for filename, data := range AllAssets {
// If file exists, skip it
if _, err := os.Stat(dir + "/" + filename); err == nil {
println("Skipping " + filename)
continue
}
err := os.WriteFile(dir+"/"+filename, data, 0644)
if err != nil {
return err
}
println("Generated " + filename)
}
return nil
}

View file

@ -0,0 +1,32 @@
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleName</key>
<string>{{.Info.ProductName}}</string>
<key>CFBundleExecutable</key>
<string>{{.Name}}</string>
<key>CFBundleIdentifier</key>
<string>com.wails.{{.Name}}</string>
<key>CFBundleVersion</key>
<string>{{.Info.ProductVersion}}</string>
<key>CFBundleGetInfoString</key>
<string>{{.Info.Comments}}</string>
<key>CFBundleShortVersionString</key>
<string>{{.Info.ProductVersion}}</string>
<key>CFBundleIconFile</key>
<string>iconfile</string>
<key>LSMinimumSystemVersion</key>
<string>10.13.0</string>
<key>NSHighResolutionCapable</key>
<string>true</string>
<key>NSHumanReadableCopyright</key>
<string>{{.Info.Copyright}}</string>
<key>NSAppTransportSecurity</key>
<dict>
<key>NSAllowsLocalNetworking</key>
<true/>
</dict>
</dict>
</plist>

View file

@ -0,0 +1,27 @@
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleName</key>
<string>{{.Info.ProductName}}</string>
<key>CFBundleExecutable</key>
<string>{{.Name}}</string>
<key>CFBundleIdentifier</key>
<string>com.wails.{{.Name}}</string>
<key>CFBundleVersion</key>
<string>{{.Info.ProductVersion}}</string>
<key>CFBundleGetInfoString</key>
<string>{{.Info.Comments}}</string>
<key>CFBundleShortVersionString</key>
<string>{{.Info.ProductVersion}}</string>
<key>CFBundleIconFile</key>
<string>iconfile</string>
<key>LSMinimumSystemVersion</key>
<string>10.13.0</string>
<key>NSHighResolutionCapable</key>
<string>true</string>
<key>NSHumanReadableCopyright</key>
<string>{{.Info.Copyright}}</string>
</dict>
</plist>

Binary file not shown.

After

Width:  |  Height:  |  Size: 130 KiB

Binary file not shown.

View file

Before

Width:  |  Height:  |  Size: 20 KiB

After

Width:  |  Height:  |  Size: 20 KiB

Before After
Before After

View file

@ -0,0 +1,15 @@
{
"fixed": {
"file_version": "v1.0.0"
},
"info": {
"0000": {
"ProductVersion": "v1.0.0",
"CompanyName": "My Company Name",
"FileDescription": "A thing that does a thing",
"LegalCopyright": "(c) 2023 My Company Name",
"ProductName": "My Product Name",
"Comments": "This is a comment"
}
}
}

View file

@ -0,0 +1,15 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly manifestVersion="1.0" xmlns="urn:schemas-microsoft-com:asm.v1" xmlns:asmv3="urn:schemas-microsoft-com:asm.v3">
<assemblyIdentity type="win32" name="com.wails.myproductname" version="v1.0.0.0" processorArchitecture="*"/>
<dependency>
<dependentAssembly>
<assemblyIdentity type="win32" name="Microsoft.Windows.Common-Controls" version="6.0.0.0" processorArchitecture="*" publicKeyToken="6595b64144ccf1df" language="*"/>
</dependentAssembly>
</dependency>
<asmv3:application>
<asmv3:windowsSettings>
<dpiAware xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">true/pm</dpiAware> <!-- fallback for Windows 7 and 8 -->
<dpiAwareness xmlns="http://schemas.microsoft.com/SMI/2016/WindowsSettings">permonitorv2,permonitor</dpiAwareness> <!-- falls back to per-monitor if per-monitor v2 is not supported -->
</asmv3:windowsSettings>
</asmv3:application>
</assembly>

View file

@ -1,15 +0,0 @@
package examples
import _ "embed"
//go:embed info.json
var Info []byte
//go:embed wails.exe.manifest
var Manifest []byte
//go:embed appicon.png
var AppIcon []byte
//go:embed icon.ico
var IconIco []byte

View file

@ -10,10 +10,9 @@ import (
"github.com/jackmordaunt/icns/v2"
"github.com/leaanthony/winicon"
"github.com/wailsapp/wails/exp/internal/commands/examples"
)
type IconOptions struct {
type IconsOptions struct {
Example bool `description:"Generate example icon file (appicon.png) in the current directory"`
Input string `description:"The input image file"`
Sizes string `description:"The sizes to generate in .ico file (comma separated)"`
@ -21,15 +20,15 @@ type IconOptions struct {
MacFilename string `description:"The output filename for the Mac icon bundle"`
}
func (i *IconOptions) Default() *IconOptions {
return &IconOptions{
func (i *IconsOptions) Default() *IconsOptions {
return &IconsOptions{
Sizes: "256,128,64,48,32,16",
MacFilename: "icons.icns",
WindowsFilename: "icons.ico",
}
}
func GenerateIcon(options *IconOptions) error {
func GenerateIcons(options *IconsOptions) error {
if options.Example {
return generateExampleIcon()
@ -76,7 +75,7 @@ func GenerateIcon(options *IconOptions) error {
}
func generateExampleIcon() error {
return os.WriteFile("appicon.png", []byte(examples.AppIcon), 0644)
return os.WriteFile("appicon.png", []byte(AppIcon), 0644)
}
func parseSizes(sizes string) ([]int, error) {
@ -98,7 +97,7 @@ func parseSizes(sizes string) ([]int, error) {
return result, nil
}
func generateMacIcon(iconData []byte, options *IconOptions) error {
func generateMacIcon(iconData []byte, options *IconsOptions) error {
srcImg, _, err := image.Decode(bytes.NewBuffer(iconData))
if err != nil {
@ -119,7 +118,7 @@ func generateMacIcon(iconData []byte, options *IconOptions) error {
return icns.Encode(dest, srcImg)
}
func generateWindowsIcon(iconData []byte, sizes []int, options *IconOptions) error {
func generateWindowsIcon(iconData []byte, sizes []int, options *IconsOptions) error {
var output bytes.Buffer

View file

@ -11,14 +11,14 @@ import (
func TestGenerateIcon(t *testing.T) {
tests := []struct {
name string
setup func() *IconOptions
setup func() *IconsOptions
wantErr bool
test func() error
}{
{
name: "should generate an icon when using the `example` flag",
setup: func() *IconOptions {
return &IconOptions{
setup: func() *IconsOptions {
return &IconsOptions{
Example: true,
}
},
@ -47,13 +47,13 @@ func TestGenerateIcon(t *testing.T) {
},
{
name: "should generate a .ico file when using the `input` flag and `windowsfilena me` flag",
setup: func() *IconOptions {
setup: func() *IconsOptions {
// Get the directory of this file
_, thisFile, _, _ := runtime.Caller(1)
localDir := filepath.Dir(thisFile)
// Get the path to the example icon
exampleIcon := filepath.Join(localDir, "examples", "appicon.png")
return &IconOptions{
return &IconsOptions{
Input: exampleIcon,
WindowsFilename: "appicon.ico",
}
@ -89,13 +89,13 @@ func TestGenerateIcon(t *testing.T) {
},
{
name: "should generate a .icns file when using the `input` flag and `macfilename` flag",
setup: func() *IconOptions {
setup: func() *IconsOptions {
// Get the directory of this file
_, thisFile, _, _ := runtime.Caller(1)
localDir := filepath.Dir(thisFile)
// Get the path to the example icon
exampleIcon := filepath.Join(localDir, "examples", "appicon.png")
return &IconOptions{
return &IconsOptions{
Input: exampleIcon,
MacFilename: "appicon.icns",
}
@ -128,13 +128,13 @@ func TestGenerateIcon(t *testing.T) {
},
{
name: "should generate a small .ico file when using the `input` flag and `sizes` flag",
setup: func() *IconOptions {
setup: func() *IconsOptions {
// Get the directory of this file
_, thisFile, _, _ := runtime.Caller(1)
localDir := filepath.Dir(thisFile)
// Get the path to the example icon
exampleIcon := filepath.Join(localDir, "examples", "appicon.png")
return &IconOptions{
return &IconsOptions{
Input: exampleIcon,
Sizes: "16",
WindowsFilename: "appicon.ico",
@ -169,20 +169,20 @@ func TestGenerateIcon(t *testing.T) {
},
{
name: "should error if no input file is provided",
setup: func() *IconOptions {
return &IconOptions{}
setup: func() *IconsOptions {
return &IconsOptions{}
},
wantErr: true,
},
{
name: "should error if neither mac or windows filename is provided",
setup: func() *IconOptions {
setup: func() *IconsOptions {
// Get the directory of this file
_, thisFile, _, _ := runtime.Caller(1)
localDir := filepath.Dir(thisFile)
// Get the path to the example icon
exampleIcon := filepath.Join(localDir, "examples", "appicon.png")
return &IconOptions{
return &IconsOptions{
Input: exampleIcon,
}
},
@ -190,13 +190,13 @@ func TestGenerateIcon(t *testing.T) {
},
{
name: "should error if bad sizes provided",
setup: func() *IconOptions {
setup: func() *IconsOptions {
// Get the directory of this file
_, thisFile, _, _ := runtime.Caller(1)
localDir := filepath.Dir(thisFile)
// Get the path to the example icon
exampleIcon := filepath.Join(localDir, "examples", "appicon.png")
return &IconOptions{
return &IconsOptions{
Input: exampleIcon,
WindowsFilename: "appicon.ico",
Sizes: "bad",
@ -206,13 +206,13 @@ func TestGenerateIcon(t *testing.T) {
},
{
name: "should ignore 0 size",
setup: func() *IconOptions {
setup: func() *IconsOptions {
// Get the directory of this file
_, thisFile, _, _ := runtime.Caller(1)
localDir := filepath.Dir(thisFile)
// Get the path to the example icon
exampleIcon := filepath.Join(localDir, "examples", "appicon.png")
return &IconOptions{
return &IconsOptions{
Input: exampleIcon,
WindowsFilename: "appicon.ico",
Sizes: "0,16",
@ -245,8 +245,8 @@ func TestGenerateIcon(t *testing.T) {
},
{
name: "should error if the input file does not exist",
setup: func() *IconOptions {
return &IconOptions{
setup: func() *IconsOptions {
return &IconsOptions{
Input: "doesnotexist.png",
WindowsFilename: "appicon.ico",
}
@ -255,10 +255,10 @@ func TestGenerateIcon(t *testing.T) {
},
{
name: "should error if the input file is not a png",
setup: func() *IconOptions {
setup: func() *IconsOptions {
// Get the directory of this file
_, thisFile, _, _ := runtime.Caller(1)
return &IconOptions{
return &IconsOptions{
Input: thisFile,
WindowsFilename: "appicon.ico",
}
@ -270,7 +270,7 @@ func TestGenerateIcon(t *testing.T) {
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
options := tt.setup()
err := GenerateIcon(options)
err := GenerateIcons(options)
if (err != nil) != tt.wantErr {
t.Errorf("GenerateIcon() error = %v, wantErr %v", err, tt.wantErr)
return

View file

@ -2,22 +2,27 @@ package commands
import (
"context"
"fmt"
"github.com/go-task/task/v3"
"github.com/go-task/task/v3/taskfile"
)
type BuildOptions struct {
type RunOptions struct {
Task string `name:"t" description:"The name of the task to run"`
}
func Build(options *BuildOptions) error {
func Run(options *RunOptions) error {
if options.Task == "" {
return fmt.Errorf("task name is required")
}
e := task.Executor{}
err := e.Setup()
if err != nil {
return err
}
build := taskfile.Call{
Task: "build",
Task: options.Task,
Vars: nil,
}
return e.Run(context.Background(), build)

View file

@ -0,0 +1,38 @@
package commands
import "testing"
func TestBuild(t *testing.T) {
type args struct {
options *RunOptions
}
tests := []struct {
name string
args args
wantErr bool
}{
{
name: "should error if task name not provided",
args: args{
options: &RunOptions{},
},
wantErr: true,
},
{
name: "should work if task name provided",
args: args{
options: &RunOptions{
Task: "build",
},
},
wantErr: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if err := Run(tt.args.options); (err != nil) != tt.wantErr {
t.Errorf("Run() error = %v, wantErr %v", err, tt.wantErr)
}
})
}
}

View file

@ -7,7 +7,6 @@ import (
"github.com/tc-hib/winres"
"github.com/tc-hib/winres/version"
"github.com/wailsapp/wails/exp/internal/commands/examples"
)
type SysoOptions struct {
@ -21,8 +20,7 @@ type SysoOptions struct {
func (i *SysoOptions) Default() *SysoOptions {
return &SysoOptions{
Arch: runtime.GOOS,
Out: "wails-res.syso",
Arch: runtime.GOARCH,
}
}
@ -85,7 +83,7 @@ func GenerateSyso(options *SysoOptions) error {
targetFile := options.Out
if targetFile == "" {
targetFile = "wails-res.syso"
targetFile = "rsrc_windows_" + options.Arch + ".syso"
}
fout, err := os.Create(targetFile)
if err != nil {
@ -112,12 +110,12 @@ func GenerateSyso(options *SysoOptions) error {
func generateExampleSyso() error {
// Generate example info.json
err := os.WriteFile("info.json", examples.Info, 0644)
err := os.WriteFile("info.json", Info, 0644)
if err != nil {
return err
}
// Generate example manifest
err = os.WriteFile("wails.exe.manifest", examples.Manifest, 0644)
err = os.WriteFile("wails.exe.manifest", Manifest, 0644)
if err != nil {
return err
}