diff --git a/CMakeLists.txt b/CMakeLists.txt index 4244b2acb..9e216175b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,7 +1,51 @@ cmake_minimum_required(VERSION 3.15) include(cmake/Utilities.cmake) +include(cmake/GetGitRevisionDescription.cmake) -set (CMAKE_CXX_STANDARD 17) +#set (CMAKE_CXX_STANDARD 17) + +SET(PROJECT_VERSION_SUFFIX + "" + CACHE + STRING + "Full version suffix to be shown on the info screen in settings (e.g. full_version=4.0.3-BETA+1035.PR111.B4, suffix=-BETA+1035.PR111.B4). Defaults to '+..' if set to ''." + ) +SET(PROJECT_VERSION_SUFFIX_SHORT + "" + CACHE + STRING + "Short version suffix to be shown on splash screen. Defaults to '+' if set to ''." + ) +SET(BUILD_NUMBER + "" + CACHE STRING "Build number of the firmware. Resolved automatically if not specified." + ) + + +INCLUDE(cmake/ProjectVersion.cmake) +resolve_version_variables() + + +SET(PROJECT_VERSION_FLAVOUR "" CACHE STRING "Firmware flavour to build - DEBUG, DEVEL, APLHA, BETA or RC") +SET(PROJECT_VERSION_FLAVOUR_REVISION "" CACHE STRING "Firmware flavour version, e.g. 1 for RC1, etc") + + +IF( NOT PROJECT_VERSION_FLAVOUR STREQUAL "") + SET(PROJECT_VERSION "${PROJECT_VERSION}-${PROJECT_VERSION_FLAVOUR}") + add_compile_definitions(FW_FLAVOR=${PROJECT_VERSION_FLAVOUR}) + IF( NOT PROJECT_VERSION_FLAVOUR_REVISION STREQUAL "") + SET(PROJECT_VERSION "${PROJECT_VERSION}${PROJECT_VERSION_FLAVOUR_REVISION}") + add_compile_definitions(FW_FLAVERSION=${PROJECT_VERSION_FLAVOUR_REVISION}) + ENDIF() +ENDIF() + +# Inform user about the resolved settings +MESSAGE(STATUS "Project version: ${PROJECT_VERSION}") +MESSAGE( + STATUS "Project version with short suffix: ${PROJECT_VERSION}${PROJECT_VERSION_SUFFIX_SHORT}" + ) + +SET(FN_PREFIX "FW${PROJECT_VERSION}${PROJECT_VERSION_SUFFIX_SHORT}") MESSAGE(WARNING " ***************** YOUR ATTENTION PLEASE ***************** @@ -113,7 +157,6 @@ endif() # enable all warnings (well, not all, but some) add_compile_options(-Wsign-compare) -add_compile_options($<$:-std=gnu++17>) add_compile_options($<$:-std=gnu11>) # support _DEBUG macro (some code uses to recognize debug builds) @@ -197,7 +240,7 @@ function(fw_add_variant variant_name) add_custom_command( TARGET ${variant_name} POST_BUILD - COMMAND ${CMAKE_OBJCOPY} -O ihex ${CMAKE_BINARY_DIR}/${variant_name} ${CMAKE_BINARY_DIR}/${variant_name}-EN_ONLY.hex + COMMAND ${CMAKE_OBJCOPY} -O ihex ${CMAKE_BINARY_DIR}/${variant_name} ${CMAKE_BINARY_DIR}/${FN_PREFIX}-${variant_name}-EN_ONLY.hex COMMENT "Generating ${variant_name} hex" ) add_dependencies(ALL_FIRMWARE "${variant_name}") @@ -258,7 +301,7 @@ function(fw_add_variant variant_name) DEPENDS ${LANG_FINAL_BIN} COMMENT "Generating Hex for language data" ) - set(LANG_HEX ${CMAKE_BINARY_DIR}/${variant_name}-lang.hex) + set(LANG_HEX ${CMAKE_BINARY_DIR}/${FN_PREFIX}-${variant_name}-lang.hex) add_custom_target(${variant_name}-languages COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_BINARY_DIR}/${variant_name}.hex ${LANG_HEX} @@ -273,7 +316,7 @@ function(fw_add_variant variant_name) # Non-xflash, e.g. MK2.5 foreach(LANG IN LISTS SELECTED_LANGUAGES) SET(LANG_HEX_FN ${variant_name}-en_${LANG}) - SET(LANG_HEX ${CMAKE_BINARY_DIR}/${LANG_HEX_FN}.hex) + SET(LANG_HEX ${CMAKE_BINARY_DIR}/${FN_PREFIX}-${LANG_HEX_FN}.hex) SET(LANG_BIN ${LANG_TMP_DIR}/${variant_name}_${LANG}.bin) SET(LANG_FWBIN_TMP ${LANG_TMP_DIR}/${variant_name}-en_${LANG}.bin) @@ -292,7 +335,7 @@ function(fw_add_variant variant_name) DEPENDS ${LANG_FWBIN_TMP} COMMENT "Creating ${LANG_HEX_FN}.hex" ) - LIST(APPEND ALL_VARIANT_HEXES ${LANG_HEX}) + LIST(APPEND ALL_VARIANT_HEXES ${LANG_HEX_FN}) endforeach() add_custom_target("${variant_name}-All-Languages" DEPENDS ${ALL_VARIANT_HEXES} diff --git a/cmake/GetGitRevisionDescription.cmake b/cmake/GetGitRevisionDescription.cmake new file mode 100644 index 000000000..0eccbc122 --- /dev/null +++ b/cmake/GetGitRevisionDescription.cmake @@ -0,0 +1,232 @@ +# * Returns a version string from Git +# +# These functions force a re-configure on each git commit so that you can trust the values of the +# variables in your build system. +# +# get_git_head_revision( [ ...]) +# +# Returns the refspec and sha hash of the current head revision +# +# git_describe( [ ...]) +# +# Returns the results of git describe on the source tree, and adjusting the output so that it tests +# false if an error occurs. +# +# git_get_exact_tag( [ ...]) +# +# Returns the results of git describe --exact-match on the source tree, and adjusting the output so +# that it tests false if there was no exact matching tag. +# +# git_local_changes() +# +# Returns either "CLEAN" or "DIRTY" with respect to uncommitted changes. Uses the return code of +# "git diff-index --quiet HEAD --". Does not regard untracked files. +# +# git_count_parent_commits() +# +# Returns number of commits preceeding current commit -1 if git rev-list --count HEAD failed or +# "GIT-NOTFOUND" if git executable was not found or "HEAD-HASH-NOTFOUND" if head hash was not found. +# I don't know if get_git_head_revision() must be called internally or not, as reason of calling it +# is not clear for me also in git_local_changes(). +# +# Requires CMake 2.6 or newer (uses the 'function' command) +# +# Original Author: 2009-2010 Ryan Pavlik +# http://academic.cleardefinition.com Iowa State University HCI Graduate Program/VRAC +# +# Copyright Iowa State University 2009-2010. Distributed under the Boost Software License, Version +# 1.0. (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +if(__get_git_revision_description) + return() +endif() +set(__get_git_revision_description YES) + +# We must run the following at "include" time, not at function call time, to find the path to this +# module rather than the path to a calling list file +get_filename_component(_gitdescmoddir ${CMAKE_CURRENT_LIST_FILE} PATH) + +function(get_git_head_revision _refspecvar _hashvar) + set(GIT_PARENT_DIR "${CMAKE_CURRENT_SOURCE_DIR}") + set(GIT_DIR "${GIT_PARENT_DIR}/.git") + while(NOT EXISTS "${GIT_DIR}") # .git dir not found, search parent directories + set(GIT_PREVIOUS_PARENT "${GIT_PARENT_DIR}") + get_filename_component(GIT_PARENT_DIR ${GIT_PARENT_DIR} PATH) + if(GIT_PARENT_DIR STREQUAL GIT_PREVIOUS_PARENT) + # We have reached the root directory, we are not in git + set(${_refspecvar} + "GITDIR-NOTFOUND" + PARENT_SCOPE + ) + set(${_hashvar} + "GITDIR-NOTFOUND" + PARENT_SCOPE + ) + return() + endif() + set(GIT_DIR "${GIT_PARENT_DIR}/.git") + endwhile() + # check if this is a submodule + if(NOT IS_DIRECTORY ${GIT_DIR}) + file(READ ${GIT_DIR} submodule) + string(REGEX REPLACE "gitdir: (.*)\n$" "\\1" GIT_DIR_RELATIVE ${submodule}) + get_filename_component(SUBMODULE_DIR ${GIT_DIR} PATH) + get_filename_component(GIT_DIR ${SUBMODULE_DIR}/${GIT_DIR_RELATIVE} ABSOLUTE) + endif() + set(GIT_DATA "${CMAKE_CURRENT_BINARY_DIR}/CMakeFiles/git-data") + if(NOT EXISTS "${GIT_DATA}") + file(MAKE_DIRECTORY "${GIT_DATA}") + endif() + + if(NOT EXISTS "${GIT_DIR}/HEAD") + return() + endif() + set(HEAD_FILE "${GIT_DATA}/HEAD") + configure_file("${GIT_DIR}/HEAD" "${HEAD_FILE}" COPYONLY) + + configure_file( + "${_gitdescmoddir}/GetGitRevisionDescription.cmake.in" "${GIT_DATA}/grabRef.cmake" @ONLY + ) + include("${GIT_DATA}/grabRef.cmake") + + set(${_refspecvar} + "${HEAD_REF}" + PARENT_SCOPE + ) + set(${_hashvar} + "${HEAD_HASH}" + PARENT_SCOPE + ) +endfunction() + +function(git_describe _var) + if(NOT GIT_FOUND) + find_package(Git QUIET) + endif() + get_git_head_revision(refspec hash) + if(NOT GIT_FOUND) + set(${_var} + "GIT-NOTFOUND" + PARENT_SCOPE + ) + return() + endif() + if(NOT hash) + set(${_var} + "HEAD-HASH-NOTFOUND" + PARENT_SCOPE + ) + return() + endif() + + # TODO sanitize if((${ARGN}" MATCHES "&&") OR (ARGN MATCHES "||") OR (ARGN MATCHES "\\;")) + # message("Please report the following error to the project!") message(FATAL_ERROR "Looks like + # someone's doing something nefarious with git_describe! Passed arguments ${ARGN}") endif() + + # message(STATUS "Arguments to execute_process: ${ARGN}") + + execute_process( + COMMAND "${GIT_EXECUTABLE}" describe ${hash} ${ARGN} + WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}" + RESULT_VARIABLE res + OUTPUT_VARIABLE out + ERROR_QUIET OUTPUT_STRIP_TRAILING_WHITESPACE + ) + if(NOT res EQUAL 0) + set(out "${out}-${res}-NOTFOUND") + endif() + + set(${_var} + "${out}" + PARENT_SCOPE + ) +endfunction() + +function(git_get_exact_tag _var) + git_describe(out --exact-match ${ARGN}) + set(${_var} + "${out}" + PARENT_SCOPE + ) +endfunction() + +function(git_local_changes _var) + if(NOT GIT_FOUND) + find_package(Git QUIET) + endif() + get_git_head_revision(refspec hash) + if(NOT GIT_FOUND) + set(${_var} + "GIT-NOTFOUND" + PARENT_SCOPE + ) + return() + endif() + if(NOT hash) + set(${_var} + "HEAD-HASH-NOTFOUND" + PARENT_SCOPE + ) + return() + endif() + + execute_process( + COMMAND "${GIT_EXECUTABLE}" diff-index --quiet HEAD -- + WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}" + RESULT_VARIABLE res + OUTPUT_VARIABLE out + ERROR_QUIET OUTPUT_STRIP_TRAILING_WHITESPACE + ) + if(res EQUAL 0) + set(${_var} + "CLEAN" + PARENT_SCOPE + ) + else() + set(${_var} + "DIRTY" + PARENT_SCOPE + ) + endif() +endfunction() + +function(git_count_parent_commits _var) + if(NOT GIT_FOUND) + find_package(Git QUIET) + endif() + get_git_head_revision(refspec hash) + if(NOT GIT_FOUND) + set(${_var} + "GIT-NOTFOUND" + PARENT_SCOPE + ) + return() + endif() + if(NOT hash) + set(${_var} + "HEAD-HASH-NOTFOUND" + PARENT_SCOPE + ) + return() + endif() + + execute_process( + COMMAND "${GIT_EXECUTABLE}" rev-list --count HEAD + WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}" + RESULT_VARIABLE res + OUTPUT_VARIABLE out + ERROR_QUIET OUTPUT_STRIP_TRAILING_WHITESPACE + ) + if(res EQUAL 0) + set(${_var} + "${out}" + PARENT_SCOPE + ) + else() + set(${_var} + "-1" + PARENT_SCOPE + ) + endif() + +endfunction() diff --git a/cmake/GetGitRevisionDescription.cmake.in b/cmake/GetGitRevisionDescription.cmake.in new file mode 100644 index 000000000..f7d93ddf8 --- /dev/null +++ b/cmake/GetGitRevisionDescription.cmake.in @@ -0,0 +1,37 @@ +# +# Internal file for GetGitRevisionDescription.cmake +# +# Requires CMake 2.6 or newer (uses the 'function' command) +# +# Original Author: 2009-2010 Ryan Pavlik +# http://academic.cleardefinition.com Iowa State University HCI Graduate Program/VRAC +# +# Copyright Iowa State University 2009-2010. Distributed under the Boost Software License, Version +# 1.0. (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +set(HEAD_HASH) + +file(READ "@HEAD_FILE@" HEAD_CONTENTS LIMIT 1024) + +string(STRIP "${HEAD_CONTENTS}" HEAD_CONTENTS) +if(HEAD_CONTENTS MATCHES "ref") + # named branch + string(REPLACE "ref: " "" HEAD_REF "${HEAD_CONTENTS}") + if(EXISTS "@GIT_DIR@/${HEAD_REF}") + configure_file("@GIT_DIR@/${HEAD_REF}" "@GIT_DATA@/head-ref" COPYONLY) + else() + configure_file("@GIT_DIR@/packed-refs" "@GIT_DATA@/packed-refs" COPYONLY) + file(READ "@GIT_DATA@/packed-refs" PACKED_REFS) + if(${PACKED_REFS} MATCHES "([0-9a-z]*) ${HEAD_REF}") + set(HEAD_HASH "${CMAKE_MATCH_1}") + endif() + endif() +else() + # detached HEAD + configure_file("@GIT_DIR@/HEAD" "@GIT_DATA@/head-ref" COPYONLY) +endif() + +if(NOT HEAD_HASH) + file(READ "@GIT_DATA@/head-ref" HEAD_HASH LIMIT 1024) + string(STRIP "${HEAD_HASH}" HEAD_HASH) +endif() diff --git a/cmake/ProjectVersion.cmake b/cmake/ProjectVersion.cmake new file mode 100644 index 000000000..259442735 --- /dev/null +++ b/cmake/ProjectVersion.cmake @@ -0,0 +1,71 @@ +# +# This file is responsible for setting the following variables: +# +# ~~~ +# BUILD_NUMBER (1035) +# PROJECT_VERSION (4.0.3) +# PROJECT_VERSION_FULL (4.0.3-BETA+1035.PR111.B4) +# PROJECT_VERSION_SUFFIX (-BETA+1035.PR111.B4) +# PROJECT_VERSION_SUFFIX_SHORT (+1035) +# +# The `PROJECT_VERSION` variable is set as soon as the file is included. +# To set the rest, the function `resolve_version_variables` has to be called. +# +# ~~~ + +FILE(STRINGS ${CMAKE_CURRENT_SOURCE_DIR}/Firmware/Configuration.h CFG_VER_DATA REGEX "#define FW_[A-Z]+ ([0-9]+)" ) +LIST(GET CFG_VER_DATA 0 PROJECT_VERSION_MAJOR) +LIST(GET CFG_VER_DATA 1 PROJECT_VERSION_MINOR) +LIST(GET CFG_VER_DATA 2 PROJECT_VERSION_REV) +STRING(REGEX MATCH "FW_MAJOR ([0-9]+)" PROJECT_VERSION_MAJOR "${PROJECT_VERSION_MAJOR}") +SET(PROJECT_VERSION_MAJOR "${CMAKE_MATCH_1}") + +STRING(REGEX MATCH "FW_MINOR ([0-9]+)" PROJECT_VERSION_MINOR "${PROJECT_VERSION_MINOR}") +SET(PROJECT_VERSION_MINOR ${CMAKE_MATCH_1}) + +STRING(REGEX MATCH "FW_REVISION +([0-9]+)" PROJECT_VERSION_REV "${PROJECT_VERSION_REV}") +SET(PROJECT_VERSION_REV ${CMAKE_MATCH_1}) + +SET(PROJECT_VERSION "${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}.${PROJECT_VERSION_REV}") +SET(PROJECT_VERSION "${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}.${PROJECT_VERSION_REV}" PARENT_SCOPE) + + +function(resolve_version_variables) + # BUILD_NUMBER + if(NOT BUILD_NUMBER) + git_count_parent_commits(BUILD_NUMBER) + set(ERRORS "GIT-NOTFOUND" "HEAD-HASH-NOTFOUND") + if(BUILD_NUMBER IN_LIST ERRORS) + message(WARNING "Failed to resolve build number: ${BUILD_NUMBER}. Setting to zero.") + set(BUILD_NUMBER "0") + endif() + set(BUILD_NUMBER + ${BUILD_NUMBER} + PARENT_SCOPE + ) + endif() + + # PROJECT_VERSION_SUFFIX + if(PROJECT_VERSION_SUFFIX STREQUAL "") + # TODO: set to +.dirty?.debug? + set(PROJECT_VERSION_SUFFIX "+${BUILD_NUMBER}.LOCAL") + set(PROJECT_VERSION_SUFFIX + "+${BUILD_NUMBER}.LOCAL" + PARENT_SCOPE + ) + endif() + + # PROJECT_VERSION_SUFFIX_SHORT + if(PROJECT_VERSION_SUFFIX_SHORT STREQUAL "") + set(PROJECT_VERSION_SUFFIX_SHORT + "+${BUILD_NUMBER}" + PARENT_SCOPE + ) + endif() + + # PROJECT_VERSION_FULL + set(PROJECT_VERSION_FULL + "${PROJECT_VERSION}${PROJECT_VERSION_SUFFIX}" + PARENT_SCOPE + ) +endfunction()