Files
wails/v2/pkg/commands/build/base.go
Lea Anthony 71bfd29376 Feature/v2 mac (#555)
* Get it compiling

* Stubs in place to compile

* Semi runs

* add darwin platform for server

* Evaluation working correctly. Still WIP

* Ignore favicon for desktop

* lint

* Remove feature flag code

* More feature flag removal

* Support sending messages to the backend

* Callbacks working

* Add Center + refactor prefs

* Fix logger

* Callback hooks for MOAE

* Update packages

* ignore test builds

* Support Un/Fullscreen

* vscode stuff

* Only show window when rendered

* Get it compiling again!

* Support tons of stuff!

* Tidy up

* More refactoring

* WIP [bugged]

* Got get frame working

* fix setsize and setposition

* Add Mac Application Options

* Add HideTitleBar

* Support more mac window options

* Add Toolbar support for Mac

* Support colour

* Support runtime colour change

* Moved options to own package

* Refactored mac titlebar options

* Support hidden titlebar

* Support HiddenInset Titlebar

* Support TitleBar Default

Fixed merging defaults

* Sample titlebars

* Fix minmax app

* Min/Max size supported

* WIP: Support multiple value return

* Support OpenDialog

* Remove old dialog code

* change service bus topics for dialogs

* Revert changes to v1

* Use options struct for dialogs

* Initial support for OpenDialog

* Support selecting files+dirs

* Support multiple files in dialog

* Support all dialog properties

* Add comments

* Filter support

* Support default directory

* Support SaveDialog

* Tidy Up

* WIP: Basics of window drag

* Support window dragging

* Update tests

* Frameless is calculated for Mac

* Tidy up

* Support vibrancy and transparency for webview

Options Colour -> RGBA

* Rename vibrancy to appearance

* Add default appearance

* Refactor part 1

* Refactor Part 2

* Support Translucent Window Background

* Update runtime test

* Add IsDarkMode

Updated runtime test

* Support theme mode change event

* Misc fixes for events

* Support OnMultiple

* Small fixes to frontend events

* Add System calls to runtime

* Add system calls to js runtime

* Support System calls in Go Runtime

* Port Sync Store

* Refactor store. Add get().

* Refactor system. Add IsDarkMode state store

* Use IsDarkMode state store

* Remove generated files

* Support setting app state at startup

* Add Store to go runtime

* Update runtime to v1.0.3

* Remove unused event messages

* Debugging

* initial kitchen sink

* Fix right click crash

* Better drag support

* WIP

* Remove log package

* Add Log to Go runtime

* Add logging to kitchen sink

* Improved CodeBlock. Dark mode to store.

* Start Events. List styling moved to global scope.

* Make logger a public package

* Revert logger package

* Major logging refactor

* Make Ffenestri use logging subsystem.

* Debug refactor

* Add trace to JS runtime

* Migrate runtime to @wails

* Support Trace in kitchensink

* Support trace in go runtime

* Support log level

* Support Print in JS runtime

* Runtime v1.0.1

* Move Info message to Trace

* Support Print logging

* Updated Logger interface

* Fix number of methods in Log

* Support SetLogLevel() at runtime

Refactor of loglevel

* Made go runtime package public.

Using loglevel store to keep loglevel in sync

* Support dynamic loglevel

* Runtime refactor

* Fully refactored logging

* Better looking scrollbar

* Terminal output component

* Link component

* SetLogLevel fully supported

* Runtime defs update.

Slight System refactor

* More Logging updates

* Move preview for SetLogLevel

* Fix log level reactivity.

Misc fixes and tweaks

* logging: slight refactor

* Update logger constants to fix default values

* @wails/runtime v1.0.4

* Fix change in logging levels

* hook in windowWillClose

* refactor clilogger

* WIP Events.On

* Add Events.On

* Improved error handling?

* Disable annoying smart quotes

* update runtime definitions

* Support Emit & Once. Improved On.

* Remove old event methods

* Remove old Event methods

* Update runtime in kitchensink

* Revert Fatal on JS Error

* Tidy up events runtime

* Finish events page

* Update Browser runtime API

* Unify Browser runtime

* JS Runtime v1.0.8

* Fix browser runtime export

* Remove debug line

* Add Browser examples

* Update title

* Improved runtime.System

* Update runtime.System to make all methods

* Expose System methods in Go runtime

* Add System to kitchensink

* Huge improvement to calls: Now handles objects

* Add JS runtime Dialog

* Dialog WIP

* Js package generation (#554)

* WIP

* Generation of index.js

* Add RelativeToCwd

* Add JSDoc comments

* Convert to ES6 syntax

* Fix typo

* Initial generation of typescript declarations

* Typescript improvements

* Improved @returns jsdoc

* Improved declaration files

* Simplified output

* Rename file

* Tidy up

* Revert "Simplified output"

This reverts commit 15cdf7382b.

* Now parsing actual code

* Support Array types

* Reimagined parser

* Wrap parsing in Parser

* Rewritten module generator (TS Only)

* Final touches

* Slight refactor to improve output

* Struct comments. External struct literal binding

* Reworked project parser *working*

* remove debug info

* Refactor of parser

* remove the spew

* Better Ts support

* Better project generation logic

* Support local functions in bind()

* JS Object generation. Linting.

* Support json tags in module generation

* Updated mod files

* Support vscode file generation

* Better global.d.ts

* add ts-check to templates

* Support TS declaration files

* improved 'generate' command for module

Co-authored-by: Travis McLane <tmclane@gmail.com>
2020-11-15 09:27:23 +11:00

352 lines
8.9 KiB
Go

package build
import (
"bytes"
"fmt"
"io/ioutil"
"os"
"os/exec"
"path/filepath"
"runtime"
"strings"
"github.com/leaanthony/slicer"
"github.com/wailsapp/wails/v2/internal/assetdb"
"github.com/wailsapp/wails/v2/internal/fs"
"github.com/wailsapp/wails/v2/internal/html"
"github.com/wailsapp/wails/v2/internal/project"
"github.com/wailsapp/wails/v2/internal/shell"
"github.com/wailsapp/wails/v2/pkg/clilogger"
)
// 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"})
var tags slicer.StringSlicer
tags.Add(options.OutputType)
if options.Mode == Debug {
tags.Add("debug")
}
// Add the output type build tag
commands.Add("-tags")
commands.Add(tags.Join(","))
// 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
outputFile := options.OutputFile
if outputFile == "" {
outputFile = b.projectData.OutputFilename
}
outputFilePath := filepath.Join(appDir, outputFile)
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 *clilogger.CLILogger) 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.Println(" - No Install command. Skipping.")
} else {
// Do install if needed
outputLogger.Println(" - 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.Println(" - No Build command. Skipping.")
// No - ignore
return nil
}
outputLogger.Println(" - 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)
}