Skip to content

Commit

Permalink
[CMake] Windows: Automatically generate the appropriate .rc file
Browse files Browse the repository at this point in the history
Based on the autorevision information and WZ_PORTABLE.
This will automatically update the main EXE's VERSIONINFO.
  • Loading branch information
past-due committed Mar 22, 2018
1 parent ea616e2 commit a17d760
Show file tree
Hide file tree
Showing 3 changed files with 230 additions and 1 deletion.
19 changes: 18 additions & 1 deletion src/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,23 @@ add_custom_command(
OUTPUT
"${CMAKE_CURRENT_BINARY_DIR}/__shouldnotexist.h" # fake - ensure we run
"${CMAKE_CURRENT_BINARY_DIR}/autorevision.h"
"${CMAKE_CURRENT_BINARY_DIR}/autorevision.cache"
# this command must generate: ${CMAKE_CURRENT_BINARY_DIR}/autorevision.h
COMMAND ${CMAKE_COMMAND} -DCACHEFILE="${CMAKE_CURRENT_BINARY_DIR}/autorevision.cache" -DOUTPUT_TYPE=h -DOUTPUT_FILE="${CMAKE_CURRENT_BINARY_DIR}/autorevision.h" -P "${CMAKE_SOURCE_DIR}/build_tools/autorevision.cmake"
WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}"
)

# On Windows, configure warzone2100.rc with updated version info
if(CMAKE_SYSTEM_NAME MATCHES "Windows")
set(_rc_template_file "${CMAKE_SOURCE_DIR}/win32/warzone2100.rc.in")
add_custom_command(
OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/warzone2100.rc"
COMMAND ${CMAKE_COMMAND} -DCACHEFILE="${CMAKE_CURRENT_BINARY_DIR}/autorevision.cache" -DPROJECT_ROOT="${PROJECT_SOURCE_DIR}" -DTEMPLATE_FILE="${_rc_template_file}" -DOUTPUT_FILE="${CMAKE_CURRENT_BINARY_DIR}/warzone2100.rc" -DPORTABLE="${WZ_PORTABLE}" -P "${CMAKE_SOURCE_DIR}/win32/autorevision_rc.cmake"
WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}"
DEPENDS "${_rc_template_file}" "${CMAKE_CURRENT_BINARY_DIR}/autorevision.cache"
)
endif()

############################
# Main Executable

Expand All @@ -36,7 +48,12 @@ file(GLOB HEADERS "*.h")
file(GLOB SRC "*.cpp")
qt5_wrap_cpp(MOCFILES qtscriptdebug.h)

add_executable(warzone2100 ${HEADERS} ${SRC} ${MOCFILES} "${CMAKE_CURRENT_BINARY_DIR}/autorevision.h" "${CMAKE_SOURCE_DIR}/win32/warzone2100.rc")
set(_additionalSourceFiles)
if(CMAKE_SYSTEM_NAME MATCHES "Windows")
set(_additionalSourceFiles "${CMAKE_CURRENT_BINARY_DIR}/warzone2100.rc")
endif()

add_executable(warzone2100 ${HEADERS} ${SRC} ${MOCFILES} "${CMAKE_CURRENT_BINARY_DIR}/autorevision.h" ${_additionalSourceFiles})
target_compile_definitions(warzone2100 PRIVATE "YY_NO_UNISTD_H")
SET_TARGET_PROPERTIES(warzone2100 PROPERTIES RUNTIME_OUTPUT_DIRECTORY_RELEASE "${CMAKE_CURRENT_BINARY_DIR}")
SET_TARGET_PROPERTIES(warzone2100 PROPERTIES RUNTIME_OUTPUT_DIRECTORY_DEBUG "${CMAKE_CURRENT_BINARY_DIR}")
Expand Down
165 changes: 165 additions & 0 deletions win32/autorevision_rc.cmake
Original file line number Diff line number Diff line change
@@ -0,0 +1,165 @@
cmake_minimum_required(VERSION 3.5)

# Automatically update the VERSION_INFO in the warzone2100.rc file
#
# Required input defines:
# - CACHEFILE: the path to the autorevision.cache file generated for the build
# - PROJECT_ROOT: the path the project root (${PROJECT_SOURCE_DIR})
# - TEMPLATE_FILE: the full filename + path for the input rc.in template file
# - OUTPUT_FILE: the full filename + path for the output .rc file
#
# Optional input defines:
# - PORTABLE: "ON" if portable build

if(NOT DEFINED CACHEFILE OR "${CACHEFILE}" STREQUAL "")
message( FATAL_ERROR "Missing required input define: CACHEFILE" )
endif()
if(NOT DEFINED PROJECT_ROOT OR "${PROJECT_ROOT}" STREQUAL "")
message( FATAL_ERROR "Missing required input define: PROJECT_ROOT" )
endif()
if(NOT DEFINED TEMPLATE_FILE OR "${TEMPLATE_FILE}" STREQUAL "")
message( FATAL_ERROR "Missing required input define: TEMPLATE_FILE" )
endif()
if(NOT DEFINED OUTPUT_FILE OR "${OUTPUT_FILE}" STREQUAL "")
message( FATAL_ERROR "Missing required input define: OUTPUT_FILE" )
endif()

#################################

execute_process(COMMAND ${CMAKE_COMMAND} -E echo "++Get build revision info from: ${CACHEFILE}")

# Attempt to get version information from the current build's autorevision cache. Fail if cache is not present.
execute_process(COMMAND ${CMAKE_COMMAND} -DCACHEFILE=${CACHEFILE} -DSKIPUPDATECACHE="1" -DCACHEFORCE="1" -DVAROUT=1 -P "${PROJECT_ROOT}/build_tools/autorevision.cmake"
WORKING_DIRECTORY "${PROJECT_ROOT}"
OUTPUT_VARIABLE autorevision_info
OUTPUT_STRIP_TRAILING_WHITESPACE
)

include("${PROJECT_ROOT}/build_tools/autorevision_helpers.cmake")

# Import the autorevision values into the current scope
cmakeSetAutorevisionValues("${autorevision_info}")

unset(RC_FILEVERSION)
unset(RC_PRODUCTVERSION)
unset(RC_StringFileInfo_FileVersion)
unset(RC_StringFileInfo_ProductVersion)

##################################
# Determine the ProductVersion

if(DEFINED VCS_TAG AND NOT "${VCS_TAG}" STREQUAL "")
# We're on an exact tag
# See if the tag contains a version number
extractVersionNumberFromGitTag("${VCS_TAG}")
if(DID_EXTRACT_VERSION)
# Able to extract a version number from the tag - use it for the PRODUCTVERSION
set(RC_PRODUCTVERSION "${EXTRACTED_VERSION_MAJOR},${EXTRACTED_VERSION_MINOR},${EXTRACTED_VERSION_PATCH},0")

# Use the full tag for the StringFileInfo ProductVersion
set(RC_StringFileInfo_ProductVersion "${VCS_TAG}")
endif()
endif()

# Extract version info from the most recent tagged version
extractVersionNumberFromGitTag("${VCS_MOST_RECENT_TAGGED_VERSION}")
if(DID_EXTRACT_VERSION)

else()
message( WARNING "The VCS_MOST_RECENT_TAGGED_VERSION tag does not seem to include a version #; defaulting to 0.0.0" )
endif()

# Determine the build-number component of the version info
if(NOT DEFINED VCS_COMMIT_COUNT_SINCE_MOST_RECENT_TAGGED_VERSION OR "${VCS_COMMIT_COUNT_SINCE_MOST_RECENT_TAGGED_VERSION}" STREQUAL "")
set(product_build_number "0")
else()
set(product_build_number "${VCS_COMMIT_COUNT_SINCE_MOST_RECENT_TAGGED_VERSION}")
endif()
if(_build_number GREATER 65534)
# Each component of the ProductVersion on Windows is a 16-bit integer.
# If the build number is greater than supported, pin it to 65534 and emit a warning
# (That's a *lot* of commits since the last tagged version...)
set(product_build_number 65534)
message( WARNING "The number of commits since the most recent tagged version exceeds 65534; the build-number component of the PRODUCTVERSION will be pinned to 65534." )
endif()

if(NOT DEFINED RC_PRODUCTVERSION)
# Set the PRODUCT_VERSION to: <MOST_RECENT_TAGGED_VERSION>.<COMMITS_SINCE_MOST_RECENT_TAGGED_VERSION>
set(RC_PRODUCTVERSION "${EXTRACTED_VERSION_MAJOR},${EXTRACTED_VERSION_MINOR},${EXTRACTED_VERSION_PATCH},${product_build_number}")
endif()

if(NOT DEFINED RC_StringFileInfo_ProductVersion)
# Set the StringFileInfo ProductVersion to: <VCS_BRANCH> <VCS_SHORTHASH>
set(RC_StringFileInfo_ProductVersion "${VCS_BRANCH} ${VCS_SHORT_HASH}")
endif()

##################################
# Determine the FileVersion

# Format: "1." (commits-on-master[-until-branch "." commits-on-branch])
if("${VCS_BRANCH}" STREQUAL "master")
# "1.{commits-on-master}"
set(RC_StringFileInfo_FileVersion "1.${VCS_COMMIT_COUNT}")
else()
# "1.{commits-on-master-until-branch}.{commits-on-branch}"
set(RC_StringFileInfo_FileVersion "1.${VCS_COMMIT_COUNT_ON_MASTER_UNTIL_BRANCH}.${VCS_BRANCH_COMMIT_COUNT}")
endif()

# The FILEVERSION number is a bit complicated:
# Each of the 4 components is a 16-bit integer, so we have to spread the data across the final 3
# For this, we simply use "1,<VCS_COMMIT_COUNT>", with the COMMIT_COUNT potentially wrapping across all 3 final 16-bit integers in a human-readable way
set(_file_version_minor "0")
set(_file_version_patch "0")
if(VCS_COMMIT_COUNT GREATER 2147483647)
# Reward yourself - we've hit the future
# Better change / review the MATH code below, because CMake (at least, 32-bit builds?) will overflow on the MATH operations that follow
message( WARNING "VCS_COMMIT_COUNT exceeds Int32.max, and the following MATH operations may not work properly. Pinning to Int32.max for now - needs to be fixed." )
set(_file_version_build "2147483647") # for now, pin to Int32.max
else()
set(_file_version_build "${VCS_COMMIT_COUNT}")
endif()
if(_file_version_build GREATER 65534)
MATH(EXPR _file_version_patch "${_file_version_build} / 10000")
MATH(EXPR _file_version_build "${_file_version_build} % 10000")
if(_file_version_patch GREATER 65534)
MATH(EXPR _file_version_minor "${_file_version_patch} / 10000")
MATH(EXPR _file_version_patch "${_file_version_patch} % 10000")
if(_file_version_minor GREATER 65534)
message( WARNING "Ran out of room to cram COMMIT_COUNT into VERSION_INFO using current algorithm. Manually increment MAJOR to 2 and augment this code in autorevision_rc.cmake." )
set(_file_version_minor 65534)
endif()
endif()
endif()
set(RC_FILEVERSION "1,${_file_version_minor},${_file_version_patch},${_file_version_build}")

##################################
# Determine the other .rc settings

if(DEFINED PORTABLE AND PORTABLE)
set(RC_StringFileInfo_FileDescription "Warzone 2100 portable")
set(RC_StringFileInfo_OriginalFilename "warzone2100_portable.exe")
set(RC_StringFileInfo_ProductName "Warzone 2100 portable")
set(RC_Icon_Filename "wz2100portable.ico")
else()
set(RC_StringFileInfo_FileDescription "Warzone 2100")
set(RC_StringFileInfo_OriginalFilename "warzone2100.exe")
set(RC_StringFileInfo_ProductName "Warzone 2100")
set(RC_Icon_Filename "warzone2100.obsolete.ico")
endif()

##################################
# Debug output

execute_process(COMMAND ${CMAKE_COMMAND} -E echo "++FILEVERSION: ${RC_FILEVERSION}")
execute_process(COMMAND ${CMAKE_COMMAND} -E echo "++PRODUCTVERSION: ${RC_PRODUCTVERSION}")
execute_process(COMMAND ${CMAKE_COMMAND} -E echo "++(StringInfo) FileVersion: ${RC_StringFileInfo_FileVersion}")
execute_process(COMMAND ${CMAKE_COMMAND} -E echo "++(StringInfo) ProductVersion: ${RC_StringFileInfo_ProductVersion}")

##################################
# Output configured file based on the template

if(NOT EXISTS "${TEMPLATE_FILE}")
message( FATAL_ERROR "Input TEMPLATE_FILE does not exist: \"${TEMPLATE_FILE}\"" )
endif()
configure_file("${TEMPLATE_FILE}" "${OUTPUT_FILE}" @ONLY)

47 changes: 47 additions & 0 deletions win32/warzone2100.rc.in
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
/////////////////////////////////////////////////////////////////////////////
//
// Version
//

1 VERSIONINFO
FILEVERSION @RC_FILEVERSION@
PRODUCTVERSION @RC_PRODUCTVERSION@
FILEFLAGSMASK 0x0L
#ifdef _DEBUG
FILEFLAGS 0x1L
#else
FILEFLAGS 0x0L
#endif
FILEOS 0x40004L
FILETYPE 0x1L
FILESUBTYPE 0x0L
BEGIN
BLOCK "StringFileInfo"
BEGIN
BLOCK "040904b0"
BEGIN
VALUE "CompanyName", "Warzone 2100 Project"
VALUE "FileDescription", "@RC_StringFileInfo_FileDescription@"
VALUE "FileVersion", "@RC_StringFileInfo_FileVersion@"
VALUE "InternalName", "Warzone 2100"
VALUE "LegalCopyright", "Copyright (C) 2005-2018 Warzone 2100 Project"
VALUE "OriginalFilename", "@RC_StringFileInfo_OriginalFilename@"
VALUE "ProductName", "@RC_StringFileInfo_ProductName@"
VALUE "ProductVersion", "@RC_StringFileInfo_ProductVersion@"
END
END
BLOCK "VarFileInfo"
BEGIN
VALUE "Translation", 0x0409, 0x04b0
END
END


/////////////////////////////////////////////////////////////////////////////
//
// Icon
//

// Icon with lowest ID value placed first to ensure application icon
// remains consistent on all systems.
2 ICON "../icons/@RC_Icon_Filename@"

0 comments on commit a17d760

Please sign in to comment.