mirror of
https://github.com/rclone/rclone.git
synced 2025-12-11 22:14:05 +01:00
build: refactor version info and icon resource handling on windows
This makes it easier to add resources with any build method, and also when building librclone.dll. Goversioninfo is now used as a library, instead of running it as a tool.
This commit is contained in:
@@ -6,7 +6,6 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"flag"
|
||||
"fmt"
|
||||
"log"
|
||||
@@ -21,23 +20,21 @@ import (
|
||||
"sync"
|
||||
"text/template"
|
||||
"time"
|
||||
|
||||
"github.com/coreos/go-semver/semver"
|
||||
)
|
||||
|
||||
var (
|
||||
// Flags
|
||||
debug = flag.Bool("d", false, "Print commands instead of running them.")
|
||||
parallel = flag.Int("parallel", runtime.NumCPU(), "Number of commands to run in parallel.")
|
||||
debug = flag.Bool("d", false, "Print commands instead of running them")
|
||||
parallel = flag.Int("parallel", runtime.NumCPU(), "Number of commands to run in parallel")
|
||||
copyAs = flag.String("release", "", "Make copies of the releases with this name")
|
||||
gitLog = flag.String("git-log", "", "git log to include as well")
|
||||
include = flag.String("include", "^.*$", "os/arch regexp to include")
|
||||
exclude = flag.String("exclude", "^$", "os/arch regexp to exclude")
|
||||
cgo = flag.Bool("cgo", false, "Use cgo for the build")
|
||||
noClean = flag.Bool("no-clean", false, "Don't clean the build directory before running.")
|
||||
noClean = flag.Bool("no-clean", false, "Don't clean the build directory before running")
|
||||
tags = flag.String("tags", "", "Space separated list of build tags")
|
||||
buildmode = flag.String("buildmode", "", "Passed to go build -buildmode flag")
|
||||
compileOnly = flag.Bool("compile-only", false, "Just build the binary, not the zip.")
|
||||
compileOnly = flag.Bool("compile-only", false, "Just build the binary, not the zip")
|
||||
extraEnv = flag.String("env", "", "comma separated list of VAR=VALUE env vars to set")
|
||||
macOSSDK = flag.String("macos-sdk", "", "macOS SDK to use")
|
||||
macOSArch = flag.String("macos-arch", "", "macOS arch to use")
|
||||
@@ -140,21 +137,21 @@ func chdir(dir string) {
|
||||
func substitute(inFile, outFile string, data interface{}) {
|
||||
t, err := template.ParseFiles(inFile)
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to read template file %q: %v %v", inFile, err)
|
||||
log.Fatalf("Failed to read template file %q: %v", inFile, err)
|
||||
}
|
||||
out, err := os.Create(outFile)
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to create output file %q: %v %v", outFile, err)
|
||||
log.Fatalf("Failed to create output file %q: %v", outFile, err)
|
||||
}
|
||||
defer func() {
|
||||
err := out.Close()
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to close output file %q: %v %v", outFile, err)
|
||||
log.Fatalf("Failed to close output file %q: %v", outFile, err)
|
||||
}
|
||||
}()
|
||||
err = t.Execute(out, data)
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to substitute template file %q: %v %v", inFile, err)
|
||||
log.Fatalf("Failed to substitute template file %q: %v", inFile, err)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -202,101 +199,6 @@ func buildDebAndRpm(dir, version, goarch string) []string {
|
||||
return artifacts
|
||||
}
|
||||
|
||||
// generate system object (syso) file to be picked up by a following go build for embedding icon and version info resources into windows executable
|
||||
func buildWindowsResourceSyso(goarch string, versionTag string) string {
|
||||
type M map[string]interface{}
|
||||
version := strings.TrimPrefix(versionTag, "v")
|
||||
semanticVersion := semver.New(version)
|
||||
|
||||
// Build json input to goversioninfo utility
|
||||
bs, err := json.Marshal(M{
|
||||
"FixedFileInfo": M{
|
||||
"FileVersion": M{
|
||||
"Major": semanticVersion.Major,
|
||||
"Minor": semanticVersion.Minor,
|
||||
"Patch": semanticVersion.Patch,
|
||||
},
|
||||
"ProductVersion": M{
|
||||
"Major": semanticVersion.Major,
|
||||
"Minor": semanticVersion.Minor,
|
||||
"Patch": semanticVersion.Patch,
|
||||
},
|
||||
},
|
||||
"StringFileInfo": M{
|
||||
"CompanyName": "https://rclone.org",
|
||||
"ProductName": "Rclone",
|
||||
"FileDescription": "Rclone",
|
||||
"InternalName": "rclone",
|
||||
"OriginalFilename": "rclone.exe",
|
||||
"LegalCopyright": "The Rclone Authors",
|
||||
"FileVersion": version,
|
||||
"ProductVersion": version,
|
||||
},
|
||||
"IconPath": "../graphics/logo/ico/logo_symbol_color.ico",
|
||||
})
|
||||
if err != nil {
|
||||
log.Printf("Failed to build version info json: %v", err)
|
||||
return ""
|
||||
}
|
||||
|
||||
// Write json to temporary file that will only be used by the goversioninfo command executed below.
|
||||
jsonPath, err := filepath.Abs("versioninfo_windows_" + goarch + ".json") // Appending goos and goarch as suffix to avoid any race conditions
|
||||
if err != nil {
|
||||
log.Printf("Failed to resolve path: %v", err)
|
||||
return ""
|
||||
}
|
||||
err = os.WriteFile(jsonPath, bs, 0644)
|
||||
if err != nil {
|
||||
log.Printf("Failed to write %s: %v", jsonPath, err)
|
||||
return ""
|
||||
}
|
||||
defer func() {
|
||||
if err := os.Remove(jsonPath); err != nil {
|
||||
if !os.IsNotExist(err) {
|
||||
log.Printf("Warning: Couldn't remove generated %s: %v. Please remove it manually.", jsonPath, err)
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
// Execute goversioninfo utility using the json file as input.
|
||||
// It will produce a system object (syso) file that a following go build should pick up.
|
||||
sysoPath, err := filepath.Abs("../resource_windows_" + goarch + ".syso") // Appending goos and goarch as suffix to avoid any race conditions, and also it is recognized by go build and avoids any builds for other systems considering it
|
||||
if err != nil {
|
||||
log.Printf("Failed to resolve path: %v", err)
|
||||
return ""
|
||||
}
|
||||
args := []string{
|
||||
"goversioninfo",
|
||||
"-o",
|
||||
sysoPath,
|
||||
}
|
||||
if strings.Contains(goarch, "64") {
|
||||
args = append(args, "-64") // Make the syso a 64-bit coff file
|
||||
}
|
||||
if strings.Contains(goarch, "arm") {
|
||||
args = append(args, "-arm") // Make the syso an arm binary
|
||||
}
|
||||
args = append(args, jsonPath)
|
||||
err = runEnv(args, nil)
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
|
||||
return sysoPath
|
||||
}
|
||||
|
||||
// delete generated system object (syso) resource file
|
||||
func cleanupResourceSyso(sysoFilePath string) {
|
||||
if sysoFilePath == "" {
|
||||
return
|
||||
}
|
||||
if err := os.Remove(sysoFilePath); err != nil {
|
||||
if !os.IsNotExist(err) {
|
||||
log.Printf("Warning: Couldn't remove generated %s: %v. Please remove it manually.", sysoFilePath, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Trip a version suffix off the arch if present
|
||||
func stripVersion(goarch string) string {
|
||||
i := strings.Index(goarch, "-")
|
||||
@@ -315,17 +217,41 @@ func runOut(command ...string) string {
|
||||
return strings.TrimSpace(string(out))
|
||||
}
|
||||
|
||||
// Generate Windows resource system object file (.syso), which can be picked
|
||||
// up by the following go build for embedding version information and icon
|
||||
// resources into the executable.
|
||||
func generateResourceWindows(version, arch string) func() {
|
||||
sysoPath := fmt.Sprintf("../resource_windows_%s.syso", arch) // Use explicit destination filename, even though it should be same as default, so that we are sure we have the correct reference to it
|
||||
if err := os.Remove(sysoPath); !os.IsNotExist(err) {
|
||||
// Note: This one we choose to treat as fatal, to avoid any risk of picking up an old .syso file without noticing.
|
||||
log.Fatalf("Failed to remove existing Windows %s resource system object file %s: %v", arch, sysoPath, err)
|
||||
}
|
||||
args := []string{"go", "run", "../bin/resource_windows.go", "-arch", arch, "-version", version, "-syso", sysoPath}
|
||||
if err := runEnv(args, nil); err != nil {
|
||||
log.Printf("Warning: Couldn't generate Windows %s resource system object file, binaries will not have version information or icon embedded", arch)
|
||||
return nil
|
||||
}
|
||||
if _, err := os.Stat(sysoPath); err != nil {
|
||||
log.Printf("Warning: Couldn't find generated Windows %s resource system object file, binaries will not have version information or icon embedded", arch)
|
||||
return nil
|
||||
}
|
||||
return func() {
|
||||
if err := os.Remove(sysoPath); err != nil && !os.IsNotExist(err) {
|
||||
log.Printf("Warning: Couldn't remove generated Windows %s resource system object file %s: %v. Please remove it manually.", arch, sysoPath, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// build the binary in dir returning success or failure
|
||||
func compileArch(version, goos, goarch, dir string) bool {
|
||||
log.Printf("Compiling %s/%s into %s", goos, goarch, dir)
|
||||
goarchBase := stripVersion(goarch)
|
||||
output := filepath.Join(dir, "rclone")
|
||||
if goos == "windows" {
|
||||
output += ".exe"
|
||||
sysoPath := buildWindowsResourceSyso(goarch, version)
|
||||
if sysoPath == "" {
|
||||
log.Printf("Warning: Windows binaries will not have file information embedded")
|
||||
if cleanupFn := generateResourceWindows(version, goarchBase); cleanupFn != nil {
|
||||
defer cleanupFn()
|
||||
}
|
||||
defer cleanupResourceSyso(sysoPath)
|
||||
}
|
||||
err := os.MkdirAll(dir, 0777)
|
||||
if err != nil {
|
||||
@@ -348,7 +274,7 @@ func compileArch(version, goos, goarch, dir string) bool {
|
||||
)
|
||||
env := []string{
|
||||
"GOOS=" + goos,
|
||||
"GOARCH=" + stripVersion(goarch),
|
||||
"GOARCH=" + goarchBase,
|
||||
}
|
||||
if *extraEnv != "" {
|
||||
env = append(env, strings.Split(*extraEnv, ",")...)
|
||||
|
||||
Reference in New Issue
Block a user