mirror of
https://github.com/wailsapp/wails.git
synced 2025-05-02 22:31:06 +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 (
|
||||
"fmt"
|
||||
"github.com/wailsapp/wails/v2/pkg/commands/buildtags"
|
||||
"io"
|
||||
"os"
|
||||
"os/exec"
|
||||
@ -75,7 +76,7 @@ func AddBuildSubcommand(app *clir.Cli, w io.Writer) {
|
||||
|
||||
// tags to pass to `go`
|
||||
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 := ""
|
||||
command.StringFlag("o", "Output filename", &outputFilename)
|
||||
@ -111,9 +112,18 @@ func AddBuildSubcommand(app *clir.Cli, w io.Writer) {
|
||||
windowsConsole := false
|
||||
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
|
||||
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 {
|
||||
|
||||
quiet := verbosity == 0
|
||||
@ -137,13 +147,10 @@ func AddBuildSubcommand(app *clir.Cli, w io.Writer) {
|
||||
return fmt.Errorf("unable to find compiler: %s", compilerCommand)
|
||||
}
|
||||
|
||||
// Tags
|
||||
userTags := []string{}
|
||||
for _, tag := range strings.Split(tags, " ") {
|
||||
thisTag := strings.TrimSpace(tag)
|
||||
if thisTag != "" {
|
||||
userTags = append(userTags, thisTag)
|
||||
}
|
||||
// Process User Tags
|
||||
userTags, err := buildtags.Parse(tags)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Webview2 installer strategy (download by default)
|
||||
@ -176,6 +183,15 @@ func AddBuildSubcommand(app *clir.Cli, w io.Writer) {
|
||||
targets.AddSlice(strings.Split(platform, ","))
|
||||
targets.Deduplicate()
|
||||
|
||||
cwd, err := os.Getwd()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
projectOptions, err := project.Load(cwd)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Create BuildOptions
|
||||
buildOptions := &build.Options{
|
||||
Logger: logger,
|
||||
@ -197,6 +213,9 @@ func AddBuildSubcommand(app *clir.Cli, w io.Writer) {
|
||||
TrimPath: trimpath,
|
||||
RaceDetector: raceDetector,
|
||||
WindowsConsole: windowsConsole,
|
||||
Obfuscated: obfuscated,
|
||||
GarbleArgs: garbleargs,
|
||||
SkipBindings: skipBindings,
|
||||
}
|
||||
|
||||
// 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, "Platforms: \t%s\n", platform)
|
||||
_, _ = 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, "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, "Compress: \t%t\n", buildOptions.Compress)
|
||||
_, _ = fmt.Fprintf(w, "Package: \t%t\n", buildOptions.Pack)
|
||||
@ -230,15 +254,6 @@ func AddBuildSubcommand(app *clir.Cli, w io.Writer) {
|
||||
return err
|
||||
}
|
||||
|
||||
cwd, err := os.Getwd()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
projectOptions, err := project.Load(cwd)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Check platform
|
||||
validPlatformArch := slicer.String([]string{
|
||||
"darwin",
|
||||
@ -329,6 +344,11 @@ func AddBuildSubcommand(app *clir.Cli, w io.Writer) {
|
||||
buildOptions.OutputFile = outputFilename
|
||||
}
|
||||
|
||||
if obfuscated && skipBindings {
|
||||
logger.Println("Warning: obfuscated flag overrides skipbindings flag.")
|
||||
buildOptions.SkipBindings = false
|
||||
}
|
||||
|
||||
if !dryRun {
|
||||
// Start Time
|
||||
start := time.Now()
|
||||
|
@ -4,6 +4,8 @@ import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/wailsapp/wails/v2/pkg/commands/bindings"
|
||||
"github.com/wailsapp/wails/v2/pkg/commands/buildtags"
|
||||
"io"
|
||||
"net"
|
||||
"net/http"
|
||||
@ -77,7 +79,7 @@ type devFlags struct {
|
||||
reloadDirs string
|
||||
openBrowser bool
|
||||
noReload bool
|
||||
noGen bool
|
||||
skipBindings bool
|
||||
wailsjsdir string
|
||||
tags string
|
||||
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.BoolFlag("browser", "Open application in browser", &flags.openBrowser)
|
||||
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("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.StringFlag("loglevel", "Loglevel to use - Trace, Debug, Info, Warning, Error", &flags.loglevel)
|
||||
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)
|
||||
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()
|
||||
if err != nil {
|
||||
return err
|
||||
@ -159,27 +153,36 @@ func AddSubcommand(app *clir.Cli, w io.Writer) error {
|
||||
return err
|
||||
}
|
||||
|
||||
// Run go mod tidy to ensure we're up to date
|
||||
err = runCommand(cwd, false, "go", "mod", "tidy", "-compat=1.17")
|
||||
// Run go mod tidy to ensure we're up-to-date
|
||||
err = runCommand(cwd, false, "go", "mod", "tidy")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if !flags.noGen {
|
||||
self := os.Args[0]
|
||||
if flags.tags != "" {
|
||||
err = runCommand(cwd, true, self, "generate", "module", "-tags", flags.tags)
|
||||
} else {
|
||||
err = runCommand(cwd, true, self, "generate", "module")
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
buildOptions := generateBuildOptions(flags)
|
||||
buildOptions.Logger = logger
|
||||
buildOptions.UserTags = internal.ParseUserTags(flags.tags)
|
||||
|
||||
userTags, err := buildtags.Parse(flags.tags)
|
||||
if err != nil {
|
||||
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 {
|
||||
return err
|
||||
}
|
||||
if flags.verbosity == build.VERBOSE {
|
||||
LogGreen(stdout)
|
||||
}
|
||||
}
|
||||
|
||||
// Setup signal handler
|
||||
quitChannel := make(chan os.Signal, 1)
|
||||
@ -269,7 +272,7 @@ func AddSubcommand(app *clir.Cli, w io.Writer) error {
|
||||
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
|
||||
appBinary = ""
|
||||
|
||||
@ -654,7 +657,7 @@ func doWatcherLoop(buildOptions *build.Options, debugBinaryProcess *process.Proc
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
if len(changedPaths) != 0 {
|
||||
|
@ -1,16 +1,10 @@
|
||||
package generate
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"strings"
|
||||
|
||||
"github.com/leaanthony/clir"
|
||||
"github.com/wailsapp/wails/v2/cmd/wails/internal"
|
||||
"github.com/wailsapp/wails/v2/internal/shell"
|
||||
"github.com/wailsapp/wails/v2/pkg/commands/bindings"
|
||||
"github.com/wailsapp/wails/v2/pkg/commands/buildtags"
|
||||
"io"
|
||||
)
|
||||
|
||||
// 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 {
|
||||
|
||||
filename := "wailsbindings"
|
||||
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()
|
||||
buildTags, err := buildtags.Parse(tags)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
tagList := internal.ParseUserTags(tags)
|
||||
tagList = append(tagList, "bindings")
|
||||
|
||||
stdout, stderr, err := shell.RunCommand(cwd, "go", "build", "-tags", strings.Join(tagList, ","), "-o", filename)
|
||||
_, err = bindings.GenerateBindings(bindings.Options{
|
||||
Tags: buildTags,
|
||||
})
|
||||
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
|
||||
|
@ -186,7 +186,7 @@ func Install(options *Options) (bool, *Template, error) {
|
||||
}
|
||||
} else {
|
||||
// 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 {
|
||||
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.OnBeforeClose,
|
||||
}
|
||||
appBindings := binding.NewBindings(a.logger, a.appoptions.Bind, bindingExemptions)
|
||||
|
||||
appBindings := binding.NewBindings(a.logger, a.appoptions.Bind, bindingExemptions, IsObfuscated())
|
||||
|
||||
err := generateBindings(appBindings)
|
||||
if err != nil {
|
||||
|
@ -191,7 +191,7 @@ func CreateApp(appoptions *options.App) (*App, error) {
|
||||
appoptions.OnDomReady,
|
||||
appoptions.OnBeforeClose,
|
||||
}
|
||||
appBindings := binding.NewBindings(myLogger, appoptions.Bind, bindingExemptions)
|
||||
appBindings := binding.NewBindings(myLogger, appoptions.Bind, bindingExemptions, false)
|
||||
|
||||
err = generateBindings(appBindings)
|
||||
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
|
||||
// +build production
|
||||
|
||||
package app
|
||||
|
||||
@ -65,6 +64,7 @@ func CreateApp(appoptions *options.App) (*App, error) {
|
||||
myLogger.SetLogLevel(appoptions.LogLevelProduction)
|
||||
}
|
||||
ctx = context.WithValue(ctx, "logger", myLogger)
|
||||
ctx = context.WithValue(ctx, "obfuscated", IsObfuscated())
|
||||
|
||||
// Preflight Checks
|
||||
err = PreflightChecks(appoptions, myLogger)
|
||||
@ -90,7 +90,7 @@ func CreateApp(appoptions *options.App) (*App, error) {
|
||||
appoptions.OnDomReady,
|
||||
appoptions.OnBeforeClose,
|
||||
}
|
||||
appBindings := binding.NewBindings(myLogger, appoptions.Bind, bindingExemptions)
|
||||
appBindings := binding.NewBindings(myLogger, appoptions.Bind, bindingExemptions, IsObfuscated())
|
||||
eventHandler := runtime.NewEvents(myLogger)
|
||||
ctx = context.WithValue(ctx, "events", eventHandler)
|
||||
// Attach logger to context
|
||||
|
@ -22,14 +22,16 @@ type Bindings struct {
|
||||
exemptions slicer.StringSlicer
|
||||
|
||||
structsToGenerateTS map[string]map[string]interface{}
|
||||
obfuscate bool
|
||||
}
|
||||
|
||||
// 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{
|
||||
db: newDB(),
|
||||
logger: logger.CustomLogger("Bindings"),
|
||||
structsToGenerateTS: make(map[string]map[string]interface{}),
|
||||
obfuscate: obfuscate,
|
||||
}
|
||||
|
||||
for _, exemption := range exemptions {
|
||||
|
@ -2,6 +2,7 @@ package binding
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"sort"
|
||||
"sync"
|
||||
"unsafe"
|
||||
)
|
||||
@ -15,6 +16,9 @@ type DB struct {
|
||||
// It used for performance gains at runtime
|
||||
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 sync.RWMutex
|
||||
}
|
||||
@ -23,6 +27,7 @@ func newDB() *DB {
|
||||
return &DB{
|
||||
store: make(map[string]map[string]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]
|
||||
}
|
||||
|
||||
// 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
|
||||
func (d *DB) AddMethod(packageName string, structName string, methodName string, methodDefinition *BoundMethod) {
|
||||
|
||||
// TODO: Validate inputs?
|
||||
|
||||
// Lock the db whilst processing and unlock on return
|
||||
d.lock.Lock()
|
||||
defer d.lock.Unlock()
|
||||
@ -97,8 +109,31 @@ func (d *DB) ToJSON() (string, error) {
|
||||
d.lock.RLock()
|
||||
defer d.lock.RUnlock()
|
||||
|
||||
d.UpdateObfuscatedCallMap()
|
||||
|
||||
bytes, err := json.Marshal(&d.store)
|
||||
|
||||
// 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 {
|
||||
store := b.db.store
|
||||
var obfuscatedBindings map[string]int
|
||||
if b.obfuscate {
|
||||
obfuscatedBindings = b.db.UpdateObfuscatedCallMap()
|
||||
}
|
||||
for packageName, structs := range store {
|
||||
packageDir := filepath.Join(baseDir, packageName)
|
||||
err := fs.Mkdir(packageDir)
|
||||
@ -54,7 +58,12 @@ func (b *Bindings) GenerateGoBindings(baseDir string) error {
|
||||
argsString := args.Join(", ")
|
||||
jsoutput.WriteString(fmt.Sprintf("\nexport function %s(%s) {", methodName, argsString))
|
||||
jsoutput.WriteString("\n")
|
||||
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(fmt.Sprintf("}"))
|
||||
jsoutput.WriteString("\n")
|
||||
|
@ -25,7 +25,7 @@ type B struct {
|
||||
|
||||
func TestNestedStruct(t *testing.T) {
|
||||
bind := &BindForTest{}
|
||||
testBindings := NewBindings(logger.New(nil), []interface{}{bind}, []interface{}{})
|
||||
testBindings := NewBindings(logger.New(nil), []interface{}{bind}, []interface{}{}, false)
|
||||
|
||||
namesStrSlicer := testBindings.getAllStructNames()
|
||||
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) {
|
||||
var buffer bytes.Buffer
|
||||
if bindingsJSON != "" {
|
||||
buffer.WriteString(`window.wailsbindings='` + bindingsJSON + `';` + "\n")
|
||||
}
|
||||
buffer.Write(runtime.RuntimeDesktopJS)
|
||||
|
||||
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 {
|
||||
result.startURL = _starturl
|
||||
} else {
|
||||
bindingsJSON, err := appBindings.ToJSON()
|
||||
var bindings string
|
||||
var err error
|
||||
if _obfuscated, _ := ctx.Value("obfuscated").(bool); !_obfuscated {
|
||||
bindings, err = appBindings.ToJSON()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
assets, err := assetserver.NewAssetServer(ctx, appoptions, bindingsJSON)
|
||||
} else {
|
||||
appBindings.DB().UpdateObfuscatedCallMap()
|
||||
}
|
||||
assets, err := assetserver.NewAssetServer(ctx, appoptions, bindings)
|
||||
if err != nil {
|
||||
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 {
|
||||
result.startURL = _starturl
|
||||
} else {
|
||||
bindingsJSON, err := appBindings.ToJSON()
|
||||
var bindings string
|
||||
var err error
|
||||
if _obfuscated, _ := ctx.Value("obfuscated").(bool); !_obfuscated {
|
||||
bindings, err = appBindings.ToJSON()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
assets, err := assetserver.NewAssetServer(ctx, appoptions, bindingsJSON)
|
||||
} else {
|
||||
appBindings.DB().UpdateObfuscatedCallMap()
|
||||
}
|
||||
assets, err := assetserver.NewAssetServer(ctx, appoptions, bindings)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
@ -91,12 +91,18 @@ func NewFrontend(ctx context.Context, appoptions *options.App, myLogger *logger.
|
||||
return result
|
||||
}
|
||||
|
||||
bindingsJSON, err := appBindings.ToJSON()
|
||||
var bindings string
|
||||
var err error
|
||||
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 {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
@ -38,6 +38,8 @@ func (d *Dispatcher) ProcessMessage(message string, sender frontend.Frontend) (s
|
||||
return d.processEventMessage(message, sender)
|
||||
case 'C':
|
||||
return d.processCallMessage(message, sender)
|
||||
case 'c':
|
||||
return d.processSecureCallMessage(message, sender)
|
||||
case 'W':
|
||||
return d.processWindowMessage(message, sender)
|
||||
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
|
||||
}
|
@ -101,6 +101,52 @@ export function Call(name, args, timeout) {
|
||||
});
|
||||
}
|
||||
|
||||
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
|
||||
window.wails.SetBindings(window.wailsbindings);
|
||||
delete window.wails.SetBindings;
|
||||
if (window.wailsbindings) {
|
||||
window.wails.SetBindings(window.wailsbindings);
|
||||
delete window.wails.SetBindings;
|
||||
}
|
||||
|
||||
// This is evaluated at build time in package.json
|
||||
// const dev = 0;
|
||||
// const production = 1;
|
||||
if (ENV === 0) {
|
||||
if (ENV === 1) {
|
||||
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"
|
||||
}
|
||||
},
|
||||
"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": {
|
||||
"version": "1.0.5",
|
||||
"resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
|
||||
@ -702,9 +711,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/shell-quote": {
|
||||
"version": "1.7.2",
|
||||
"resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.7.2.tgz",
|
||||
"integrity": "sha512-mRz/m/JVscCrkMyPqHc/bczi3OQHkLTqXHEFu0zDhK/qfv3UcOA4SVmRCLmos4bhjr9ekVQubj/R7waKapmiQg==",
|
||||
"version": "1.7.3",
|
||||
"resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.7.3.tgz",
|
||||
"integrity": "sha512-Vpfqwm4EnqGdlsBFNmHhxhElJYrdfcxPThu+ryKS5J8L/fhAwLazFZtq+S+TWZ9ANj2piSQLGj6NQg+lKPmxrw==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/side-channel": {
|
||||
@ -1028,6 +1037,14 @@
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"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": {
|
||||
@ -1388,9 +1405,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"shell-quote": {
|
||||
"version": "1.7.2",
|
||||
"resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.7.2.tgz",
|
||||
"integrity": "sha512-mRz/m/JVscCrkMyPqHc/bczi3OQHkLTqXHEFu0zDhK/qfv3UcOA4SVmRCLmos4bhjr9ekVQubj/R7waKapmiQg==",
|
||||
"version": "1.7.3",
|
||||
"resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.7.3.tgz",
|
||||
"integrity": "sha512-Vpfqwm4EnqGdlsBFNmHhxhElJYrdfcxPThu+ryKS5J8L/fhAwLazFZtq+S+TWZ9ANj2piSQLGj6NQg+lKPmxrw==",
|
||||
"dev": true
|
||||
},
|
||||
"side-channel": {
|
||||
|
File diff suppressed because one or more lines are too long
@ -1,5 +1,4 @@
|
||||
//go:build dev || bindings || (!dev && !production && !bindings)
|
||||
// +build dev bindings !dev,!production,!bindings
|
||||
|
||||
package runtime
|
||||
|
||||
|
File diff suppressed because one or more lines are too long
@ -1,5 +1,4 @@
|
||||
//go:build production && desktop
|
||||
// +build production,desktop
|
||||
//go:build production
|
||||
|
||||
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 string `json:"nsisType"`
|
||||
|
||||
// Garble
|
||||
Obfuscated bool `json:"obfuscated"`
|
||||
GarbleArgs string `json:"garbleargs"`
|
||||
}
|
||||
|
||||
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
|
||||
// Run go mod tidy first
|
||||
if !options.SkipModTidy {
|
||||
cmd := exec.Command(options.Compiler, "mod", "tidy", "-compat=1.17")
|
||||
cmd := exec.Command(options.Compiler, "mod", "tidy")
|
||||
cmd.Stderr = os.Stderr
|
||||
if verbose {
|
||||
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
|
||||
commands := slicer.String([]string{"build"})
|
||||
commands.Add("build")
|
||||
|
||||
// Add better debugging flags
|
||||
if options.Mode == Dev || options.Mode == Debug {
|
||||
@ -203,6 +218,10 @@ func (b *BaseBuilder) CompileProject(options *Options) error {
|
||||
tags.Add("debug")
|
||||
}
|
||||
|
||||
if options.Obfuscated {
|
||||
tags.Add("obfuscated")
|
||||
}
|
||||
|
||||
tags.Deduplicate()
|
||||
|
||||
// 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)
|
||||
options.CompiledBinary = compiledBinary
|
||||
|
||||
// Create the command
|
||||
cmd := exec.Command(options.Compiler, commands.AsSlice()...)
|
||||
// Build the application
|
||||
cmd := exec.Command(compiler, commands.AsSlice()...)
|
||||
cmd.Stderr = os.Stderr
|
||||
if verbose {
|
||||
println(" Build command:", commands.Join(" "))
|
||||
println(" Build command:", compiler, commands.Join(" "))
|
||||
cmd.Stdout = os.Stdout
|
||||
}
|
||||
// Set the directory
|
||||
|
@ -2,6 +2,7 @@ package build
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/wailsapp/wails/v2/pkg/commands/bindings"
|
||||
"log"
|
||||
"os"
|
||||
"path/filepath"
|
||||
@ -59,6 +60,9 @@ type Options struct {
|
||||
TrimPath bool // Use Go's trimpath compiler flag
|
||||
RaceDetector bool // Build with Go's race detector
|
||||
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!
|
||||
@ -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 {
|
||||
err = builder.BuildFrontend(outputLogger)
|
||||
if err != nil {
|
||||
@ -151,6 +163,34 @@ func Build(options *Options) (string, error) {
|
||||
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) {
|
||||
// Extract 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.
|
||||
|
||||
| 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`. |
|
||||
| -clean | Cleans the `build/bin` directory | |
|
||||
| -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 | |
|
||||
| -s | Skip building the frontend | 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" | |
|
||||
| -upxflags | Flags to pass to upx | |
|
||||
| -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.........']",
|
||||
"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