From 6a2b7b97d7b1dc252dd4285a08c7f91358235d6d Mon Sep 17 00:00:00 2001 From: albertony <12441419+albertony@users.noreply.github.com> Date: Thu, 11 Jun 2020 10:26:14 +0200 Subject: [PATCH] build: Add file properties and icon to Windows executable (fixes #4304) --- Makefile | 1 + bin/cross-compile.go | 100 ++++++++++++++++++++++++ graphics/logo/ico/logo_symbol_color.ico | Bin 0 -> 24841 bytes 3 files changed, 101 insertions(+) create mode 100644 graphics/logo/ico/logo_symbol_color.ico diff --git a/Makefile b/Makefile index af7d78986..25dc499b9 100644 --- a/Makefile +++ b/Makefile @@ -85,6 +85,7 @@ check: rclone # Get the build dependencies build_dep: go run bin/get-github-release.go -extract golangci-lint golangci/golangci-lint 'golangci-lint-.*\.tar\.gz' + GO111MODULE=off go get github.com/josephspurrier/goversioninfo/cmd/goversioninfo # Get the release dependencies release_dep: diff --git a/bin/cross-compile.go b/bin/cross-compile.go index 22f57d3fd..dd541fec3 100644 --- a/bin/cross-compile.go +++ b/bin/cross-compile.go @@ -5,6 +5,7 @@ package main import ( + "encoding/json" "flag" "fmt" "io/ioutil" @@ -20,6 +21,8 @@ import ( "sync" "text/template" "time" + + "github.com/coreos/go-semver/semver" ) var ( @@ -168,12 +171,109 @@ 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": "Rsync for cloud storage", + "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 = ioutil.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, + jsonPath, + } + if goarch == "amd64" { + args = append(args, "-64") // Make the syso a 64-bit coff file (but does not matter, results are identical) + } + 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) + } + } +} + // build the binary in dir returning success or failure func compileArch(version, goos, goarch, dir string) bool { log.Printf("Compiling %s/%s", goos, 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") + } + defer cleanupResourceSyso(sysoPath) } err := os.MkdirAll(dir, 0777) if err != nil { diff --git a/graphics/logo/ico/logo_symbol_color.ico b/graphics/logo/ico/logo_symbol_color.ico new file mode 100644 index 0000000000000000000000000000000000000000..131eb9ec8bd36d9f97f5d92f0d11b3a962b9334c GIT binary patch literal 24841 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