mirror of
https://github.com/wailsapp/wails.git
synced 2025-05-02 22:13:36 +08:00
Add garble support (#1793)
Co-authored-by: AlbinoDrought <sean@albinodrought.com> Co-authored-by: stffabi <stffabi@users.noreply.github.com>
This commit is contained in:
parent
eef99ee577
commit
052b9222c1
@ -2,6 +2,7 @@ package build
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"github.com/wailsapp/wails/v2/pkg/commands/buildtags"
|
||||||
"io"
|
"io"
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
@ -75,7 +76,7 @@ func AddBuildSubcommand(app *clir.Cli, w io.Writer) {
|
|||||||
|
|
||||||
// tags to pass to `go`
|
// tags to pass to `go`
|
||||||
tags := ""
|
tags := ""
|
||||||
command.StringFlag("tags", "tags to pass to Go compiler (quoted and space separated)", &tags)
|
command.StringFlag("tags", "Build tags to pass to Go compiler. Must be quoted. Space or comma (but not both) separated", &tags)
|
||||||
|
|
||||||
outputFilename := ""
|
outputFilename := ""
|
||||||
command.StringFlag("o", "Output filename", &outputFilename)
|
command.StringFlag("o", "Output filename", &outputFilename)
|
||||||
@ -111,9 +112,18 @@ func AddBuildSubcommand(app *clir.Cli, w io.Writer) {
|
|||||||
windowsConsole := false
|
windowsConsole := false
|
||||||
command.BoolFlag("windowsconsole", "Keep the console when building for Windows", &windowsConsole)
|
command.BoolFlag("windowsconsole", "Keep the console when building for Windows", &windowsConsole)
|
||||||
|
|
||||||
|
obfuscated := false
|
||||||
|
command.BoolFlag("obfuscated", "Code obfuscation of bound Wails methods", &obfuscated)
|
||||||
|
|
||||||
|
garbleargs := "-literals -tiny -seed=random"
|
||||||
|
command.StringFlag("garbleargs", "Arguments to pass to garble", &garbleargs)
|
||||||
|
|
||||||
dryRun := false
|
dryRun := false
|
||||||
command.BoolFlag("dryrun", "Dry run, prints the config for the command that would be executed", &dryRun)
|
command.BoolFlag("dryrun", "Dry run, prints the config for the command that would be executed", &dryRun)
|
||||||
|
|
||||||
|
skipBindings := false
|
||||||
|
command.BoolFlag("skipbindings", "Skips generation of bindings", &skipBindings)
|
||||||
|
|
||||||
command.Action(func() error {
|
command.Action(func() error {
|
||||||
|
|
||||||
quiet := verbosity == 0
|
quiet := verbosity == 0
|
||||||
@ -137,13 +147,10 @@ func AddBuildSubcommand(app *clir.Cli, w io.Writer) {
|
|||||||
return fmt.Errorf("unable to find compiler: %s", compilerCommand)
|
return fmt.Errorf("unable to find compiler: %s", compilerCommand)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Tags
|
// Process User Tags
|
||||||
userTags := []string{}
|
userTags, err := buildtags.Parse(tags)
|
||||||
for _, tag := range strings.Split(tags, " ") {
|
if err != nil {
|
||||||
thisTag := strings.TrimSpace(tag)
|
return err
|
||||||
if thisTag != "" {
|
|
||||||
userTags = append(userTags, thisTag)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Webview2 installer strategy (download by default)
|
// Webview2 installer strategy (download by default)
|
||||||
@ -176,6 +183,15 @@ func AddBuildSubcommand(app *clir.Cli, w io.Writer) {
|
|||||||
targets.AddSlice(strings.Split(platform, ","))
|
targets.AddSlice(strings.Split(platform, ","))
|
||||||
targets.Deduplicate()
|
targets.Deduplicate()
|
||||||
|
|
||||||
|
cwd, err := os.Getwd()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
projectOptions, err := project.Load(cwd)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
// Create BuildOptions
|
// Create BuildOptions
|
||||||
buildOptions := &build.Options{
|
buildOptions := &build.Options{
|
||||||
Logger: logger,
|
Logger: logger,
|
||||||
@ -197,6 +213,9 @@ func AddBuildSubcommand(app *clir.Cli, w io.Writer) {
|
|||||||
TrimPath: trimpath,
|
TrimPath: trimpath,
|
||||||
RaceDetector: raceDetector,
|
RaceDetector: raceDetector,
|
||||||
WindowsConsole: windowsConsole,
|
WindowsConsole: windowsConsole,
|
||||||
|
Obfuscated: obfuscated,
|
||||||
|
GarbleArgs: garbleargs,
|
||||||
|
SkipBindings: skipBindings,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Start a new tabwriter
|
// Start a new tabwriter
|
||||||
@ -208,7 +227,12 @@ func AddBuildSubcommand(app *clir.Cli, w io.Writer) {
|
|||||||
_, _ = fmt.Fprintf(w, "App Type: \t%s\n", buildOptions.OutputType)
|
_, _ = fmt.Fprintf(w, "App Type: \t%s\n", buildOptions.OutputType)
|
||||||
_, _ = fmt.Fprintf(w, "Platforms: \t%s\n", platform)
|
_, _ = fmt.Fprintf(w, "Platforms: \t%s\n", platform)
|
||||||
_, _ = fmt.Fprintf(w, "Compiler: \t%s\n", compilerPath)
|
_, _ = fmt.Fprintf(w, "Compiler: \t%s\n", compilerPath)
|
||||||
|
_, _ = fmt.Fprintf(w, "Skip Bindings: \t%t\n", skipBindings)
|
||||||
_, _ = fmt.Fprintf(w, "Build Mode: \t%s\n", modeString)
|
_, _ = fmt.Fprintf(w, "Build Mode: \t%s\n", modeString)
|
||||||
|
_, _ = fmt.Fprintf(w, "Obfuscated: \t%t\n", buildOptions.Obfuscated)
|
||||||
|
if buildOptions.Obfuscated {
|
||||||
|
_, _ = fmt.Fprintf(w, "Garble Args: \t%s\n", buildOptions.GarbleArgs)
|
||||||
|
}
|
||||||
_, _ = fmt.Fprintf(w, "Skip Frontend: \t%t\n", skipFrontend)
|
_, _ = fmt.Fprintf(w, "Skip Frontend: \t%t\n", skipFrontend)
|
||||||
_, _ = fmt.Fprintf(w, "Compress: \t%t\n", buildOptions.Compress)
|
_, _ = fmt.Fprintf(w, "Compress: \t%t\n", buildOptions.Compress)
|
||||||
_, _ = fmt.Fprintf(w, "Package: \t%t\n", buildOptions.Pack)
|
_, _ = fmt.Fprintf(w, "Package: \t%t\n", buildOptions.Pack)
|
||||||
@ -230,15 +254,6 @@ func AddBuildSubcommand(app *clir.Cli, w io.Writer) {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
cwd, err := os.Getwd()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
projectOptions, err := project.Load(cwd)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check platform
|
// Check platform
|
||||||
validPlatformArch := slicer.String([]string{
|
validPlatformArch := slicer.String([]string{
|
||||||
"darwin",
|
"darwin",
|
||||||
@ -329,6 +344,11 @@ func AddBuildSubcommand(app *clir.Cli, w io.Writer) {
|
|||||||
buildOptions.OutputFile = outputFilename
|
buildOptions.OutputFile = outputFilename
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if obfuscated && skipBindings {
|
||||||
|
logger.Println("Warning: obfuscated flag overrides skipbindings flag.")
|
||||||
|
buildOptions.SkipBindings = false
|
||||||
|
}
|
||||||
|
|
||||||
if !dryRun {
|
if !dryRun {
|
||||||
// Start Time
|
// Start Time
|
||||||
start := time.Now()
|
start := time.Now()
|
||||||
|
@ -4,6 +4,8 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"github.com/wailsapp/wails/v2/pkg/commands/bindings"
|
||||||
|
"github.com/wailsapp/wails/v2/pkg/commands/buildtags"
|
||||||
"io"
|
"io"
|
||||||
"net"
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
@ -77,7 +79,7 @@ type devFlags struct {
|
|||||||
reloadDirs string
|
reloadDirs string
|
||||||
openBrowser bool
|
openBrowser bool
|
||||||
noReload bool
|
noReload bool
|
||||||
noGen bool
|
skipBindings bool
|
||||||
wailsjsdir string
|
wailsjsdir string
|
||||||
tags string
|
tags string
|
||||||
verbosity int
|
verbosity int
|
||||||
@ -106,9 +108,9 @@ func AddSubcommand(app *clir.Cli, w io.Writer) error {
|
|||||||
command.StringFlag("reloaddirs", "Additional directories to trigger reloads (comma separated)", &flags.reloadDirs)
|
command.StringFlag("reloaddirs", "Additional directories to trigger reloads (comma separated)", &flags.reloadDirs)
|
||||||
command.BoolFlag("browser", "Open application in browser", &flags.openBrowser)
|
command.BoolFlag("browser", "Open application in browser", &flags.openBrowser)
|
||||||
command.BoolFlag("noreload", "Disable reload on asset change", &flags.noReload)
|
command.BoolFlag("noreload", "Disable reload on asset change", &flags.noReload)
|
||||||
command.BoolFlag("nogen", "Disable generate module", &flags.noGen)
|
command.BoolFlag("skipbindings", "Skip bindings generation", &flags.skipBindings)
|
||||||
command.StringFlag("wailsjsdir", "Directory to generate the Wails JS modules", &flags.wailsjsdir)
|
command.StringFlag("wailsjsdir", "Directory to generate the Wails JS modules", &flags.wailsjsdir)
|
||||||
command.StringFlag("tags", "tags to pass to Go compiler (quoted and space separated)", &flags.tags)
|
command.StringFlag("tags", "Build tags to pass to Go compiler. Must be quoted. Space or comma (but not both) separated", &flags.tags)
|
||||||
command.IntFlag("v", "Verbosity level (0 - silent, 1 - standard, 2 - verbose)", &flags.verbosity)
|
command.IntFlag("v", "Verbosity level (0 - silent, 1 - standard, 2 - verbose)", &flags.verbosity)
|
||||||
command.StringFlag("loglevel", "Loglevel to use - Trace, Debug, Info, Warning, Error", &flags.loglevel)
|
command.StringFlag("loglevel", "Loglevel to use - Trace, Debug, Info, Warning, Error", &flags.loglevel)
|
||||||
command.BoolFlag("f", "Force build application", &flags.forceBuild)
|
command.BoolFlag("f", "Force build application", &flags.forceBuild)
|
||||||
@ -125,14 +127,6 @@ func AddSubcommand(app *clir.Cli, w io.Writer) error {
|
|||||||
logger := clilogger.New(w)
|
logger := clilogger.New(w)
|
||||||
app.PrintBanner()
|
app.PrintBanner()
|
||||||
|
|
||||||
userTags := []string{}
|
|
||||||
for _, tag := range strings.Split(flags.tags, " ") {
|
|
||||||
thisTag := strings.TrimSpace(tag)
|
|
||||||
if thisTag != "" {
|
|
||||||
userTags = append(userTags, thisTag)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
cwd, err := os.Getwd()
|
cwd, err := os.Getwd()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@ -159,28 +153,37 @@ func AddSubcommand(app *clir.Cli, w io.Writer) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Run go mod tidy to ensure we're up to date
|
// Run go mod tidy to ensure we're up-to-date
|
||||||
err = runCommand(cwd, false, "go", "mod", "tidy", "-compat=1.17")
|
err = runCommand(cwd, false, "go", "mod", "tidy")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if !flags.noGen {
|
buildOptions := generateBuildOptions(flags)
|
||||||
self := os.Args[0]
|
buildOptions.Logger = logger
|
||||||
if flags.tags != "" {
|
|
||||||
err = runCommand(cwd, true, self, "generate", "module", "-tags", flags.tags)
|
userTags, err := buildtags.Parse(flags.tags)
|
||||||
} else {
|
if err != nil {
|
||||||
err = runCommand(cwd, true, self, "generate", "module")
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
buildOptions.UserTags = userTags
|
||||||
|
|
||||||
|
if !flags.skipBindings {
|
||||||
|
if flags.verbosity == build.VERBOSE {
|
||||||
|
LogGreen("Generating Bindings...")
|
||||||
}
|
}
|
||||||
|
stdout, err := bindings.GenerateBindings(bindings.Options{
|
||||||
|
Tags: buildOptions.UserTags,
|
||||||
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
if flags.verbosity == build.VERBOSE {
|
||||||
|
LogGreen(stdout)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
buildOptions := generateBuildOptions(flags)
|
|
||||||
buildOptions.Logger = logger
|
|
||||||
buildOptions.UserTags = internal.ParseUserTags(flags.tags)
|
|
||||||
|
|
||||||
// Setup signal handler
|
// Setup signal handler
|
||||||
quitChannel := make(chan os.Signal, 1)
|
quitChannel := make(chan os.Signal, 1)
|
||||||
signal.Notify(quitChannel, os.Interrupt, os.Kill, syscall.SIGTERM)
|
signal.Notify(quitChannel, os.Interrupt, os.Kill, syscall.SIGTERM)
|
||||||
@ -269,7 +272,7 @@ func AddSubcommand(app *clir.Cli, w io.Writer) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Reset the process and the binary so the defer knows about it and is a nop.
|
// Reset the process and the binary so defer knows about it and is a nop.
|
||||||
debugBinaryProcess = nil
|
debugBinaryProcess = nil
|
||||||
appBinary = ""
|
appBinary = ""
|
||||||
|
|
||||||
@ -654,7 +657,7 @@ func doWatcherLoop(buildOptions *build.Options, debugBinaryProcess *process.Proc
|
|||||||
}
|
}
|
||||||
|
|
||||||
if flags.frontendDevServerURL != "" {
|
if flags.frontendDevServerURL != "" {
|
||||||
// If we are using an external dev server all the reload of the frontend part can be skipped
|
// If we are using an external dev server, the reloading of the frontend part can be skipped
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if len(changedPaths) != 0 {
|
if len(changedPaths) != 0 {
|
||||||
|
@ -1,16 +1,10 @@
|
|||||||
package generate
|
package generate
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
|
||||||
"io"
|
|
||||||
"os"
|
|
||||||
"path/filepath"
|
|
||||||
"runtime"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/leaanthony/clir"
|
"github.com/leaanthony/clir"
|
||||||
"github.com/wailsapp/wails/v2/cmd/wails/internal"
|
"github.com/wailsapp/wails/v2/pkg/commands/bindings"
|
||||||
"github.com/wailsapp/wails/v2/internal/shell"
|
"github.com/wailsapp/wails/v2/pkg/commands/buildtags"
|
||||||
|
"io"
|
||||||
)
|
)
|
||||||
|
|
||||||
// AddModuleCommand adds the `module` subcommand for the `generate` command
|
// AddModuleCommand adds the `module` subcommand for the `generate` command
|
||||||
@ -22,37 +16,18 @@ func AddModuleCommand(app *clir.Cli, parent *clir.Command, w io.Writer) error {
|
|||||||
|
|
||||||
command.Action(func() error {
|
command.Action(func() error {
|
||||||
|
|
||||||
filename := "wailsbindings"
|
buildTags, err := buildtags.Parse(tags)
|
||||||
if runtime.GOOS == "windows" {
|
|
||||||
filename += ".exe"
|
|
||||||
}
|
|
||||||
// go build -tags bindings -o bindings.exe
|
|
||||||
tempDir := os.TempDir()
|
|
||||||
filename = filepath.Join(tempDir, filename)
|
|
||||||
|
|
||||||
cwd, err := os.Getwd()
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
tagList := internal.ParseUserTags(tags)
|
_, err = bindings.GenerateBindings(bindings.Options{
|
||||||
tagList = append(tagList, "bindings")
|
Tags: buildTags,
|
||||||
|
})
|
||||||
stdout, stderr, err := shell.RunCommand(cwd, "go", "build", "-tags", strings.Join(tagList, ","), "-o", filename)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("%s\n%s\n%s", stdout, stderr, err)
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
stdout, stderr, err = shell.RunCommand(cwd, filename)
|
|
||||||
println(stdout)
|
|
||||||
println(stderr)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("%s\n%s\n%s", stdout, stderr, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Best effort removal of temp file
|
|
||||||
_ = os.Remove(filename)
|
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
return nil
|
return nil
|
||||||
|
@ -186,7 +186,7 @@ func Install(options *Options) (bool, *Template, error) {
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Get the absolute path of the given directory
|
// Get the absolute path of the given directory
|
||||||
targetDir, err := filepath.Abs(filepath.Join(cwd, options.TargetDir))
|
targetDir, err := filepath.Abs(options.TargetDir)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false, nil, err
|
return false, nil, err
|
||||||
}
|
}
|
||||||
|
@ -1,15 +0,0 @@
|
|||||||
package internal
|
|
||||||
|
|
||||||
import "strings"
|
|
||||||
|
|
||||||
// ParseUserTags takes the string form of tags and converts to a slice of strings
|
|
||||||
func ParseUserTags(tagString string) []string {
|
|
||||||
userTags := make([]string, 0)
|
|
||||||
for _, tag := range strings.Split(tagString, " ") {
|
|
||||||
thisTag := strings.TrimSpace(tag)
|
|
||||||
if thisTag != "" {
|
|
||||||
userTags = append(userTags, thisTag)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return userTags
|
|
||||||
}
|
|
@ -31,7 +31,8 @@ func (a *App) Run() error {
|
|||||||
a.appoptions.OnDomReady,
|
a.appoptions.OnDomReady,
|
||||||
a.appoptions.OnBeforeClose,
|
a.appoptions.OnBeforeClose,
|
||||||
}
|
}
|
||||||
appBindings := binding.NewBindings(a.logger, a.appoptions.Bind, bindingExemptions)
|
|
||||||
|
appBindings := binding.NewBindings(a.logger, a.appoptions.Bind, bindingExemptions, IsObfuscated())
|
||||||
|
|
||||||
err := generateBindings(appBindings)
|
err := generateBindings(appBindings)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -191,7 +191,7 @@ func CreateApp(appoptions *options.App) (*App, error) {
|
|||||||
appoptions.OnDomReady,
|
appoptions.OnDomReady,
|
||||||
appoptions.OnBeforeClose,
|
appoptions.OnBeforeClose,
|
||||||
}
|
}
|
||||||
appBindings := binding.NewBindings(myLogger, appoptions.Bind, bindingExemptions)
|
appBindings := binding.NewBindings(myLogger, appoptions.Bind, bindingExemptions, false)
|
||||||
|
|
||||||
err = generateBindings(appBindings)
|
err = generateBindings(appBindings)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
8
v2/internal/app/app_not_obfuscated.go
Normal file
8
v2/internal/app/app_not_obfuscated.go
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
//go:build !obfuscated
|
||||||
|
|
||||||
|
package app
|
||||||
|
|
||||||
|
// IsObfuscated returns false if the obfuscated build tag is not set
|
||||||
|
func IsObfuscated() bool {
|
||||||
|
return false
|
||||||
|
}
|
8
v2/internal/app/app_obfuscated.go
Normal file
8
v2/internal/app/app_obfuscated.go
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
//go:build obfuscated
|
||||||
|
|
||||||
|
package app
|
||||||
|
|
||||||
|
// IsObfuscated returns true if the obfuscated build tag is set
|
||||||
|
func IsObfuscated() bool {
|
||||||
|
return true
|
||||||
|
}
|
@ -1,5 +1,4 @@
|
|||||||
//go:build production
|
//go:build production
|
||||||
// +build production
|
|
||||||
|
|
||||||
package app
|
package app
|
||||||
|
|
||||||
@ -65,6 +64,7 @@ func CreateApp(appoptions *options.App) (*App, error) {
|
|||||||
myLogger.SetLogLevel(appoptions.LogLevelProduction)
|
myLogger.SetLogLevel(appoptions.LogLevelProduction)
|
||||||
}
|
}
|
||||||
ctx = context.WithValue(ctx, "logger", myLogger)
|
ctx = context.WithValue(ctx, "logger", myLogger)
|
||||||
|
ctx = context.WithValue(ctx, "obfuscated", IsObfuscated())
|
||||||
|
|
||||||
// Preflight Checks
|
// Preflight Checks
|
||||||
err = PreflightChecks(appoptions, myLogger)
|
err = PreflightChecks(appoptions, myLogger)
|
||||||
@ -90,7 +90,7 @@ func CreateApp(appoptions *options.App) (*App, error) {
|
|||||||
appoptions.OnDomReady,
|
appoptions.OnDomReady,
|
||||||
appoptions.OnBeforeClose,
|
appoptions.OnBeforeClose,
|
||||||
}
|
}
|
||||||
appBindings := binding.NewBindings(myLogger, appoptions.Bind, bindingExemptions)
|
appBindings := binding.NewBindings(myLogger, appoptions.Bind, bindingExemptions, IsObfuscated())
|
||||||
eventHandler := runtime.NewEvents(myLogger)
|
eventHandler := runtime.NewEvents(myLogger)
|
||||||
ctx = context.WithValue(ctx, "events", eventHandler)
|
ctx = context.WithValue(ctx, "events", eventHandler)
|
||||||
// Attach logger to context
|
// Attach logger to context
|
||||||
|
@ -22,14 +22,16 @@ type Bindings struct {
|
|||||||
exemptions slicer.StringSlicer
|
exemptions slicer.StringSlicer
|
||||||
|
|
||||||
structsToGenerateTS map[string]map[string]interface{}
|
structsToGenerateTS map[string]map[string]interface{}
|
||||||
|
obfuscate bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewBindings returns a new Bindings object
|
// NewBindings returns a new Bindings object
|
||||||
func NewBindings(logger *logger.Logger, structPointersToBind []interface{}, exemptions []interface{}) *Bindings {
|
func NewBindings(logger *logger.Logger, structPointersToBind []interface{}, exemptions []interface{}, obfuscate bool) *Bindings {
|
||||||
result := &Bindings{
|
result := &Bindings{
|
||||||
db: newDB(),
|
db: newDB(),
|
||||||
logger: logger.CustomLogger("Bindings"),
|
logger: logger.CustomLogger("Bindings"),
|
||||||
structsToGenerateTS: make(map[string]map[string]interface{}),
|
structsToGenerateTS: make(map[string]map[string]interface{}),
|
||||||
|
obfuscate: obfuscate,
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, exemption := range exemptions {
|
for _, exemption := range exemptions {
|
||||||
|
@ -2,6 +2,7 @@ package binding
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
"sort"
|
||||||
"sync"
|
"sync"
|
||||||
"unsafe"
|
"unsafe"
|
||||||
)
|
)
|
||||||
@ -15,14 +16,18 @@ type DB struct {
|
|||||||
// It used for performance gains at runtime
|
// It used for performance gains at runtime
|
||||||
methodMap map[string]*BoundMethod
|
methodMap map[string]*BoundMethod
|
||||||
|
|
||||||
|
// This uses ids to reference bound methods at runtime
|
||||||
|
obfuscatedMethodMap map[int]*BoundMethod
|
||||||
|
|
||||||
// Lock to ensure sync access to the data
|
// Lock to ensure sync access to the data
|
||||||
lock sync.RWMutex
|
lock sync.RWMutex
|
||||||
}
|
}
|
||||||
|
|
||||||
func newDB() *DB {
|
func newDB() *DB {
|
||||||
return &DB{
|
return &DB{
|
||||||
store: make(map[string]map[string]map[string]*BoundMethod),
|
store: make(map[string]map[string]map[string]*BoundMethod),
|
||||||
methodMap: make(map[string]*BoundMethod),
|
methodMap: make(map[string]*BoundMethod),
|
||||||
|
obfuscatedMethodMap: make(map[int]*BoundMethod),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -56,11 +61,18 @@ func (d *DB) GetMethod(qualifiedMethodName string) *BoundMethod {
|
|||||||
return d.methodMap[qualifiedMethodName]
|
return d.methodMap[qualifiedMethodName]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetObfuscatedMethod returns the method for the given ID
|
||||||
|
func (d *DB) GetObfuscatedMethod(id int) *BoundMethod {
|
||||||
|
// Lock the db whilst processing and unlock on return
|
||||||
|
d.lock.RLock()
|
||||||
|
defer d.lock.RUnlock()
|
||||||
|
|
||||||
|
return d.obfuscatedMethodMap[id]
|
||||||
|
}
|
||||||
|
|
||||||
// AddMethod adds the given method definition to the db using the given qualified path: packageName.structName.methodName
|
// AddMethod adds the given method definition to the db using the given qualified path: packageName.structName.methodName
|
||||||
func (d *DB) AddMethod(packageName string, structName string, methodName string, methodDefinition *BoundMethod) {
|
func (d *DB) AddMethod(packageName string, structName string, methodName string, methodDefinition *BoundMethod) {
|
||||||
|
|
||||||
// TODO: Validate inputs?
|
|
||||||
|
|
||||||
// Lock the db whilst processing and unlock on return
|
// Lock the db whilst processing and unlock on return
|
||||||
d.lock.Lock()
|
d.lock.Lock()
|
||||||
defer d.lock.Unlock()
|
defer d.lock.Unlock()
|
||||||
@ -97,8 +109,31 @@ func (d *DB) ToJSON() (string, error) {
|
|||||||
d.lock.RLock()
|
d.lock.RLock()
|
||||||
defer d.lock.RUnlock()
|
defer d.lock.RUnlock()
|
||||||
|
|
||||||
|
d.UpdateObfuscatedCallMap()
|
||||||
|
|
||||||
bytes, err := json.Marshal(&d.store)
|
bytes, err := json.Marshal(&d.store)
|
||||||
|
|
||||||
// Return zero copy string as this string will be read only
|
// Return zero copy string as this string will be read only
|
||||||
return *(*string)(unsafe.Pointer(&bytes)), err
|
result := *(*string)(unsafe.Pointer(&bytes))
|
||||||
|
return result, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// UpdateObfuscatedCallMap sets up the secure call mappings
|
||||||
|
func (d *DB) UpdateObfuscatedCallMap() map[string]int {
|
||||||
|
|
||||||
|
var mappings = make(map[string]int)
|
||||||
|
|
||||||
|
// Iterate map keys and sort them
|
||||||
|
keys := make([]string, 0, len(d.methodMap))
|
||||||
|
for k := range d.methodMap {
|
||||||
|
keys = append(keys, k)
|
||||||
|
}
|
||||||
|
sort.Strings(keys)
|
||||||
|
|
||||||
|
// Iterate sorted keys and add to obfuscated method map
|
||||||
|
for id, k := range keys {
|
||||||
|
mappings[k] = id
|
||||||
|
d.obfuscatedMethodMap[id] = d.methodMap[k]
|
||||||
|
}
|
||||||
|
return mappings
|
||||||
}
|
}
|
||||||
|
@ -16,6 +16,10 @@ import (
|
|||||||
|
|
||||||
func (b *Bindings) GenerateGoBindings(baseDir string) error {
|
func (b *Bindings) GenerateGoBindings(baseDir string) error {
|
||||||
store := b.db.store
|
store := b.db.store
|
||||||
|
var obfuscatedBindings map[string]int
|
||||||
|
if b.obfuscate {
|
||||||
|
obfuscatedBindings = b.db.UpdateObfuscatedCallMap()
|
||||||
|
}
|
||||||
for packageName, structs := range store {
|
for packageName, structs := range store {
|
||||||
packageDir := filepath.Join(baseDir, packageName)
|
packageDir := filepath.Join(baseDir, packageName)
|
||||||
err := fs.Mkdir(packageDir)
|
err := fs.Mkdir(packageDir)
|
||||||
@ -54,7 +58,12 @@ func (b *Bindings) GenerateGoBindings(baseDir string) error {
|
|||||||
argsString := args.Join(", ")
|
argsString := args.Join(", ")
|
||||||
jsoutput.WriteString(fmt.Sprintf("\nexport function %s(%s) {", methodName, argsString))
|
jsoutput.WriteString(fmt.Sprintf("\nexport function %s(%s) {", methodName, argsString))
|
||||||
jsoutput.WriteString("\n")
|
jsoutput.WriteString("\n")
|
||||||
jsoutput.WriteString(fmt.Sprintf(" return window['go']['%s']['%s']['%s'](%s);", packageName, structName, methodName, argsString))
|
if b.obfuscate {
|
||||||
|
id := obfuscatedBindings[strings.Join([]string{packageName, structName, methodName}, ".")]
|
||||||
|
jsoutput.WriteString(fmt.Sprintf(" return ObfuscatedCall(%d, [%s]);", id, argsString))
|
||||||
|
} else {
|
||||||
|
jsoutput.WriteString(fmt.Sprintf(" return window['go']['%s']['%s']['%s'](%s);", packageName, structName, methodName, argsString))
|
||||||
|
}
|
||||||
jsoutput.WriteString("\n")
|
jsoutput.WriteString("\n")
|
||||||
jsoutput.WriteString(fmt.Sprintf("}"))
|
jsoutput.WriteString(fmt.Sprintf("}"))
|
||||||
jsoutput.WriteString("\n")
|
jsoutput.WriteString("\n")
|
||||||
|
@ -25,7 +25,7 @@ type B struct {
|
|||||||
|
|
||||||
func TestNestedStruct(t *testing.T) {
|
func TestNestedStruct(t *testing.T) {
|
||||||
bind := &BindForTest{}
|
bind := &BindForTest{}
|
||||||
testBindings := NewBindings(logger.New(nil), []interface{}{bind}, []interface{}{})
|
testBindings := NewBindings(logger.New(nil), []interface{}{bind}, []interface{}{}, false)
|
||||||
|
|
||||||
namesStrSlicer := testBindings.getAllStructNames()
|
namesStrSlicer := testBindings.getAllStructNames()
|
||||||
names := []string{}
|
names := []string{}
|
||||||
|
@ -42,7 +42,9 @@ func NewAssetServer(ctx context.Context, options *options.App, bindingsJSON stri
|
|||||||
|
|
||||||
func NewAssetServerWithHandler(ctx context.Context, handler http.Handler, bindingsJSON string) (*AssetServer, error) {
|
func NewAssetServerWithHandler(ctx context.Context, handler http.Handler, bindingsJSON string) (*AssetServer, error) {
|
||||||
var buffer bytes.Buffer
|
var buffer bytes.Buffer
|
||||||
buffer.WriteString(`window.wailsbindings='` + bindingsJSON + `';` + "\n")
|
if bindingsJSON != "" {
|
||||||
|
buffer.WriteString(`window.wailsbindings='` + bindingsJSON + `';` + "\n")
|
||||||
|
}
|
||||||
buffer.Write(runtime.RuntimeDesktopJS)
|
buffer.Write(runtime.RuntimeDesktopJS)
|
||||||
|
|
||||||
result := &AssetServer{
|
result := &AssetServer{
|
||||||
|
@ -72,12 +72,17 @@ func NewFrontend(ctx context.Context, appoptions *options.App, myLogger *logger.
|
|||||||
if _starturl, _ := ctx.Value("starturl").(*url.URL); _starturl != nil {
|
if _starturl, _ := ctx.Value("starturl").(*url.URL); _starturl != nil {
|
||||||
result.startURL = _starturl
|
result.startURL = _starturl
|
||||||
} else {
|
} else {
|
||||||
bindingsJSON, err := appBindings.ToJSON()
|
var bindings string
|
||||||
if err != nil {
|
var err error
|
||||||
log.Fatal(err)
|
if _obfuscated, _ := ctx.Value("obfuscated").(bool); !_obfuscated {
|
||||||
|
bindings, err = appBindings.ToJSON()
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
appBindings.DB().UpdateObfuscatedCallMap()
|
||||||
}
|
}
|
||||||
|
assets, err := assetserver.NewAssetServer(ctx, appoptions, bindings)
|
||||||
assets, err := assetserver.NewAssetServer(ctx, appoptions, bindingsJSON)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
|
@ -75,12 +75,17 @@ func NewFrontend(ctx context.Context, appoptions *options.App, myLogger *logger.
|
|||||||
if _starturl, _ := ctx.Value("starturl").(*url.URL); _starturl != nil {
|
if _starturl, _ := ctx.Value("starturl").(*url.URL); _starturl != nil {
|
||||||
result.startURL = _starturl
|
result.startURL = _starturl
|
||||||
} else {
|
} else {
|
||||||
bindingsJSON, err := appBindings.ToJSON()
|
var bindings string
|
||||||
if err != nil {
|
var err error
|
||||||
log.Fatal(err)
|
if _obfuscated, _ := ctx.Value("obfuscated").(bool); !_obfuscated {
|
||||||
|
bindings, err = appBindings.ToJSON()
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
appBindings.DB().UpdateObfuscatedCallMap()
|
||||||
}
|
}
|
||||||
|
assets, err := assetserver.NewAssetServer(ctx, appoptions, bindings)
|
||||||
assets, err := assetserver.NewAssetServer(ctx, appoptions, bindingsJSON)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
|
@ -91,12 +91,18 @@ func NewFrontend(ctx context.Context, appoptions *options.App, myLogger *logger.
|
|||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
bindingsJSON, err := appBindings.ToJSON()
|
var bindings string
|
||||||
if err != nil {
|
var err error
|
||||||
log.Fatal(err)
|
if _obfuscated, _ := ctx.Value("obfuscated").(bool); !_obfuscated {
|
||||||
|
bindings, err = appBindings.ToJSON()
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
appBindings.DB().UpdateObfuscatedCallMap()
|
||||||
}
|
}
|
||||||
|
|
||||||
assets, err := assetserver.NewAssetServer(ctx, appoptions, bindingsJSON)
|
assets, err := assetserver.NewAssetServer(ctx, appoptions, bindings)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
|
@ -38,6 +38,8 @@ func (d *Dispatcher) ProcessMessage(message string, sender frontend.Frontend) (s
|
|||||||
return d.processEventMessage(message, sender)
|
return d.processEventMessage(message, sender)
|
||||||
case 'C':
|
case 'C':
|
||||||
return d.processCallMessage(message, sender)
|
return d.processCallMessage(message, sender)
|
||||||
|
case 'c':
|
||||||
|
return d.processSecureCallMessage(message, sender)
|
||||||
case 'W':
|
case 'W':
|
||||||
return d.processWindowMessage(message, sender)
|
return d.processWindowMessage(message, sender)
|
||||||
case 'B':
|
case 'B':
|
||||||
|
57
v2/internal/frontend/dispatcher/securecalls.go
Normal file
57
v2/internal/frontend/dispatcher/securecalls.go
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
package dispatcher
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"github.com/wailsapp/wails/v2/internal/frontend"
|
||||||
|
)
|
||||||
|
|
||||||
|
type secureCallMessage struct {
|
||||||
|
ID int `json:"id"`
|
||||||
|
Args []json.RawMessage `json:"args"`
|
||||||
|
CallbackID string `json:"callbackID"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Dispatcher) processSecureCallMessage(message string, sender frontend.Frontend) (string, error) {
|
||||||
|
|
||||||
|
var payload secureCallMessage
|
||||||
|
err := json.Unmarshal([]byte(message[1:]), &payload)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
var result interface{}
|
||||||
|
|
||||||
|
// Lookup method
|
||||||
|
registeredMethod := d.bindingsDB.GetObfuscatedMethod(payload.ID)
|
||||||
|
|
||||||
|
// Check we have it
|
||||||
|
if registeredMethod == nil {
|
||||||
|
return "", fmt.Errorf("method '%d' not registered", payload.ID)
|
||||||
|
}
|
||||||
|
|
||||||
|
args, err2 := registeredMethod.ParseArgs(payload.Args)
|
||||||
|
if err2 != nil {
|
||||||
|
errmsg := fmt.Errorf("error parsing arguments: %s", err2.Error())
|
||||||
|
result, _ := d.NewErrorCallback(errmsg.Error(), payload.CallbackID)
|
||||||
|
return result, errmsg
|
||||||
|
}
|
||||||
|
result, err = registeredMethod.Call(args)
|
||||||
|
|
||||||
|
callbackMessage := &CallbackMessage{
|
||||||
|
CallbackID: payload.CallbackID,
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
callbackMessage.Err = err.Error()
|
||||||
|
} else {
|
||||||
|
callbackMessage.Result = result
|
||||||
|
}
|
||||||
|
messageData, err := json.Marshal(callbackMessage)
|
||||||
|
d.log.Trace("json call result data: %+v\n", string(messageData))
|
||||||
|
if err != nil {
|
||||||
|
// what now?
|
||||||
|
d.log.Fatal(err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
return "c" + string(messageData), nil
|
||||||
|
}
|
@ -92,15 +92,61 @@ export function Call(name, args, timeout) {
|
|||||||
callbackID,
|
callbackID,
|
||||||
};
|
};
|
||||||
|
|
||||||
// Make the call
|
// Make the call
|
||||||
window.WailsInvoke('C' + JSON.stringify(payload));
|
window.WailsInvoke('C' + JSON.stringify(payload));
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
// eslint-disable-next-line
|
// eslint-disable-next-line
|
||||||
console.error(e);
|
console.error(e);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
window.ObfuscatedCall = (id, args, timeout) => {
|
||||||
|
|
||||||
|
// Timeout infinite by default
|
||||||
|
if (timeout == null) {
|
||||||
|
timeout = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create a promise
|
||||||
|
return new Promise(function (resolve, reject) {
|
||||||
|
|
||||||
|
// Create a unique callbackID
|
||||||
|
var callbackID;
|
||||||
|
do {
|
||||||
|
callbackID = id + '-' + randomFunc();
|
||||||
|
} while (callbacks[callbackID]);
|
||||||
|
|
||||||
|
var timeoutHandle;
|
||||||
|
// Set timeout
|
||||||
|
if (timeout > 0) {
|
||||||
|
timeoutHandle = setTimeout(function () {
|
||||||
|
reject(Error('Call to method ' + id + ' timed out. Request ID: ' + callbackID));
|
||||||
|
}, timeout);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Store callback
|
||||||
|
callbacks[callbackID] = {
|
||||||
|
timeoutHandle: timeoutHandle,
|
||||||
|
reject: reject,
|
||||||
|
resolve: resolve
|
||||||
|
};
|
||||||
|
|
||||||
|
try {
|
||||||
|
const payload = {
|
||||||
|
id,
|
||||||
|
args,
|
||||||
|
callbackID,
|
||||||
|
};
|
||||||
|
|
||||||
|
// Make the call
|
||||||
|
window.WailsInvoke('c' + JSON.stringify(payload));
|
||||||
|
} catch (e) {
|
||||||
|
// eslint-disable-next-line
|
||||||
|
console.error(e);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -70,13 +70,15 @@ window.wails = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// Set the bindings
|
// Set the bindings
|
||||||
window.wails.SetBindings(window.wailsbindings);
|
if (window.wailsbindings) {
|
||||||
delete window.wails.SetBindings;
|
window.wails.SetBindings(window.wailsbindings);
|
||||||
|
delete window.wails.SetBindings;
|
||||||
|
}
|
||||||
|
|
||||||
// This is evaluated at build time in package.json
|
// This is evaluated at build time in package.json
|
||||||
// const dev = 0;
|
// const dev = 0;
|
||||||
// const production = 1;
|
// const production = 1;
|
||||||
if (ENV === 0) {
|
if (ENV === 1) {
|
||||||
delete window.wailsbindings;
|
delete window.wailsbindings;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
29
v2/internal/frontend/runtime/dev/package-lock.json
generated
29
v2/internal/frontend/runtime/dev/package-lock.json
generated
@ -198,6 +198,15 @@
|
|||||||
"esbuild": ">=0.9.6"
|
"esbuild": ">=0.9.6"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/esbuild-svelte/node_modules/svelte": {
|
||||||
|
"version": "3.43.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/svelte/-/svelte-3.43.1.tgz",
|
||||||
|
"integrity": "sha512-nvPIaKx4HLzYlSdquISZpgG1Kqr2VAWQjZOt3Iwm3UhbqmA0LnSx4k1YpRMEhjQYW3ZCqQoK8Egto9tv4YewMA==",
|
||||||
|
"dev": true,
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 8"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/escape-string-regexp": {
|
"node_modules/escape-string-regexp": {
|
||||||
"version": "1.0.5",
|
"version": "1.0.5",
|
||||||
"resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
|
"resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
|
||||||
@ -702,9 +711,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/shell-quote": {
|
"node_modules/shell-quote": {
|
||||||
"version": "1.7.2",
|
"version": "1.7.3",
|
||||||
"resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.7.2.tgz",
|
"resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.7.3.tgz",
|
||||||
"integrity": "sha512-mRz/m/JVscCrkMyPqHc/bczi3OQHkLTqXHEFu0zDhK/qfv3UcOA4SVmRCLmos4bhjr9ekVQubj/R7waKapmiQg==",
|
"integrity": "sha512-Vpfqwm4EnqGdlsBFNmHhxhElJYrdfcxPThu+ryKS5J8L/fhAwLazFZtq+S+TWZ9ANj2piSQLGj6NQg+lKPmxrw==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"node_modules/side-channel": {
|
"node_modules/side-channel": {
|
||||||
@ -1028,6 +1037,14 @@
|
|||||||
"dev": true,
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"svelte": "^3.42.6"
|
"svelte": "^3.42.6"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"svelte": {
|
||||||
|
"version": "3.43.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/svelte/-/svelte-3.43.1.tgz",
|
||||||
|
"integrity": "sha512-nvPIaKx4HLzYlSdquISZpgG1Kqr2VAWQjZOt3Iwm3UhbqmA0LnSx4k1YpRMEhjQYW3ZCqQoK8Egto9tv4YewMA==",
|
||||||
|
"dev": true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"escape-string-regexp": {
|
"escape-string-regexp": {
|
||||||
@ -1388,9 +1405,9 @@
|
|||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"shell-quote": {
|
"shell-quote": {
|
||||||
"version": "1.7.2",
|
"version": "1.7.3",
|
||||||
"resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.7.2.tgz",
|
"resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.7.3.tgz",
|
||||||
"integrity": "sha512-mRz/m/JVscCrkMyPqHc/bczi3OQHkLTqXHEFu0zDhK/qfv3UcOA4SVmRCLmos4bhjr9ekVQubj/R7waKapmiQg==",
|
"integrity": "sha512-Vpfqwm4EnqGdlsBFNmHhxhElJYrdfcxPThu+ryKS5J8L/fhAwLazFZtq+S+TWZ9ANj2piSQLGj6NQg+lKPmxrw==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"side-channel": {
|
"side-channel": {
|
||||||
|
File diff suppressed because one or more lines are too long
@ -1,5 +1,4 @@
|
|||||||
//go:build dev || bindings || (!dev && !production && !bindings)
|
//go:build dev || bindings || (!dev && !production && !bindings)
|
||||||
// +build dev bindings !dev,!production,!bindings
|
|
||||||
|
|
||||||
package runtime
|
package runtime
|
||||||
|
|
||||||
|
File diff suppressed because one or more lines are too long
@ -1,5 +1,4 @@
|
|||||||
//go:build production && desktop
|
//go:build production
|
||||||
// +build production,desktop
|
|
||||||
|
|
||||||
package runtime
|
package runtime
|
||||||
|
|
||||||
|
File diff suppressed because one or more lines are too long
@ -84,6 +84,10 @@ type Project struct {
|
|||||||
|
|
||||||
// NSISType to be build
|
// NSISType to be build
|
||||||
NSISType string `json:"nsisType"`
|
NSISType string `json:"nsisType"`
|
||||||
|
|
||||||
|
// Garble
|
||||||
|
Obfuscated bool `json:"obfuscated"`
|
||||||
|
GarbleArgs string `json:"garbleargs"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *Project) GetDevBuildCommand() string {
|
func (p *Project) GetDevBuildCommand() string {
|
||||||
|
66
v2/pkg/commands/bindings/bindings.go
Normal file
66
v2/pkg/commands/bindings/bindings.go
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
package bindings
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"github.com/samber/lo"
|
||||||
|
"github.com/wailsapp/wails/v2/internal/shell"
|
||||||
|
"github.com/wailsapp/wails/v2/pkg/commands/buildtags"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"runtime"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Options for generating bindings
|
||||||
|
type Options struct {
|
||||||
|
Filename string
|
||||||
|
Tags []string
|
||||||
|
ProjectDirectory string
|
||||||
|
GoModTidy bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// GenerateBindings generates bindings for the Wails project in the given ProjectDirectory.
|
||||||
|
// If no project directory is given then the current working directory is used.
|
||||||
|
func GenerateBindings(options Options) (string, error) {
|
||||||
|
|
||||||
|
filename, _ := lo.Coalesce(options.Filename, "wailsbindings")
|
||||||
|
if runtime.GOOS == "windows" {
|
||||||
|
filename += ".exe"
|
||||||
|
}
|
||||||
|
|
||||||
|
// go build -tags bindings -o bindings.exe
|
||||||
|
tempDir := os.TempDir()
|
||||||
|
filename = filepath.Join(tempDir, filename)
|
||||||
|
|
||||||
|
workingDirectory, _ := lo.Coalesce(options.ProjectDirectory, lo.Must(os.Getwd()))
|
||||||
|
|
||||||
|
var stdout, stderr string
|
||||||
|
var err error
|
||||||
|
|
||||||
|
tags := append(options.Tags, "bindings")
|
||||||
|
genModuleTags := lo.Without(tags, "desktop", "production", "debug", "dev")
|
||||||
|
tagString := buildtags.Stringify(genModuleTags)
|
||||||
|
|
||||||
|
if options.GoModTidy {
|
||||||
|
stdout, stderr, err = shell.RunCommand(workingDirectory, "go", "mod", "tidy")
|
||||||
|
if err != nil {
|
||||||
|
return stdout, fmt.Errorf("%s\n%s\n%s", stdout, stderr, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
stdout, stderr, err = shell.RunCommand(workingDirectory, "go", "build", "-tags", tagString, "-o", filename)
|
||||||
|
if err != nil {
|
||||||
|
return stdout, fmt.Errorf("%s\n%s\n%s", stdout, stderr, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
defer func() {
|
||||||
|
// Best effort removal of temp file
|
||||||
|
_ = os.Remove(filename)
|
||||||
|
}()
|
||||||
|
|
||||||
|
stdout, stderr, err = shell.RunCommand(workingDirectory, filename)
|
||||||
|
if err != nil {
|
||||||
|
return stdout, fmt.Errorf("%s\n%s\n%s", stdout, stderr, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return stdout, nil
|
||||||
|
}
|
119
v2/pkg/commands/bindings/bindings_test.go
Normal file
119
v2/pkg/commands/bindings/bindings_test.go
Normal file
@ -0,0 +1,119 @@
|
|||||||
|
package bindings
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/matryer/is"
|
||||||
|
"github.com/wailsapp/wails/v2/cmd/wails/internal/commands/initialise/templates"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
const standardBindings = `// @ts-check
|
||||||
|
// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL
|
||||||
|
// This file is automatically generated. DO NOT EDIT
|
||||||
|
|
||||||
|
export function Greet(arg1) {
|
||||||
|
return window['go']['main']['App']['Greet'](arg1);
|
||||||
|
}
|
||||||
|
`
|
||||||
|
|
||||||
|
const obfuscatedBindings = `// @ts-check
|
||||||
|
// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL
|
||||||
|
// This file is automatically generated. DO NOT EDIT
|
||||||
|
|
||||||
|
export function Greet(arg1) {
|
||||||
|
return ObfuscatedCall(0, [arg1]);
|
||||||
|
}
|
||||||
|
`
|
||||||
|
|
||||||
|
func TestGenerateBindings(t *testing.T) {
|
||||||
|
|
||||||
|
i := is.New(t)
|
||||||
|
|
||||||
|
cwd, err := os.Getwd()
|
||||||
|
if err != nil {
|
||||||
|
println(err.Error())
|
||||||
|
t.Fail()
|
||||||
|
}
|
||||||
|
|
||||||
|
projectDir := filepath.Join(cwd, "test")
|
||||||
|
|
||||||
|
_ = os.RemoveAll(projectDir)
|
||||||
|
|
||||||
|
_, _, err = templates.Install(&templates.Options{
|
||||||
|
ProjectName: "test",
|
||||||
|
TemplateName: "plain",
|
||||||
|
WailsVersion: "latest",
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
println(err.Error())
|
||||||
|
t.Fail()
|
||||||
|
}
|
||||||
|
|
||||||
|
defer func() {
|
||||||
|
_ = os.RemoveAll("test")
|
||||||
|
}()
|
||||||
|
|
||||||
|
// Make the go.mod point to local
|
||||||
|
goModPath := filepath.Join(projectDir, "go.mod")
|
||||||
|
goMod, err := os.ReadFile(goModPath)
|
||||||
|
i.NoErr(err)
|
||||||
|
goMod = []byte(strings.ReplaceAll(string(goMod), "// replace", "replace"))
|
||||||
|
// Write file back
|
||||||
|
err = os.WriteFile(goModPath, goMod, 0755)
|
||||||
|
i.NoErr(err)
|
||||||
|
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
options Options
|
||||||
|
stdout string
|
||||||
|
expectedBindings string
|
||||||
|
wantErr bool
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "should generate standard bindings with no user tags",
|
||||||
|
options: Options{
|
||||||
|
ProjectDirectory: projectDir,
|
||||||
|
GoModTidy: true,
|
||||||
|
},
|
||||||
|
expectedBindings: standardBindings,
|
||||||
|
stdout: "",
|
||||||
|
wantErr: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "should generate bindings when given tags",
|
||||||
|
options: Options{
|
||||||
|
ProjectDirectory: projectDir,
|
||||||
|
Tags: []string{"test"},
|
||||||
|
GoModTidy: true,
|
||||||
|
},
|
||||||
|
expectedBindings: standardBindings,
|
||||||
|
stdout: "",
|
||||||
|
wantErr: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "should generate obfuscated bindings",
|
||||||
|
options: Options{
|
||||||
|
ProjectDirectory: projectDir,
|
||||||
|
Tags: []string{"obfuscated"},
|
||||||
|
GoModTidy: true,
|
||||||
|
},
|
||||||
|
expectedBindings: obfuscatedBindings,
|
||||||
|
stdout: "",
|
||||||
|
wantErr: false,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
stdout, err := GenerateBindings(tt.options)
|
||||||
|
i.True((err != nil) == tt.wantErr)
|
||||||
|
i.Equal(stdout, tt.stdout)
|
||||||
|
// Read bindings
|
||||||
|
bindingsFile := filepath.Join(projectDir, "frontend", "wailsjs", "go", "main", "App.js")
|
||||||
|
bindings, err := os.ReadFile(bindingsFile)
|
||||||
|
i.NoErr(err)
|
||||||
|
i.Equal(string(bindings), tt.expectedBindings)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
@ -153,7 +153,7 @@ func (b *BaseBuilder) CompileProject(options *Options) error {
|
|||||||
verbose := options.Verbosity == VERBOSE
|
verbose := options.Verbosity == VERBOSE
|
||||||
// Run go mod tidy first
|
// Run go mod tidy first
|
||||||
if !options.SkipModTidy {
|
if !options.SkipModTidy {
|
||||||
cmd := exec.Command(options.Compiler, "mod", "tidy", "-compat=1.17")
|
cmd := exec.Command(options.Compiler, "mod", "tidy")
|
||||||
cmd.Stderr = os.Stderr
|
cmd.Stderr = os.Stderr
|
||||||
if verbose {
|
if verbose {
|
||||||
println("")
|
println("")
|
||||||
@ -165,8 +165,23 @@ func (b *BaseBuilder) CompileProject(options *Options) error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
commands := slicer.String()
|
||||||
|
|
||||||
|
compiler := options.Compiler
|
||||||
|
if options.Obfuscated {
|
||||||
|
if !shell.CommandExists("garble") {
|
||||||
|
return fmt.Errorf("the 'garble' command was not found. Please install it with `go install mvdan.cc/garble@latest`")
|
||||||
|
} else {
|
||||||
|
compiler = "garble"
|
||||||
|
if options.GarbleArgs != "" {
|
||||||
|
commands.AddSlice(strings.Split(options.GarbleArgs, " "))
|
||||||
|
}
|
||||||
|
options.UserTags = append(options.UserTags, "obfuscated")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Default go build command
|
// Default go build command
|
||||||
commands := slicer.String([]string{"build"})
|
commands.Add("build")
|
||||||
|
|
||||||
// Add better debugging flags
|
// Add better debugging flags
|
||||||
if options.Mode == Dev || options.Mode == Debug {
|
if options.Mode == Dev || options.Mode == Debug {
|
||||||
@ -203,6 +218,10 @@ func (b *BaseBuilder) CompileProject(options *Options) error {
|
|||||||
tags.Add("debug")
|
tags.Add("debug")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if options.Obfuscated {
|
||||||
|
tags.Add("obfuscated")
|
||||||
|
}
|
||||||
|
|
||||||
tags.Deduplicate()
|
tags.Deduplicate()
|
||||||
|
|
||||||
// Add the output type build tag
|
// Add the output type build tag
|
||||||
@ -247,11 +266,11 @@ func (b *BaseBuilder) CompileProject(options *Options) error {
|
|||||||
b.projectData.OutputFilename = strings.TrimPrefix(compiledBinary, options.ProjectData.Path)
|
b.projectData.OutputFilename = strings.TrimPrefix(compiledBinary, options.ProjectData.Path)
|
||||||
options.CompiledBinary = compiledBinary
|
options.CompiledBinary = compiledBinary
|
||||||
|
|
||||||
// Create the command
|
// Build the application
|
||||||
cmd := exec.Command(options.Compiler, commands.AsSlice()...)
|
cmd := exec.Command(compiler, commands.AsSlice()...)
|
||||||
cmd.Stderr = os.Stderr
|
cmd.Stderr = os.Stderr
|
||||||
if verbose {
|
if verbose {
|
||||||
println(" Build command:", commands.Join(" "))
|
println(" Build command:", compiler, commands.Join(" "))
|
||||||
cmd.Stdout = os.Stdout
|
cmd.Stdout = os.Stdout
|
||||||
}
|
}
|
||||||
// Set the directory
|
// Set the directory
|
||||||
|
@ -2,6 +2,7 @@ package build
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"github.com/wailsapp/wails/v2/pkg/commands/bindings"
|
||||||
"log"
|
"log"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
@ -59,6 +60,9 @@ type Options struct {
|
|||||||
TrimPath bool // Use Go's trimpath compiler flag
|
TrimPath bool // Use Go's trimpath compiler flag
|
||||||
RaceDetector bool // Build with Go's race detector
|
RaceDetector bool // Build with Go's race detector
|
||||||
WindowsConsole bool // Indicates that the windows console should be kept
|
WindowsConsole bool // Indicates that the windows console should be kept
|
||||||
|
Obfuscated bool // Indicates that bound methods should be obfuscated
|
||||||
|
GarbleArgs string // The arguments for Garble
|
||||||
|
SkipBindings bool // Skip binding generation
|
||||||
}
|
}
|
||||||
|
|
||||||
// Build the project!
|
// Build the project!
|
||||||
@ -126,6 +130,14 @@ func Build(options *Options) (string, error) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Generate bindings
|
||||||
|
if !options.SkipBindings {
|
||||||
|
err = GenerateBindings(options)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if !options.IgnoreFrontend {
|
if !options.IgnoreFrontend {
|
||||||
err = builder.BuildFrontend(outputLogger)
|
err = builder.BuildFrontend(outputLogger)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -151,6 +163,34 @@ func Build(options *Options) (string, error) {
|
|||||||
return compileBinary, nil
|
return compileBinary, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func GenerateBindings(buildOptions *Options) error {
|
||||||
|
|
||||||
|
obfuscated := buildOptions.Obfuscated
|
||||||
|
if obfuscated {
|
||||||
|
buildOptions.Logger.Print(" - Generating obfuscated bindings: ")
|
||||||
|
buildOptions.UserTags = append(buildOptions.UserTags, "obfuscated")
|
||||||
|
} else {
|
||||||
|
buildOptions.Logger.Print(" - Generating bindings: ")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generate Bindings
|
||||||
|
output, err := bindings.GenerateBindings(bindings.Options{
|
||||||
|
Tags: buildOptions.UserTags,
|
||||||
|
GoModTidy: !buildOptions.SkipModTidy,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if buildOptions.Verbosity == VERBOSE {
|
||||||
|
buildOptions.Logger.Println(output)
|
||||||
|
}
|
||||||
|
|
||||||
|
buildOptions.Logger.Println("Done.")
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func execBuildApplication(builder Builder, options *Options) (string, error) {
|
func execBuildApplication(builder Builder, options *Options) (string, error) {
|
||||||
// Extract logger
|
// Extract logger
|
||||||
outputLogger := options.Logger
|
outputLogger := options.Logger
|
||||||
|
45
v2/pkg/commands/buildtags/buildtags.go
Normal file
45
v2/pkg/commands/buildtags/buildtags.go
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
package buildtags
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"github.com/samber/lo"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Parse parses the given tags string and returns
|
||||||
|
// a cleaned slice of strings. Both comma and space delimeted
|
||||||
|
// tags are supported but not mixed. If mixed, an error is returned.
|
||||||
|
func Parse(tags string) ([]string, error) {
|
||||||
|
|
||||||
|
if tags == "" {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var userTags []string
|
||||||
|
separator := ""
|
||||||
|
if strings.Contains(tags, ",") {
|
||||||
|
separator = ","
|
||||||
|
}
|
||||||
|
if strings.Contains(tags, " ") {
|
||||||
|
if separator != "" {
|
||||||
|
return nil, errors.New("cannot use both space and comma separated values with `-tags` flag")
|
||||||
|
}
|
||||||
|
separator = " "
|
||||||
|
}
|
||||||
|
for _, tag := range strings.Split(tags, separator) {
|
||||||
|
thisTag := strings.TrimSpace(tag)
|
||||||
|
if thisTag != "" {
|
||||||
|
userTags = append(userTags, thisTag)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return userTags, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Stringify converts the given tags slice to a string compatible
|
||||||
|
// with the go build -tags flag
|
||||||
|
func Stringify(tags []string) string {
|
||||||
|
tags = lo.Map(tags, func(tag string, _ int) string {
|
||||||
|
return strings.TrimSpace(tag)
|
||||||
|
})
|
||||||
|
return strings.Join(tags, ",")
|
||||||
|
}
|
43
website/docs/guides/obfuscated.mdx
Normal file
43
website/docs/guides/obfuscated.mdx
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
# Obfuscated Builds
|
||||||
|
|
||||||
|
Wails includes support for obfuscating your application using [garble](https://github.com/burrowers/garble).
|
||||||
|
|
||||||
|
To produce an obfuscated build, you can use the `-obfuscate` flag with the `wails build` command:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
wails build -obfuscate
|
||||||
|
```
|
||||||
|
To customise the obfuscation settings, you can use the `-garbleargs` flag:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
wails build -obfuscate -garbleargs "-literals -tiny -seed=myrandomseed"
|
||||||
|
```
|
||||||
|
|
||||||
|
These settings may be persisted in your [project config](/guides/reference/project-config).
|
||||||
|
|
||||||
|
## How it works
|
||||||
|
|
||||||
|
In a standard build, all bound methods are available in the frontend under the `window.go`
|
||||||
|
variable. When these methods are called, the corresponding backend method is called using
|
||||||
|
the fully qualified function name. When using an obfuscated build, methods are bound using
|
||||||
|
an ID instead of a name. The bindings generated in the `wailsjs` directory use these IDs to
|
||||||
|
call the backend functions.
|
||||||
|
|
||||||
|
:::note
|
||||||
|
To ensure that your application will work in obfuscated mode, you must use the generated
|
||||||
|
bindings under the `wailsjs` directory in your application.
|
||||||
|
:::
|
||||||
|
|
||||||
|
## Example
|
||||||
|
|
||||||
|
Importing the "Greet" method from the bindings like this:
|
||||||
|
|
||||||
|
```js
|
||||||
|
import { Greet } from '../../wailsjs/go/main/App';
|
||||||
|
|
||||||
|
// snip
|
||||||
|
Greet('World');
|
||||||
|
```
|
||||||
|
|
||||||
|
will ensure that the method will work correctly in obfuscated mode, as the bindings will
|
||||||
|
be regenerated with IDs and the call mechanism updated.
|
@ -53,7 +53,7 @@ If you are unsure about a template, inspect `package.json` and `wails.json` for
|
|||||||
`wails build` is used for compiling your project to a production-ready binary.
|
`wails build` is used for compiling your project to a production-ready binary.
|
||||||
|
|
||||||
| Flag | Description | Default |
|
| Flag | Description | Default |
|
||||||
| :------------------- | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :-------------------------------------------------------------------------------------------------------------------------------------------- |
|
| :------------------- |:----------------------------------------------------------------------------------------------------------------------------------------------------------------------------| :-------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||||
| -platform | Build for the given (comma delimited) [platforms](../reference/cli.mdx#platforms) eg. `windows/arm64`. Note, if you do not give the architecture, `runtime.GOARCH` is used. | platform = `GOOS` environment variable if given else `runtime.GOOS`.<br/>arch = `GOARCH` envrionment variable if given else `runtime.GOARCH`. |
|
| -platform | Build for the given (comma delimited) [platforms](../reference/cli.mdx#platforms) eg. `windows/arm64`. Note, if you do not give the architecture, `runtime.GOARCH` is used. | platform = `GOOS` environment variable if given else `runtime.GOOS`.<br/>arch = `GOARCH` envrionment variable if given else `runtime.GOARCH`. |
|
||||||
| -clean | Cleans the `build/bin` directory | |
|
| -clean | Cleans the `build/bin` directory | |
|
||||||
| -compiler "compiler" | Use a different go compiler to build, eg go1.15beta1 | go |
|
| -compiler "compiler" | Use a different go compiler to build, eg go1.15beta1 | go |
|
||||||
@ -62,7 +62,7 @@ If you are unsure about a template, inspect `package.json` and `wails.json` for
|
|||||||
| -o filename | Output filename | |
|
| -o filename | Output filename | |
|
||||||
| -s | Skip building the frontend | false |
|
| -s | Skip building the frontend | false |
|
||||||
| -f | Force build application | false |
|
| -f | Force build application | false |
|
||||||
| -tags "extra tags" | Build tags to pass to compiler (quoted and space separated) | |
|
| -tags "extra tags" | Build tags to pass to Go compiler. Must be quoted. Space or comma (but not both) separated | |
|
||||||
| -upx | Compress final binary using "upx" | |
|
| -upx | Compress final binary using "upx" | |
|
||||||
| -upxflags | Flags to pass to upx | |
|
| -upxflags | Flags to pass to upx | |
|
||||||
| -v int | Verbosity level (0 - silent, 1 - default, 2 - verbose) | 1 |
|
| -v int | Verbosity level (0 - silent, 1 - default, 2 - verbose) | 1 |
|
||||||
|
@ -42,7 +42,9 @@ The project config resides in the `wails.json` file in the project directory. Th
|
|||||||
"copyright": "[The copyright of the product. Default: 'Copyright.........']",
|
"copyright": "[The copyright of the product. Default: 'Copyright.........']",
|
||||||
"comments": "[A short comment of the app. Default: 'Built using Wails (https://wails.app)']"
|
"comments": "[A short comment of the app. Default: 'Built using Wails (https://wails.app)']"
|
||||||
},
|
},
|
||||||
"nsisType": "['multiple': One installer per architecture. 'single': Single universal installer for all architectures being built. Default: 'multiple']"
|
"nsisType": "['multiple': One installer per architecture. 'single': Single universal installer for all architectures being built. Default: 'multiple']",
|
||||||
|
"obfuscated": "[Whether the app should be obfuscated. Default: false]",
|
||||||
|
"garbleargs": "[The arguments to pass to the garble command when using the obfuscated flag]"
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user