Merge commit '25a157e6614739fbd4df1da3d11d46afe72ae9a8' as 'v2'

This commit is contained in:
Travis McLane
2020-09-01 19:34:51 -05:00
208 changed files with 23636 additions and 0 deletions

View File

@@ -0,0 +1,340 @@
package build
import (
"bytes"
"fmt"
"io/ioutil"
"os"
"os/exec"
"path/filepath"
"runtime"
"strings"
"github.com/leaanthony/slicer"
"github.com/leaanthony/wailsv2/v2/internal/assetdb"
"github.com/leaanthony/wailsv2/v2/internal/fs"
"github.com/leaanthony/wailsv2/v2/internal/html"
"github.com/leaanthony/wailsv2/v2/internal/logger"
"github.com/leaanthony/wailsv2/v2/internal/project"
"github.com/leaanthony/wailsv2/v2/internal/shell"
)
// BaseBuilder is the common builder struct
type BaseBuilder struct {
filesToDelete slicer.StringSlicer
projectData *project.Project
}
// NewBaseBuilder creates a new BaseBuilder
func NewBaseBuilder() *BaseBuilder {
result := &BaseBuilder{}
return result
}
// SetProjectData sets the project data for this builder
func (b *BaseBuilder) SetProjectData(projectData *project.Project) {
b.projectData = projectData
}
func (b *BaseBuilder) addFileToDelete(filename string) {
b.filesToDelete.Add(filename)
}
func (b *BaseBuilder) fileExists(path string) bool {
// if file doesn't exist, ignore
_, err := os.Stat(path)
if err != nil {
return !os.IsNotExist(err)
}
return true
}
// buildStaticAssets will iterate through the projects static directory and add all files
// to the application wide asset database.
func (b *BaseBuilder) buildStaticAssets(projectData *project.Project) error {
// Add trailing slash to Asset directory
assetsDir := filepath.Join(projectData.Path, "assets") + "/"
assets := assetdb.NewAssetDB()
if b.fileExists(assetsDir) {
err := filepath.Walk(assetsDir, func(path string, info os.FileInfo, err error) error {
if err != nil {
return err
}
normalisedPath := filepath.ToSlash(path)
localPath := strings.TrimPrefix(normalisedPath, assetsDir)
if len(localPath) == 0 {
return nil
}
if data, err := ioutil.ReadFile(filepath.Join(assetsDir, localPath)); err == nil {
assets.AddAsset(localPath, data)
}
return nil
})
if err != nil {
return err
}
}
// Write assetdb out to root directory
assetsDbFilename := fs.RelativePath("../../../assetsdb.go")
b.addFileToDelete(assetsDbFilename)
err := ioutil.WriteFile(assetsDbFilename, []byte(assets.Serialize("assets", "wails")), 0644)
if err != nil {
return err
}
return nil
}
func (b *BaseBuilder) convertFileToIntegerString(filename string) (string, error) {
rawData, err := ioutil.ReadFile(filename)
if err != nil {
return "", err
}
return b.convertByteSliceToIntegerString(rawData), nil
}
func (b *BaseBuilder) convertByteSliceToIntegerString(data []byte) string {
// Create string builder
var result strings.Builder
if len(data) > 0 {
// Loop over all but 1 bytes
for i := 0; i < len(data)-1; i++ {
result.WriteString(fmt.Sprintf("%v,", data[i]))
}
result.WriteString(fmt.Sprintf("%v", data[len(data)-1]))
}
return result.String()
}
// CleanUp does post-build housekeeping
func (b *BaseBuilder) CleanUp() {
// Delete all the files
b.filesToDelete.Each(func(filename string) {
// if file doesn't exist, ignore
if !b.fileExists(filename) {
return
}
// Delete file. We ignore errors because these files will be overwritten
// by the next build anyway.
os.Remove(filename)
})
}
// CompileProject compiles the project
func (b *BaseBuilder) CompileProject(options *Options) error {
// Default go build command
commands := slicer.String([]string{"build"})
// Add the output type build tag
commands.Add("-tags")
commands.Add(options.OutputType)
// Strip binary in Production mode
if options.Mode == Production {
// Different linker flags depending on platform
commands.Add("-ldflags")
switch runtime.GOOS {
case "windows":
commands.Add("-w -s -H windowsgui")
default:
commands.Add("-w -s")
}
}
// Get application build directory
appDir, err := getApplicationBuildDirectory(options, options.Platform)
if err != nil {
return err
}
if options.LDFlags != "" {
commands.Add("-ldflags")
commands.Add(options.LDFlags)
}
// Set up output filename
outputFilePath := filepath.Join(appDir, b.projectData.OutputFilename)
commands.Add("-o")
commands.Add(outputFilePath)
b.projectData.OutputFilename = strings.TrimPrefix(outputFilePath, options.ProjectData.Path)
// Create the command
cmd := exec.Command(options.Compiler, commands.AsSlice()...)
// Set the directory
cmd.Dir = b.projectData.Path
// Set GO111MODULE environment variable
cmd.Env = append(os.Environ(), "GO111MODULE=on")
// Setup buffers
var stdo, stde bytes.Buffer
cmd.Stdout = &stdo
cmd.Stderr = &stde
// Run command
err = cmd.Run()
// Format error if we have one
if err != nil {
return fmt.Errorf("%s\n%s", err, string(stde.Bytes()))
}
return nil
}
// NpmInstall runs "npm install" in the given directory
func (b *BaseBuilder) NpmInstall(sourceDir string) error {
return b.NpmInstallUsingCommand(sourceDir, "npm install")
}
// NpmInstallUsingCommand runs the given install command in the specified npm project directory
func (b *BaseBuilder) NpmInstallUsingCommand(sourceDir string, installCommand string) error {
packageJSON := filepath.Join(sourceDir, "package.json")
// Check package.json exists
if !fs.FileExists(packageJSON) {
return fmt.Errorf("unable to load package.json at '%s'", packageJSON)
}
install := false
// Get the MD5 sum of package.json
packageJSONMD5 := fs.MustMD5File(packageJSON)
// Check whether we need to npm install
packageChecksumFile := filepath.Join(sourceDir, "package.json.md5")
if fs.FileExists(packageChecksumFile) {
// Compare checksums
storedChecksum := fs.MustLoadString(packageChecksumFile)
if storedChecksum != packageJSONMD5 {
fs.MustWriteString(packageChecksumFile, packageJSONMD5)
install = true
}
} else {
install = true
fs.MustWriteString(packageChecksumFile, packageJSONMD5)
}
// Install if node_modules doesn't exist
nodeModulesDir := filepath.Join(sourceDir, "node_modules")
if !fs.DirExists(nodeModulesDir) {
install = true
}
// Shortcut installation
if install == false {
return nil
}
// Split up the InstallCommand and execute it
cmd := strings.Split(installCommand, " ")
stdout, stderr, err := shell.RunCommand(sourceDir, cmd[0], cmd[1:]...)
if err != nil {
for _, l := range strings.Split(stdout, "\n") {
fmt.Printf(" %s\n", l)
}
for _, l := range strings.Split(stderr, "\n") {
fmt.Printf(" %s\n", l)
}
}
return err
}
// NpmRun executes the npm target in the provided directory
func (b *BaseBuilder) NpmRun(projectDir, buildTarget string, verbose bool) error {
stdout, stderr, err := shell.RunCommand(projectDir, "npm", "run", buildTarget)
if verbose || err != nil {
for _, l := range strings.Split(stdout, "\n") {
fmt.Printf(" %s\n", l)
}
for _, l := range strings.Split(stderr, "\n") {
fmt.Printf(" %s\n", l)
}
}
return err
}
// NpmRunWithEnvironment executes the npm target in the provided directory, with the given environment variables
func (b *BaseBuilder) NpmRunWithEnvironment(projectDir, buildTarget string, verbose bool, envvars []string) error {
cmd := shell.CreateCommand(projectDir, "npm", "run", buildTarget)
cmd.Env = append(os.Environ(), envvars...)
var stdo, stde bytes.Buffer
cmd.Stdout = &stdo
cmd.Stderr = &stde
err := cmd.Run()
if verbose || err != nil {
for _, l := range strings.Split(stdo.String(), "\n") {
fmt.Printf(" %s\n", l)
}
for _, l := range strings.Split(stde.String(), "\n") {
fmt.Printf(" %s\n", l)
}
}
return err
}
// BuildFrontend executes the `npm build` command for the frontend directory
func (b *BaseBuilder) BuildFrontend(outputLogger *logger.Logger) error {
verbose := false
frontendDir := filepath.Join(b.projectData.Path, "frontend")
// Check there is an 'InstallCommand' provided in wails.json
if b.projectData.InstallCommand == "" {
// No - don't install
outputLogger.Writeln(" - No Install command. Skipping.")
} else {
// Do install if needed
outputLogger.Writeln(" - Installing dependencies...")
if err := b.NpmInstallUsingCommand(frontendDir, b.projectData.InstallCommand); err != nil {
return err
}
}
// Check if there is a build command
if b.projectData.BuildCommand == "" {
outputLogger.Writeln(" - No Build command. Skipping.")
// No - ignore
return nil
}
outputLogger.Writeln(" - Compiling Frontend Project")
cmd := strings.Split(b.projectData.BuildCommand, " ")
stdout, stderr, err := shell.RunCommand(frontendDir, cmd[0], cmd[1:]...)
if verbose || err != nil {
for _, l := range strings.Split(stdout, "\n") {
fmt.Printf(" %s\n", l)
}
for _, l := range strings.Split(stderr, "\n") {
fmt.Printf(" %s\n", l)
}
}
return err
}
// ExtractAssets gets the assets from the index.html file
func (b *BaseBuilder) ExtractAssets() (*html.AssetBundle, error) {
// Read in html
return html.NewAssetBundle(b.projectData.HTML)
}

View File

@@ -0,0 +1,133 @@
package build
import (
"fmt"
"os"
"runtime"
"github.com/leaanthony/slicer"
"github.com/leaanthony/wailsv2/v2/internal/logger"
"github.com/leaanthony/wailsv2/v2/internal/project"
)
// Mode is the type used to indicate the build modes
type Mode int
const (
// Debug mode
Debug Mode = iota
// Production mode
Production
)
var modeMap = []string{"Debug", "Production"}
// Options contains all the build options as well as the project data
type Options struct {
LDFlags string // Optional flags to pass to linker
Logger *logger.Logger // All output to the logger
OutputType string // EG: desktop, server....
Mode Mode // release or debug
ProjectData *project.Project // The project data
Pack bool // Create a package for the app after building
Platform string // The platform to build for
Compiler string // The compiler command to use
}
// GetModeAsString returns the current mode as a string
func GetModeAsString(mode Mode) string {
return modeMap[mode]
}
// Build the project!
func Build(options *Options) (string, error) {
// Extract logger
outputLogger := options.Logger
// Create a default logger if it doesn't exist
if outputLogger == nil {
outputLogger = logger.New()
}
// Get working directory
cwd, err := os.Getwd()
if err != nil {
return "", err
}
// Check platform
validPlatforms := slicer.String([]string{"linux"})
if !validPlatforms.Contains(options.Platform) {
return "", fmt.Errorf("platform %s not supported", options.Platform)
}
// Load project
projectData, err := project.Load(cwd)
if err != nil {
return "", err
}
options.ProjectData = projectData
// Save the project type
projectData.OutputType = options.OutputType
// Create builder
var builder Builder
switch projectData.OutputType {
case "desktop":
builder = newDesktopBuilder()
case "hybrid":
builder = newHybridBuilder()
case "server":
builder = newServerBuilder()
default:
return "", fmt.Errorf("cannot build assets for output type %s", projectData.OutputType)
}
// Set up our clean up method
defer builder.CleanUp()
// Initialise Builder
builder.SetProjectData(projectData)
outputLogger.Writeln(" - Building Wails Frontend")
err = builder.BuildFrontend(outputLogger)
if err != nil {
return "", err
}
// Build the base assets
outputLogger.Writeln(" - Compiling Assets")
err = builder.BuildRuntime(options)
if err != nil {
return "", err
}
err = builder.BuildAssets(options)
if err != nil {
return "", err
}
// Compile the application
outputLogger.Write(" - Compiling Application in " + GetModeAsString(options.Mode) + " mode...")
err = builder.CompileProject(options)
if err != nil {
return "", err
}
outputLogger.Writeln("done.")
// Do we need to pack the app?
if options.Pack {
outputLogger.Writeln(" - Packaging Application")
// TODO: Allow cross platform build
err = packageProject(options, runtime.GOOS)
if err != nil {
return "", err
}
}
return projectData.OutputFilename, nil
}

View File

@@ -0,0 +1,16 @@
package build
import (
"github.com/leaanthony/wailsv2/v2/internal/logger"
"github.com/leaanthony/wailsv2/v2/internal/project"
)
// Builder defines a builder that can build Wails applications
type Builder interface {
SetProjectData(projectData *project.Project)
BuildAssets(*Options) error
BuildFrontend(*logger.Logger) error
BuildRuntime(*Options) error
CompileProject(*Options) error
CleanUp()
}

View File

@@ -0,0 +1,136 @@
package build
import (
"fmt"
"io/ioutil"
"path/filepath"
"github.com/leaanthony/wailsv2/v2/internal/fs"
"github.com/leaanthony/wailsv2/v2/internal/html"
"github.com/leaanthony/wailsv2/v2/internal/logger"
)
// DesktopBuilder builds applications for the desktop
type DesktopBuilder struct {
*BaseBuilder
}
func newDesktopBuilder() *DesktopBuilder {
return &DesktopBuilder{
BaseBuilder: NewBaseBuilder(),
}
}
// BuildAssets builds the assets for the desktop application
func (d *DesktopBuilder) BuildAssets(options *Options) error {
var err error
// Get a list of assets from the HTML
assets, err := d.BaseBuilder.ExtractAssets()
if err != nil {
return err
}
// Build base assets (HTML/JS/CSS/etc)
err = d.BuildBaseAssets(assets, options.Logger)
if err != nil {
return err
}
// Build static assets
err = d.buildStaticAssets(d.projectData)
if err != nil {
return err
}
return nil
}
// BuildBaseAssets builds the assets for the desktop application
func (d *DesktopBuilder) BuildBaseAssets(assets *html.AssetBundle, outputLogger *logger.Logger) error {
var err error
outputLogger.Write(" - Embedding Assets...")
// Get target asset directory
assetDir := fs.RelativePath("../../../internal/ffenestri")
// Dump assets as C
assetsFile, err := assets.WriteToCFile(assetDir)
if err != nil {
return err
}
d.addFileToDelete(assetsFile)
// Process Icon
err = d.processIcon(assetDir)
if err != nil {
return err
}
outputLogger.Writeln("done.")
return nil
}
// processIcon will copy a default icon if one doesn't exist, then, if
// needed, will compile the icon
func (d *DesktopBuilder) processIcon(assetDir string) error {
// Copy default icon if one doesn't exist
iconFile := filepath.Join(d.projectData.Path, "icon.png")
if !fs.FileExists(iconFile) {
err := fs.CopyFile(defaultIconPath(), iconFile)
if err != nil {
return err
}
}
// Compile Icon
return d.compileIcon(assetDir, iconFile)
}
// BuildRuntime builds the Wails javascript runtime and then converts it into a C file
func (d *DesktopBuilder) BuildRuntime(options *Options) error {
outputLogger := options.Logger
sourceDir := fs.RelativePath("../../../internal/runtime/js")
if err := d.NpmInstall(sourceDir); err != nil {
return err
}
outputLogger.Write(" - Embedding Runtime...")
envvars := []string{"WAILSPLATFORM=" + options.Platform}
if err := d.NpmRunWithEnvironment(sourceDir, "build:desktop", false, envvars); err != nil {
return err
}
wailsJS := fs.RelativePath("../../../internal/runtime/assets/desktop.js")
runtimeData, err := ioutil.ReadFile(wailsJS)
if err != nil {
return err
}
outputLogger.Writeln("done.")
// Convert to C structure
runtimeC := `
// runtime.c (c) 2019-Present Lea Anthony.
// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL
// This file was auto-generated. DO NOT MODIFY.
const unsigned char runtime[]={`
for _, b := range runtimeData {
runtimeC += fmt.Sprintf("0x%x, ", b)
}
runtimeC += "0x00};"
// Save file
outputFile := fs.RelativePath("../../../internal/ffenestri/runtime.c")
if err := ioutil.WriteFile(outputFile, []byte(runtimeC), 0600); err != nil {
return err
}
return nil
}

View File

@@ -0,0 +1,8 @@
// +build !linux
package build
// This is used when there is no compilation to be done for the asset
func (d *DesktopBuilder) compileIcon(assetDir string, iconFile string) error {
return nil
}

View File

@@ -0,0 +1,54 @@
// +build linux
package build
import (
"image/png"
"io/ioutil"
"os"
"path/filepath"
"strings"
"github.com/xyproto/xpm"
)
// compileIcon will compile the icon found at <projectdir>/icon.png into the application
func (d *DesktopBuilder) compileIcon(assetDir string, iconFile string) error {
// Load icon into a databuffer
targetFilename := "icon"
targetFile := filepath.Join(assetDir, targetFilename+".c")
d.addFileToDelete(targetFile)
// Create a new XPM encoder
enc := xpm.NewEncoder(targetFilename)
// Open the PNG file
f, err := os.Open(iconFile)
if err != nil {
return err
}
m, err := png.Decode(f)
if err != nil {
return err
}
f.Close()
var buf strings.Builder
// Generate and output the XPM data
err = enc.Encode(&buf, m)
if err != nil {
return err
}
// Massage the output so we can extern reference it
output := buf.String()
output = strings.Replace(output, "static char", "const char", 1)
// save icon.c
err = ioutil.WriteFile(targetFile, []byte(output), 0755)
return err
}

View File

@@ -0,0 +1,101 @@
package build
import (
"github.com/leaanthony/wailsv2/v2/internal/project"
)
// HybridBuilder builds applications as a server
type HybridBuilder struct {
*BaseBuilder
desktop *DesktopBuilder
server *ServerBuilder
}
func newHybridBuilder() Builder {
result := &HybridBuilder{
BaseBuilder: NewBaseBuilder(),
desktop: newDesktopBuilder(),
server: newServerBuilder(),
}
return result
}
// BuildAssets builds the assets for the desktop application
func (b *HybridBuilder) BuildAssets(options *Options) error {
var err error
// Build base assets (HTML/JS/CSS/etc)
err = b.BuildBaseAssets(options)
if err != nil {
return err
}
// Build static assets
err = b.buildStaticAssets(b.projectData)
if err != nil {
return err
}
return nil
}
// BuildAssets builds the assets for the desktop application
func (b *HybridBuilder) BuildBaseAssets(options *Options) error {
assets, err := b.BaseBuilder.ExtractAssets()
if err != nil {
return err
}
err = b.desktop.BuildBaseAssets(assets, options.Logger)
if err != nil {
return err
}
err = b.server.BuildBaseAssets(assets)
if err != nil {
return err
}
// Build desktop static assets
err = b.desktop.buildStaticAssets(b.projectData)
if err != nil {
return err
}
// Build server static assets
err = b.server.buildStaticAssets(b.projectData)
if err != nil {
return err
}
return nil
}
func (b *HybridBuilder) BuildRuntime(options *Options) error {
err := b.desktop.BuildRuntime(options)
if err != nil {
return err
}
err = b.server.BuildRuntime(options)
if err != nil {
return err
}
return nil
}
func (b *HybridBuilder) SetProjectData(projectData *project.Project) {
b.BaseBuilder.SetProjectData(projectData)
b.desktop.SetProjectData(projectData)
b.server.SetProjectData(projectData)
}
func (b *HybridBuilder) CompileProject(options *Options) error {
return b.BaseBuilder.CompileProject(options)
}
func (b *HybridBuilder) CleanUp() {
b.desktop.CleanUp()
b.server.CleanUp()
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 44 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.6 KiB

View File

@@ -0,0 +1,3 @@
#!/bin/sh
cd "$(dirname "$0")"
exec ./{{.OutputFilename}}

View File

@@ -0,0 +1,6 @@
[Desktop Entry]
Type=Application
Exec={{.Name}}
Name={{.Name}}
Icon={{.Name}}
Categories=Utility;

View File

@@ -0,0 +1,73 @@
package build
import (
"fmt"
"os"
"path/filepath"
"runtime"
"github.com/leaanthony/wailsv2/v2/internal/fs"
)
// PackageProject packages the application
func packageProject(options *Options, platform string) error {
var err error
switch platform {
case "linux":
err = packageLinuxApplication(options)
default:
err = fmt.Errorf("packing not supported for %s yet", platform)
}
if err != nil {
return err
}
return nil
}
// Gets (and creates) the platform/target build directory
func getApplicationBuildDirectory(options *Options, platform string) (string, error) {
buildDirectory := filepath.Join(options.ProjectData.Path, "build", platform, options.OutputType)
// Clear out old builds
if fs.DirExists(buildDirectory) {
err := os.RemoveAll(buildDirectory)
if err != nil {
return "", err
}
}
// Create clean directory
err := os.MkdirAll(buildDirectory, 0700)
if err != nil {
return "", err
}
return buildDirectory, nil
}
func copyFileToBuildDirectory() {}
// 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 path to the default icon
func defaultIconPath() string {
return fs.RelativePath("internal/packager/icon64.png")
}
// Gets the platform dependent package assets directory
func getPackageAssetsDirectory() string {
return fs.RelativePath("internal/packager", runtime.GOOS)
}

View File

@@ -0,0 +1,134 @@
package build
import (
"io/ioutil"
"os"
"path/filepath"
"strings"
"github.com/leaanthony/slicer"
"github.com/leaanthony/wailsv2/v2/internal/fs"
"github.com/leaanthony/wailsv2/v2/internal/shell"
)
func deleteLinuxPackFiles(appDirBase string) {
// Delete appdir
appDir := filepath.Join(appDirBase, "AppDir")
os.RemoveAll(appDir)
}
func packageLinuxApplication(options *Options) error {
// Check we have AppImage tools
// Create AppImage build directory
buildDirectory, err := getApplicationBuildDirectory(options, "linux")
if err != nil {
return err
}
defer deleteLinuxPackFiles(buildDirectory)
// Get the name of the application and ensure we lower+kebab case it
name := filepath.Base(options.ProjectData.OutputFilename)
// Calculate asset directory
assetDir := getPackageAssetsDirectory()
// Copy default icon if one doesn't exist
baseBuildDirectory, err := getBuildBaseDirectory(options)
if err != nil {
return err
}
iconFile := filepath.Join(baseBuildDirectory, "icon.png")
if !fs.FileExists(iconFile) {
err = fs.CopyFile(defaultIconPath(), iconFile)
if err != nil {
return err
}
}
// Copy Icon
targetIcon := filepath.Join(buildDirectory, name+".png")
err = fs.CopyFile(iconFile, targetIcon)
if err != nil {
return err
}
// Copy app.desktop
dotDesktopFile := filepath.Join(baseBuildDirectory, "linux", name+".desktop")
if !fs.FileExists(dotDesktopFile) {
bytes, err := ioutil.ReadFile(filepath.Join(assetDir, "app.desktop"))
if err != nil {
return err
}
appDesktop := string(bytes)
appDesktop = strings.ReplaceAll(appDesktop, `{{.Name}}`, name)
err = ioutil.WriteFile(dotDesktopFile, []byte(appDesktop), 0644)
if err != nil {
return err
}
}
// Copy AppRun file
// targetFilename = filepath.Join(buildDirectory, "AppRun")
// if !fs.FileExists(targetFilename) {
// bytes, err := ioutil.ReadFile(filepath.Join(assetDir, "AppRun"))
// if err != nil {
// return err
// }
// appRun := string(bytes)
// appRun = strings.ReplaceAll(appRun, `{{.OutputFilename}}`, name)
// err = ioutil.WriteFile(targetFilename, []byte(appRun), 0644)
// if err != nil {
// return err
// }
// }
// Copy Binary
sourceFile := filepath.Join(options.ProjectData.Path, options.ProjectData.OutputFilename)
targetFile := filepath.Join(buildDirectory, options.ProjectData.OutputFilename)
err = fs.CopyFile(sourceFile, targetFile)
if err != nil {
return err
}
err = os.Chmod(targetFile, 0777)
if err != nil {
return err
}
/** Pack App **/
// Make file executable
// Set environment variable: OUTPUT=outputfilename
command := shell.NewCommand("linuxdeploy-x86_64.AppImage")
command.Dir(buildDirectory)
argslice := slicer.String()
argslice.Add("--appdir", "AppDir")
argslice.Add("-d", filepath.Join("..", name+".desktop"))
argslice.Add("-i", name+".png")
argslice.Add("-e", name)
argslice.Add("--output", "appimage")
command.AddArgs(argslice.AsSlice())
command.Env("OUTPUT", name+".AppImage")
err = command.Run()
if err != nil {
println(command.Stdout())
println(command.Stderr())
return err
}
// Copy app to project dir
println(buildDirectory)
return nil
}
func deleteDirectory(directory string) {
os.RemoveAll(directory)
}

View File

@@ -0,0 +1,11 @@
package webserver
// This sets up the assets that are defined in `webassets.go`
func init() {
html = []byte{$HTMLBYTES$}
js = []byte{$JSBYTES$}
css = []byte{$CSSBYTES$}
wailsjs = []byte{$WAILSJSBYTES$}
jsurl = []byte{$JSURLBYTES$}
cssurl = []byte{$CSSURLBYTES$}
}

View File

@@ -0,0 +1,111 @@
package build
import (
"fmt"
"io/ioutil"
"os"
"strings"
"github.com/leaanthony/wailsv2/v2/internal/fs"
"github.com/leaanthony/wailsv2/v2/internal/html"
)
// ServerBuilder builds applications as a server
type ServerBuilder struct {
*BaseBuilder
}
func newServerBuilder() *ServerBuilder {
result := &ServerBuilder{
BaseBuilder: NewBaseBuilder(),
}
return result
}
// BuildAssets builds the assets for the desktop application
func (s *ServerBuilder) BuildAssets(options *Options) error {
var err error
assets, err := s.BaseBuilder.ExtractAssets()
if err != nil {
return err
}
// Build embedded assets (HTML/JS/CSS/etc)
err = s.BuildBaseAssets(assets)
if err != nil {
return err
}
// Build static assets
err = s.buildStaticAssets(s.projectData)
if err != nil {
return err
}
return nil
}
// BuildBaseAssets builds the base assets
func (s *ServerBuilder) BuildBaseAssets(assets *html.AssetBundle) error {
db, err := assets.ConvertToAssetDB()
if err != nil {
return err
}
// Fetch, update, and reinject index.html
index, err := db.Read("index.html")
if err != nil {
return fmt.Errorf(`Failed to locate "index.html"`)
}
splits := strings.Split(string(index), "</body>")
if len(splits) != 2 {
return fmt.Errorf("Unable to locate a </body> tag in your frontend/index.html")
}
injectScript := `<script defer src="/wails.js"></script><script defer src="/bindings.js"></script>`
result := []string{}
result = append(result, splits[0])
result = append(result, injectScript)
result = append(result, "</body>")
result = append(result, splits[1])
db.Remove("index.html") // Remove the non-prefixed index.html
db.AddAsset("/index.html", []byte(strings.Join(result, "")))
// Add wails.js
wailsjsPath := fs.RelativePath("../../../internal/runtime/assets/server.js")
if rawData, err := ioutil.ReadFile(wailsjsPath); err == nil {
db.AddAsset("/wails.js", rawData)
}
targetFile := fs.RelativePath("../../../internal/webserver/webassetsdb.go")
s.addFileToDelete(targetFile)
f, err := os.Create(targetFile)
if err != nil {
return err
}
defer f.Close()
f.WriteString(db.Serialize("db", "webserver"))
return nil
}
// BuildRuntime builds the javascript runtime used by the HTML client to connect to the websocket
func (s *ServerBuilder) BuildRuntime(options *Options) error {
sourceDir := fs.RelativePath("../../../internal/runtime/js")
if err := s.NpmInstall(sourceDir); err != nil {
return err
}
options.Logger.Write(" - Embedding Runtime...")
envvars := []string{"WAILSPLATFORM=" + options.Platform}
var err error
if err = s.NpmRunWithEnvironment(sourceDir, "build:server", false, envvars); err != nil {
return err
}
options.Logger.Writeln("done.")
return nil
}