1 # Generate Gromacs development build version information.
3 # The script generates version information for a build from a development
4 # source tree based on git repository information.
5 # It is assumed that by default the script is run in cmake script mode.
6 # If *not* called in script mode but used in generating cache variables,
7 # GEN_VERSION_INFO_INTERNAL has to be set ON.
9 # The following variables have to be previously defined:
10 # GIT_EXECUTABLE - path to git binary
11 # GIT_VERSION - git version (if not defined it's assumed that >=1.5.3)
12 # PROJECT_VERSION - hard-coded version string, should have the following structure:
13 # VERSION[-dev-SUFFIX] where the VERSION can have any form and the suffix
14 # is optional but should start with -dev
15 # PROJECT_SOURCE_DIR - top level source directory (which has to be in git)
16 # VERSION_C_CMAKEIN - path to the version.c.cmakein file
17 # VERSION_C_OUT - path to the version.c output file
20 # i) Script mode: version.c configured from the input version.c.cmakein using
21 # the variables listed below.
22 # ii) Cache variable mode: the varables below are set in cache.
24 # GMX_PROJECT_VERSION_STR - version string
25 # GMX_GIT_HEAD_HASH - git hash of current local HEAD
26 # GMX_GIT_REMOTE_HASH - git hash of the first ancestor commit from the
27 # main Gromacs repository
29 # Szilard Pall (pszilard@cbr.su.se)
31 if("${PROJECT_VERSION}" STREQUAL "")
32 message(FATAL_ERROR "PROJECT_VERSION undefined!")
34 set(VER ${PROJECT_VERSION})
36 # if we're generating variables for cache unset the variables
37 if(GEN_VERSION_INFO_INTERNAL)
38 set(GMX_PROJECT_VERSION_STR)
39 set(GMX_GIT_HEAD_HASH)
40 set(GMX_GIT_REMOTE_HASH)
43 # bail if the source tree is not in a git repository
44 if(NOT EXISTS "${PROJECT_SOURCE_DIR}/.git")
45 message(FATAL_ERROR "Project source directory ${PROJECT_SOURCE_DIR} not in git")
48 # if git executable exists and it's compatible version
49 # build the development version string
50 # this should at some point become VERSION_LESS
51 if(EXISTS "${GIT_EXECUTABLE}" AND NOT GIT_VERSION STRLESS "1.5.3")
53 execute_process(COMMAND ${GIT_EXECUTABLE} update-index -q --refresh
54 WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}
57 ERROR_VARIABLE EXEC_ERR
58 OUTPUT_STRIP_TRAILING_WHITESPACE
61 # get the full hash of the current HEAD
62 execute_process(COMMAND ${GIT_EXECUTABLE} rev-parse HEAD
63 WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}
64 OUTPUT_VARIABLE HEAD_HASH
65 ERROR_VARIABLE EXEC_ERR
66 OUTPUT_STRIP_TRAILING_WHITESPACE
68 set(GMX_GIT_HEAD_HASH ${HEAD_HASH})
69 # extract the shortened hash (7 char)
70 execute_process(COMMAND ${GIT_EXECUTABLE} rev-parse --short HEAD
71 WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}
72 OUTPUT_VARIABLE HEAD_HASH_SHORT
73 ERROR_VARIABLE EXEC_ERR
74 OUTPUT_STRIP_TRAILING_WHITESPACE
77 # if there are local uncommitted changes, the build gets labeled "dirty"
78 execute_process(COMMAND ${GIT_EXECUTABLE} diff-index --name-only HEAD
79 WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}
80 OUTPUT_VARIABLE SRC_LOCAL_CHANGES
81 ERROR_VARIABLE EXEC_ERR
82 OUTPUT_STRIP_TRAILING_WHITESPACE
84 if(NOT "${SRC_LOCAL_CHANGES}" STREQUAL "")
85 set(DIRTY_STR "-dirty")
86 set(GMX_GIT_HEAD_HASH "${GMX_GIT_HEAD_HASH} (dirty)")
89 # get the date of the HEAD commit
90 execute_process(COMMAND ${GIT_EXECUTABLE} rev-list -n1 "--pretty=format:%ci" HEAD
91 WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}
92 OUTPUT_VARIABLE HEAD_DATE
93 ERROR_VARIABLE EXEC_ERR
94 OUTPUT_STRIP_TRAILING_WHITESPACE
96 string(REGEX REPLACE "\n| " ";" HEAD_DATE "${HEAD_DATE}")
97 list(GET HEAD_DATE 2 HEAD_DATE)
98 string(REGEX REPLACE "-" "" HEAD_DATE "${HEAD_DATE}")
100 # compile the version string suffix
101 set(VERSION_STR_SUFFIX "${HEAD_DATE}-${HEAD_HASH_SHORT}${DIRTY_STR}")
103 # find the names of remotes that are located on the official gromacs
105 execute_process(COMMAND ${GIT_EXECUTABLE} config --get-regexp
106 "remote\\..*\\.url" "\\.gromacs\\.org[:/].*gromacs(\\.git)?$"
107 WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}
108 OUTPUT_VARIABLE GMX_REMOTES
109 ERROR_VARIABLE EXEC_ERR
110 OUTPUT_STRIP_TRAILING_WHITESPACE
112 # if there are remotes from the gromacs git servers, try to find ancestor
113 # commits of the current HEAD from this remote;
114 # otherwise, label the build "unknown"
115 if("${GMX_REMOTES}" STREQUAL "")
116 set(VERSION_STR_SUFFIX "${VERSION_STR_SUFFIX}-unknown")
117 set(GMX_GIT_REMOTE_HASH "unknown")
119 string(REPLACE "\n" ";" GMX_REMOTES ${GMX_REMOTES})
120 # construct a command pipeline that produces a reverse-time-ordered
121 # list of commits and their annotated names in GMX_REMOTES
122 # the max-count limit is there to put an upper bound on the execution time
123 set(BASEREVCOMMAND "COMMAND ${GIT_EXECUTABLE} rev-list --max-count=1000 HEAD")
124 foreach(REMOTE ${GMX_REMOTES})
125 string(REGEX REPLACE "remote\\.(.*)\\.url.*" "\\1" REMOTE ${REMOTE})
126 set(BASEREVCOMMAND "${BASEREVCOMMAND} COMMAND ${GIT_EXECUTABLE} name-rev --stdin --refs=refs/remotes/${REMOTE}/*")
128 # this is necessary for CMake to properly split the variable into
129 # parameters for execute_process().
130 string(REPLACE " " ";" BASEREVCOMMAND ${BASEREVCOMMAND})
131 # find the first ancestor in the list provided by rev-list (not
132 # necessarily the last though) which is in GMX_REMOTE, extract the
133 # hash and the number of commits HEAD is ahead with
134 execute_process(${BASEREVCOMMAND}
135 WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}
136 OUTPUT_VARIABLE ANCESTOR_LIST
138 string(REGEX REPLACE "\n" ";" ANCESTOR_LIST "${ANCESTOR_LIST}")
141 set(GMX_GIT_REMOTE_HASH "")
142 foreach(ANCESTOR ${ANCESTOR_LIST})
143 string(REPLACE "\n" "" HASH_AND_REVNAMES "${ANCESTOR}")
144 string(REPLACE " " ";" HASH_AND_REVNAMES "${HASH_AND_REVNAMES}")
145 list(LENGTH HASH_AND_REVNAMES COUNT)
146 # stop and set the hash if we have a hit, otherwise loop and count
147 # how far ahead is the local repo
149 LIST(GET HASH_AND_REVNAMES 0 GMX_GIT_REMOTE_HASH)
152 math(EXPR AHEAD ${AHEAD}+1)
154 # mark the build "local" if didn't find any commits that are from
155 # remotes/${GMX_REMOTE}/*
156 if("${GMX_GIT_REMOTE_HASH}" STREQUAL "")
157 set(GMX_GIT_REMOTE_HASH "unknown")
158 set(VERSION_STR_SUFFIX "${VERSION_STR_SUFFIX}-local")
159 # don't print the remote hash if there are no local commits
160 elseif("${GMX_GIT_REMOTE_HASH}" STREQUAL "${HEAD_HASH}")
161 set(GMX_GIT_REMOTE_HASH "")
163 set(GMX_GIT_REMOTE_HASH "${GMX_GIT_REMOTE_HASH} (${AHEAD} newer local commits)")
167 # compile final version string, if there is already a -dev suffix in VER
168 # remove everything after this and replace it with the generated suffix
169 string(REGEX REPLACE "(.*)-dev.*" "\\1" VER "${VER}")
170 set(GMX_PROJECT_VERSION_STR "${VER}-dev-${VERSION_STR_SUFFIX}")
172 # the version has to be defined - if not we're not using version.h/.c and set
173 # the GIT related information to "unknown"
174 message(WARNING "Source tree seems to be a repository, but no compatible git is available, using hard-coded version string")
175 set(GMX_PROJECT_VERSION_STR "${PROJECT_VERSION}")
176 set(GMX_GIT_HEAD_HASH "unknown")
177 set(GMX_GIT_REMOTE_HASH "unknown")
180 # if we're generating cache variables set these
181 # otherwise it's assumed that it's called in script mode to generate version.c
182 if(GEN_VERSION_INFO_INTERNAL)
183 set(GMX_PROJECT_VERSION_STR ${GMX_PROJECT_VERSION_STR}
184 CACHE STRING "Gromacs version string" FORCE)
185 set(GMX_GIT_HEAD_HASH ${GMX_GIT_HEAD_HASH}${DIRTY_STR}
186 CACHE STRING "Current git HEAD commit object" FORCE)
187 set(GMX_GIT_REMOTE_HASH ${GMX_GIT_REMOTE_HASH}
188 CACHE STRING "Commmit object of the nearest ancestor present in the Gromacs git repository" FORCE)
189 mark_as_advanced(GMX_GIT_HEAD_HASH GMX_GIT_REMOTE_HASH)
191 if("${VERSION_C_CMAKEIN}" STREQUAL "")
192 message(FATAL_ERROR "Missing input parameter VERSION_C_CMAKEIN!")
194 if("${VERSION_C_OUT}" STREQUAL "")
195 message(FATAL_ERROR "Missing input parameter VERSION_C_OUT!")
198 configure_file(${VERSION_C_CMAKEIN} ${VERSION_C_OUT})