# Copyright (C) 2011-2020 Daniel Scharrer # # This software is provided 'as-is', without any express or implied # warranty. In no event will the author(s) be held liable for any damages # arising from the use of this software. # # Permission is granted to anyone to use this software for any purpose, # including commercial applications, and to alter it and redistribute it # freely, subject to the following restrictions: # # 1. The origin of this software must not be misrepresented; you must not # claim that you wrote the original software. If you use this software # in a product, an acknowledgment in the product documentation would be # appreciated but is not required. # 2. Altered source versions must be plainly marked as such, and must not be # misrepresented as being the original software. # 3. This notice may not be removed or altered from any source distribution. get_filename_component(VERSION_SCRIPT_DIR "${CMAKE_CURRENT_LIST_FILE}" PATH) set(VERSION_STRING_SCRIPT "${VERSION_SCRIPT_DIR}/VersionScript.cmake") # Create a rule to generate a version string at compile time. # # An optional fifth argument can be used to add additional cmake defines. # # SRC is processed using the configure_file() cmake command # at build to produce DST with the following variable available: # # VERSION_SOURCES: # List of (${var} ${file}) pairs. # # for each variable ${var} # - ${var}: The contents of the associated file # - ${var}_COUNT: Number of lines in the associated file # - For each line ${i}: # - ${var}_${i}: The ${i}-th line of the associated file # - ${var}_${i}_PREFIX: The first component in the line # - ${var}_${i}_LINE: Everything except the first component of the line # - ${var}_${i}_NAME: Everything except the last component of the line # - ${var}_${i}_STRING: The last component (excluding optional suffix) of the line # - ${var}_${i}_SUFFIX: Suffix (separated by " + ") of the line # - ${var}_${i}_MAJOR: First version component in ${var}_${i}_STRING # - ${var}_${i}_MINOR: Second version component in ${var}_${i}_STRING # - ${var}_${i}_PATCH: Third version component in ${var}_${i}_STRING # - ${var}_${i}_BUILD: Fourth version component in ${var}_${i}_STRING # - ${var}_${i}_NUMBER: Reassembled verion components # - ${var}_${i}_PRERELEASE: If the version indicates a prerelease build # - ${var}_${i}_PRIVATE: If the version indicates a private build # - ${var}_HEAD: The first paragraph of the associated file # - ${var}_TAIL: The remaining paragraphs of the associated file # # - GIT_COMMIT: The current git commit. (not defined if there is no GIT_DIR directory) # - GIT_COMMIT_PREFIX_${i}: The first ${i} characters of GIT_COMMIT (i=0..39) # For the exact syntax of SRC see the documentation of the configure_file() cmake command. # The version file is regenerated whenever VERSION_FILE or the current commit changes. function(version_file SRC DST VERSION_SOURCES GIT_DIR) set(MODE_VARIABLE 0) set(MODE_FILE 1) set(mode ${MODE_VARIABLE}) set(args) set(dependencies "${VERSION_STRING_SCRIPT}") foreach(arg IN LISTS VERSION_SOURCES) if(mode EQUAL MODE_VARIABLE) set(mode ${MODE_FILE}) else() get_filename_component(arg "${arg}" ABSOLUTE) list(APPEND dependencies ${arg}) set(mode ${MODE_VARIABLE}) endif() list(APPEND args ${arg}) endforeach() get_filename_component(abs_src "${SRC}" ABSOLUTE) get_filename_component(abs_dst "${DST}" ABSOLUTE) get_filename_component(abs_git_dir "${GIT_DIR}" ABSOLUTE) set(defines) if(ARGC GREATER 4) set(defines ${ARGV4}) endif() if(EXISTS "${abs_git_dir}") find_program(GIT_COMMAND git) if(GIT_COMMAND) list(APPEND dependencies "${GIT_COMMAND}") endif() if(EXISTS "${abs_git_dir}/HEAD") list(APPEND dependencies "${abs_git_dir}/HEAD") endif() if(EXISTS "${abs_git_dir}/packed-refs") list(APPEND dependencies "${abs_git_dir}/packed-refs") endif() if(EXISTS "${abs_git_dir}/logs/HEAD") list(APPEND dependencies "${abs_git_dir}/logs/HEAD") endif() else() set(abs_git_dir "") endif() add_custom_command( OUTPUT "${abs_dst}" COMMAND ${CMAKE_COMMAND} "-DINPUT=${abs_src}" "-DOUTPUT=${abs_dst}" "-DVERSION_SOURCES=${args}" "-DGIT_DIR=${abs_git_dir}" "-DGIT_COMMAND=${GIT_COMMAND}" ${defines} -P "${VERSION_STRING_SCRIPT}" COMMAND ${CMAKE_COMMAND} -E touch "${abs_dst}" MAIN_DEPENDENCY "${abs_src}" DEPENDS ${dependencies} COMMENT "" VERBATIM ) endfunction() macro(_version_escape var string) set(${var} "${string}") endmacro() macro(_define_version_var_nostrip suffix contents) _version_escape(${var}_${suffix} "${${contents}}") list(APPEND variables ${var}_${suffix}) endmacro() macro(_define_version_var suffix contents) string(STRIP "${${contents}}" tmp) _define_version_var_nostrip(${suffix} tmp) endmacro() macro(_define_version_line_var_nostrip suffix contents) _define_version_var_nostrip(${i}_${suffix} ${contents}) if(line_name) set(${line_name}_${suffix} "${${var}_${i}_${suffix}}") list(APPEND variables ${line_name}_${suffix}) endif() endmacro() macro(_define_version_line_var suffix contents) string(STRIP "${${contents}}" tmp) _define_version_line_var_nostrip(${suffix} tmp) endmacro() function(parse_version_file names file) list(GET names 0 var) list(LENGTH names names_count) set(variables) file(READ "${file}" contents) string(STRIP "${contents}" contents) string(REGEX REPLACE "\r\n" "\n" contents "${contents}") string(REGEX REPLACE "\r" "\n" contents "${contents}") _version_escape(${var} "${contents}") list(APPEND variables ${var}) # Split the version file into lines. string(REGEX MATCHALL "[^\r\n]+" lines "${contents}") set(i 0) foreach(line IN LISTS lines) if(i LESS names_count) list(GET names ${i} line_name) else() set(line_name) endif() _define_version_var(${i} line) if(line MATCHES "^([^ ]*) (.*)$") set(prefix "${CMAKE_MATCH_1}") set(notprefix "${CMAKE_MATCH_2}") _define_version_line_var(PREFIX prefix) _define_version_line_var(LINE notprefix) else() _define_version_line_var(PREFIX line) endif() if(line MATCHES "^(.*[^+] )?([^ ]+)( \\+ [^ ]+)?$") set(name "${CMAKE_MATCH_1}") set(string "${CMAKE_MATCH_2}") set(suffix "${CMAKE_MATCH_3}") _define_version_line_var(NAME name) _define_version_line_var(STRING string) _define_version_line_var_nostrip(SUFFIX suffix) if(i GREATER 0 AND line_name) set(${line_name} "${${var}_${i}_STRING}") list(APPEND variables ${line_name}) endif() if(string MATCHES "^([0-9]+)(\\.([0-9]+)(\\.([0-9]+)(\\.([0-9]+))?)?)?(.*)?$") set(major "${CMAKE_MATCH_1}") set(minor "${CMAKE_MATCH_3}") set(patch "${CMAKE_MATCH_5}") set(build "${CMAKE_MATCH_7}") set(release "${CMAKE_MATCH_8}") set(error 0) set(newpatch) set(newbuild) set(prerelease 0) set(private 0) if(release MATCHES "^\\-dev\\-([2-9][0-9][0-9][0-9]+)\\-([0-9][0-9])\\-([0-9][0-9])$") set(prerelease 1) set(newpatch "${CMAKE_MATCH_1}") set(newbuild "${CMAKE_MATCH_2}${CMAKE_MATCH_3}") elseif(release MATCHES "^\\-rc([0-9]+)$") set(prerelease 1) set(newpatch "9999") set(newbuild "${CMAKE_MATCH_1}") elseif(release MATCHES "^\\-r([0-9]+)$") if(build STREQUAL "") set(build "${CMAKE_MATCH_1}") else() set(error 1) endif() elseif(release OR suffix) set(prerelease 1) set(private 1) set(newpatch 9999) set(newbuild 9999) endif() foreach(component IN ITEMS major minor patch build newpatch newbuild) string(REGEX REPLACE "^0+" "" ${component} "${${component}}") if(NOT ${component}) set(${component} 0) endif() endforeach() if(newpatch) set(prerelease 1) if(build) set(error 1) else() if(patch) if(newpatch EQUAL 9999) math(EXPR patch "${patch} - 1") if(newbuild EQUAL 9999) set(build 9999) else() math(EXPR build "${newbuild} + 1000") endif() else() set(error 1) endif() else() if(minor) math(EXPR minor "${minor} - 1") else() if(major) math(EXPR major "${major} - 1") else() set(error 1) endif() set(minor 9999) endif() set(patch "${newpatch}") set(build "${newbuild}") endif() endif() endif() set(number "${major}") if(minor OR patch OR build) set(number "${number}.${minor}") if(patch OR build) set(number "${number}.${patch}") if(build) set(number "${number}.${build}") endif() endif() endif() _define_version_line_var(MAJOR major) _define_version_line_var(MINOR minor) _define_version_line_var(PATCH patch) _define_version_line_var(BUILD build) _define_version_line_var(PRERELEASE prerelease) _define_version_line_var(PRIVATE private) _define_version_line_var(NUMBER number) _define_version_line_var(ERROR error) endif() endif() math(EXPR i "${i} + 1") endforeach() set(${var}_COUNT ${i} PARENT_SCOPE) string(REGEX REPLACE "\n\n.*$" "" head "${contents}") _define_version_var(HEAD head) string(REGEX MATCH "\n\n.*" tail "${contents}") _define_version_var(TAIL tail) foreach(var IN LISTS variables) set(${var} "${${var}}" PARENT_SCOPE) endforeach() endfunction()