mirror of https://github.com/liamg/aminal.git
Add Windows launcher
The launcher looks at directory "Versions" next to its executable. It finds the latest version and runs the executable in that directory with the same name as itself. For instance: Aminal.exe <- the launcher Versions/ 1.0.0/ Aminal.exe 1.0.1/ Aminal.exe In this example, running the top-level Aminal.exe (the launcher) starts Versions/1.0.1/Aminal.exe. Having a launcher allows Aminal to be updated while it is running. For example, version 1.0.1 could be downloaded without disturbing running instances of Aminal 1.0.0.
This commit is contained in:
parent
150b0493de
commit
6e28eb11b5
|
@ -1,4 +1,5 @@
|
|||
aminal
|
||||
aminal.exe
|
||||
*.syso
|
||||
.idea
|
||||
.idea
|
||||
tmp/
|
21
Makefile
21
Makefile
|
@ -1,6 +1,11 @@
|
|||
SHELL := /bin/bash
|
||||
BINARY := aminal
|
||||
FONTPATH := ./gui/packed-fonts
|
||||
TMPDIR := ./tmp
|
||||
VERSION_MAJOR := 0
|
||||
VERSION_MINOR := 9
|
||||
VERSION_PATCH := 0
|
||||
VERSION := ${VERSION_MAJOR}.${VERSION_MINOR}.${VERSION_PATCH}
|
||||
|
||||
.PHONY: build
|
||||
build:
|
||||
|
@ -50,6 +55,22 @@ build-windows:
|
|||
windres -o aminal.syso aminal.rc
|
||||
go build -o ${BINARY}-windows-amd64.exe
|
||||
|
||||
.PHONY: launcher-windows
|
||||
launcher-windows: build-windows
|
||||
if exist "${TMPDIR}\launcher-src" rmdir /S /Q "${TMPDIR}\launcher-src"
|
||||
xcopy "windows\launcher\*.*" "${TMPDIR}\launcher-src" /K /H /Y /Q /I
|
||||
powershell -Command "(gc ${TMPDIR}\launcher-src\versioninfo.json) -replace 'VERSION_MAJOR', '${VERSION_MAJOR}' | Out-File -Encoding default ${TMPDIR}\launcher-src\versioninfo.json"
|
||||
powershell -Command "(gc ${TMPDIR}\launcher-src\versioninfo.json) -replace 'VERSION_MINOR', '${VERSION_MINOR}' | Out-File -Encoding default ${TMPDIR}\launcher-src\versioninfo.json"
|
||||
powershell -Command "(gc ${TMPDIR}\launcher-src\versioninfo.json) -replace 'VERSION_PATCH', '${VERSION_PATCH}' | Out-File -Encoding default ${TMPDIR}\launcher-src\versioninfo.json"
|
||||
powershell -Command "(gc ${TMPDIR}\launcher-src\versioninfo.json) -replace 'VERSION', '${VERSION}' | Out-File -Encoding default ${TMPDIR}\launcher-src\versioninfo.json"
|
||||
powershell -Command "(gc ${TMPDIR}\launcher-src\versioninfo.json) -replace 'YEAR', (Get-Date -UFormat '%Y') | Out-File -Encoding default ${TMPDIR}\launcher-src\versioninfo.json"
|
||||
copy aminal.ico "${TMPDIR}\launcher-src" /Y
|
||||
go generate "${TMPDIR}\launcher-src"
|
||||
if exist "${TMPDIR}\launcher" rmdir /S /Q "${TMPDIR}\launcher"
|
||||
mkdir "${TMPDIR}\launcher\Versions\${VERSION}"
|
||||
go build -o "${TMPDIR}\launcher\${BINARY}.exe" -ldflags "-H windowsgui" "${TMPDIR}\launcher-src"
|
||||
copy ${BINARY}-windows-amd64.exe "${TMPDIR}\launcher\Versions\${VERSION}\${BINARY}.exe" /Y
|
||||
|
||||
.PHONY: build-darwin-native-travis
|
||||
build-darwin-native-travis:
|
||||
mkdir -p bin/darwin
|
||||
|
|
|
@ -0,0 +1,123 @@
|
|||
//go:generate goversioninfo -icon=aminal.ico
|
||||
|
||||
/*
|
||||
Looks at directory "Versions" next to this executable. Finds the latest version
|
||||
and runs the executable with the same name as this executable in that directory.
|
||||
Eg.:
|
||||
Aminal.exe (=launcher.exe)
|
||||
Versions/
|
||||
1.0.0/
|
||||
Aminal.exe
|
||||
1.0.1
|
||||
Aminal.exe
|
||||
-> Launches Versions/1.0.1/Aminal.exe.
|
||||
*/
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
"github.com/liamg/aminal/windows/winutil"
|
||||
)
|
||||
|
||||
type Version struct {
|
||||
number [3]int
|
||||
name string
|
||||
}
|
||||
type Versions []Version
|
||||
|
||||
func main() {
|
||||
executable, err := winutil.GetExecutablePath()
|
||||
check(err)
|
||||
executableDir, executableName := filepath.Split(executable)
|
||||
versionsDir := filepath.Join(executableDir, "Versions")
|
||||
latestVersion, err := getLatestVersion(versionsDir)
|
||||
check(err)
|
||||
target := filepath.Join(versionsDir, latestVersion, executableName)
|
||||
cmd := exec.Command(target, os.Args[1:]...)
|
||||
check(cmd.Start())
|
||||
}
|
||||
|
||||
func getLatestVersion(versionsDir string) (string, error) {
|
||||
potentialVersions, err := ioutil.ReadDir(versionsDir)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
var versions Versions
|
||||
for _, file := range potentialVersions {
|
||||
if !file.IsDir() {
|
||||
continue
|
||||
}
|
||||
version, err := parseVersionString(file.Name())
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
versions = append(versions, version)
|
||||
}
|
||||
if len(versions) == 0 {
|
||||
errMsg := fmt.Sprintf("No valid version in %s.", versionsDir)
|
||||
return "", errors.New(errMsg)
|
||||
}
|
||||
sort.Sort(versions)
|
||||
return versions[len(versions)-1].String(), nil
|
||||
}
|
||||
|
||||
func parseVersionString(version string) (Version, error) {
|
||||
var result Version
|
||||
result.name = version
|
||||
err := error(nil)
|
||||
version = strings.TrimSuffix(version, "-SNAPSHOT")
|
||||
parts := strings.Split(version, ".")
|
||||
if len(parts) != len(result.number) {
|
||||
err = errors.New("Wrong number of parts.")
|
||||
} else {
|
||||
for i, partStr := range parts {
|
||||
result.number[i], err = strconv.Atoi(partStr)
|
||||
if err != nil {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
return result, err
|
||||
}
|
||||
|
||||
func (arr Versions) Len() int {
|
||||
return len(arr)
|
||||
}
|
||||
|
||||
func (arr Versions) Less(i, j int) bool {
|
||||
for k, left := range arr[i].number {
|
||||
right := arr[j].number[k]
|
||||
if left > right {
|
||||
return false
|
||||
} else if left < right {
|
||||
return true
|
||||
}
|
||||
}
|
||||
fmt.Printf("%s < %s\n", arr[i], arr[j])
|
||||
return true
|
||||
}
|
||||
|
||||
func (arr Versions) Swap(i, j int) {
|
||||
tmp := arr[j]
|
||||
arr[j] = arr[i]
|
||||
arr[i] = tmp
|
||||
}
|
||||
|
||||
func (version Version) String() string {
|
||||
return version.name
|
||||
}
|
||||
|
||||
func check(e error) {
|
||||
if e != nil {
|
||||
panic(e)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,35 @@
|
|||
{
|
||||
"FixedFileInfo":
|
||||
{
|
||||
"FileVersion": {
|
||||
"Major": VERSION_MAJOR,
|
||||
"Minor": VERSION_MINOR,
|
||||
"Patch": VERSION_PATCH,
|
||||
"Build": 0
|
||||
},
|
||||
"ProductVersion": {
|
||||
"Major": VERSION_MAJOR,
|
||||
"Minor": VERSION_MINOR,
|
||||
"Patch": VERSION_PATCH,
|
||||
"Build": 0
|
||||
},
|
||||
"FileFlagsMask": "3f",
|
||||
"FileFlags ": "00",
|
||||
"FileOS": "040004",
|
||||
"FileType": "01",
|
||||
"FileSubType": "00"
|
||||
},
|
||||
"StringFileInfo":
|
||||
{
|
||||
"ProductName": "Aminal",
|
||||
"ProductVersion": "VERSION",
|
||||
"LegalCopyright": "Copyright 2018-YEAR Liam Galvin"
|
||||
},
|
||||
"VarFileInfo":
|
||||
{
|
||||
"Translation": {
|
||||
"LangID": "0409",
|
||||
"CharsetID": "04B0"
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,25 @@
|
|||
package winutil
|
||||
|
||||
import (
|
||||
"golang.org/x/sys/windows"
|
||||
"unicode/utf16"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
var (
|
||||
kernel = windows.MustLoadDLL("kernel32.dll")
|
||||
getModuleFileNameProc = kernel.MustFindProc("GetModuleFileNameW")
|
||||
)
|
||||
|
||||
func GetExecutablePath() (string, error) {
|
||||
var n uint32
|
||||
b := make([]uint16, windows.MAX_PATH)
|
||||
size := uint32(len(b))
|
||||
bPtr := uintptr(unsafe.Pointer(&b[0]))
|
||||
r0, _, e1 := getModuleFileNameProc.Call(0, bPtr, uintptr(size))
|
||||
n = uint32(r0)
|
||||
if n == 0 {
|
||||
return "", e1
|
||||
}
|
||||
return string(utf16.Decode(b[0:n])), nil
|
||||
}
|
Loading…
Reference in New Issue