mirror of
https://github.com/wailsapp/wails.git
synced 2026-03-14 14:45:49 +01:00
Update default taskfile. WIP linux packaging
This commit is contained in:
parent
b9558cc3cb
commit
12efb8b981
14 changed files with 1525 additions and 44 deletions
|
|
@ -2,4 +2,4 @@
|
|||
`wails init`,:material-check-bold:,:material-check-bold:,:material-check-bold:
|
||||
`wails build`,:material-check-bold:,:material-check-bold:,:material-check-bold:
|
||||
`wails dev`," "," "," "
|
||||
`wails package`," ",:material-check-bold:," "
|
||||
`wails package`," ",:material-check-bold:,:material-check-bold:
|
||||
|
|
|
|||
|
|
|
@ -45,6 +45,8 @@ func main() {
|
|||
generate.NewSubCommandFunction("bindings", "Generate bindings + models", commands.GenerateBindings)
|
||||
generate.NewSubCommandFunction("constants", "Generate JS constants from Go", commands.GenerateConstants)
|
||||
generate.NewSubCommandFunction(".desktop", "Generate .desktop file", commands.GenerateDotDesktop)
|
||||
generate.NewSubCommandFunction("appimage", "Generate Linux AppImage", commands.GenerateAppImage)
|
||||
|
||||
plugin := app.NewSubCommand("plugin", "Plugin tools")
|
||||
//plugin.NewSubCommandFunction("list", "List plugins", commands.PluginList)
|
||||
plugin.NewSubCommandFunction("init", "Initialise a new plugin", commands.PluginInit)
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ require (
|
|||
github.com/go-task/task/v3 v3.31.0
|
||||
github.com/godbus/dbus/v5 v5.1.0
|
||||
github.com/google/go-cmp v0.5.9
|
||||
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510
|
||||
github.com/google/uuid v1.3.0
|
||||
github.com/gorilla/pat v1.0.1
|
||||
github.com/gorilla/sessions v1.2.1
|
||||
|
|
|
|||
|
|
@ -173,6 +173,8 @@ github.com/google/pprof v0.0.0-20200905233945-acf8798be1f7/go.mod h1:ZgVRPoUq/hf
|
|||
github.com/google/pprof v0.0.0-20221118152302-e6195bd50e26 h1:Xim43kblpZXfIBQsbuBVKCudVG457BR2GZFIz3uw3hQ=
|
||||
github.com/google/pprof v0.0.0-20221118152302-e6195bd50e26/go.mod h1:dDKJzRmX4S37WGHujM7tX//fmj1uioxKzKxz3lo4HJo=
|
||||
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
|
||||
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 h1:El6M4kTTCOh6aBiKaUGG7oYTSPP8MxqL4YI3kZKwcP4=
|
||||
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ=
|
||||
github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
|
||||
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
|
||||
|
|
|
|||
173
v3/internal/commands/appimage.go
Normal file
173
v3/internal/commands/appimage.go
Normal file
|
|
@ -0,0 +1,173 @@
|
|||
package commands
|
||||
|
||||
import (
|
||||
_ "embed"
|
||||
"fmt"
|
||||
"github.com/pterm/pterm"
|
||||
"github.com/wailsapp/wails/v3/internal/s"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"sync"
|
||||
)
|
||||
|
||||
//go:embed linuxdeploy-plugin-gtk.sh
|
||||
var gtkPlugin []byte
|
||||
|
||||
func log(p *pterm.ProgressbarPrinter, message string) {
|
||||
p.UpdateTitle(message)
|
||||
pterm.Info.Println(message)
|
||||
p.Increment()
|
||||
}
|
||||
|
||||
type GenerateAppImageOptions struct {
|
||||
Binary string `description:"The binary to package including path"`
|
||||
Icon string `description:"Path to the icon"`
|
||||
DesktopFile string `description:"Path to the desktop file"`
|
||||
OutputDir string `description:"Path to the output directory" default:"."`
|
||||
BuildDir string `description:"Path to the build directory"`
|
||||
}
|
||||
|
||||
func GenerateAppImage(options *GenerateAppImageOptions) error {
|
||||
|
||||
defer func() {
|
||||
pterm.DefaultSpinner.Stop()
|
||||
}()
|
||||
|
||||
if options.Binary == "" {
|
||||
return fmt.Errorf("binary not provided")
|
||||
}
|
||||
if options.Icon == "" {
|
||||
return fmt.Errorf("icon path not provided")
|
||||
}
|
||||
if options.DesktopFile == "" {
|
||||
return fmt.Errorf("desktop file path not provided")
|
||||
}
|
||||
if options.BuildDir == "" {
|
||||
// Create temp directory
|
||||
var err error
|
||||
options.BuildDir, err = os.MkdirTemp("", "wails-appimage-*")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
var err error
|
||||
options.OutputDir, err = filepath.Abs(options.OutputDir)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return generateAppImage(options)
|
||||
}
|
||||
|
||||
func generateAppImage(options *GenerateAppImageOptions) error {
|
||||
numberOfSteps := 5
|
||||
p, _ := pterm.DefaultProgressbar.WithTotal(numberOfSteps).WithTitle("Generating AppImage").Start()
|
||||
|
||||
// Get the last path of the binary and normalise the name
|
||||
name := normaliseName(filepath.Base(options.Binary))
|
||||
|
||||
appDir := filepath.Join(options.BuildDir, name+"-x86_64.AppDir")
|
||||
s.RMDIR(appDir)
|
||||
|
||||
log(p, "Preparing AppImage Directory: "+appDir)
|
||||
|
||||
usrBin := filepath.Join(appDir, "usr", "bin")
|
||||
s.MKDIR(options.BuildDir)
|
||||
s.MKDIR(usrBin)
|
||||
s.COPY(options.Binary, usrBin)
|
||||
s.CHMOD(filepath.Join(usrBin, filepath.Base(options.Binary)), 0755)
|
||||
dotDirIcon := filepath.Join(appDir, ".DirIcon")
|
||||
s.COPY(options.Icon, dotDirIcon)
|
||||
iconLink := filepath.Join(appDir, filepath.Base(options.Icon))
|
||||
s.DELETE(iconLink)
|
||||
s.SYMLINK(".DirIcon", iconLink)
|
||||
s.COPY(options.DesktopFile, appDir)
|
||||
|
||||
// Download linuxdeploy and make it executable
|
||||
s.CD(options.BuildDir)
|
||||
|
||||
// Download necessary files
|
||||
log(p, "Downloading AppImage tooling")
|
||||
var wg sync.WaitGroup
|
||||
wg.Add(2)
|
||||
go func() {
|
||||
if !s.EXISTS(filepath.Join(options.BuildDir, "linuxdeploy-x86_64.AppImage")) {
|
||||
s.DOWNLOAD("https://github.com/linuxdeploy/linuxdeploy/releases/download/continuous/linuxdeploy-x86_64.AppImage", filepath.Join(options.BuildDir, "linuxdeploy-x86_64.AppImage"))
|
||||
}
|
||||
s.CHMOD(filepath.Join(options.BuildDir, "linuxdeploy-x86_64.AppImage"), 0755)
|
||||
wg.Done()
|
||||
}()
|
||||
go func() {
|
||||
target := filepath.Join(appDir, "AppRun")
|
||||
if !s.EXISTS(target) {
|
||||
s.DOWNLOAD("https://github.com/AppImage/AppImageKit/releases/download/continuous/AppRun-x86_64", target)
|
||||
}
|
||||
s.CHMOD(target, 0755)
|
||||
wg.Done()
|
||||
}()
|
||||
wg.Wait()
|
||||
|
||||
log(p, "Processing GTK files.")
|
||||
files := s.FINDFILES("/usr/lib", "WebKitNetworkProcess", "WebKitWebProcess", "libwebkit2gtkinjectedbundle.so")
|
||||
if len(files) != 3 {
|
||||
return fmt.Errorf("unable to locate all WebKit libraries")
|
||||
}
|
||||
s.CD(appDir)
|
||||
for _, file := range files {
|
||||
targetDir := filepath.Dir(file)
|
||||
// Strip leading forward slash
|
||||
if targetDir[0] == '/' {
|
||||
targetDir = targetDir[1:]
|
||||
}
|
||||
var err error
|
||||
targetDir, err = filepath.Abs(targetDir)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
s.MKDIR(targetDir)
|
||||
s.COPY(file, targetDir)
|
||||
}
|
||||
// Copy GTK Plugin
|
||||
err := os.WriteFile(filepath.Join(options.BuildDir, "linuxdeploy-plugin-gtk.sh"), gtkPlugin, 0755)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Determine GTK Version
|
||||
// Run ldd on the binary and capture the output
|
||||
targetBinary := filepath.Join(appDir, "usr", "bin", options.Binary)
|
||||
lddOutput, err := s.EXEC(fmt.Sprintf("ldd %s", targetBinary))
|
||||
if err != nil {
|
||||
println(string(lddOutput))
|
||||
return err
|
||||
}
|
||||
lddString := string(lddOutput)
|
||||
// Check if GTK3 is present
|
||||
var DeployGtkVersion string
|
||||
if s.CONTAINS(lddString, "libgtk-x11-2.0.so") {
|
||||
DeployGtkVersion = "2"
|
||||
} else if s.CONTAINS(lddString, "libgtk-3.so") {
|
||||
DeployGtkVersion = "3"
|
||||
} else if s.CONTAINS(lddString, "libgtk-4.so") {
|
||||
DeployGtkVersion = "4"
|
||||
} else {
|
||||
return fmt.Errorf("unable to determine GTK version")
|
||||
}
|
||||
// Run linuxdeploy to bundle the application
|
||||
s.CD(options.BuildDir)
|
||||
log(p, "Generating AppImage (This may take a while...)")
|
||||
cmd := fmt.Sprintf("./linuxdeploy-x86_64.AppImage --appimage-extract-and-run --appdir %s --output appimage --plugin gtk", appDir)
|
||||
s.SETENV("DEPLOY_GTK_VERSION", DeployGtkVersion)
|
||||
output, err := s.EXEC(cmd)
|
||||
if err != nil {
|
||||
println(output)
|
||||
return err
|
||||
}
|
||||
|
||||
// Move file to output directory
|
||||
targetFile := filepath.Join(options.BuildDir, name+"-x86_64.AppImage")
|
||||
s.MOVE(targetFile, options.OutputDir)
|
||||
|
||||
log(p, "AppImage created: "+targetFile)
|
||||
return nil
|
||||
}
|
||||
80
v3/internal/commands/appimage_test.go
Normal file
80
v3/internal/commands/appimage_test.go
Normal file
|
|
@ -0,0 +1,80 @@
|
|||
//go:build full_test
|
||||
|
||||
package commands_test
|
||||
|
||||
import (
|
||||
"github.com/wailsapp/wails/v3/internal/commands"
|
||||
"github.com/wailsapp/wails/v3/internal/s"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func Test_generateAppImage(t *testing.T) {
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
options *commands.GenerateAppImageOptions
|
||||
wantErr bool
|
||||
setup func()
|
||||
teardown func()
|
||||
}{
|
||||
{
|
||||
name: "Should fail if binary path is not provided",
|
||||
options: &commands.GenerateAppImageOptions{},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "Should fail if Icon is not provided",
|
||||
options: &commands.GenerateAppImageOptions{
|
||||
Binary: "testapp",
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
|
||||
{
|
||||
name: "Should fail if desktop file is not provided",
|
||||
options: &commands.GenerateAppImageOptions{
|
||||
Binary: "testapp",
|
||||
Icon: "testicon",
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "Should work if inputs are valid",
|
||||
options: &commands.GenerateAppImageOptions{
|
||||
Binary: "testapp",
|
||||
Icon: "appicon.png",
|
||||
DesktopFile: "testapp.desktop",
|
||||
},
|
||||
setup: func() {
|
||||
// Compile the test application
|
||||
s.CD("appimage_testfiles")
|
||||
testDir := s.CWD()
|
||||
_, err := s.EXEC(`go build -ldflags="-s -w" -o testapp`)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
s.DEFER(func() {
|
||||
s.CD(testDir)
|
||||
s.RM("testapp")
|
||||
s.RM("testapp-x86_64.AppImage")
|
||||
})
|
||||
},
|
||||
teardown: func() {
|
||||
s.CALLDEFER()
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
if tt.setup != nil {
|
||||
tt.setup()
|
||||
}
|
||||
if err := commands.GenerateAppImage(tt.options); (err != nil) != tt.wantErr {
|
||||
t.Errorf("generateAppImage() error = %v, wantErr %v", err, tt.wantErr)
|
||||
}
|
||||
if tt.teardown != nil {
|
||||
tt.teardown()
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
BIN
v3/internal/commands/appimage_testfiles/appicon.png
Normal file
BIN
v3/internal/commands/appimage_testfiles/appicon.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 130 KiB |
417
v3/internal/commands/appimage_testfiles/main.go
Normal file
417
v3/internal/commands/appimage_testfiles/main.go
Normal file
|
|
@ -0,0 +1,417 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
_ "embed"
|
||||
"fmt"
|
||||
"log"
|
||||
"math/rand"
|
||||
"runtime"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/samber/lo"
|
||||
"github.com/wailsapp/wails/v3/pkg/events"
|
||||
|
||||
"github.com/wailsapp/wails/v3/pkg/application"
|
||||
)
|
||||
|
||||
func main() {
|
||||
app := application.New(application.Options{
|
||||
Name: "WebviewWindow Demo",
|
||||
Description: "A demo of the WebviewWindow API",
|
||||
Assets: application.AlphaAssets,
|
||||
Mac: application.MacOptions{
|
||||
ApplicationShouldTerminateAfterLastWindowClosed: false,
|
||||
},
|
||||
})
|
||||
app.On(events.Mac.ApplicationDidFinishLaunching, func(event *application.Event) {
|
||||
log.Println("ApplicationDidFinishLaunching")
|
||||
})
|
||||
|
||||
var hiddenWindows []*application.WebviewWindow
|
||||
|
||||
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)).
|
||||
SetRelativePosition(rand.Intn(1000), rand.Intn(800)).
|
||||
SetURL("https://wails.io").
|
||||
Show()
|
||||
windowCounter++
|
||||
})
|
||||
myMenu.Add("New WebviewWindow (Hides on Close one time)").
|
||||
SetAccelerator("CmdOrCtrl+H").
|
||||
OnClick(func(ctx *application.Context) {
|
||||
app.NewWebviewWindowWithOptions(application.WebviewWindowOptions{
|
||||
// This will be called when the user clicks the close button
|
||||
// on the window. It will hide the window for 5 seconds.
|
||||
// If the user clicks the close button again, the window will
|
||||
// close.
|
||||
ShouldClose: func(window *application.WebviewWindow) bool {
|
||||
if !lo.Contains(hiddenWindows, window) {
|
||||
hiddenWindows = append(hiddenWindows, window)
|
||||
go func() {
|
||||
time.Sleep(5 * time.Second)
|
||||
window.Show()
|
||||
}()
|
||||
window.Hide()
|
||||
return false
|
||||
}
|
||||
// Remove the window from the hiddenWindows list
|
||||
hiddenWindows = lo.Without(hiddenWindows, window)
|
||||
return true
|
||||
},
|
||||
}).
|
||||
SetTitle("WebviewWindow "+strconv.Itoa(windowCounter)).
|
||||
SetRelativePosition(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.NewWebviewWindowWithOptions(application.WebviewWindowOptions{
|
||||
X: rand.Intn(1000),
|
||||
Y: rand.Intn(800),
|
||||
BackgroundColour: application.NewRGB(33, 37, 41),
|
||||
Frameless: true,
|
||||
Mac: application.MacWindow{
|
||||
InvisibleTitleBarHeight: 50,
|
||||
},
|
||||
}).Show()
|
||||
windowCounter++
|
||||
})
|
||||
myMenu.Add("New WebviewWindow (ignores mouse events").
|
||||
SetAccelerator("CmdOrCtrl+F").
|
||||
OnClick(func(ctx *application.Context) {
|
||||
app.NewWebviewWindowWithOptions(application.WebviewWindowOptions{
|
||||
HTML: "<div style='width: 100%; height: 95%; border: 3px solid red; background-color: \"0000\";'></div>",
|
||||
X: rand.Intn(1000),
|
||||
Y: rand.Intn(800),
|
||||
IgnoreMouseEvents: true,
|
||||
BackgroundType: application.BackgroundTypeTransparent,
|
||||
Mac: application.MacWindow{
|
||||
InvisibleTitleBarHeight: 50,
|
||||
},
|
||||
}).Show()
|
||||
windowCounter++
|
||||
})
|
||||
if runtime.GOOS == "darwin" {
|
||||
myMenu.Add("New WebviewWindow (MacTitleBarHiddenInset)").
|
||||
OnClick(func(ctx *application.Context) {
|
||||
app.NewWebviewWindowWithOptions(application.WebviewWindowOptions{
|
||||
Mac: application.MacWindow{
|
||||
TitleBar: application.MacTitleBarHiddenInset,
|
||||
InvisibleTitleBarHeight: 25,
|
||||
},
|
||||
}).
|
||||
SetBackgroundColour(application.NewRGB(33, 37, 41)).
|
||||
SetTitle("WebviewWindow "+strconv.Itoa(windowCounter)).
|
||||
SetRelativePosition(rand.Intn(1000), rand.Intn(800)).
|
||||
SetHTML("<br/><br/><p>A MacTitleBarHiddenInset WebviewWindow example</p>").
|
||||
Show()
|
||||
windowCounter++
|
||||
})
|
||||
myMenu.Add("New WebviewWindow (MacTitleBarHiddenInsetUnified)").
|
||||
OnClick(func(ctx *application.Context) {
|
||||
app.NewWebviewWindowWithOptions(application.WebviewWindowOptions{
|
||||
Mac: application.MacWindow{
|
||||
TitleBar: application.MacTitleBarHiddenInsetUnified,
|
||||
InvisibleTitleBarHeight: 50,
|
||||
},
|
||||
}).
|
||||
SetTitle("WebviewWindow "+strconv.Itoa(windowCounter)).
|
||||
SetRelativePosition(rand.Intn(1000), rand.Intn(800)).
|
||||
SetHTML("<br/><br/><p>A MacTitleBarHiddenInsetUnified WebviewWindow example</p>").
|
||||
Show()
|
||||
windowCounter++
|
||||
})
|
||||
myMenu.Add("New WebviewWindow (MacTitleBarHidden)").
|
||||
OnClick(func(ctx *application.Context) {
|
||||
app.NewWebviewWindowWithOptions(application.WebviewWindowOptions{
|
||||
Mac: application.MacWindow{
|
||||
TitleBar: application.MacTitleBarHidden,
|
||||
InvisibleTitleBarHeight: 25,
|
||||
},
|
||||
}).
|
||||
SetTitle("WebviewWindow "+strconv.Itoa(windowCounter)).
|
||||
SetRelativePosition(rand.Intn(1000), rand.Intn(800)).
|
||||
SetHTML("<br/><br/><p>A MacTitleBarHidden WebviewWindow example</p>").
|
||||
Show()
|
||||
windowCounter++
|
||||
})
|
||||
}
|
||||
if runtime.GOOS == "windows" {
|
||||
myMenu.Add("New WebviewWindow (Mica)").
|
||||
OnClick(func(ctx *application.Context) {
|
||||
app.NewWebviewWindowWithOptions(application.WebviewWindowOptions{
|
||||
Title: "WebviewWindow " + strconv.Itoa(windowCounter),
|
||||
X: rand.Intn(1000),
|
||||
Y: rand.Intn(800),
|
||||
BackgroundType: application.BackgroundTypeTranslucent,
|
||||
HTML: "<html style='background-color: rgba(0,0,0,0);'><body></body></html>",
|
||||
Windows: application.WindowsWindow{
|
||||
BackdropType: application.Mica,
|
||||
},
|
||||
}).Show()
|
||||
windowCounter++
|
||||
})
|
||||
myMenu.Add("New WebviewWindow (Acrylic)").
|
||||
OnClick(func(ctx *application.Context) {
|
||||
app.NewWebviewWindowWithOptions(application.WebviewWindowOptions{
|
||||
Title: "WebviewWindow " + strconv.Itoa(windowCounter),
|
||||
X: rand.Intn(1000),
|
||||
Y: rand.Intn(800),
|
||||
BackgroundType: application.BackgroundTypeTranslucent,
|
||||
HTML: "<html style='background-color: rgba(0,0,0,0);'><body></body></html>",
|
||||
Windows: application.WindowsWindow{
|
||||
BackdropType: application.Acrylic,
|
||||
},
|
||||
}).Show()
|
||||
windowCounter++
|
||||
})
|
||||
myMenu.Add("New WebviewWindow (Tabbed)").
|
||||
OnClick(func(ctx *application.Context) {
|
||||
app.NewWebviewWindowWithOptions(application.WebviewWindowOptions{
|
||||
Title: "WebviewWindow " + strconv.Itoa(windowCounter),
|
||||
X: rand.Intn(1000),
|
||||
Y: rand.Intn(800),
|
||||
BackgroundType: application.BackgroundTypeTranslucent,
|
||||
HTML: "<html style='background-color: rgba(0,0,0,0);'><body></body></html>",
|
||||
Windows: application.WindowsWindow{
|
||||
BackdropType: application.Tabbed,
|
||||
},
|
||||
}).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()
|
||||
application.InfoDialog().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 Relative Position (0,0)").OnClick(func(ctx *application.Context) {
|
||||
currentWindow(func(w *application.WebviewWindow) {
|
||||
w.SetRelativePosition(0, 0)
|
||||
})
|
||||
})
|
||||
positionMenu.Add("Set Relative Position (Random)").OnClick(func(ctx *application.Context) {
|
||||
currentWindow(func(w *application.WebviewWindow) {
|
||||
w.SetRelativePosition(rand.Intn(1000), rand.Intn(800))
|
||||
})
|
||||
})
|
||||
|
||||
positionMenu.Add("Get Relative Position").OnClick(func(ctx *application.Context) {
|
||||
currentWindow(func(w *application.WebviewWindow) {
|
||||
x, y := w.RelativePosition()
|
||||
application.InfoDialog().SetTitle("Current WebviewWindow Position").SetMessage("X: " + strconv.Itoa(x) + " Y: " + strconv.Itoa(y)).Show()
|
||||
})
|
||||
})
|
||||
|
||||
positionMenu.Add("Set Absolute Position (0,0)").OnClick(func(ctx *application.Context) {
|
||||
currentWindow(func(w *application.WebviewWindow) {
|
||||
w.SetAbsolutePosition(0, 0)
|
||||
})
|
||||
})
|
||||
|
||||
positionMenu.Add("Set Absolute Position (Random)").OnClick(func(ctx *application.Context) {
|
||||
currentWindow(func(w *application.WebviewWindow) {
|
||||
w.SetAbsolutePosition(rand.Intn(1000), rand.Intn(800))
|
||||
})
|
||||
})
|
||||
|
||||
positionMenu.Add("Get Absolute Position").OnClick(func(ctx *application.Context) {
|
||||
currentWindow(func(w *application.WebviewWindow) {
|
||||
x, y := w.AbsolutePosition()
|
||||
application.InfoDialog().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 {
|
||||
application.ErrorDialog().SetTitle("Error").SetMessage(err.Error()).Show()
|
||||
return
|
||||
}
|
||||
msg := fmt.Sprintf("Screen: %+v", screen)
|
||||
application.InfoDialog().SetTitle("Primary Screen").SetMessage(msg).Show()
|
||||
})
|
||||
stateMenu.Add("Get Screens").OnClick(func(ctx *application.Context) {
|
||||
screens, err := app.GetScreens()
|
||||
if err != nil {
|
||||
application.ErrorDialog().SetTitle("Error").SetMessage(err.Error()).Show()
|
||||
return
|
||||
}
|
||||
for _, screen := range screens {
|
||||
msg := fmt.Sprintf("Screen: %+v", screen)
|
||||
application.InfoDialog().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 {
|
||||
application.ErrorDialog().SetTitle("Error").SetMessage(err.Error()).Show()
|
||||
return
|
||||
}
|
||||
msg := fmt.Sprintf("Screen: %+v", screen)
|
||||
application.InfoDialog().SetTitle(fmt.Sprintf("Screen %s", screen.ID)).SetMessage(msg).Show()
|
||||
})
|
||||
})
|
||||
stateMenu.Add("Disable for 5s").OnClick(func(ctx *application.Context) {
|
||||
currentWindow(func(w *application.WebviewWindow) {
|
||||
w.SetEnabled(false)
|
||||
time.Sleep(5 * time.Second)
|
||||
w.SetEnabled(true)
|
||||
})
|
||||
})
|
||||
|
||||
if runtime.GOOS == "windows" {
|
||||
stateMenu.Add("Flash Start").OnClick(func(ctx *application.Context) {
|
||||
currentWindow(func(w *application.WebviewWindow) {
|
||||
time.Sleep(2 * time.Second)
|
||||
w.Flash(true)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
printMenu := menu.AddSubmenu("Print")
|
||||
printMenu.Add("Print").OnClick(func(ctx *application.Context) {
|
||||
currentWindow(func(w *application.WebviewWindow) {
|
||||
_ = w.Print()
|
||||
})
|
||||
})
|
||||
|
||||
app.NewWebviewWindowWithOptions(application.WebviewWindowOptions{
|
||||
BackgroundColour: application.NewRGB(33, 37, 41),
|
||||
Mac: application.MacWindow{
|
||||
DisableShadow: true,
|
||||
},
|
||||
})
|
||||
|
||||
app.SetMenu(menu)
|
||||
err := app.Run()
|
||||
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
}
|
||||
10
v3/internal/commands/appimage_testfiles/testapp.desktop
Normal file
10
v3/internal/commands/appimage_testfiles/testapp.desktop
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
[Desktop Entry]
|
||||
Type=Application
|
||||
Name=testapp
|
||||
Exec=testapp
|
||||
Icon=appicon
|
||||
Categories=Development;
|
||||
Terminal=false
|
||||
Keywords=wails
|
||||
Version=1.0
|
||||
StartupNotify=false
|
||||
|
|
@ -35,9 +35,7 @@ func (d *DotDesktopOptions) asBytes() []byte {
|
|||
if d.Icon != "" {
|
||||
buf.WriteString(fmt.Sprintf("Icon=%s\n", d.Icon))
|
||||
}
|
||||
if d.Categories != "" {
|
||||
buf.WriteString(fmt.Sprintf("Categories=%s\n", d.Categories))
|
||||
}
|
||||
buf.WriteString(fmt.Sprintf("Categories=%s\n", d.Categories))
|
||||
if d.Comment != "" {
|
||||
buf.WriteString(fmt.Sprintf("Comment=%s\n", d.Comment))
|
||||
}
|
||||
|
|
|
|||
376
v3/internal/commands/linuxdeploy-plugin-gtk.sh
Normal file
376
v3/internal/commands/linuxdeploy-plugin-gtk.sh
Normal file
|
|
@ -0,0 +1,376 @@
|
|||
#! /usr/bin/env bash
|
||||
|
||||
# Source: https://raw.githubusercontent.com/linuxdeploy/linuxdeploy-plugin-gtk/master/linuxdeploy-plugin-gtk.sh
|
||||
# License: MIT (https://github.com/linuxdeploy/linuxdeploy-plugin-gtk/blob/master/LICENSE.txt)
|
||||
|
||||
# GTK3 environment variables: https://developer.gnome.org/gtk3/stable/gtk-running.html
|
||||
# GTK4 environment variables: https://developer.gnome.org/gtk4/stable/gtk-running.html
|
||||
|
||||
# abort on all errors
|
||||
set -e
|
||||
|
||||
if [ "$DEBUG" != "" ]; then
|
||||
set -x
|
||||
verbose="--verbose"
|
||||
fi
|
||||
|
||||
SCRIPT="$(basename "$(readlink -f "$0")")"
|
||||
|
||||
show_usage() {
|
||||
echo "Usage: $SCRIPT --appdir <path to AppDir>"
|
||||
echo
|
||||
echo "Bundles resources for applications that use GTK into an AppDir"
|
||||
echo
|
||||
echo "Required variables:"
|
||||
echo " LINUXDEPLOY=\".../linuxdeploy\" path to linuxdeploy (e.g., AppImage); set automatically when plugin is run directly by linuxdeploy"
|
||||
echo
|
||||
echo "Optional variables:"
|
||||
echo " DEPLOY_GTK_VERSION (major version of GTK to deploy, e.g. '2', '3' or '4'; auto-detect by default)"
|
||||
}
|
||||
|
||||
variable_is_true() {
|
||||
local var="$1"
|
||||
|
||||
if [ -n "$var" ] && { [ "$var" == "true" ] || [ "$var" -gt 0 ]; } 2> /dev/null; then
|
||||
return 0 # true
|
||||
else
|
||||
return 1 # false
|
||||
fi
|
||||
}
|
||||
|
||||
get_pkgconf_variable() {
|
||||
local variable="$1"
|
||||
local library="$2"
|
||||
local default_value="$3"
|
||||
|
||||
pkgconfig_ret="$("$PKG_CONFIG" --variable="$variable" "$library")"
|
||||
if [ -n "$pkgconfig_ret" ]; then
|
||||
echo "$pkgconfig_ret"
|
||||
elif [ -n "$default_value" ]; then
|
||||
echo "$default_value"
|
||||
else
|
||||
echo "$0: there is no '$variable' variable for '$library' library." > /dev/stderr
|
||||
echo "Please check the '$library.pc' file is present in \$PKG_CONFIG_PATH (you may need to install the appropriate -dev/-devel package)." > /dev/stderr
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
copy_tree() {
|
||||
local src=("${@:1:$#-1}")
|
||||
local dst="${*:$#}"
|
||||
|
||||
for elem in "${src[@]}"; do
|
||||
mkdir -p "${dst::-1}$elem"
|
||||
cp "$elem" --archive --parents --target-directory="$dst" $verbose
|
||||
done
|
||||
}
|
||||
|
||||
copy_lib_tree() {
|
||||
# The source lib directory could be /usr/lib, /usr/lib64, or /usr/lib/x86_64-linux-gnu
|
||||
# Therefore, when copying lib directories, we need to transform that target path
|
||||
# to a consistent /usr/lib
|
||||
local src=("${@:1:$#-1}")
|
||||
local dst="${*:$#}"
|
||||
|
||||
for elem in "${src[@]}"; do
|
||||
mkdir -p "${dst::-1}${elem/$LD_GTK_LIBRARY_PATH//usr/lib}"
|
||||
pushd "$LD_GTK_LIBRARY_PATH"
|
||||
cp "$(realpath --relative-to="$LD_GTK_LIBRARY_PATH" "$elem")" --archive --parents --target-directory="$dst/usr/lib" $verbose
|
||||
popd
|
||||
done
|
||||
}
|
||||
|
||||
get_triplet_path() {
|
||||
if command -v dpkg-architecture > /dev/null; then
|
||||
echo "/usr/lib/$(dpkg-architecture -qDEB_HOST_MULTIARCH)"
|
||||
fi
|
||||
}
|
||||
|
||||
|
||||
|
||||
search_library_path() {
|
||||
PATH_ARRAY=(
|
||||
"$(get_triplet_path)"
|
||||
"/usr/lib64"
|
||||
"/usr/lib"
|
||||
)
|
||||
|
||||
for path in "${PATH_ARRAY[@]}"; do
|
||||
if [ -d "$path" ]; then
|
||||
echo "$path"
|
||||
return 0
|
||||
fi
|
||||
done
|
||||
}
|
||||
|
||||
search_tool() {
|
||||
local tool="$1"
|
||||
local directory="$2"
|
||||
|
||||
if command -v "$tool"; then
|
||||
return 0
|
||||
fi
|
||||
|
||||
PATH_ARRAY=(
|
||||
"$(get_triplet_path)/$directory/$tool"
|
||||
"/usr/lib64/$directory/$tool"
|
||||
"/usr/lib/$directory/$tool"
|
||||
"/usr/bin/$tool"
|
||||
"/usr/bin/$tool-64"
|
||||
"/usr/bin/$tool-32"
|
||||
)
|
||||
|
||||
for path in "${PATH_ARRAY[@]}"; do
|
||||
if [ -x "$path" ]; then
|
||||
echo "$path"
|
||||
return 0
|
||||
fi
|
||||
done
|
||||
}
|
||||
|
||||
DEPLOY_GTK_VERSION="${DEPLOY_GTK_VERSION:-0}" # When not set by user, this variable use the integer '0' as a sentinel value
|
||||
APPDIR=
|
||||
|
||||
while [ "$1" != "" ]; do
|
||||
case "$1" in
|
||||
--plugin-api-version)
|
||||
echo "0"
|
||||
exit 0
|
||||
;;
|
||||
--appdir)
|
||||
APPDIR="$2"
|
||||
shift
|
||||
shift
|
||||
;;
|
||||
--help)
|
||||
show_usage
|
||||
exit 0
|
||||
;;
|
||||
*)
|
||||
echo "Invalid argument: $1"
|
||||
echo
|
||||
show_usage
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
if [ "$APPDIR" == "" ]; then
|
||||
show_usage
|
||||
exit 1
|
||||
fi
|
||||
|
||||
APPDIR="$(realpath "$APPDIR")"
|
||||
mkdir -p "$APPDIR"
|
||||
|
||||
. /etc/os-release
|
||||
if [ "$ID" = "debian" ] || [ "$ID" = "ubuntu" ]; then
|
||||
if ! command -v dpkg-architecture &>/dev/null; then
|
||||
echo -e "$0: dpkg-architecture not found.\nInstall dpkg-dev then re-run the plugin."
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
if command -v pkgconf > /dev/null; then
|
||||
PKG_CONFIG="pkgconf"
|
||||
elif command -v pkg-config > /dev/null; then
|
||||
PKG_CONFIG="pkg-config"
|
||||
else
|
||||
echo "$0: pkg-config/pkgconf not found in PATH, aborting"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# GTK's library path *must not* have a trailing slash for later parameter substitution to work properly
|
||||
LD_GTK_LIBRARY_PATH="$(realpath "${LD_GTK_LIBRARY_PATH:-$(search_library_path)}")"
|
||||
|
||||
if ! command -v find &>/dev/null && ! type find &>/dev/null; then
|
||||
echo -e "$0: find not found.\nInstall findutils then re-run the plugin."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ -z "$LINUXDEPLOY" ]; then
|
||||
echo -e "$0: LINUXDEPLOY environment variable is not set.\nDownload a suitable linuxdeploy AppImage, set the environment variable and re-run the plugin."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
gtk_versions=0 # Count major versions of GTK when auto-detect GTK version
|
||||
if [ "$DEPLOY_GTK_VERSION" -eq 0 ]; then
|
||||
echo "Determining which GTK version to deploy"
|
||||
while IFS= read -r -d '' file; do
|
||||
if [ "$DEPLOY_GTK_VERSION" -ne 2 ] && ldd "$file" | grep -q "libgtk-x11-2.0.so"; then
|
||||
DEPLOY_GTK_VERSION=2
|
||||
gtk_versions="$((gtk_versions+1))"
|
||||
fi
|
||||
if [ "$DEPLOY_GTK_VERSION" -ne 3 ] && ldd "$file" | grep -q "libgtk-3.so"; then
|
||||
DEPLOY_GTK_VERSION=3
|
||||
gtk_versions="$((gtk_versions+1))"
|
||||
fi
|
||||
if [ "$DEPLOY_GTK_VERSION" -ne 4 ] && ldd "$file" | grep -q "libgtk-4.so"; then
|
||||
DEPLOY_GTK_VERSION=4
|
||||
gtk_versions="$((gtk_versions+1))"
|
||||
fi
|
||||
done < <(find "$APPDIR/usr/bin" -executable -type f -print0)
|
||||
fi
|
||||
|
||||
if [ "$gtk_versions" -gt 1 ]; then
|
||||
echo "$0: can not deploy multiple GTK versions at the same time."
|
||||
echo "Please set DEPLOY_GTK_VERSION to {2, 3, 4}."
|
||||
exit 1
|
||||
elif [ "$DEPLOY_GTK_VERSION" -eq 0 ]; then
|
||||
echo "$0: failed to auto-detect GTK version."
|
||||
echo "Please set DEPLOY_GTK_VERSION to {2, 3, 4}."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "Installing AppRun hook"
|
||||
HOOKSDIR="$APPDIR/apprun-hooks"
|
||||
HOOKFILE="$HOOKSDIR/linuxdeploy-plugin-gtk.sh"
|
||||
mkdir -p "$HOOKSDIR"
|
||||
cat > "$HOOKFILE" <<\EOF
|
||||
#! /usr/bin/env bash
|
||||
|
||||
COLOR_SCHEME="$(dbus-send --session --dest=org.freedesktop.portal.Desktop --type=method_call --print-reply --reply-timeout=1000 /org/freedesktop/portal/desktop org.freedesktop.portal.Settings.Read 'string:org.freedesktop.appearance' 'string:color-scheme' 2> /dev/null | tail -n1 | cut -b35- | cut -d' ' -f2 || printf '')"
|
||||
if [ -z "$COLOR_SCHEME" ]; then
|
||||
COLOR_SCHEME="$(gsettings get org.gnome.desktop.interface color-scheme 2> /dev/null || printf '')"
|
||||
fi
|
||||
case "$COLOR_SCHEME" in
|
||||
"1"|"'prefer-dark'") GTK_THEME_VARIANT="dark";;
|
||||
"2"|"'prefer-light'") GTK_THEME_VARIANT="light";;
|
||||
*) GTK_THEME_VARIANT="light";;
|
||||
esac
|
||||
APPIMAGE_GTK_THEME="${APPIMAGE_GTK_THEME:-"Adwaita:$GTK_THEME_VARIANT"}" # Allow user to override theme (discouraged)
|
||||
|
||||
export APPDIR="${APPDIR:-"$(dirname "$(realpath "$0")")"}" # Workaround to run extracted AppImage
|
||||
export GTK_DATA_PREFIX="$APPDIR"
|
||||
export GTK_THEME="$APPIMAGE_GTK_THEME" # Custom themes are broken
|
||||
export GDK_BACKEND=x11 # Crash with Wayland backend on Wayland
|
||||
export XDG_DATA_DIRS="$APPDIR/usr/share:/usr/share:$XDG_DATA_DIRS" # g_get_system_data_dirs() from GLib
|
||||
EOF
|
||||
|
||||
echo "Installing GLib schemas"
|
||||
# Note: schemasdir is undefined on Ubuntu 16.04
|
||||
glib_schemasdir="$(get_pkgconf_variable "schemasdir" "gio-2.0" "/usr/share/glib-2.0/schemas")"
|
||||
copy_tree "$glib_schemasdir" "$APPDIR/"
|
||||
glib-compile-schemas "$APPDIR/$glib_schemasdir"
|
||||
cat >> "$HOOKFILE" <<EOF
|
||||
export GSETTINGS_SCHEMA_DIR="\$APPDIR/$glib_schemasdir"
|
||||
EOF
|
||||
|
||||
echo "Installing GIRepository Typelibs"
|
||||
gi_typelibsdir="$(get_pkgconf_variable "typelibdir" "gobject-introspection-1.0" "$LD_GTK_LIBRARY_PATH/girepository-1.0")"
|
||||
copy_lib_tree "$gi_typelibsdir" "$APPDIR/"
|
||||
cat >> "$HOOKFILE" <<EOF
|
||||
export GI_TYPELIB_PATH="\$APPDIR/${gi_typelibsdir/$LD_GTK_LIBRARY_PATH//usr/lib}"
|
||||
EOF
|
||||
|
||||
case "$DEPLOY_GTK_VERSION" in
|
||||
2)
|
||||
# https://github.com/linuxdeploy/linuxdeploy-plugin-gtk/pull/20#issuecomment-826354261
|
||||
echo "WARNING: Gtk+2 applications are not fully supported by this plugin"
|
||||
;;
|
||||
3)
|
||||
echo "Installing GTK 3.0 modules"
|
||||
gtk3_exec_prefix="$(get_pkgconf_variable "exec_prefix" "gtk+-3.0" "/usr")"
|
||||
gtk3_libdir="$(get_pkgconf_variable "libdir" "gtk+-3.0" "$LD_GTK_LIBRARY_PATH")/gtk-3.0"
|
||||
gtk3_path="$gtk3_libdir"
|
||||
gtk3_immodulesdir="$gtk3_libdir/$(get_pkgconf_variable "gtk_binary_version" "gtk+-3.0" "3.0.0")/immodules"
|
||||
gtk3_printbackendsdir="$gtk3_libdir/$(get_pkgconf_variable "gtk_binary_version" "gtk+-3.0" "3.0.0")/printbackends"
|
||||
gtk3_immodules_cache_file="$(dirname "$gtk3_immodulesdir")/immodules.cache"
|
||||
gtk3_immodules_query="$(search_tool "gtk-query-immodules-3.0" "libgtk-3-0")"
|
||||
copy_lib_tree "$gtk3_libdir" "$APPDIR/"
|
||||
cat >> "$HOOKFILE" <<EOF
|
||||
export GTK_EXE_PREFIX="\$APPDIR/$gtk3_exec_prefix"
|
||||
export GTK_PATH="\$APPDIR/${gtk3_path/$LD_GTK_LIBRARY_PATH//usr/lib}"
|
||||
export GTK_IM_MODULE_FILE="\$APPDIR/${gtk3_immodules_cache_file/$LD_GTK_LIBRARY_PATH//usr/lib}"
|
||||
EOF
|
||||
|
||||
if [ -x "$gtk3_immodules_query" ]; then
|
||||
echo "Updating immodules cache in $APPDIR/${gtk3_immodules_cache_file/$LD_GTK_LIBRARY_PATH//usr/lib}"
|
||||
"$gtk3_immodules_query" > "$APPDIR/${gtk3_immodules_cache_file/$LD_GTK_LIBRARY_PATH//usr/lib}"
|
||||
else
|
||||
echo "WARNING: gtk-query-immodules-3.0 not found"
|
||||
fi
|
||||
if [ ! -f "$APPDIR/${gtk3_immodules_cache_file/$LD_GTK_LIBRARY_PATH//usr/lib}" ]; then
|
||||
echo "WARNING: immodules.cache file is missing"
|
||||
fi
|
||||
sed -i "s|$gtk3_libdir/3.0.0/immodules/||g" "$APPDIR/${gtk3_immodules_cache_file/$LD_GTK_LIBRARY_PATH//usr/lib}"
|
||||
;;
|
||||
4)
|
||||
echo "Installing GTK 4.0 modules"
|
||||
gtk4_exec_prefix="$(get_pkgconf_variable "exec_prefix" "gtk4" "/usr")"
|
||||
gtk4_libdir="$(get_pkgconf_variable "libdir" "gtk4")/gtk-4.0"
|
||||
gtk4_path="$gtk4_libdir"
|
||||
copy_lib_tree "$gtk4_libdir" "$APPDIR/"
|
||||
cat >> "$HOOKFILE" <<EOF
|
||||
export GTK_EXE_PREFIX="\$APPDIR/$gtk4_exec_prefix"
|
||||
export GTK_PATH="\$APPDIR/${gtk4_path/$LD_GTK_LIBRARY_PATH//usr/lib}"
|
||||
EOF
|
||||
;;
|
||||
*)
|
||||
echo "$0: '$DEPLOY_GTK_VERSION' is not a valid GTK major version."
|
||||
echo "Please set DEPLOY_GTK_VERSION to {2, 3, 4}."
|
||||
exit 1
|
||||
esac
|
||||
|
||||
echo "Installing GDK PixBufs"
|
||||
gdk_libdir="$(get_pkgconf_variable "libdir" "gdk-pixbuf-2.0" "$LD_GTK_LIBRARY_PATH")"
|
||||
gdk_pixbuf_binarydir="$(get_pkgconf_variable "gdk_pixbuf_binarydir" "gdk-pixbuf-2.0" "$gdk_libdir/gdk-pixbuf-2.0/2.10.0")"
|
||||
gdk_pixbuf_cache_file="$(get_pkgconf_variable "gdk_pixbuf_cache_file" "gdk-pixbuf-2.0" "$gdk_pixbuf_binarydir/loaders.cache")"
|
||||
gdk_pixbuf_moduledir="$(get_pkgconf_variable "gdk_pixbuf_moduledir" "gdk-pixbuf-2.0" "$gdk_pixbuf_binarydir/loaders")"
|
||||
# Note: gdk_pixbuf_query_loaders variable is not defined on some systems
|
||||
gdk_pixbuf_query="$(search_tool "gdk-pixbuf-query-loaders" "gdk-pixbuf-2.0")"
|
||||
copy_lib_tree "$gdk_pixbuf_binarydir" "$APPDIR/"
|
||||
cat >> "$HOOKFILE" <<EOF
|
||||
export GDK_PIXBUF_MODULE_FILE="\$APPDIR/${gdk_pixbuf_cache_file/$LD_GTK_LIBRARY_PATH//usr/lib}"
|
||||
EOF
|
||||
if [ -x "$gdk_pixbuf_query" ]; then
|
||||
echo "Updating pixbuf cache in $APPDIR/${gdk_pixbuf_cache_file/$LD_GTK_LIBRARY_PATH//usr/lib}"
|
||||
"$gdk_pixbuf_query" > "$APPDIR/${gdk_pixbuf_cache_file/$LD_GTK_LIBRARY_PATH//usr/lib}"
|
||||
else
|
||||
echo "WARNING: gdk-pixbuf-query-loaders not found"
|
||||
fi
|
||||
if [ ! -f "$APPDIR/${gdk_pixbuf_cache_file/$LD_GTK_LIBRARY_PATH//usr/lib}" ]; then
|
||||
echo "WARNING: loaders.cache file is missing"
|
||||
fi
|
||||
sed -i "s|$gdk_pixbuf_moduledir/||g" "$APPDIR/${gdk_pixbuf_cache_file/$LD_GTK_LIBRARY_PATH//usr/lib}"
|
||||
|
||||
echo "Copying more libraries"
|
||||
gobject_libdir="$(get_pkgconf_variable "libdir" "gobject-2.0" "$LD_GTK_LIBRARY_PATH")"
|
||||
gio_libdir="$(get_pkgconf_variable "libdir" "gio-2.0" "$LD_GTK_LIBRARY_PATH")"
|
||||
librsvg_libdir="$(get_pkgconf_variable "libdir" "librsvg-2.0" "$LD_GTK_LIBRARY_PATH")"
|
||||
pango_libdir="$(get_pkgconf_variable "libdir" "pango" "$LD_GTK_LIBRARY_PATH")"
|
||||
pangocairo_libdir="$(get_pkgconf_variable "libdir" "pangocairo" "$LD_GTK_LIBRARY_PATH")"
|
||||
pangoft2_libdir="$(get_pkgconf_variable "libdir" "pangoft2" "$LD_GTK_LIBRARY_PATH")"
|
||||
FIND_ARRAY=(
|
||||
"$gdk_libdir" "libgdk_pixbuf-*.so*"
|
||||
"$gobject_libdir" "libgobject-*.so*"
|
||||
"$gio_libdir" "libgio-*.so*"
|
||||
"$librsvg_libdir" "librsvg-*.so*"
|
||||
"$pango_libdir" "libpango-*.so*"
|
||||
"$pangocairo_libdir" "libpangocairo-*.so*"
|
||||
"$pangoft2_libdir" "libpangoft2-*.so*"
|
||||
)
|
||||
LIBRARIES=()
|
||||
for (( i=0; i<${#FIND_ARRAY[@]}; i+=2 )); do
|
||||
directory=${FIND_ARRAY[i]}
|
||||
library=${FIND_ARRAY[i+1]}
|
||||
while IFS= read -r -d '' file; do
|
||||
LIBRARIES+=( "--library=$file" )
|
||||
done < <(find "$directory" \( -type l -o -type f \) -name "$library" -print0)
|
||||
done
|
||||
|
||||
env LINUXDEPLOY_PLUGIN_MODE=1 "$LINUXDEPLOY" --appdir="$APPDIR" "${LIBRARIES[@]}"
|
||||
|
||||
# Create symbolic links as a workaround
|
||||
# Details: https://github.com/linuxdeploy/linuxdeploy-plugin-gtk/issues/24#issuecomment-1030026529
|
||||
echo "Manually setting rpath for GTK modules"
|
||||
PATCH_ARRAY=(
|
||||
"$gtk3_immodulesdir"
|
||||
"$gtk3_printbackendsdir"
|
||||
"$gdk_pixbuf_moduledir"
|
||||
)
|
||||
for directory in "${PATCH_ARRAY[@]}"; do
|
||||
while IFS= read -r -d '' file; do
|
||||
ln $verbose -sf "${file/$LD_GTK_LIBRARY_PATH\//}" "$APPDIR/usr/lib"
|
||||
done < <(find "$directory" -name '*.so' -print0)
|
||||
done
|
||||
|
|
@ -4,7 +4,6 @@ import (
|
|||
"context"
|
||||
"fmt"
|
||||
"github.com/go-task/task/v3/errors"
|
||||
"log"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
|
@ -21,6 +20,11 @@ import (
|
|||
// BuildSettings contains the CLI build settings
|
||||
var BuildSettings = map[string]string{}
|
||||
|
||||
func fatal(message string) {
|
||||
pterm.Error.Println(message)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
type RunTaskOptions struct {
|
||||
Name string `pos:"1"`
|
||||
Help bool `name:"h" description:"shows Task usage"`
|
||||
|
|
@ -111,7 +115,7 @@ func RunTask(options *RunTaskOptions, otherArgs []string) error {
|
|||
|
||||
var listOptions = task.NewListOptions(options.List, options.ListAll, options.ListJSON)
|
||||
if err := listOptions.Validate(); err != nil {
|
||||
log.Fatal(err)
|
||||
fatal(err.Error())
|
||||
}
|
||||
|
||||
if (listOptions.ShouldListTasks()) && options.Silent {
|
||||
|
|
@ -120,7 +124,7 @@ func RunTask(options *RunTaskOptions, otherArgs []string) error {
|
|||
}
|
||||
|
||||
if err := e.Setup(); err != nil {
|
||||
log.Fatal(err)
|
||||
fatal(err.Error())
|
||||
}
|
||||
|
||||
if listOptions.ShouldListTasks() {
|
||||
|
|
|
|||
417
v3/internal/s/s.go
Normal file
417
v3/internal/s/s.go
Normal file
|
|
@ -0,0 +1,417 @@
|
|||
package s
|
||||
|
||||
import (
|
||||
"crypto/md5"
|
||||
"fmt"
|
||||
"github.com/google/shlex"
|
||||
"io"
|
||||
"net/http"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
)
|
||||
|
||||
var (
|
||||
Output io.Writer = io.Discard
|
||||
IndentSize int
|
||||
originalOutput io.Writer
|
||||
currentIndent int
|
||||
dryRun bool
|
||||
deferred []func()
|
||||
)
|
||||
|
||||
func checkError(err error) {
|
||||
if err != nil {
|
||||
println("\nERROR:", err.Error())
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
func mute() {
|
||||
originalOutput = Output
|
||||
Output = io.Discard
|
||||
}
|
||||
|
||||
func unmute() {
|
||||
Output = originalOutput
|
||||
}
|
||||
|
||||
func indent() {
|
||||
currentIndent += IndentSize
|
||||
}
|
||||
|
||||
func unindent() {
|
||||
currentIndent -= IndentSize
|
||||
}
|
||||
|
||||
func log(message string, args ...interface{}) {
|
||||
indent := strings.Repeat(" ", currentIndent)
|
||||
_, err := fmt.Fprintf(Output, indent+message+"\n", args...)
|
||||
checkError(err)
|
||||
}
|
||||
|
||||
// RENAME a file or directory
|
||||
func RENAME(source string, target string) {
|
||||
log("RENAME %s -> %s", source, target)
|
||||
err := os.Rename(source, target)
|
||||
checkError(err)
|
||||
}
|
||||
|
||||
// MUSTDELETE a file.
|
||||
func MUSTDELETE(filename string) {
|
||||
log("DELETE %s", filename)
|
||||
err := os.Remove(filepath.Join(CWD(), filename))
|
||||
checkError(err)
|
||||
}
|
||||
|
||||
// DELETE a file.
|
||||
func DELETE(filename string) {
|
||||
log("DELETE %s", filename)
|
||||
_ = os.Remove(filepath.Join(CWD(), filename))
|
||||
}
|
||||
|
||||
func CONTAINS(list string, item string) bool {
|
||||
result := strings.Contains(list, item)
|
||||
listTrimmed := list
|
||||
if len(listTrimmed) > 30 {
|
||||
listTrimmed = listTrimmed[:30] + "..."
|
||||
}
|
||||
log("CONTAINS %s in %s: %t", item, listTrimmed, result)
|
||||
return result
|
||||
}
|
||||
|
||||
func SETENV(key string, value string) {
|
||||
log("SETENV %s=%s", key, value)
|
||||
err := os.Setenv(key, value)
|
||||
checkError(err)
|
||||
}
|
||||
|
||||
func CD(dir string) {
|
||||
err := os.Chdir(dir)
|
||||
checkError(err)
|
||||
log("CD %s", dir)
|
||||
}
|
||||
func MKDIR(path string, mode ...os.FileMode) {
|
||||
var perms os.FileMode
|
||||
perms = 0755
|
||||
if len(mode) == 1 {
|
||||
perms = mode[0]
|
||||
}
|
||||
log("MKDIR %s (perms: %v)", path, perms)
|
||||
err := os.MkdirAll(path, perms)
|
||||
checkError(err)
|
||||
}
|
||||
|
||||
// ENDIR ensures that the path gets created if it doesn't exist
|
||||
func ENDIR(path string, mode ...os.FileMode) {
|
||||
var perms os.FileMode
|
||||
perms = 0755
|
||||
if len(mode) == 1 {
|
||||
perms = mode[0]
|
||||
}
|
||||
_ = os.MkdirAll(path, perms)
|
||||
}
|
||||
|
||||
// COPYDIR recursively copies a directory tree, attempting to preserve permissions.
|
||||
// Source directory must exist, destination directory must *not* exist.
|
||||
// Symlinks are ignored and skipped.
|
||||
// Credit: https://gist.github.com/r0l1/92462b38df26839a3ca324697c8cba04
|
||||
func COPYDIR(src string, dst string) {
|
||||
log("COPYDIR %s -> %s", src, dst)
|
||||
src = filepath.Clean(src)
|
||||
dst = filepath.Clean(dst)
|
||||
|
||||
si, err := os.Stat(src)
|
||||
checkError(err)
|
||||
if !si.IsDir() {
|
||||
checkError(fmt.Errorf("source is not a directory"))
|
||||
}
|
||||
|
||||
_, err = os.Stat(dst)
|
||||
if err != nil && !os.IsNotExist(err) {
|
||||
checkError(err)
|
||||
}
|
||||
if err == nil {
|
||||
checkError(fmt.Errorf("destination already exists"))
|
||||
}
|
||||
|
||||
indent()
|
||||
MKDIR(dst)
|
||||
|
||||
entries, err := os.ReadDir(src)
|
||||
checkError(err)
|
||||
|
||||
for _, entry := range entries {
|
||||
srcPath := filepath.Join(src, entry.Name())
|
||||
dstPath := filepath.Join(dst, entry.Name())
|
||||
|
||||
if entry.IsDir() {
|
||||
COPYDIR(srcPath, dstPath)
|
||||
} else {
|
||||
// Skip symlinks.
|
||||
if entry.Type()&os.ModeSymlink != 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
COPY(srcPath, dstPath)
|
||||
}
|
||||
}
|
||||
unindent()
|
||||
}
|
||||
|
||||
func SYMLINK(source string, target string) {
|
||||
// trim string to first 30 chars
|
||||
var trimTarget = target
|
||||
if len(trimTarget) > 30 {
|
||||
trimTarget = trimTarget[:30] + "..."
|
||||
}
|
||||
log("SYMLINK %s -> %s", source, trimTarget)
|
||||
err := os.Symlink(source, target)
|
||||
checkError(err)
|
||||
}
|
||||
|
||||
// COPY file from source to target
|
||||
func COPY(source string, target string) {
|
||||
log("COPY %s -> %s", source, target)
|
||||
src, err := os.Open(source)
|
||||
checkError(err)
|
||||
defer closefile(src)
|
||||
if ISDIR(target) {
|
||||
target = filepath.Join(target, filepath.Base(source))
|
||||
}
|
||||
d, err := os.Create(target)
|
||||
checkError(err)
|
||||
_, err = io.Copy(d, src)
|
||||
checkError(err)
|
||||
}
|
||||
|
||||
// Move file from source to target
|
||||
func MOVE(source string, target string) {
|
||||
// If target is a directory, append the source filename
|
||||
if ISDIR(target) {
|
||||
target = filepath.Join(target, filepath.Base(source))
|
||||
}
|
||||
log("MOVE %s -> %s", source, target)
|
||||
err := os.Rename(source, target)
|
||||
checkError(err)
|
||||
}
|
||||
|
||||
func CWD() string {
|
||||
result, err := os.Getwd()
|
||||
checkError(err)
|
||||
log("CWD %s", result)
|
||||
return result
|
||||
}
|
||||
|
||||
func RMDIR(target string) {
|
||||
log("RMDIR %s", target)
|
||||
err := os.RemoveAll(target)
|
||||
checkError(err)
|
||||
}
|
||||
|
||||
func RM(target string) {
|
||||
log("RM %s", target)
|
||||
err := os.Remove(target)
|
||||
checkError(err)
|
||||
}
|
||||
|
||||
func ECHO(message string) {
|
||||
println(message)
|
||||
}
|
||||
|
||||
func TOUCH(filepath string) {
|
||||
log("TOUCH %s", filepath)
|
||||
f, err := os.Create(filepath)
|
||||
checkError(err)
|
||||
closefile(f)
|
||||
}
|
||||
|
||||
func EXEC(command string) ([]byte, error) {
|
||||
log("EXEC %s", command)
|
||||
|
||||
// Split input using shlex
|
||||
args, err := shlex.Split(command)
|
||||
checkError(err)
|
||||
// Execute command
|
||||
cmd := exec.Command(args[0], args[1:]...)
|
||||
cmd.Dir = CWD()
|
||||
cmd.Env = os.Environ()
|
||||
return cmd.CombinedOutput()
|
||||
}
|
||||
|
||||
func CHMOD(path string, mode os.FileMode) {
|
||||
log("CHMOD %s %v", path, mode)
|
||||
err := os.Chmod(path, mode)
|
||||
checkError(err)
|
||||
}
|
||||
|
||||
// EXISTS - Returns true if the given path exists
|
||||
func EXISTS(path string) bool {
|
||||
_, err := os.Lstat(path)
|
||||
log("EXISTS %s -> %t", path, err == nil)
|
||||
return err == nil
|
||||
}
|
||||
|
||||
// ISDIR returns true if the given directory exists
|
||||
func ISDIR(path string) bool {
|
||||
fi, err := os.Lstat(path)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
|
||||
return fi.Mode().IsDir()
|
||||
}
|
||||
|
||||
// ISDIREMPTY returns true if the given directory is empty
|
||||
func ISDIREMPTY(dir string) bool {
|
||||
|
||||
// CREDIT: https://stackoverflow.com/a/30708914/8325411
|
||||
f, err := os.Open(dir)
|
||||
checkError(err)
|
||||
defer closefile(f)
|
||||
|
||||
_, err = f.Readdirnames(1) // Or f.Readdir(1)
|
||||
if err == io.EOF {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// ISFILE returns true if the given file exists
|
||||
func ISFILE(path string) bool {
|
||||
fi, err := os.Lstat(path)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
|
||||
return fi.Mode().IsRegular()
|
||||
}
|
||||
|
||||
// SUBDIRS returns a list of subdirectories for the given directory
|
||||
func SUBDIRS(rootDir string) []string {
|
||||
var result []string
|
||||
|
||||
// Iterate root dir
|
||||
err := filepath.Walk(rootDir, func(path string, info os.FileInfo, err error) error {
|
||||
checkError(err)
|
||||
// If we have a directory, save it
|
||||
if info.IsDir() {
|
||||
result = append(result, path)
|
||||
}
|
||||
return nil
|
||||
})
|
||||
checkError(err)
|
||||
return result
|
||||
}
|
||||
|
||||
// SAVESTRING will create a file with the given string
|
||||
func SAVESTRING(filename string, data string) {
|
||||
log("SAVESTRING %s", filename)
|
||||
mute()
|
||||
SAVEBYTES(filename, []byte(data))
|
||||
unmute()
|
||||
}
|
||||
|
||||
// LOADSTRING returns the contents of the given filename as a string
|
||||
func LOADSTRING(filename string) string {
|
||||
log("LOADSTRING %s", filename)
|
||||
mute()
|
||||
data := LOADBYTES(filename)
|
||||
unmute()
|
||||
return string(data)
|
||||
}
|
||||
|
||||
// SAVEBYTES will create a file with the given string
|
||||
func SAVEBYTES(filename string, data []byte) {
|
||||
log("SAVEBYTES %s", filename)
|
||||
err := os.WriteFile(filename, data, 0755)
|
||||
checkError(err)
|
||||
}
|
||||
|
||||
// LOADBYTES returns the contents of the given filename as a string
|
||||
func LOADBYTES(filename string) []byte {
|
||||
log("LOADBYTES %s", filename)
|
||||
data, err := os.ReadFile(filename)
|
||||
checkError(err)
|
||||
return data
|
||||
}
|
||||
|
||||
func closefile(f *os.File) {
|
||||
err := f.Close()
|
||||
checkError(err)
|
||||
}
|
||||
|
||||
// MD5FILE returns the md5sum of the given file
|
||||
func MD5FILE(filename string) string {
|
||||
f, err := os.Open(filename)
|
||||
checkError(err)
|
||||
defer closefile(f)
|
||||
|
||||
h := md5.New()
|
||||
_, err = io.Copy(h, f)
|
||||
checkError(err)
|
||||
|
||||
return fmt.Sprintf("%x", h.Sum(nil))
|
||||
}
|
||||
|
||||
// Sub is the substitution type
|
||||
type Sub map[string]string
|
||||
|
||||
// REPLACEALL replaces all substitution keys with associated values in the given file
|
||||
func REPLACEALL(filename string, substitutions Sub) {
|
||||
log("REPLACEALL %s (%v)", filename, substitutions)
|
||||
data := LOADSTRING(filename)
|
||||
for old, newText := range substitutions {
|
||||
data = strings.ReplaceAll(data, old, newText)
|
||||
}
|
||||
SAVESTRING(filename, data)
|
||||
}
|
||||
|
||||
func DOWNLOAD(url string, target string) {
|
||||
log("DOWNLOAD %s -> %s", url, target)
|
||||
// create HTTP client
|
||||
resp, err := http.Get(url)
|
||||
checkError(err)
|
||||
defer resp.Body.Close()
|
||||
|
||||
out, err := os.Create(target)
|
||||
checkError(err)
|
||||
defer out.Close()
|
||||
|
||||
// Write the body to file
|
||||
_, err = io.Copy(out, resp.Body)
|
||||
checkError(err)
|
||||
}
|
||||
|
||||
func FINDFILES(root string, filenames ...string) []string {
|
||||
var result []string
|
||||
// Walk the root directory trying to find all the files
|
||||
err := filepath.Walk(root, func(path string, info os.FileInfo, err error) error {
|
||||
checkError(err)
|
||||
// If we have a file, check if it is in the list
|
||||
if info.Mode().IsRegular() {
|
||||
for _, filename := range filenames {
|
||||
if info.Name() == filename {
|
||||
result = append(result, path)
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
})
|
||||
checkError(err)
|
||||
log("FINDFILES in %s -> [%v]", root, strings.Join(result, ", "))
|
||||
return result
|
||||
}
|
||||
|
||||
func DEFER(fn func()) {
|
||||
log("DEFER")
|
||||
deferred = append(deferred, fn)
|
||||
}
|
||||
|
||||
func CALLDEFER() {
|
||||
log("CALLDEFER")
|
||||
for _, fn := range deferred {
|
||||
fn()
|
||||
}
|
||||
}
|
||||
|
|
@ -287,6 +287,7 @@ tasks:
|
|||
|
||||
create:appimage:
|
||||
summary: Creates an AppImage
|
||||
dir: build/appimage
|
||||
platforms: [ linux ]
|
||||
deps:
|
||||
- task: build:linux
|
||||
|
|
@ -294,44 +295,44 @@ tasks:
|
|||
PRODUCTION: "true"
|
||||
- task: generate:linux:dotdesktop
|
||||
cmds:
|
||||
- chmod +x {{ "{{.BIN_DIR}}" }}/{{ "{{.APP_NAME}}" }}
|
||||
- chmod +x build/appimage/build.sh
|
||||
- sh: build/appimage/build.sh
|
||||
env:
|
||||
- APP_NAME: {{ "'{{.APP_NAME}}'" }}
|
||||
- APP_BINARY: '{{ "{{.ROOT_DIR}}"}}/{{ "{{.BIN_DIR}}" }}/{{ "{{.APP_NAME}}" }}'
|
||||
- ICON_PATH: '{{ "{{.ROOT_DIR}}"}}/build/appicon.png'
|
||||
- DESKTOP_FILE: '{{ "{{.ROOT_DIR}}"}}/build/{{ "{{.APP_NAME}}" }}.desktop'
|
||||
# Copy binary + icon to appimage dir
|
||||
- cp {{ "{{.APP_BINARY}}" }} {{ "{{.APP_NAME}}" }}
|
||||
- cp ../appicon.png appicon.png
|
||||
# Generate AppImage
|
||||
- wails3 generate appimage -binary {{ "{{.APP_NAME}}" }} -icon {{ "{{.ICON}}" }} -desktopfile {{ "{{.DESKTOP_FILE}}" }} -outputdir {{ "{{.OUTPUT_DIR}}" }} -builddir {{ "{{.ROOT_DIR}}" }}/build/appimage
|
||||
vars:
|
||||
APP_NAME: '{{ "{{.APP_NAME}}" }}'
|
||||
APP_BINARY: '../../bin/{{ "{{.APP_NAME}}" }}'
|
||||
ICON: '../appicon.png'
|
||||
DESKTOP_FILE: '{{ "{{.APP_NAME}}" }}.desktop'
|
||||
OUTPUT_DIR: '../../bin'
|
||||
|
||||
generate:linux:dotdesktop:
|
||||
summary: Generates a `.desktop` file
|
||||
dir: build
|
||||
sources:
|
||||
- "appicon.png"
|
||||
generates:
|
||||
- "{{ "{{.ROOT_DIR}}"}}/build/appimage/{{ "{{.APP_NAME}}" }}.desktop"
|
||||
cmds:
|
||||
# Run `wails3 generate .desktop -help` for all the options
|
||||
- |
|
||||
wails3 generate .desktop
|
||||
-name "{{ "{{.APP_NAME}}" }}"
|
||||
-exec "{{ "{{.EXEC}}" }}"
|
||||
-icon "{{ "{{.ICON}}" }}"
|
||||
-outputfile {{ "{{.ROOT_DIR}}"}}/build/appimage/{{ "{{.APP_NAME}}" }}.desktop
|
||||
# -categories "Development;Graphics;"
|
||||
# -comment "A comment"
|
||||
# -terminal "true"
|
||||
# -version "1.0"
|
||||
# -genericname "Generic Name"
|
||||
# -keywords "keyword1;keyword2;"
|
||||
# -startupnotify "true"
|
||||
# -mimetype "application/x-extension1;application/x-extension2;"
|
||||
generate:linux:dotdesktop:
|
||||
summary: Generates a `.desktop` file
|
||||
dir: build
|
||||
sources:
|
||||
- "appicon.png"
|
||||
generates:
|
||||
- '{{ "{{.ROOT_DIR}}"}}/build/appimage/{{ "{{.APP_NAME}}" }}.desktop'
|
||||
cmds:
|
||||
- mkdir -p {{ "{{.ROOT_DIR}}"}}/build/appimage
|
||||
# Run `wails3 generate .desktop -help` for all the options
|
||||
- wails3 generate .desktop -name "{{ "{{.APP_NAME}}" }}" -exec "{{ "{{.EXEC}}" }}" -icon "{{ "{{.ICON}}" }}" -outputfile {{ "{{.ROOT_DIR}}"}}/build/appimage/{{ "{{.APP_NAME}}" }}.desktop -categories "{{ "{{.CATEGORIES}}" }}"
|
||||
# -comment "A comment"
|
||||
# -terminal "true"
|
||||
# -version "1.0"
|
||||
# -genericname "Generic Name"
|
||||
# -keywords "keyword1;keyword2;"
|
||||
# -startupnotify "true"
|
||||
# -mimetype "application/x-extension1;application/x-extension2;"
|
||||
|
||||
vars:
|
||||
APP_NAME: '{{ "{{.APP_NAME}}" }}'
|
||||
EXEC: '{{ "{{.APP_NAME}}" }}'
|
||||
ICON: 'appicon'
|
||||
CATEGORIES: 'Development;'
|
||||
OUTPUTFILE: '{{ "{{.ROOT_DIR}}"}}/build/appimage/{{ "{{.APP_NAME}}" }}.desktop'
|
||||
|
||||
vars:
|
||||
APP_NAME: '-name \"{{ "{{.APP_NAME}}" }}\"'
|
||||
EXEC: '{{ "{{.ROOT_DIR}}"}}/{{ "{{.BIN_DIR}}" }}/{{ "{{.APP_NAME}}" }}'
|
||||
ICON: '{{ "{{.ROOT_DIR}}"}}/build/appicon.png'
|
||||
OUTPUTFILE: '-outputfile {{ "{{.ROOT_DIR}}"}}/build/appimage/{{ "{{.APP_NAME}}" }}.desktop'
|
||||
## -------------------------- Misc -------------------------- ##
|
||||
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue