mirror of
https://github.com/wailsapp/wails.git
synced 2025-05-03 01:30:32 +08:00
[v2] NSIS installer support for Windows (#1184)
* [v2] Add support for post build hooks Currently only supports build-level hooks * [v2] Improve build assets handling and use single source for manifest generation The manifest asset files are now a go template and data will be resolved before they are included into the build output. Breaking Change: Windows manifest file must be named “wails.exe.manifest” and doesn’t depend on the project name anymore. * [v2, windows] NSIS installer generation
This commit is contained in:
parent
c63b1f1981
commit
b02dbfaddf
@ -93,6 +93,9 @@ func AddBuildSubcommand(app *clir.Cli, w io.Writer) {
|
|||||||
debug := false
|
debug := false
|
||||||
command.BoolFlag("debug", "Retains debug data in the compiled application", &debug)
|
command.BoolFlag("debug", "Retains debug data in the compiled application", &debug)
|
||||||
|
|
||||||
|
nsis := false
|
||||||
|
command.BoolFlag("nsis", "Generate NSIS installer for Windows", &nsis)
|
||||||
|
|
||||||
command.Action(func() error {
|
command.Action(func() error {
|
||||||
|
|
||||||
quiet := verbosity == 0
|
quiet := verbosity == 0
|
||||||
@ -206,6 +209,9 @@ func AddBuildSubcommand(app *clir.Cli, w io.Writer) {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
projectOptions, err := project.Load(cwd)
|
projectOptions, err := project.Load(cwd)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
// Check platform
|
// Check platform
|
||||||
validPlatformArch := slicer.String([]string{
|
validPlatformArch := slicer.String([]string{
|
||||||
@ -221,7 +227,15 @@ func AddBuildSubcommand(app *clir.Cli, w io.Writer) {
|
|||||||
"windows/arm64",
|
"windows/arm64",
|
||||||
})
|
})
|
||||||
|
|
||||||
|
outputBinaries := map[string]string{}
|
||||||
|
|
||||||
|
// Allows cancelling the build after the first error. It would be nice if targets.Each would support funcs
|
||||||
|
// returning an error.
|
||||||
|
var targetErr error
|
||||||
targets.Each(func(platform string) {
|
targets.Each(func(platform string) {
|
||||||
|
if targetErr != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
if !validPlatformArch.Contains(platform) {
|
if !validPlatformArch.Contains(platform) {
|
||||||
buildOptions.Logger.Println("platform '%s' is not supported - skipping. Supported platforms: %s", platform, validPlatformArch.Join(","))
|
buildOptions.Logger.Println("platform '%s' is not supported - skipping. Supported platforms: %s", platform, validPlatformArch.Join(","))
|
||||||
@ -293,18 +307,35 @@ func AddBuildSubcommand(app *clir.Cli, w io.Writer) {
|
|||||||
|
|
||||||
outputFilename, err := build.Build(buildOptions)
|
outputFilename, err := build.Build(buildOptions)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.Println("Error: ", err.Error())
|
logger.Println("Error: %s", err.Error())
|
||||||
|
targetErr = err
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Subsequent iterations
|
|
||||||
buildOptions.IgnoreFrontend = true
|
buildOptions.IgnoreFrontend = true
|
||||||
buildOptions.CleanBuildDirectory = false
|
buildOptions.CleanBuildDirectory = false
|
||||||
|
|
||||||
// Output stats
|
// Output stats
|
||||||
buildOptions.Logger.Println(fmt.Sprintf("Built '%s' in %s.\n", outputFilename, time.Since(start).Round(time.Millisecond).String()))
|
buildOptions.Logger.Println(fmt.Sprintf("Built '%s' in %s.\n", outputFilename, time.Since(start).Round(time.Millisecond).String()))
|
||||||
|
|
||||||
|
outputBinaries[platform] = outputFilename
|
||||||
})
|
})
|
||||||
|
|
||||||
|
if targetErr != nil {
|
||||||
|
return targetErr
|
||||||
|
}
|
||||||
|
|
||||||
|
if nsis {
|
||||||
|
amd64Binary := outputBinaries["windows/amd64"]
|
||||||
|
arm64Binary := outputBinaries["windows/arm64"]
|
||||||
|
if amd64Binary == "" && arm64Binary == "" {
|
||||||
|
return fmt.Errorf("cannot build nsis installer - no windows targets")
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := build.GenerateNSISInstaller(buildOptions, amd64Binary, arm64Binary); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -2,8 +2,6 @@ package initialise
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/flytam/filenamify"
|
|
||||||
"github.com/leaanthony/slicer"
|
|
||||||
"io"
|
"io"
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
@ -11,6 +9,9 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/flytam/filenamify"
|
||||||
|
"github.com/leaanthony/slicer"
|
||||||
|
|
||||||
"github.com/wailsapp/wails/v2/pkg/buildassets"
|
"github.com/wailsapp/wails/v2/pkg/buildassets"
|
||||||
|
|
||||||
"github.com/wailsapp/wails/v2/cmd/wails/internal/commands/initialise/templates"
|
"github.com/wailsapp/wails/v2/cmd/wails/internal/commands/initialise/templates"
|
||||||
@ -149,7 +150,7 @@ func initProject(options *templates.Options, quiet bool) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Install the default assets
|
// Install the default assets
|
||||||
err = buildassets.Install(options.TargetDir, options.ProjectName)
|
err = buildassets.Install(options.TargetDir)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,8 @@
|
|||||||
# Wails bin directory
|
# Wails bin directory
|
||||||
build/bin
|
build/bin
|
||||||
|
# Wails Windows NSIS support files
|
||||||
|
build/windows/installer/wails_tools.nsh
|
||||||
|
build/windows/installer/tmp/
|
||||||
|
|
||||||
# IDEs
|
# IDEs
|
||||||
.idea
|
.idea
|
||||||
|
@ -1,5 +1,8 @@
|
|||||||
# Wails bin directory
|
# Wails bin directory
|
||||||
build/bin
|
build/bin
|
||||||
|
# Wails Windows NSIS support files
|
||||||
|
build/windows/installer/wails_tools.nsh
|
||||||
|
build/windows/installer/tmp/
|
||||||
|
|
||||||
# IDEs
|
# IDEs
|
||||||
.idea
|
.idea
|
||||||
|
@ -46,9 +46,25 @@ type Project struct {
|
|||||||
// The platform to target
|
// The platform to target
|
||||||
Platform string
|
Platform string
|
||||||
|
|
||||||
|
// RunNonNativeBuildHooks will run build hooks though they are defined for a GOOS which is not equal to the host os
|
||||||
|
RunNonNativeBuildHooks bool `json:"runNonNativeBuildHooks"`
|
||||||
|
|
||||||
|
// Post build hooks for different targets, the hooks are executed in the following order
|
||||||
|
// Key: GOOS/GOARCH - Executed at build level after a build of the specific platform and arch
|
||||||
|
// Key: GOOS/* - Executed at build level after a build of the specific platform
|
||||||
|
// Key: */* - Executed at build level after a build
|
||||||
|
// The following keys are not yet supported.
|
||||||
|
// Key: GOOS - Executed at platform level after all builds of the specific platform
|
||||||
|
// Key: * - Executed at platform level after all builds of a platform
|
||||||
|
// Key: [empty] - Executed at global level after all builds of all platforms
|
||||||
|
PostBuildHooks map[string]string `json:"postBuildHooks"`
|
||||||
|
|
||||||
// The application author
|
// The application author
|
||||||
Author Author
|
Author Author
|
||||||
|
|
||||||
|
// The application information
|
||||||
|
Info Info
|
||||||
|
|
||||||
// Fully qualified filename
|
// Fully qualified filename
|
||||||
filename string
|
filename string
|
||||||
|
|
||||||
@ -60,6 +76,9 @@ type Project struct {
|
|||||||
|
|
||||||
// Arguments that are forwared to the application in dev mode
|
// Arguments that are forwared to the application in dev mode
|
||||||
AppArgs string `json:"appargs"`
|
AppArgs string `json:"appargs"`
|
||||||
|
|
||||||
|
// NSISType to be build
|
||||||
|
NSISType string `json:"nsisType"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *Project) Save() error {
|
func (p *Project) Save() error {
|
||||||
@ -76,6 +95,14 @@ type Author struct {
|
|||||||
Email string `json:"email"`
|
Email string `json:"email"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type Info struct {
|
||||||
|
CompanyName string `json:"companyName"`
|
||||||
|
ProductName string `json:"productName"`
|
||||||
|
ProductVersion string `json:"productVersion"`
|
||||||
|
Copyright *string `json:"copyright"`
|
||||||
|
Comments *string `json:"comments"`
|
||||||
|
}
|
||||||
|
|
||||||
// Load the project from the current working directory
|
// Load the project from the current working directory
|
||||||
func Load(projectPath string) (*Project, error) {
|
func Load(projectPath string) (*Project, error) {
|
||||||
|
|
||||||
@ -117,6 +144,24 @@ func Load(projectPath string) (*Project, error) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if result.Info.CompanyName == "" {
|
||||||
|
result.Info.CompanyName = result.Name
|
||||||
|
}
|
||||||
|
if result.Info.ProductName == "" {
|
||||||
|
result.Info.ProductName = result.Name
|
||||||
|
}
|
||||||
|
if result.Info.ProductVersion == "" {
|
||||||
|
result.Info.ProductVersion = "1.0.0"
|
||||||
|
}
|
||||||
|
if result.Info.Copyright == nil {
|
||||||
|
v := "Copyright........."
|
||||||
|
result.Info.Copyright = &v
|
||||||
|
}
|
||||||
|
if result.Info.Comments == nil {
|
||||||
|
v := "Built using Wails (https://wails.app)"
|
||||||
|
result.Info.Comments = &v
|
||||||
|
}
|
||||||
|
|
||||||
// Return our project data
|
// Return our project data
|
||||||
return &result, nil
|
return &result, nil
|
||||||
}
|
}
|
||||||
|
@ -46,6 +46,9 @@ func (a *Apt) Packages() packagemap {
|
|||||||
"docker": []*Package{
|
"docker": []*Package{
|
||||||
{Name: "docker.io", SystemPackage: true, Optional: true},
|
{Name: "docker.io", SystemPackage: true, Optional: true},
|
||||||
},
|
},
|
||||||
|
"nsis": []*Package{
|
||||||
|
{Name: "nsis", SystemPackage: true, Optional: true},
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,10 +1,11 @@
|
|||||||
package system
|
package system
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/wailsapp/wails/v2/internal/system/operatingsystem"
|
|
||||||
"github.com/wailsapp/wails/v2/internal/system/packagemanager"
|
|
||||||
"os/exec"
|
"os/exec"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"github.com/wailsapp/wails/v2/internal/system/operatingsystem"
|
||||||
|
"github.com/wailsapp/wails/v2/internal/system/packagemanager"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
@ -75,6 +76,28 @@ func checkUPX() *packagemanager.Dependancy {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func checkNSIS() *packagemanager.Dependancy {
|
||||||
|
|
||||||
|
// Check for nsis installer
|
||||||
|
output, err := exec.Command("makensis", "-VERSION").Output()
|
||||||
|
installed := true
|
||||||
|
version := ""
|
||||||
|
if err != nil {
|
||||||
|
installed = false
|
||||||
|
} else {
|
||||||
|
version = strings.TrimSpace(strings.Split(string(output), "\n")[0])
|
||||||
|
}
|
||||||
|
return &packagemanager.Dependancy{
|
||||||
|
Name: "nsis ",
|
||||||
|
PackageName: "N/A",
|
||||||
|
Installed: installed,
|
||||||
|
InstallCommand: "Available at https://nsis.sourceforge.io/Download",
|
||||||
|
Version: version,
|
||||||
|
Optional: true,
|
||||||
|
External: false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func checkDocker() *packagemanager.Dependancy {
|
func checkDocker() *packagemanager.Dependancy {
|
||||||
|
|
||||||
// Check for npm
|
// Check for npm
|
||||||
|
@ -54,5 +54,6 @@ func (i *Info) discover() error {
|
|||||||
i.Dependencies = append(i.Dependencies, xcodeDep)
|
i.Dependencies = append(i.Dependencies, xcodeDep)
|
||||||
i.Dependencies = append(i.Dependencies, checkNPM())
|
i.Dependencies = append(i.Dependencies, checkNPM())
|
||||||
i.Dependencies = append(i.Dependencies, checkUPX())
|
i.Dependencies = append(i.Dependencies, checkUPX())
|
||||||
|
i.Dependencies = append(i.Dependencies, checkNSIS())
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -45,6 +45,13 @@ func (i *Info) discover() error {
|
|||||||
dep.Version = locallyInstalled.Version
|
dep.Version = locallyInstalled.Version
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if dep.Name == "nsis" {
|
||||||
|
locallyInstalled := checkNSIS()
|
||||||
|
if locallyInstalled.Installed {
|
||||||
|
dep.Installed = true
|
||||||
|
dep.Version = locallyInstalled.Version
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
i.Dependencies = dependencies
|
i.Dependencies = dependencies
|
||||||
}
|
}
|
||||||
|
@ -21,6 +21,7 @@ func (i *Info) discover() error {
|
|||||||
i.Dependencies = append(i.Dependencies, checkWebView2())
|
i.Dependencies = append(i.Dependencies, checkWebView2())
|
||||||
i.Dependencies = append(i.Dependencies, checkNPM())
|
i.Dependencies = append(i.Dependencies, checkNPM())
|
||||||
i.Dependencies = append(i.Dependencies, checkUPX())
|
i.Dependencies = append(i.Dependencies, checkUPX())
|
||||||
|
i.Dependencies = append(i.Dependencies, checkNSIS())
|
||||||
//i.Dependencies = append(i.Dependencies, checkDocker())
|
//i.Dependencies = append(i.Dependencies, checkDocker())
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
21
v2/internal/webview2runtime/webview2installer.go
Normal file
21
v2/internal/webview2runtime/webview2installer.go
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
package webview2runtime
|
||||||
|
|
||||||
|
import (
|
||||||
|
_ "embed"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
)
|
||||||
|
|
||||||
|
//go:embed MicrosoftEdgeWebview2Setup.exe
|
||||||
|
var setupexe []byte
|
||||||
|
|
||||||
|
// WriteInstallerToFile writes the installer file to the given file.
|
||||||
|
func WriteInstallerToFile(targetFile string) error {
|
||||||
|
return os.WriteFile(targetFile, setupexe, 0755)
|
||||||
|
}
|
||||||
|
|
||||||
|
// WriteInstaller writes the installer exe file to the given directory and returns the path to it.
|
||||||
|
func WriteInstaller(targetPath string) (string, error) {
|
||||||
|
installer := filepath.Join(targetPath, `MicrosoftEdgeWebview2Setup.exe`)
|
||||||
|
return installer, WriteInstallerToFile(installer)
|
||||||
|
}
|
@ -14,9 +14,6 @@ import (
|
|||||||
"unsafe"
|
"unsafe"
|
||||||
)
|
)
|
||||||
|
|
||||||
//go:embed MicrosoftEdgeWebview2Setup.exe
|
|
||||||
var setupexe []byte
|
|
||||||
|
|
||||||
// Info contains all the information about an installation of the webview2 runtime.
|
// Info contains all the information about an installation of the webview2 runtime.
|
||||||
type Info struct {
|
type Info struct {
|
||||||
Location string
|
Location string
|
||||||
@ -170,9 +167,3 @@ func OpenInstallerDownloadWebpage() error {
|
|||||||
cmd := exec.Command("rundll32", "url.dll,FileProtocolHandler", "https://developer.microsoft.com/en-us/microsoft-edge/webview2/")
|
cmd := exec.Command("rundll32", "url.dll,FileProtocolHandler", "https://developer.microsoft.com/en-us/microsoft-edge/webview2/")
|
||||||
return cmd.Run()
|
return cmd.Run()
|
||||||
}
|
}
|
||||||
|
|
||||||
// WriteInstaller writes the installer exe file to the given path
|
|
||||||
func WriteInstaller(targetPath string) (string, error) {
|
|
||||||
installer := filepath.Join(targetPath, `MicrosoftEdgeWebview2Setup.exe`)
|
|
||||||
return installer, os.WriteFile(installer, setupexe, 0755)
|
|
||||||
}
|
|
||||||
|
@ -1,14 +1,14 @@
|
|||||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||||
<plist version="1.0"><dict>
|
<plist version="1.0"><dict>
|
||||||
<key>CFBundlePackageType</key><string>APPL</string>
|
<key>CFBundlePackageType</key><string>APPL</string>
|
||||||
<key>CFBundleName</key><string>{{.Name}}</string>
|
<key>CFBundleName</key><string>{{.Info.ProductName}}</string>
|
||||||
<key>CFBundleExecutable</key><string>{{.Name}}</string>
|
<key>CFBundleExecutable</key><string>{{.Name}}</string>
|
||||||
<key>CFBundleIdentifier</key><string>com.wails.{{.Name}}</string>
|
<key>CFBundleIdentifier</key><string>com.wails.{{.Name}}</string>
|
||||||
<key>CFBundleVersion</key><string>1.0.0</string>
|
<key>CFBundleVersion</key><string>{{.Info.ProductVersion}}</string>
|
||||||
<key>CFBundleGetInfoString</key><string>Built using Wails (https://wails.app)</string>
|
<key>CFBundleGetInfoString</key><string>{{.Info.Comments}}</string>
|
||||||
<key>CFBundleShortVersionString</key><string>1.0.0</string>
|
<key>CFBundleShortVersionString</key><string>{{.Info.ProductVersion}}</string>
|
||||||
<key>CFBundleIconFile</key><string>iconfile</string>
|
<key>CFBundleIconFile</key><string>iconfile</string>
|
||||||
<key>LSMinimumSystemVersion</key><string>10.13.0</string>
|
<key>LSMinimumSystemVersion</key><string>10.13.0</string>
|
||||||
<key>NSHighResolutionCapable</key><string>true</string>
|
<key>NSHighResolutionCapable</key><string>true</string>
|
||||||
<key>NSHumanReadableCopyright</key><string>Copyright.........</string>
|
<key>NSHumanReadableCopyright</key><string>{{.Info.Copyright}}</string>
|
||||||
</dict></plist>
|
</dict></plist>
|
15
v2/pkg/buildassets/build/windows/info.json
Normal file
15
v2/pkg/buildassets/build/windows/info.json
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
{
|
||||||
|
"fixed": {
|
||||||
|
"file_version": "{{.Info.ProductVersion}}"
|
||||||
|
},
|
||||||
|
"info": {
|
||||||
|
"0000": {
|
||||||
|
"ProductVersion": "{{.Info.ProductVersion}}",
|
||||||
|
"CompanyName": "{{.Info.CompanyName}}",
|
||||||
|
"FileDescription": "{{.Info.ProductName}}",
|
||||||
|
"LegalCopyright": "{{.Info.Copyright}}",
|
||||||
|
"ProductName": "{{.Info.ProductName}}",
|
||||||
|
"Comments": "{{.Info.Comments}}"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,15 +0,0 @@
|
|||||||
{
|
|
||||||
"fixed": {
|
|
||||||
"file_version": "1.0.0"
|
|
||||||
},
|
|
||||||
"info": {
|
|
||||||
"0000": {
|
|
||||||
"ProductVersion": "1.0.0",
|
|
||||||
"CompanyName": "{{.Name}}",
|
|
||||||
"FileDescription": "{{.Name}}",
|
|
||||||
"LegalCopyright": "Copyright.........",
|
|
||||||
"ProductName": "{{.Name}}",
|
|
||||||
"Comments": "Built using Wails (https://wails.app)"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
101
v2/pkg/buildassets/build/windows/installer/project.nsi
Normal file
101
v2/pkg/buildassets/build/windows/installer/project.nsi
Normal file
@ -0,0 +1,101 @@
|
|||||||
|
Unicode true
|
||||||
|
|
||||||
|
####
|
||||||
|
## Please note: Template replacements don't work in this file. They are provided with default defines like
|
||||||
|
## mentioned underneath.
|
||||||
|
## If the keyword is not defined, "wails_tools.nsh" will populate them with the values from ProjectInfo.
|
||||||
|
## If they are defined here, "wails_tools.nsh" will not touch them. This allows to use this project.nsi manually
|
||||||
|
## from outside of Wails for debugging and development of the installer.
|
||||||
|
##
|
||||||
|
## For development first make a wails nsis build to populate the "wails_tools.nsh":
|
||||||
|
## > wails build --target windows/amd64 --nsis
|
||||||
|
## Then you can call makensis on this file with specifying the path to your binary:
|
||||||
|
## For a AMD64 only installer:
|
||||||
|
## > makensis -DARG_WAILS_AMD64_BINARY=../../bin/app.exe
|
||||||
|
## For a ARM64 only installer:
|
||||||
|
## > makensis -DARG_WAILS_ARM64_BINARY=../../bin/app.exe
|
||||||
|
## For a installer with both architectures:
|
||||||
|
## > makensis -DARG_WAILS_AMD64_BINARY=../../bin/app-amd64.exe -DARG_WAILS_ARM64_BINARY=../../bin/app-arm64.exe
|
||||||
|
####
|
||||||
|
## The following information is taken from the ProjectInfo file, but they can be overwritten here.
|
||||||
|
####
|
||||||
|
## !define INFO_PROJECTNAME "MyProject" # Default "{{.Name}}"
|
||||||
|
## !define INFO_COMPANYNAME "MyCompany" # Default "{{.Info.CompanyName}}"
|
||||||
|
## !define INFO_PRODUCTNAME "MyProduct" # Default "{{.Info.ProductName}}"
|
||||||
|
## !define INFO_PRODUCTVERSION "1.0.0" # Default "{{.Info.ProductVersion}}"
|
||||||
|
## !define INFO_COPYRIGHT "Copyright" # Default "{{.Info.Copyright}}"
|
||||||
|
###
|
||||||
|
## !define PRODUCT_EXECUTABLE "Application.exe" # Default "${INFO_PROJECTNAME}.exe"
|
||||||
|
## !define UNINST_KEY_NAME "UninstKeyInRegistry" # Default "${INFO_COMPANYNAME}${INFO_PRODUCTNAME}"
|
||||||
|
####
|
||||||
|
## !define REQUEST_EXECUTION_LEVEL "admin" # Default "admin" see also https://nsis.sourceforge.io/Docs/Chapter4.html
|
||||||
|
####
|
||||||
|
## Include the wails tools
|
||||||
|
####
|
||||||
|
!include "wails_tools.nsh"
|
||||||
|
|
||||||
|
# The version information for this two must consist of 4 parts
|
||||||
|
VIProductVersion "${INFO_PRODUCTVERSION}.0"
|
||||||
|
VIFileVersion "${INFO_PRODUCTVERSION}.0"
|
||||||
|
|
||||||
|
VIAddVersionKey "CompanyName" "${INFO_COMPANYNAME}"
|
||||||
|
VIAddVersionKey "FileDescription" "${INFO_PRODUCTNAME} Installer"
|
||||||
|
VIAddVersionKey "ProductVersion" "${INFO_PRODUCTVERSION}"
|
||||||
|
VIAddVersionKey "FileVersion" "${INFO_PRODUCTVERSION}"
|
||||||
|
VIAddVersionKey "LegalCopyright" "${INFO_COPYRIGHT}"
|
||||||
|
VIAddVersionKey "ProductName" "${INFO_PRODUCTNAME}"
|
||||||
|
|
||||||
|
!include "MUI.nsh"
|
||||||
|
|
||||||
|
!define MUI_ICON "../icon.ico"
|
||||||
|
!define MUI_UNICON "../icon.ico"
|
||||||
|
# !define MUI_WELCOMEFINISHPAGE_BITMAP "resources/leftimage.bmp" #Include this to add a bitmap on the left side of the Welcome Page. Must be a size of 164x314
|
||||||
|
!define MUI_FINISHPAGE_NOAUTOCLOSE # Wait on the INSTFILES page so the user can take a look into the details of the installation steps
|
||||||
|
!define MUI_ABORTWARNING # This will warn the user if they exit from the installer.
|
||||||
|
|
||||||
|
!insertmacro MUI_PAGE_WELCOME # Welcome to the installer page.
|
||||||
|
# !insertmacro MUI_PAGE_LICENSE "resources/eula.txt" # Adds a EULA page to the installer
|
||||||
|
!insertmacro MUI_PAGE_DIRECTORY # In which folder install page.
|
||||||
|
!insertmacro MUI_PAGE_INSTFILES # Installing page.
|
||||||
|
!insertmacro MUI_PAGE_FINISH # Finished installation page.
|
||||||
|
|
||||||
|
!insertmacro MUI_UNPAGE_INSTFILES # Uinstalling page
|
||||||
|
|
||||||
|
!insertmacro MUI_LANGUAGE "English" # Set the Language of the installer
|
||||||
|
|
||||||
|
## The following two statements can be used to sign the installer and the uninstaller. The path to the binaries are provided in %1
|
||||||
|
#!uninstfinalize 'signtool --file "%1"'
|
||||||
|
#!finalize 'signtool --file "%1"'
|
||||||
|
|
||||||
|
Name "${INFO_PRODUCTNAME}"
|
||||||
|
OutFile "../../bin/${INFO_PROJECTNAME}-${ARCH}-installer.exe" # Name of the installer's file.
|
||||||
|
InstallDir "$PROGRAMFILES64\${INFO_COMPANYNAME}\${INFO_PRODUCTNAME}" # Default installing folder ($PROGRAMFILES is Program Files folder).
|
||||||
|
ShowInstDetails show # This will always show the installation details.
|
||||||
|
|
||||||
|
Function .onInit
|
||||||
|
!insertmacro wails.checkArchitecture
|
||||||
|
FunctionEnd
|
||||||
|
|
||||||
|
Section
|
||||||
|
!insertmacro wails.webview2runtime
|
||||||
|
|
||||||
|
SetOutPath $INSTDIR
|
||||||
|
|
||||||
|
!insertmacro wails.files
|
||||||
|
|
||||||
|
CreateShortcut "$SMPROGRAMS\${INFO_PRODUCTNAME}.lnk" "$INSTDIR\${PRODUCT_EXECUTABLE}"
|
||||||
|
CreateShortCut "$DESKTOP\${INFO_PRODUCTNAME}.lnk" "$INSTDIR\${PRODUCT_EXECUTABLE}"
|
||||||
|
|
||||||
|
!insertmacro wails.writeUninstaller
|
||||||
|
SectionEnd
|
||||||
|
|
||||||
|
Section "uninstall"
|
||||||
|
RMDir /r "$AppData\${PRODUCT_EXECUTABLE}" # Remove the WebView2 DataPath
|
||||||
|
|
||||||
|
RMDir /r $INSTDIR
|
||||||
|
|
||||||
|
Delete "$SMPROGRAMS\${INFO_PRODUCTNAME}.lnk"
|
||||||
|
Delete "$DESKTOP\${INFO_PRODUCTNAME}.lnk"
|
||||||
|
|
||||||
|
!insertmacro wails.deleteUninstaller
|
||||||
|
SectionEnd
|
171
v2/pkg/buildassets/build/windows/installer/wails_tools.nsh
Normal file
171
v2/pkg/buildassets/build/windows/installer/wails_tools.nsh
Normal file
@ -0,0 +1,171 @@
|
|||||||
|
# DO NOT EDIT - Generated automatically by `wails build`
|
||||||
|
|
||||||
|
!include "x64.nsh"
|
||||||
|
!include "WinVer.nsh"
|
||||||
|
!include "FileFunc.nsh"
|
||||||
|
|
||||||
|
!ifndef INFO_PROJECTNAME
|
||||||
|
!define INFO_PROJECTNAME "{{.Name}}"
|
||||||
|
!endif
|
||||||
|
!ifndef INFO_COMPANYNAME
|
||||||
|
!define INFO_COMPANYNAME "{{.Info.CompanyName}}"
|
||||||
|
!endif
|
||||||
|
!ifndef INFO_PRODUCTNAME
|
||||||
|
!define INFO_PRODUCTNAME "{{.Info.ProductName}}"
|
||||||
|
!endif
|
||||||
|
!ifndef INFO_PRODUCTVERSION
|
||||||
|
!define INFO_PRODUCTVERSION "{{.Info.ProductVersion}}"
|
||||||
|
!endif
|
||||||
|
!ifndef INFO_COPYRIGHT
|
||||||
|
!define INFO_COPYRIGHT "{{.Info.Copyright}}"
|
||||||
|
!endif
|
||||||
|
!ifndef PRODUCT_EXECUTABLE
|
||||||
|
!define PRODUCT_EXECUTABLE "${INFO_PROJECTNAME}.exe"
|
||||||
|
!endif
|
||||||
|
!ifndef UNINST_KEY_NAME
|
||||||
|
!define UNINST_KEY_NAME "${INFO_COMPANYNAME}${INFO_PRODUCTNAME}"
|
||||||
|
!endif
|
||||||
|
!define UNINST_KEY "Software\Microsoft\Windows\CurrentVersion\Uninstall\${UNINST_KEY_NAME}"
|
||||||
|
|
||||||
|
!ifndef REQUEST_EXECUTION_LEVEL
|
||||||
|
!define REQUEST_EXECUTION_LEVEL "admin"
|
||||||
|
!endif
|
||||||
|
|
||||||
|
RequestExecutionLevel "${REQUEST_EXECUTION_LEVEL}"
|
||||||
|
|
||||||
|
!ifdef ARG_WAILS_AMD64_BINARY
|
||||||
|
!define SUPPORTS_AMD64
|
||||||
|
!endif
|
||||||
|
|
||||||
|
!ifdef ARG_WAILS_ARM64_BINARY
|
||||||
|
!define SUPPORTS_ARM64
|
||||||
|
!endif
|
||||||
|
|
||||||
|
!ifdef SUPPORTS_AMD64
|
||||||
|
!ifdef SUPPORTS_ARM64
|
||||||
|
!define ARCH "amd64_arm64"
|
||||||
|
!else
|
||||||
|
!define ARCH "amd64"
|
||||||
|
!endif
|
||||||
|
!else
|
||||||
|
!ifdef SUPPORTS_ARM64
|
||||||
|
!define ARCH "arm64"
|
||||||
|
!else
|
||||||
|
!error "Wails: Undefined ARCH, please provide at least one of ARG_WAILS_AMD64_BINARY or ARG_WAILS_ARM64_BINARY"
|
||||||
|
!endif
|
||||||
|
!endif
|
||||||
|
|
||||||
|
!macro wails.checkArchitecture
|
||||||
|
!ifndef WAILS_WIN10_REQUIRED
|
||||||
|
!define WAILS_WIN10_REQUIRED "This product is only supported on Windows 10 (Server 2016) and later."
|
||||||
|
!endif
|
||||||
|
|
||||||
|
!ifndef WAILS_ARCHITECTURE_NOT_SUPPORTED
|
||||||
|
!define WAILS_ARCHITECTURE_NOT_SUPPORTED "This product can't be installed on the current Windows architecture. Supports: ${ARCH}"
|
||||||
|
!endif
|
||||||
|
|
||||||
|
${If} ${AtLeastWin10}
|
||||||
|
!ifdef SUPPORTS_AMD64
|
||||||
|
${if} ${IsNativeAMD64}
|
||||||
|
Goto ok
|
||||||
|
${EndIf}
|
||||||
|
!endif
|
||||||
|
|
||||||
|
!ifdef SUPPORTS_ARM64
|
||||||
|
${if} ${IsNativeARM64}
|
||||||
|
Goto ok
|
||||||
|
${EndIf}
|
||||||
|
!endif
|
||||||
|
|
||||||
|
IfSilent silentArch notSilentArch
|
||||||
|
silentArch:
|
||||||
|
SetErrorLevel 65
|
||||||
|
Abort
|
||||||
|
notSilentArch:
|
||||||
|
MessageBox MB_OK "${WAILS_ARCHITECTURE_NOT_SUPPORTED}"
|
||||||
|
Quit
|
||||||
|
${else}
|
||||||
|
IfSilent silentWin notSilentWin
|
||||||
|
silentWin:
|
||||||
|
SetErrorLevel 64
|
||||||
|
Abort
|
||||||
|
notSilentWin:
|
||||||
|
MessageBox MB_OK "${WAILS_WIN10_REQUIRED}"
|
||||||
|
Quit
|
||||||
|
${EndIf}
|
||||||
|
|
||||||
|
ok:
|
||||||
|
!macroend
|
||||||
|
|
||||||
|
!macro wails.files
|
||||||
|
!ifdef SUPPORTS_AMD64
|
||||||
|
${if} ${IsNativeAMD64}
|
||||||
|
File "/oname=${PRODUCT_EXECUTABLE}" "${ARG_WAILS_AMD64_BINARY}"
|
||||||
|
${EndIf}
|
||||||
|
!endif
|
||||||
|
|
||||||
|
!ifdef SUPPORTS_ARM64
|
||||||
|
${if} ${IsNativeARM64}
|
||||||
|
File "/oname=${PRODUCT_EXECUTABLE}" "${ARG_WAILS_ARM64_BINARY}"
|
||||||
|
${EndIf}
|
||||||
|
!endif
|
||||||
|
!macroend
|
||||||
|
|
||||||
|
!macro wails.writeUninstaller
|
||||||
|
WriteUninstaller "$INSTDIR\uninstall.exe"
|
||||||
|
|
||||||
|
SetRegView 64
|
||||||
|
WriteRegStr HKLM "${UNINST_KEY}" "Publisher" "${INFO_COMPANYNAME}"
|
||||||
|
WriteRegStr HKLM "${UNINST_KEY}" "DisplayName" "${INFO_PRODUCTNAME}"
|
||||||
|
WriteRegStr HKLM "${UNINST_KEY}" "DisplayVersion" "${INFO_PRODUCTVERSION}"
|
||||||
|
WriteRegStr HKLM "${UNINST_KEY}" "DisplayIcon" "$INSTDIR\${PRODUCT_EXECUTABLE}"
|
||||||
|
WriteRegStr HKLM "${UNINST_KEY}" "UninstallString" "$\"$INSTDIR\uninstall.exe$\""
|
||||||
|
WriteRegStr HKLM "${UNINST_KEY}" "QuietUninstallString" "$\"$INSTDIR\uninstall.exe$\" /S"
|
||||||
|
|
||||||
|
${GetSize} "$INSTDIR" "/S=0K" $0 $1 $2
|
||||||
|
IntFmt $0 "0x%08X" $0
|
||||||
|
WriteRegDWORD HKLM "${UNINST_KEY}" "EstimatedSize" "$0"
|
||||||
|
!macroend
|
||||||
|
|
||||||
|
!macro wails.deleteUninstaller
|
||||||
|
Delete "$INSTDIR\uninstall.exe"
|
||||||
|
|
||||||
|
SetRegView 64
|
||||||
|
DeleteRegKey HKLM "${UNINST_KEY}"
|
||||||
|
!macroend
|
||||||
|
|
||||||
|
# Install webview2 by launching the bootstrapper
|
||||||
|
# See https://docs.microsoft.com/en-us/microsoft-edge/webview2/concepts/distribution#online-only-deployment
|
||||||
|
!macro wails.webview2runtime
|
||||||
|
!ifndef WAILS_INSTALL_WEBVIEW_DETAILPRINT
|
||||||
|
!define WAILS_INSTALL_WEBVIEW_DETAILPRINT "Installing: WebView2 Runtime"
|
||||||
|
!endif
|
||||||
|
|
||||||
|
SetRegView 64
|
||||||
|
# If the admin key exists and is not empty then webview2 is already installed
|
||||||
|
ReadRegStr $0 HKLM "SOFTWARE\WOW6432Node\Microsoft\EdgeUpdate\Clients\{F3017226-FE2A-4295-8BDF-00C3A9A7E4C5}" "pv"
|
||||||
|
${If} $0 != ""
|
||||||
|
Goto ok
|
||||||
|
${EndIf}
|
||||||
|
|
||||||
|
${If} ${REQUEST_EXECUTION_LEVEL} == "user"
|
||||||
|
# If the installer is run in user level, check the user specific key exists and is not empty then webview2 is already installed
|
||||||
|
ReadRegStr $0 HKCU "Software\Microsoft\EdgeUpdate\Clients{F3017226-FE2A-4295-8BDF-00C3A9A7E4C5}" "pv"
|
||||||
|
${If} $0 != ""
|
||||||
|
Goto ok
|
||||||
|
${EndIf}
|
||||||
|
${EndIf}
|
||||||
|
|
||||||
|
SetDetailsPrint both
|
||||||
|
DetailPrint "${WAILS_INSTALL_WEBVIEW_DETAILPRINT}"
|
||||||
|
SetDetailsPrint listonly
|
||||||
|
|
||||||
|
InitPluginsDir
|
||||||
|
CreateDirectory "$pluginsdir\webview2bootstrapper"
|
||||||
|
SetOutPath "$pluginsdir\webview2bootstrapper"
|
||||||
|
File "tmp/MicrosoftEdgeWebview2Setup.exe"
|
||||||
|
ExecWait '"$pluginsdir\webview2bootstrapper\MicrosoftEdgeWebview2Setup.exe" /silent /install'
|
||||||
|
|
||||||
|
SetDetailsPrint both
|
||||||
|
ok:
|
||||||
|
!macroend
|
@ -1,6 +1,6 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
||||||
<assembly manifestVersion="1.0" xmlns="urn:schemas-microsoft-com:asm.v1" xmlns:asmv3="urn:schemas-microsoft-com:asm.v3">
|
<assembly manifestVersion="1.0" xmlns="urn:schemas-microsoft-com:asm.v1" xmlns:asmv3="urn:schemas-microsoft-com:asm.v3">
|
||||||
<assemblyIdentity type="win32" name="MyApplication" version="1.0.0.0" processorArchitecture="amd64"/>
|
<assemblyIdentity type="win32" name="com.wails.{{.Name}}" version="{{.Info.ProductVersion}}.0" processorArchitecture="*"/>
|
||||||
<dependency>
|
<dependency>
|
||||||
<dependentAssembly>
|
<dependentAssembly>
|
||||||
<assemblyIdentity type="win32" name="Microsoft.Windows.Common-Controls" version="6.0.0.0" processorArchitecture="*" publicKeyToken="6595b64144ccf1df" language="*"/>
|
<assemblyIdentity type="win32" name="Microsoft.Windows.Common-Controls" version="6.0.0.0" processorArchitecture="*" publicKeyToken="6595b64144ccf1df" language="*"/>
|
||||||
|
@ -1,34 +1,32 @@
|
|||||||
package buildassets
|
package buildassets
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
"embed"
|
"embed"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
iofs "io/fs"
|
||||||
"os"
|
"os"
|
||||||
|
"path"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
"text/template"
|
||||||
|
|
||||||
"github.com/leaanthony/debme"
|
|
||||||
"github.com/leaanthony/gosod"
|
"github.com/leaanthony/gosod"
|
||||||
|
"github.com/wailsapp/wails/v2/internal/fs"
|
||||||
|
"github.com/wailsapp/wails/v2/internal/project"
|
||||||
)
|
)
|
||||||
|
|
||||||
//go:embed build
|
//go:embed build
|
||||||
var assets embed.FS
|
var assets embed.FS
|
||||||
|
|
||||||
type assetData struct {
|
const (
|
||||||
Name string
|
rootFolder = "build"
|
||||||
}
|
)
|
||||||
|
|
||||||
// Install will install all default project assets
|
// Install will install all default project assets
|
||||||
func Install(targetDir string, projectName string) error {
|
func Install(targetDir string) error {
|
||||||
templateDir := gosod.New(assets)
|
templateDir := gosod.New(assets)
|
||||||
err := templateDir.Extract(targetDir, &assetData{Name: projectName})
|
err := templateDir.Extract(targetDir, nil)
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Rename the manifest file
|
|
||||||
windowsDir := filepath.Join(targetDir, "build", "windows")
|
|
||||||
manifest := filepath.Join(windowsDir, "wails.exe.manifest")
|
|
||||||
targetFile := filepath.Join(windowsDir, projectName+".exe.manifest")
|
|
||||||
err = os.Rename(manifest, targetFile)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -36,32 +34,106 @@ func Install(targetDir string, projectName string) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func RegenerateManifest(target string) error {
|
// GetLocalPath returns the local path of the requested build asset file
|
||||||
a, err := debme.FS(assets, "build")
|
func GetLocalPath(projectData *project.Project, file string) string {
|
||||||
if err != nil {
|
return filepath.Clean(filepath.Join(projectData.Path, rootFolder, filepath.FromSlash(file)))
|
||||||
return err
|
|
||||||
}
|
|
||||||
return a.CopyFile("windows/wails.exe.manifest", target, 0644)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func RegenerateAppIcon(target string) error {
|
// ReadFile reads the file from the project build folder.
|
||||||
a, err := debme.FS(assets, "build")
|
// If the file does not exist it falls back to the embedded file and the file will be written
|
||||||
|
// to the disk for customisation.
|
||||||
|
func ReadFile(projectData *project.Project, file string) ([]byte, error) {
|
||||||
|
fs := os.DirFS(filepath.ToSlash(projectData.Path)) // os.DirFs always operates on "/" as separatator
|
||||||
|
file = path.Join(rootFolder, file)
|
||||||
|
|
||||||
|
content, err := iofs.ReadFile(fs, file)
|
||||||
|
if errors.Is(err, iofs.ErrNotExist) {
|
||||||
|
// The file does not exist, let's read it from the assets FS and write it to disk
|
||||||
|
content, err := iofs.ReadFile(assets, file)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return nil, err
|
||||||
}
|
}
|
||||||
return a.CopyFile("appicon.png", target, 0644)
|
|
||||||
|
if err := writeFileSystemFile(projectData, file, content); err != nil {
|
||||||
|
return nil, fmt.Errorf("Unable to create file in build folder: %s", err)
|
||||||
|
}
|
||||||
|
return content, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return content, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func RegeneratePlist(targetDir string, projectName string) error {
|
// ReadFileWithProjectData reads the file from the project build folder and replaces ProjectInfo if necessary.
|
||||||
darwinAssets, err := debme.FS(assets, "build/darwin")
|
// If the file does not exist it falls back to the embedded file and the file will be written
|
||||||
|
// to the disk for customisation. The file written is the original unresolved one.
|
||||||
|
func ReadFileWithProjectData(projectData *project.Project, file string) ([]byte, error) {
|
||||||
|
content, err := ReadFile(projectData, file)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return nil, err
|
||||||
}
|
|
||||||
templateDir := gosod.New(darwinAssets)
|
|
||||||
err = templateDir.Extract(targetDir, &assetData{Name: projectName})
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
content, err = resolveProjectData(content, projectData)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("Unable to resolve data in %s: %w", file, err)
|
||||||
|
}
|
||||||
|
return content, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ReadOriginalFileWithProjectDataAndSave reads the file from the embedded assets and replaces
|
||||||
|
// ProjectInfo if necessary.
|
||||||
|
// It will also write the resolved final file back to the project build folder.
|
||||||
|
func ReadOriginalFileWithProjectDataAndSave(projectData *project.Project, file string) ([]byte, error) {
|
||||||
|
file = path.Join(rootFolder, file)
|
||||||
|
content, err := iofs.ReadFile(assets, file)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("Unable to read file %s: %w", file, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
content, err = resolveProjectData(content, projectData)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("Unable to resolve data in %s: %w", file, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := writeFileSystemFile(projectData, file, content); err != nil {
|
||||||
|
return nil, fmt.Errorf("Unable to create file in build folder: %w", err)
|
||||||
|
}
|
||||||
|
return content, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type assetData struct {
|
||||||
|
Name string
|
||||||
|
Info project.Info
|
||||||
|
}
|
||||||
|
|
||||||
|
func resolveProjectData(content []byte, projectData *project.Project) ([]byte, error) {
|
||||||
|
tmpl, err := template.New("").Parse(string(content))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
data := &assetData{
|
||||||
|
Name: projectData.Name,
|
||||||
|
Info: projectData.Info,
|
||||||
|
}
|
||||||
|
|
||||||
|
var out bytes.Buffer
|
||||||
|
if err := tmpl.Execute(&out, data); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return out.Bytes(), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func writeFileSystemFile(projectData *project.Project, file string, content []byte) error {
|
||||||
|
path := filepath.Clean(filepath.Join(projectData.Path, filepath.FromSlash(file)))
|
||||||
|
|
||||||
|
if dir := filepath.Dir(path); !fs.DirExists(dir) {
|
||||||
|
if err := fs.MkDirs(dir, 0755); err != nil {
|
||||||
|
return fmt.Errorf("Unable to create directory: %w", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := os.WriteFile(path, content, 0644); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -6,6 +6,7 @@ import (
|
|||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"runtime"
|
"runtime"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"github.com/wailsapp/wails/v2/internal/fs"
|
"github.com/wailsapp/wails/v2/internal/fs"
|
||||||
|
|
||||||
@ -213,8 +214,63 @@ func Build(options *Options) (string, error) {
|
|||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
|
|
||||||
result := options.CompiledBinary
|
compileBinary := options.CompiledBinary
|
||||||
|
hookArgs := map[string]string{
|
||||||
|
"${platform}": options.Platform + "/" + options.Arch,
|
||||||
|
"${bin}": compileBinary,
|
||||||
|
}
|
||||||
|
|
||||||
return result, nil
|
for _, hook := range []string{options.Platform + "/" + options.Arch, options.Platform + "/*", "*/*"} {
|
||||||
|
if err := execPostBuildHook(outputLogger, options, hook, hookArgs); err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return compileBinary, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func execPostBuildHook(outputLogger *clilogger.CLILogger, options *Options, hookIdentifier string, argReplacements map[string]string) error {
|
||||||
|
postBuildHook := options.ProjectData.PostBuildHooks[hookIdentifier]
|
||||||
|
if postBuildHook == "" {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if !options.ProjectData.RunNonNativeBuildHooks {
|
||||||
|
if hookIdentifier == "" {
|
||||||
|
// That's the global hook
|
||||||
|
} else {
|
||||||
|
platformOfHook := strings.Split(hookIdentifier, "/")[0]
|
||||||
|
if platformOfHook == "*" {
|
||||||
|
// Thats OK, we don't have a specific platform of the hook
|
||||||
|
} else if platformOfHook == runtime.GOOS {
|
||||||
|
// The hook is for host platform
|
||||||
|
} else {
|
||||||
|
// Skip a hook which is not native
|
||||||
|
outputLogger.Println(" - Non native build hook '%s': Skipping.", hookIdentifier)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
outputLogger.Print(" - Executing post build hook '%s': ", hookIdentifier)
|
||||||
|
args := strings.Split(postBuildHook, " ")
|
||||||
|
for i, arg := range args {
|
||||||
|
newArg := argReplacements[arg]
|
||||||
|
if newArg == "" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
args[i] = newArg
|
||||||
|
}
|
||||||
|
|
||||||
|
if options.Verbosity == VERBOSE {
|
||||||
|
outputLogger.Println("%s", strings.Join(args, " "))
|
||||||
|
}
|
||||||
|
|
||||||
|
_, stderr, err := shell.RunCommand(options.BuildDirectory, args[0], args[1:]...)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("%s - %s", err.Error(), stderr)
|
||||||
|
}
|
||||||
|
outputLogger.Println("Done.")
|
||||||
|
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -3,7 +3,6 @@ package build
|
|||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
|
||||||
|
|
||||||
"github.com/wailsapp/wails/v2/internal/fs"
|
"github.com/wailsapp/wails/v2/internal/fs"
|
||||||
"github.com/wailsapp/wails/v2/internal/html"
|
"github.com/wailsapp/wails/v2/internal/html"
|
||||||
@ -27,7 +26,7 @@ func (d *DesktopBuilder) BuildAssets(options *Options) error {
|
|||||||
// Check assets directory exists
|
// Check assets directory exists
|
||||||
if !fs.DirExists(options.ProjectData.BuildDir) {
|
if !fs.DirExists(options.ProjectData.BuildDir) {
|
||||||
// Path to default assets
|
// Path to default assets
|
||||||
err := buildassets.Install(options.ProjectData.Path, options.ProjectData.Name)
|
err := buildassets.Install(options.ProjectData.Path)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -81,7 +80,7 @@ func (d *DesktopBuilder) BuildBaseAssets(assets *html.AssetBundle, options *Opti
|
|||||||
d.addFileToDelete(assetsFile)
|
d.addFileToDelete(assetsFile)
|
||||||
|
|
||||||
// Process Icon
|
// Process Icon
|
||||||
err = d.processApplicationIcon(assetDir)
|
err = d.processApplicationIcon(assetDir, options)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -105,16 +104,11 @@ func (d *DesktopBuilder) BuildBaseAssets(assets *html.AssetBundle, options *Opti
|
|||||||
|
|
||||||
// processApplicationIcon will copy a default icon if one doesn't exist, then, if
|
// processApplicationIcon will copy a default icon if one doesn't exist, then, if
|
||||||
// needed, will compile the icon
|
// needed, will compile the icon
|
||||||
func (d *DesktopBuilder) processApplicationIcon(assetDir string) error {
|
func (d *DesktopBuilder) processApplicationIcon(assetDir string, options *Options) error {
|
||||||
|
iconFile, err := buildassets.ReadFile(options.ProjectData, "appicon.png")
|
||||||
// Copy default icon if one doesn't exist
|
|
||||||
iconFile := filepath.Join(d.projectData.BuildDir, "appicon.png")
|
|
||||||
if !fs.FileExists(iconFile) {
|
|
||||||
err := buildassets.RegenerateAppIcon(iconFile)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// Compile Icon
|
// Compile Icon
|
||||||
return d.compileIcon(assetDir, iconFile)
|
return d.compileIcon(assetDir, iconFile)
|
||||||
|
@ -24,7 +24,7 @@ func (d *DesktopBuilder) convertToHexLiteral(bytes []byte) string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// compileIcon will compile the icon found at <projectdir>/icon.png into the application
|
// compileIcon will compile the icon found at <projectdir>/icon.png into the application
|
||||||
func (d *DesktopBuilder) compileIcon(assetDir string, iconFile string) error {
|
func (d *DesktopBuilder) compileIcon(assetDir string, iconFile []byte) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -4,7 +4,7 @@
|
|||||||
package build
|
package build
|
||||||
|
|
||||||
// compileIcon will compile the icon found at <projectdir>/icon.png into the application
|
// compileIcon will compile the icon found at <projectdir>/icon.png into the application
|
||||||
func (d *DesktopBuilder) compileIcon(assetDir string, iconFile string) error {
|
func (d *DesktopBuilder) compileIcon(assetDir string, iconFile []byte) error {
|
||||||
//
|
//
|
||||||
//// Load icon into a databuffer
|
//// Load icon into a databuffer
|
||||||
//targetFilename := "icon"
|
//targetFilename := "icon"
|
||||||
|
@ -106,7 +106,7 @@ func (d *DesktopBuilder) processTrayIcons(assetDir string, options *Options) err
|
|||||||
}
|
}
|
||||||
|
|
||||||
// compileIcon will compile the icon found at <projectdir>/icon.png into the application
|
// compileIcon will compile the icon found at <projectdir>/icon.png into the application
|
||||||
func (d *DesktopBuilder) compileIcon(assetDir string, iconFile string) error {
|
func (d *DesktopBuilder) compileIcon(assetDir string, iconFile []byte) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
119
v2/pkg/commands/build/nsis_installer.go
Normal file
119
v2/pkg/commands/build/nsis_installer.go
Normal file
@ -0,0 +1,119 @@
|
|||||||
|
package build
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"path"
|
||||||
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/wailsapp/wails/v2/internal/fs"
|
||||||
|
"github.com/wailsapp/wails/v2/internal/shell"
|
||||||
|
"github.com/wailsapp/wails/v2/internal/webview2runtime"
|
||||||
|
"github.com/wailsapp/wails/v2/pkg/buildassets"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
nsisTypeSingle = "single"
|
||||||
|
nsisTypeMultiple = "multiple"
|
||||||
|
|
||||||
|
nsisFolder = "windows/installer"
|
||||||
|
nsisProjectFile = "project.nsi"
|
||||||
|
nsisToolsFile = "wails_tools.nsh"
|
||||||
|
nsisWebView2SetupFile = "tmp/MicrosoftEdgeWebview2Setup.exe"
|
||||||
|
)
|
||||||
|
|
||||||
|
func GenerateNSISInstaller(options *Options, amd64Binary string, arm64Binary string) error {
|
||||||
|
outputLogger := options.Logger
|
||||||
|
outputLogger.Println("Creating NSIS installer\n------------------------------")
|
||||||
|
|
||||||
|
// Ensure the file exists, if not the template will be written.
|
||||||
|
projectFile := path.Join(nsisFolder, nsisProjectFile)
|
||||||
|
if _, err := buildassets.ReadFile(options.ProjectData, projectFile); err != nil {
|
||||||
|
return fmt.Errorf("Unable to generate NSIS installer project template: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write the resolved nsis tools
|
||||||
|
toolsFile := path.Join(nsisFolder, nsisToolsFile)
|
||||||
|
if _, err := buildassets.ReadOriginalFileWithProjectDataAndSave(options.ProjectData, toolsFile); err != nil {
|
||||||
|
return fmt.Errorf("Unable to generate NSIS tools file: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write the WebView2 SetupFile
|
||||||
|
webviewSetup := buildassets.GetLocalPath(options.ProjectData, path.Join(nsisFolder, nsisWebView2SetupFile))
|
||||||
|
if dir := filepath.Dir(webviewSetup); !fs.DirExists(dir) {
|
||||||
|
if err := fs.MkDirs(dir, 0755); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := webview2runtime.WriteInstallerToFile(webviewSetup); err != nil {
|
||||||
|
return fmt.Errorf("Unable to write Webview2 Bootstrapper Setup: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !shell.CommandExists("makensis") {
|
||||||
|
outputLogger.Println("Warning: Cannot create installer: makensis not found")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
nsisType := options.ProjectData.NSISType
|
||||||
|
if nsisType == nsisTypeSingle && (amd64Binary == "" || arm64Binary == "") {
|
||||||
|
nsisType = ""
|
||||||
|
}
|
||||||
|
|
||||||
|
switch nsisType {
|
||||||
|
case "":
|
||||||
|
fallthrough
|
||||||
|
case nsisTypeMultiple:
|
||||||
|
if amd64Binary != "" {
|
||||||
|
if err := makeNSIS(options, "amd64", amd64Binary, ""); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if arm64Binary != "" {
|
||||||
|
if err := makeNSIS(options, "arm64", "", arm64Binary); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
case nsisTypeSingle:
|
||||||
|
if err := makeNSIS(options, "single", amd64Binary, arm64Binary); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return fmt.Errorf("Unsupported nsisType: %s", nsisType)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func makeNSIS(options *Options, installerKind string, amd64Binary string, arm64Binary string) error {
|
||||||
|
verbose := options.Verbosity == VERBOSE
|
||||||
|
outputLogger := options.Logger
|
||||||
|
|
||||||
|
outputLogger.Print(" - Building '%s' installer: ", installerKind)
|
||||||
|
var args = []string{}
|
||||||
|
if amd64Binary != "" {
|
||||||
|
args = append(args, "-DARG_WAILS_AMD64_BINARY="+amd64Binary)
|
||||||
|
}
|
||||||
|
if arm64Binary != "" {
|
||||||
|
args = append(args, "-DARG_WAILS_ARM64_BINARY="+arm64Binary)
|
||||||
|
}
|
||||||
|
args = append(args, nsisProjectFile)
|
||||||
|
|
||||||
|
if verbose {
|
||||||
|
outputLogger.Println("makensis %s", strings.Join(args, " "))
|
||||||
|
}
|
||||||
|
|
||||||
|
installerDir := buildassets.GetLocalPath(options.ProjectData, nsisFolder)
|
||||||
|
stdOut, stdErr, err := shell.RunCommand(installerDir, "makensis", args...)
|
||||||
|
if err != nil || verbose {
|
||||||
|
outputLogger.Println(stdOut)
|
||||||
|
outputLogger.Println(stdErr)
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("Error during creation of the installer: %w", err)
|
||||||
|
}
|
||||||
|
outputLogger.Println("Done.")
|
||||||
|
return nil
|
||||||
|
}
|
@ -1,10 +1,10 @@
|
|||||||
package build
|
package build
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
"fmt"
|
"fmt"
|
||||||
"image"
|
"image"
|
||||||
"os"
|
"os"
|
||||||
"path"
|
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"runtime"
|
"runtime"
|
||||||
|
|
||||||
@ -63,18 +63,6 @@ func cleanBuildDirectory(options *Options) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Gets (and creates) the build base directory
|
|
||||||
func getBuildBaseDirectory(options *Options) (string, error) {
|
|
||||||
buildDirectory := filepath.Join(options.ProjectData.Path, "build")
|
|
||||||
if !fs.DirExists(buildDirectory) {
|
|
||||||
err := os.MkdirAll(buildDirectory, 0700)
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return buildDirectory, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Gets the platform dependent package assets directory
|
// Gets the platform dependent package assets directory
|
||||||
func getPackageAssetsDirectory() string {
|
func getPackageAssetsDirectory() string {
|
||||||
return fs.RelativePath("internal/packager", runtime.GOOS)
|
return fs.RelativePath("internal/packager", runtime.GOOS)
|
||||||
@ -115,11 +103,7 @@ func packageApplicationForDarwin(options *Options) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Generate Icons
|
// Generate Icons
|
||||||
buildDir, err := getBuildBaseDirectory(options)
|
err = processApplicationIcon(options, resourceDir)
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
err = processApplicationIcon(resourceDir, buildDir)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -130,53 +114,28 @@ func packageApplicationForDarwin(options *Options) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func processPList(options *Options, contentsDirectory string) error {
|
func processPList(options *Options, contentsDirectory string) error {
|
||||||
|
// Read the resolved BuildAssets file and copy it to the destination
|
||||||
// Check if plist already exists in project dir
|
content, err := buildassets.ReadFileWithProjectData(options.ProjectData, "darwin/Info.plist")
|
||||||
plistFileDir := filepath.Join(options.ProjectData.Path, "build", "darwin")
|
|
||||||
plistFile := filepath.Join(plistFileDir, "Info.plist")
|
|
||||||
// If the file doesn't exist, generate it
|
|
||||||
if !fs.FileExists(plistFile) {
|
|
||||||
err := buildassets.RegeneratePlist(plistFileDir, options.ProjectData.Name)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// Copy it to the contents directory
|
|
||||||
targetFile := filepath.Join(contentsDirectory, "Info.plist")
|
targetFile := filepath.Join(contentsDirectory, "Info.plist")
|
||||||
return fs.CopyFile(plistFile, targetFile)
|
return os.WriteFile(targetFile, content, 0644)
|
||||||
}
|
}
|
||||||
|
|
||||||
func processApplicationIcon(resourceDir string, iconsDir string) (err error) {
|
func processApplicationIcon(options *Options, resourceDir string) (err error) {
|
||||||
|
appIcon, err := buildassets.ReadFile(options.ProjectData, "appicon.png")
|
||||||
appIcon := filepath.Join(iconsDir, "appicon.png")
|
|
||||||
|
|
||||||
// Install default icon if one doesn't exist
|
|
||||||
if !fs.FileExists(appIcon) {
|
|
||||||
// No - Install default icon
|
|
||||||
err = buildassets.RegenerateAppIcon(appIcon)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
tgtBundle := path.Join(resourceDir, "iconfile.icns")
|
|
||||||
imageFile, err := os.Open(appIcon)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
defer func() {
|
srcImg, _, err := image.Decode(bytes.NewBuffer(appIcon))
|
||||||
err = imageFile.Close()
|
|
||||||
if err == nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
srcImg, _, err := image.Decode(imageFile)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
tgtBundle := filepath.Join(resourceDir, "iconfile.icns")
|
||||||
dest, err := os.Create(tgtBundle)
|
dest, err := os.Create(tgtBundle)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@ -199,12 +158,6 @@ func packageApplicationForWindows(options *Options) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ensure Manifest is present
|
|
||||||
err = generateManifest(options)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create syso file
|
// Create syso file
|
||||||
err = compileResources(options)
|
err = compileResources(options)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -238,33 +191,31 @@ func packageApplicationForLinux(options *Options) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func generateManifest(options *Options) error {
|
func generateManifest(options *Options) error {
|
||||||
filename := options.ProjectData.Name + ".exe.manifest"
|
|
||||||
manifestFile := filepath.Join(options.ProjectData.Path, "build", "windows", filename)
|
|
||||||
if !fs.FileExists(manifestFile) {
|
|
||||||
return buildassets.RegenerateManifest(manifestFile)
|
|
||||||
}
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func generateIcoFile(options *Options) error {
|
func generateIcoFile(options *Options) error {
|
||||||
// Check ico file exists already
|
// Check ico file exists already
|
||||||
icoFile := filepath.Join(options.ProjectData.Path, "build", "windows", "icon.ico")
|
icoFile := buildassets.GetLocalPath(options.ProjectData, "windows/icon.ico")
|
||||||
if !fs.FileExists(icoFile) {
|
if !fs.FileExists(icoFile) {
|
||||||
// Check icon exists
|
content, err := buildassets.ReadFile(options.ProjectData, "appicon.png")
|
||||||
appicon := filepath.Join(options.ProjectData.Path, "build", "appicon.png")
|
|
||||||
if !fs.FileExists(appicon) {
|
|
||||||
return fmt.Errorf("application icon missing: %s", appicon)
|
|
||||||
}
|
|
||||||
// Load icon
|
|
||||||
input, err := os.Open(appicon)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if dir := filepath.Dir(icoFile); !fs.DirExists(dir) {
|
||||||
|
if err := fs.MkDirs(dir, 0755); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
output, err := os.OpenFile(icoFile, os.O_CREATE|os.O_WRONLY, 0644)
|
output, err := os.OpenFile(icoFile, os.O_CREATE|os.O_WRONLY, 0644)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
err = winicon.GenerateIcon(input, output, []int{256, 128, 64, 48, 32, 16})
|
defer output.Close()
|
||||||
|
|
||||||
|
err = winicon.GenerateIcon(bytes.NewBuffer(content), output, []int{256, 128, 64, 48, 32, 16})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -302,15 +253,23 @@ func compileResources(options *Options) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
ManifestFilename := options.ProjectData.Name + ".exe.manifest"
|
manifestData, err := buildassets.ReadFileWithProjectData(options.ProjectData, "windows/wails.exe.manifest")
|
||||||
manifestData, err := os.ReadFile(ManifestFilename)
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
xmlData, err := winres.AppManifestFromXML(manifestData)
|
xmlData, err := winres.AppManifestFromXML(manifestData)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
rs.SetManifest(xmlData)
|
rs.SetManifest(xmlData)
|
||||||
|
|
||||||
if versionInfo, _ := os.ReadFile("info.json"); len(versionInfo) != 0 {
|
versionInfo, err := buildassets.ReadFileWithProjectData(options.ProjectData, "windows/info.json")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(versionInfo) != 0 {
|
||||||
var v version.Info
|
var v version.Info
|
||||||
if err := v.UnmarshalJSON(versionInfo); err != nil {
|
if err := v.UnmarshalJSON(versionInfo); err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -20,8 +20,21 @@ The project config resides in the `wails.json` file in the project directory. Th
|
|||||||
"outputfilename": "[The name of the binary]",
|
"outputfilename": "[The name of the binary]",
|
||||||
"debounceMS": 100, // The default time the dev server waits to reload when it detects a vhange in assets
|
"debounceMS": 100, // The default time the dev server waits to reload when it detects a vhange in assets
|
||||||
"devserverurl": "[URL to the dev server serving local assets. Default: http://localhost:34115]",
|
"devserverurl": "[URL to the dev server serving local assets. Default: http://localhost:34115]",
|
||||||
"appargs": "[Arguments passed to the application in shell style when in dev mode]"
|
"appargs": "[Arguments passed to the application in shell style when in dev mode]",
|
||||||
|
"runNonNativeBuildHooks": false, // Defines if build hooks should be run though they are defined for an OS other than the host OS.
|
||||||
|
"postBuildHooks": {
|
||||||
|
"GOOS/GOARCH": "[The command that will be executed after a build of the specified GOOS/GOARCH: ${platform} is replaced with the "GOOS/GOARCH" and ${bin} with the path to the compiled binary. The "GOOS/GOARCH" hook is executed before the "GOOS/*" and "*/*" hook.]",
|
||||||
|
"GOOS/*": "[The command that will be executed after a build of the specified GOOS: ${platform} is replaced with the "GOOS/GOARCH" and ${bin} with the path to the compiled binary. The "GOOS/*" hook is executed before the "*/*" hook.]",
|
||||||
|
"*/*": "[The command that will be executed after every build: ${platform} is replaced with the "GOOS/GOARCH" and ${bin} with the path to the compiled binary.]"
|
||||||
|
},
|
||||||
|
"info": { // Data used to populate manifests and version info.
|
||||||
|
"companyName": "[The company name. Default: [The project name]]",
|
||||||
|
"productName": "[The product name. Default: [The project name]]",
|
||||||
|
"productVersion": "[The version of the product. Default: '1.0.0']",
|
||||||
|
"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 achitecture. 'single': Single universal installer for all architectures being built. Default: 'multiple']"
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user