From 687646d407eacd884d04ad12315c1109f13ffee9 Mon Sep 17 00:00:00 2001 From: Dan Paulat Date: Thu, 8 Aug 2024 00:04:53 -0500 Subject: [PATCH] Autogenerate Windows resource and add build number to executable --- scwx-qt/res/{scwx-qt.rc => scwx-qt.rc.in} | 12 +-- scwx-qt/scwx-qt.cmake | 40 ++++++-- scwx-qt/tools/generate_versions.py | 114 ++++++++++++++++++---- 3 files changed, 133 insertions(+), 33 deletions(-) rename scwx-qt/res/{scwx-qt.rc => scwx-qt.rc.in} (58%) diff --git a/scwx-qt/res/scwx-qt.rc b/scwx-qt/res/scwx-qt.rc.in similarity index 58% rename from scwx-qt/res/scwx-qt.rc rename to scwx-qt/res/scwx-qt.rc.in index 9bb51c0d..2d03e06c 100644 --- a/scwx-qt/res/scwx-qt.rc +++ b/scwx-qt/res/scwx-qt.rc.in @@ -1,10 +1,10 @@ #include "winver.h" -IDI_ICON1 ICON "icons\\scwx-256.ico" +IDI_ICON1 ICON "${resource_dir}\\scwx-qt\\res\\icons\\scwx-256.ico" VS_VERSION_INFO VERSIONINFO - FILEVERSION 0,4,5,0 - PRODUCTVERSION 0,4,5,0 + FILEVERSION ${version_commas},${build_number} + PRODUCTVERSION ${version_commas},${build_number} FILEFLAGS 0x0L FILEFLAGSMASK 0x3fL FILEOS 0x00040004L @@ -17,12 +17,12 @@ BEGIN BEGIN VALUE "CompanyName", "Dan Paulat" VALUE "FileDescription", "Supercell Wx" - VALUE "FileVersion", "0.4.5.0" - VALUE "LegalCopyright", "Copyright (C) 2021-2024 Dan Paulat" + VALUE "FileVersion", "${version_string}.${build_number}" + VALUE "LegalCopyright", "Copyright (C) 2021-${copyright_year} Dan Paulat" VALUE "InternalName", "scwx" VALUE "OriginalFilename", "supercell-wx.exe" VALUE "ProductName", "Supercell Wx" - VALUE "ProductVersion", "0.4.5.0" + VALUE "ProductVersion", "${version_string}.${build_number}" END END BLOCK "VarFileInfo" diff --git a/scwx-qt/scwx-qt.cmake b/scwx-qt/scwx-qt.cmake index b6e2c44e..c8e1d4ad 100644 --- a/scwx-qt/scwx-qt.cmake +++ b/scwx-qt/scwx-qt.cmake @@ -389,6 +389,8 @@ set(ZONE_DBF_FILES ${SCWX_DIR}/data/db/fz05mr24.dbf set(STATE_DBF_FILES ${SCWX_DIR}/data/db/s_05mr24.dbf) set(COUNTIES_SQLITE_DB ${scwx-qt_BINARY_DIR}/res/db/counties.db) +set(RESOURCE_INPUT ${scwx-qt_SOURCE_DIR}/res/scwx-qt.rc.in) +set(RESOURCE_OUTPUT ${scwx-qt_BINARY_DIR}/res/scwx-qt.rc) set(VERSIONS_INPUT ${scwx-qt_SOURCE_DIR}/source/scwx/qt/main/versions.hpp.in) set(VERSIONS_CACHE ${scwx-qt_BINARY_DIR}/versions_cache.json) set(VERSIONS_HEADER ${scwx-qt_BINARY_DIR}/scwx/qt/main/versions.hpp) @@ -491,14 +493,34 @@ add_custom_target(scwx-qt_generate_counties_db ALL add_dependencies(scwx-qt scwx-qt_generate_counties_db) -add_custom_command(OUTPUT ${VERSIONS_HEADER} ${VERSIONS_HEADER}-ALWAYS_RUN - COMMAND ${Python_EXECUTABLE} - ${scwx-qt_SOURCE_DIR}/tools/generate_versions.py - -g ${SCWX_DIR} - -v ${SCWX_VERSION} - -c ${VERSIONS_CACHE} - -i ${VERSIONS_INPUT} - -o ${VERSIONS_HEADER}) +if (DEFINED ENV{GITHUB_RUN_NUMBER}) + set(SCWX_BUILD_NUM $ENV{GITHUB_RUN_NUMBER}) +else() + set(SCWX_BUILD_NUM 0) +endif() + +if (WIN32) + add_custom_command(OUTPUT ${VERSIONS_HEADER} ${RESOURCE_OUTPUT} ${VERSIONS_HEADER}-ALWAYS_RUN + COMMAND ${Python_EXECUTABLE} + ${scwx-qt_SOURCE_DIR}/tools/generate_versions.py + -g ${SCWX_DIR} + -v ${SCWX_VERSION} + -c ${VERSIONS_CACHE} + -i ${VERSIONS_INPUT} + -o ${VERSIONS_HEADER} + -b ${SCWX_BUILD_NUM} + --input-resource ${RESOURCE_INPUT} + --output-resource ${RESOURCE_OUTPUT}) +else() + add_custom_command(OUTPUT ${VERSIONS_HEADER} ${VERSIONS_HEADER}-ALWAYS_RUN + COMMAND ${Python_EXECUTABLE} + ${scwx-qt_SOURCE_DIR}/tools/generate_versions.py + -g ${SCWX_DIR} + -v ${SCWX_VERSION} + -c ${VERSIONS_CACHE} + -i ${VERSIONS_INPUT} + -o ${VERSIONS_HEADER}) +endif() add_custom_target(scwx-qt_generate_versions ALL DEPENDS ${VERSIONS_HEADER}) @@ -541,7 +563,7 @@ set_target_properties(scwx-qt_generate_versions PROPERTIES FOLDER generate) set_target_properties(scwx-qt_update_radar_sites PROPERTIES FOLDER generate) if (WIN32) - set(APP_ICON_RESOURCE_WINDOWS "${scwx-qt_SOURCE_DIR}/res/scwx-qt.rc") + set(APP_ICON_RESOURCE_WINDOWS ${RESOURCE_OUTPUT}) qt_add_executable(supercell-wx ${EXECUTABLE_SOURCES} ${APP_ICON_RESOURCE_WINDOWS}) set_target_properties(supercell-wx PROPERTIES WIN32_EXECUTABLE $,TRUE,FALSE>) else() diff --git a/scwx-qt/tools/generate_versions.py b/scwx-qt/tools/generate_versions.py index e7c470ef..c6c94020 100644 --- a/scwx-qt/tools/generate_versions.py +++ b/scwx-qt/tools/generate_versions.py @@ -4,36 +4,59 @@ import git import json import os import pathlib +import sys class Keys: + BuildNumber = "build_number" CommitString = "commit_string" CopyrightYear = "copyright_year" + ResourceDir = "resource_dir" + VersionCommas = "version_commas" VersionString = "version_string" class VersionInfo: def __init__(self): + self.buildNumber_ = None self.commitString_ = None self.copyrightYear_ = None + self.resourceDir_ = None + self.versionCommas_ = None self.versionString_ = None def __eq__(self, other): if isinstance(other, VersionInfo): - return self.commitString_ == other.commitString_ and \ + return self.buildNumber_ == other.buildNumber_ and \ + self.commitString_ == other.commitString_ and \ self.copyrightYear_ == other.copyrightYear_ and \ + self.resourceDir_ == other.resourceDir_ and \ self.versionString_ == other.versionString_ - + + def Calculate(self): + self.versionCommas_ = self.versionString_.replace('.', ',') + def Value(self, key): match key: + case Keys.BuildNumber: + return self.buildNumber_ case Keys.CommitString: return self.commitString_ case Keys.CopyrightYear: return self.copyrightYear_ + case Keys.ResourceDir: + return self.resourceDir_ + case Keys.VersionCommas: + return self.versionCommas_ case Keys.VersionString: return self.versionString_ case _: return None -kKeys_ = [Keys.CommitString, Keys.CopyrightYear, Keys.VersionString] +kKeys_ = [Keys.BuildNumber, + Keys.CommitString, + Keys.CopyrightYear, + Keys.ResourceDir, + Keys.VersionCommas, + Keys.VersionString] def ParseArguments(): parser = argparse.ArgumentParser(description='Generate versions') @@ -43,6 +66,12 @@ def ParseArguments(): dest = "cache_", default = None, type = pathlib.Path) + parser.add_argument("-b", "--build-number", + metavar = "value", + help = "build number", + dest = "buildNumber_", + default = 0, + type = str) parser.add_argument("-g", "--git-repo", metavar = "path", help = "base git repository path", @@ -61,6 +90,18 @@ def ParseArguments(): dest = "outputHeader_", type = pathlib.Path, required = True) + parser.add_argument("--input-resource", + metavar = "filename", + help = "input resource template", + dest = "inputResource_", + default = None, + type = pathlib.Path) + parser.add_argument("-r", "--output-resource", + metavar = "filename", + help = "output resource", + dest = "outputResource_", + default = None, + type = pathlib.Path) parser.add_argument("-v", "--version", metavar = "version", help = "version string", @@ -77,17 +118,24 @@ def CollectVersionInfo(args): repo = git.Repo(args.gitRepo_, search_parent_directories = True) commitString = str(repo.head.commit)[:10] - + if not repo.is_dirty(submodules = False): copyrightYear = datetime.datetime.fromtimestamp(repo.head.commit.committed_date).year else: commitString = commitString + "+dirty" copyrightYear = datetime.date.today().year - + + resourceDir = str(args.gitRepo_).replace("\\", "\\\\") + + versionInfo.buildNumber_ = args.buildNumber_ versionInfo.commitString_ = commitString versionInfo.copyrightYear_ = copyrightYear + versionInfo.resourceDir_ = resourceDir versionInfo.versionString_ = args.version_ + versionInfo.Calculate() + + print("Build Number: " + str(versionInfo.buildNumber_)) print("Commit String: " + str(versionInfo.commitString_)) print("Copyright Year: " + str(versionInfo.copyrightYear_)) print("Version String: " + str(versionInfo.versionString_)) @@ -103,24 +151,27 @@ def LoadCache(args): with open(args.cache_) as f: data = json.load(f) cache = VersionInfo() + if Keys.BuildNumber in data: + cache.buildNumber_ = data[Keys.BuildNumber] if Keys.CommitString in data: cache.commitString_ = data[Keys.CommitString] if Keys.CopyrightYear in data: cache.copyrightYear_ = data[Keys.CopyrightYear] + if Keys.ResourceDir in data: + cache.resourceDir_ = data[Keys.ResourceDir] if Keys.VersionString in data: cache.versionString_ = data[Keys.VersionString] + cache.Calculate() except Exception as ex: # Ignore error if cache is not found pass return cache -def WriteHeader(versionInfo: VersionInfo, args): - print("Writing header") - +def WriteTemplate(versionInfo: VersionInfo, inputFile, outputFile): try: - pathlib.Path(args.outputHeader_).parent.mkdir(exist_ok=True, parents=True) - with open(args.inputHeader_) as fi, open(args.outputHeader_, 'w') as fo: + pathlib.Path(outputFile).parent.mkdir(exist_ok=True, parents=True) + with open(inputFile) as fi, open(outputFile, 'w') as fo: for line in fi: for key in kKeys_: line = line.replace("${" + key + "}", str(versionInfo.Value(key))) @@ -131,12 +182,24 @@ def WriteHeader(versionInfo: VersionInfo, args): return True +def WriteHeader(versionInfo: VersionInfo, args): + print("Writing header") + return WriteTemplate(versionInfo, args.inputHeader_, args.outputHeader_) + +def WriteResource(versionInfo: VersionInfo, args): + if args.inputResource_ == None or args.outputResource_ == None: + return None + print("Writing resource") + return WriteTemplate(versionInfo, args.inputResource_, args.outputResource_) + def UpdateCache(versionInfo: VersionInfo, args): print("Updating cache") data = {} + data[Keys.BuildNumber] = versionInfo.buildNumber_ data[Keys.CommitString] = versionInfo.commitString_ data[Keys.CopyrightYear] = versionInfo.copyrightYear_ + data[Keys.ResourceDir] = versionInfo.resourceDir_ data[Keys.VersionString] = versionInfo.versionString_ try: @@ -146,12 +209,27 @@ def UpdateCache(versionInfo: VersionInfo, args): except Exception as ex: print("Error updating cache: " + repr(ex)) -args = ParseArguments() -versionInfo = CollectVersionInfo(args) -cache = LoadCache(args) +def main() -> int: + status = 0 -if versionInfo == cache: - print("No version changes detected") -else: - if WriteHeader(versionInfo, args): - UpdateCache(versionInfo, args) + args = ParseArguments() + versionInfo = CollectVersionInfo(args) + cache = LoadCache(args) + + if versionInfo == cache and \ + (args.outputHeader_ is None or os.path.exists(args.outputHeader_)) and \ + (args.outputResource_ is None or os.path.exists(args.outputResource_)): + print("No version changes detected") + else: + writeHeaderStatus = WriteHeader(versionInfo, args) + writeResourceStatus = WriteResource(versionInfo, args) + + if writeHeaderStatus or writeResourceStatus: + UpdateCache(versionInfo, args) + if writeHeaderStatus == False or writeResourceStatus == False: + status = -1 + + return status + +if __name__ == "__main__": + sys.exit(main())