Merge branch release-4-6 into master
[alexxy/gromacs.git] / cmake / gmxGenerateVersionInfo.cmake
1 # Generate Gromacs development build version information.
2 #
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.
8 #
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 (generated info is appended)
13 # PROJECT_SOURCE_DIR    - top level source directory (which has to be in git)
14 # VERSION_C_CMAKEIN     - path to the gitversion.c.cmakein file
15 # VERSION_C_OUT         - path to the gitversion.c output file
16 #
17 # Output:
18 # i)  Script mode: gitversion.c configured from the input gitversion.c.cmakein
19 # using the variables listed below.
20 # ii) Cache variable mode: the varables below are set in cache.
21 #
22 # GMX_PROJECT_VERSION_STR   - version string
23 # GMX_GIT_HEAD_HASH         - git hash of current local HEAD
24 # GMX_GIT_REMOTE_HASH       - git hash of the first ancestor commit from the
25 #                             main Gromacs repository
26 #
27 # Szilard Pall (pszilard@cbr.su.se)
28
29 if("${PROJECT_VERSION}" STREQUAL "")
30     message(FATAL_ERROR "PROJECT_VERSION undefined!")
31 endif()
32
33 # if we're generating variables for cache unset the variables
34 if(GEN_VERSION_INFO_INTERNAL)
35     set(GMX_PROJECT_VERSION_STR)
36     set(GMX_GIT_HEAD_HASH)
37     set(GMX_GIT_REMOTE_HASH)
38 endif()
39
40 # bail if the source tree is not in a git repository
41 if(NOT EXISTS "${PROJECT_SOURCE_DIR}/.git")
42     message(FATAL_ERROR "Project source directory ${PROJECT_SOURCE_DIR} not in git")
43 endif()
44
45 # if git executable exists and it's compatible version
46 # build the development version string
47 if(EXISTS "${GIT_EXECUTABLE}" AND NOT GIT_VERSION VERSION_LESS "1.5.3")
48     # refresh git index
49     execute_process(COMMAND ${GIT_EXECUTABLE} update-index -q --refresh
50         WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}
51         TIMEOUT 5
52         OUTPUT_QUIET
53         ERROR_VARIABLE EXEC_ERR
54         OUTPUT_STRIP_TRAILING_WHITESPACE
55     )
56
57     # get the full hash of the current HEAD
58     execute_process(COMMAND ${GIT_EXECUTABLE} rev-parse HEAD
59         WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}
60         OUTPUT_VARIABLE HEAD_HASH
61         ERROR_VARIABLE EXEC_ERR
62         OUTPUT_STRIP_TRAILING_WHITESPACE
63     )
64     set(GMX_GIT_HEAD_HASH ${HEAD_HASH})
65     # extract the shortened hash (7 char)
66     execute_process(COMMAND ${GIT_EXECUTABLE} rev-parse --short HEAD
67         WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}
68         OUTPUT_VARIABLE HEAD_HASH_SHORT
69         ERROR_VARIABLE EXEC_ERR
70         OUTPUT_STRIP_TRAILING_WHITESPACE
71     )
72
73     # if there are local uncommitted changes, the build gets labeled "dirty"
74     execute_process(COMMAND ${GIT_EXECUTABLE} diff-index --name-only HEAD
75         WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}
76         OUTPUT_VARIABLE SRC_LOCAL_CHANGES
77         ERROR_VARIABLE EXEC_ERR
78         OUTPUT_STRIP_TRAILING_WHITESPACE
79     )
80     if(NOT "${SRC_LOCAL_CHANGES}" STREQUAL "")
81         set(DIRTY_STR "-dirty")
82         set(GMX_GIT_HEAD_HASH "${GMX_GIT_HEAD_HASH} (dirty)")
83     endif()
84
85     # get the date of the HEAD commit
86     execute_process(COMMAND ${GIT_EXECUTABLE} rev-list -n1 "--pretty=format:%ci" HEAD
87         WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}
88         OUTPUT_VARIABLE HEAD_DATE
89         ERROR_VARIABLE EXEC_ERR
90         OUTPUT_STRIP_TRAILING_WHITESPACE
91     )
92     string(REGEX REPLACE "\n| " ";" HEAD_DATE "${HEAD_DATE}")
93     list(GET HEAD_DATE 2 HEAD_DATE)
94     string(REGEX REPLACE "-" "" HEAD_DATE "${HEAD_DATE}")
95
96     # compile the version string suffix
97     set(VERSION_STR_SUFFIX "${HEAD_DATE}-${HEAD_HASH_SHORT}${DIRTY_STR}")
98
99     # find the names of remotes that are located on the official gromacs
100     # git/gerrit servers
101     execute_process(COMMAND ${GIT_EXECUTABLE} config --get-regexp
102                     "remote\\..*\\.url" "\\.gromacs\\.org[:/].*gromacs(\\.git)?$"
103         WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}
104         OUTPUT_VARIABLE GMX_REMOTES
105         ERROR_VARIABLE EXEC_ERR
106         OUTPUT_STRIP_TRAILING_WHITESPACE
107     )
108     # if there are remotes from the gromacs git servers, try to find ancestor
109     # commits of the current HEAD from this remote;
110     # otherwise, label the build "unknown"
111     if("${GMX_REMOTES}" STREQUAL "")
112         set(VERSION_STR_SUFFIX "${VERSION_STR_SUFFIX}-unknown")
113         set(GMX_GIT_REMOTE_HASH "unknown")
114     else()
115         string(REPLACE "\n" ";" GMX_REMOTES ${GMX_REMOTES})
116         # construct a command pipeline that produces a reverse-time-ordered
117         # list of commits and their annotated names in GMX_REMOTES
118         # the max-count limit is there to put an upper bound on the execution time
119         set(BASEREVCOMMAND "COMMAND ${GIT_EXECUTABLE} rev-list --max-count=1000 HEAD")
120         foreach(REMOTE ${GMX_REMOTES})
121             string(REGEX REPLACE "remote\\.(.*)\\.url.*" "\\1" REMOTE ${REMOTE})
122             set(BASEREVCOMMAND "${BASEREVCOMMAND} COMMAND ${GIT_EXECUTABLE} name-rev --stdin --refs=refs/remotes/${REMOTE}/*")
123         endforeach(REMOTE)
124         # this is necessary for CMake to properly split the variable into
125         # parameters for execute_process().
126         string(REPLACE " " ";" BASEREVCOMMAND ${BASEREVCOMMAND})
127         # find the first ancestor in the list provided by rev-list (not
128         # necessarily the last though) which is in GMX_REMOTE, extract the
129         # hash and the number of commits HEAD is ahead with
130         execute_process(${BASEREVCOMMAND}
131             WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}
132             OUTPUT_VARIABLE ANCESTOR_LIST
133         )
134         string(REGEX REPLACE "\n" ";" ANCESTOR_LIST "${ANCESTOR_LIST}")
135
136         set(AHEAD 0)
137         set(GMX_GIT_REMOTE_HASH "")
138         foreach(ANCESTOR ${ANCESTOR_LIST})
139             string(REPLACE "\n" "" HASH_AND_REVNAMES "${ANCESTOR}")
140             string(REPLACE " " ";" HASH_AND_REVNAMES "${HASH_AND_REVNAMES}")
141             list(LENGTH HASH_AND_REVNAMES COUNT)
142             # stop and set the hash if we have a hit, otherwise loop and count
143             # how far ahead is the local repo
144             if(COUNT GREATER 1)
145                 LIST(GET HASH_AND_REVNAMES 0 GMX_GIT_REMOTE_HASH)
146                 break()
147             endif()
148             math(EXPR AHEAD ${AHEAD}+1)
149         endforeach(ANCESTOR)
150         # mark the build "local" if didn't find any commits that are from
151         # remotes/${GMX_REMOTE}/*
152         if("${GMX_GIT_REMOTE_HASH}" STREQUAL "")
153             set(GMX_GIT_REMOTE_HASH "unknown")
154             set(VERSION_STR_SUFFIX "${VERSION_STR_SUFFIX}-local")
155         # don't print the remote hash if there are no local commits
156         elseif("${GMX_GIT_REMOTE_HASH}" STREQUAL "${HEAD_HASH}")
157             set(GMX_GIT_REMOTE_HASH "")
158         else()
159             set(GMX_GIT_REMOTE_HASH "${GMX_GIT_REMOTE_HASH} (${AHEAD} newer local commits)")
160         endif()
161     endif()
162
163     # compile final version string
164     set(GMX_PROJECT_VERSION_STR "${PROJECT_VERSION}-${VERSION_STR_SUFFIX}")
165 else()
166     # the version has to be defined - if not we're not using gitversion.h/.c;
167     # set the GIT related information to "unknown"
168     message(WARNING "Source tree seems to be a repository, but no compatible git is available, using hard-coded version string")
169     set(GMX_PROJECT_VERSION_STR "${PROJECT_VERSION}")
170     set(GMX_GIT_HEAD_HASH "unknown")
171     set(GMX_GIT_REMOTE_HASH "unknown")
172 endif()
173
174 # if we're generating cache variables set these
175 # otherwise it's assumed that it's called in script mode to generate gitversion.c
176 if(GEN_VERSION_INFO_INTERNAL)
177     set(GMX_PROJECT_VERSION_STR ${GMX_PROJECT_VERSION_STR}
178         CACHE STRING "Gromacs version string" FORCE)
179     set(GMX_GIT_HEAD_HASH ${GMX_GIT_HEAD_HASH}${DIRTY_STR}
180         CACHE STRING "Current git HEAD commit object" FORCE)
181     set(GMX_GIT_REMOTE_HASH ${GMX_GIT_REMOTE_HASH}
182         CACHE STRING "Commmit object of the nearest ancestor present in the Gromacs git repository" FORCE)
183     mark_as_advanced(GMX_GIT_HEAD_HASH GMX_GIT_REMOTE_HASH)
184 else()
185     if("${VERSION_C_CMAKEIN}" STREQUAL "")
186         message(FATAL_ERROR "Missing input parameter VERSION_C_CMAKEIN!")
187     endif()
188     if("${VERSION_C_OUT}" STREQUAL "")
189         message(FATAL_ERROR "Missing input parameter VERSION_C_OUT!")
190     endif()
191     # generate gitversion.c
192     configure_file(${VERSION_C_CMAKEIN} ${VERSION_C_OUT})
193 endif()