8db1e10400d878cb679d0a653d8e271a7abe02e8
[alexxy/gromacs.git] / cmake / gmxGenerateVersionInfo.cmake
1 #
2 # This file is part of the GROMACS molecular simulation package.
3 #
4 # Copyright (c) 2010,2011,2012,2013,2014, by the GROMACS development team, led by
5 # Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
6 # and including many others, as listed in the AUTHORS file in the
7 # top-level source directory and at http://www.gromacs.org.
8 #
9 # GROMACS is free software; you can redistribute it and/or
10 # modify it under the terms of the GNU Lesser General Public License
11 # as published by the Free Software Foundation; either version 2.1
12 # of the License, or (at your option) any later version.
13 #
14 # GROMACS is distributed in the hope that it will be useful,
15 # but WITHOUT ANY WARRANTY; without even the implied warranty of
16 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17 # Lesser General Public License for more details.
18 #
19 # You should have received a copy of the GNU Lesser General Public
20 # License along with GROMACS; if not, see
21 # http://www.gnu.org/licenses, or write to the Free Software Foundation,
22 # Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
23 #
24 # If you want to redistribute modifications to GROMACS, please
25 # consider that scientific software is very special. Version
26 # control is crucial - bugs must be traceable. We will be happy to
27 # consider code for inclusion in the official distribution, but
28 # derived work must not be called official GROMACS. Details are found
29 # in the README & COPYING files - if they are missing, get the
30 # official version at http://www.gromacs.org.
31 #
32 # To help us fund GROMACS development, we humbly ask that you cite
33 # the research papers on the package. Check out http://www.gromacs.org.
34
35 # Generate Gromacs development build version information.
36
37 # This script generates version information for a build from a development
38 # source tree based on git repository information.
39 # It is assumed that by default the script is run in cmake script mode.
40 # If *not* called in script mode but used in generating cache variables,
41 # GEN_VERSION_INFO_INTERNAL has to be set ON.
42 #
43 # The following variables have to be previously defined:
44 # GIT_EXECUTABLE         - path to git binary (must be >=1.5.3)
45 # PROJECT_VERSION        - hard-coded version string (generated info is appended)
46 # PROJECT_SOURCE_DIR     - top level source directory (which has to be in git)
47 # VERSION_CMAKEIN        - path to an input template file
48 # VERSION_OUT            - path to the output file
49 # VERSION_NO_REMOTE_HASH - if set, GMX_GIT_REMOTE_HASH is not generated
50 #
51 # Output:
52 # i)  Script mode: VERSION_OUT is configured from the input VERSION_CMAKEIN
53 # using the variables listed below.
54 # ii) Cache variable mode: the variables below are set in cache.
55 #
56 # GMX_PROJECT_VERSION_STR   - version string
57 # GMX_GIT_HEAD_HASH         - git hash of current local HEAD
58 # GMX_GIT_REMOTE_HASH       - git hash of the first ancestor commit from the
59 #                             main Gromacs repository
60 #
61 # Szilard Pall (pszilard@cbr.su.se)
62 # Teemu Murtola (teemu.murtola@gmail.com)
63
64 if("${PROJECT_VERSION}" STREQUAL "")
65     message(FATAL_ERROR "PROJECT_VERSION undefined!")
66 endif()
67
68 # if we're generating variables for cache unset the variables
69 if(GEN_VERSION_INFO_INTERNAL)
70     set(GMX_PROJECT_VERSION_STR)
71     set(GMX_GIT_HEAD_HASH)
72     set(GMX_GIT_REMOTE_HASH)
73 endif()
74
75 # bail if the source tree is not in a git repository
76 if(NOT EXISTS "${PROJECT_SOURCE_DIR}/.git")
77     message(FATAL_ERROR "Project source directory ${PROJECT_SOURCE_DIR} not in git")
78 endif()
79
80 # If git executable exists, build the development version string.
81 if(EXISTS "${GIT_EXECUTABLE}")
82     # refresh git index
83     execute_process(COMMAND ${GIT_EXECUTABLE} update-index -q --refresh
84         WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}
85         TIMEOUT 5
86         OUTPUT_QUIET
87         ERROR_VARIABLE EXEC_ERR
88         OUTPUT_STRIP_TRAILING_WHITESPACE
89     )
90
91     # get the full hash of the current HEAD
92     execute_process(COMMAND ${GIT_EXECUTABLE} rev-parse HEAD
93         WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}
94         OUTPUT_VARIABLE HEAD_HASH
95         ERROR_VARIABLE EXEC_ERR
96         OUTPUT_STRIP_TRAILING_WHITESPACE
97     )
98     set(GMX_GIT_HEAD_HASH ${HEAD_HASH})
99     # extract the shortened hash (7 char)
100     execute_process(COMMAND ${GIT_EXECUTABLE} rev-parse --short HEAD
101         WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}
102         OUTPUT_VARIABLE HEAD_HASH_SHORT
103         ERROR_VARIABLE EXEC_ERR
104         OUTPUT_STRIP_TRAILING_WHITESPACE
105     )
106
107     # if there are local uncommitted changes, the build gets labeled "dirty"
108     execute_process(COMMAND ${GIT_EXECUTABLE} diff-index --name-only HEAD
109         WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}
110         OUTPUT_VARIABLE SRC_LOCAL_CHANGES
111         ERROR_VARIABLE EXEC_ERR
112         OUTPUT_STRIP_TRAILING_WHITESPACE
113     )
114     if(NOT "${SRC_LOCAL_CHANGES}" STREQUAL "")
115         set(DIRTY_STR "-dirty")
116         set(GMX_GIT_HEAD_HASH "${GMX_GIT_HEAD_HASH} (dirty)")
117     endif()
118
119     # get the date of the HEAD commit
120     execute_process(COMMAND ${GIT_EXECUTABLE} rev-list -n1 "--pretty=format:%ci" HEAD
121         WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}
122         OUTPUT_VARIABLE HEAD_DATE
123         ERROR_VARIABLE EXEC_ERR
124         OUTPUT_STRIP_TRAILING_WHITESPACE
125     )
126     string(REGEX REPLACE "\n| " ";" HEAD_DATE "${HEAD_DATE}")
127     list(GET HEAD_DATE 2 HEAD_DATE)
128     string(REGEX REPLACE "-" "" HEAD_DATE "${HEAD_DATE}")
129
130     # compile the version string suffix
131     set(VERSION_STR_SUFFIX "${HEAD_DATE}-${HEAD_HASH_SHORT}${DIRTY_STR}")
132
133     if (DEFINED VERSION_NO_REMOTE_HASH)
134         set(GMX_REMOTES "")
135     else()
136         # find the names of remotes that are located on the official gromacs
137         # git/gerrit servers
138         execute_process(COMMAND ${GIT_EXECUTABLE} config --get-regexp
139                         "remote\\..*\\.url" "\\.gromacs\\.org[:/].*gromacs(\\.git)?$"
140             WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}
141             OUTPUT_VARIABLE GMX_REMOTES
142             ERROR_VARIABLE EXEC_ERR
143             OUTPUT_STRIP_TRAILING_WHITESPACE
144         )
145     endif()
146
147     # if there are remotes from the gromacs git servers, try to find ancestor
148     # commits of the current HEAD from this remote;
149     # otherwise, label the build "unknown"
150     if("${GMX_REMOTES}" STREQUAL "")
151         if (NOT DEFINED VERSION_NO_REMOTE_HASH)
152             set(VERSION_STR_SUFFIX "${VERSION_STR_SUFFIX}-unknown")
153         endif()
154         set(GMX_GIT_REMOTE_HASH "unknown")
155     else()
156         string(REPLACE "\n" ";" GMX_REMOTES ${GMX_REMOTES})
157         # construct a command pipeline that produces a reverse-time-ordered
158         # list of commits and their annotated names in GMX_REMOTES
159         # the max-count limit is there to put an upper bound on the execution time
160         set(BASEREVCOMMAND "COMMAND ${GIT_EXECUTABLE} rev-list --max-count=1000 HEAD")
161         foreach(REMOTE ${GMX_REMOTES})
162             string(REGEX REPLACE "remote\\.(.*)\\.url.*" "\\1" REMOTE ${REMOTE})
163             set(BASEREVCOMMAND "${BASEREVCOMMAND} COMMAND ${GIT_EXECUTABLE} name-rev --stdin --refs=refs/remotes/${REMOTE}/*")
164         endforeach(REMOTE)
165         # this is necessary for CMake to properly split the variable into
166         # parameters for execute_process().
167         string(REPLACE " " ";" BASEREVCOMMAND ${BASEREVCOMMAND})
168         # find the first ancestor in the list provided by rev-list (not
169         # necessarily the last though) which is in GMX_REMOTE, extract the
170         # hash and the number of commits HEAD is ahead with
171         execute_process(${BASEREVCOMMAND}
172             WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}
173             OUTPUT_VARIABLE ANCESTOR_LIST
174         )
175         string(REGEX REPLACE "\n" ";" ANCESTOR_LIST "${ANCESTOR_LIST}")
176
177         set(AHEAD 0)
178         set(GMX_GIT_REMOTE_HASH "")
179         foreach(ANCESTOR ${ANCESTOR_LIST})
180             string(REPLACE "\n" "" HASH_AND_REVNAMES "${ANCESTOR}")
181             string(REPLACE " " ";" HASH_AND_REVNAMES "${HASH_AND_REVNAMES}")
182             list(LENGTH HASH_AND_REVNAMES COUNT)
183             # stop and set the hash if we have a hit, otherwise loop and count
184             # how far ahead is the local repo
185             if(COUNT GREATER 1)
186                 LIST(GET HASH_AND_REVNAMES 0 GMX_GIT_REMOTE_HASH)
187                 break()
188             endif()
189             math(EXPR AHEAD ${AHEAD}+1)
190         endforeach(ANCESTOR)
191         # mark the build "local" if didn't find any commits that are from
192         # remotes/${GMX_REMOTE}/*
193         if("${GMX_GIT_REMOTE_HASH}" STREQUAL "")
194             set(GMX_GIT_REMOTE_HASH "unknown")
195             set(VERSION_STR_SUFFIX "${VERSION_STR_SUFFIX}-local")
196         # don't print the remote hash if there are no local commits
197         elseif("${GMX_GIT_REMOTE_HASH}" STREQUAL "${HEAD_HASH}")
198             set(GMX_GIT_REMOTE_HASH "")
199         else()
200             set(GMX_GIT_REMOTE_HASH "${GMX_GIT_REMOTE_HASH} (${AHEAD} newer local commits)")
201         endif()
202     endif()
203
204     # compile final version string
205     set(GMX_PROJECT_VERSION_STR "${PROJECT_VERSION}-${VERSION_STR_SUFFIX}")
206 else()
207     # the version has to be defined
208     # set the GIT related information to "unknown"
209     message(WARNING "Source tree seems to be a repository, but no compatible git is available, using hard-coded version string")
210     set(GMX_PROJECT_VERSION_STR "${PROJECT_VERSION}")
211     set(GMX_GIT_HEAD_HASH "unknown")
212     set(GMX_GIT_REMOTE_HASH "unknown")
213 endif()
214
215 # if we're generating cache variables set these
216 # otherwise it's assumed that it's called in script mode to generate a file
217 if(GEN_VERSION_INFO_INTERNAL)
218     set(GMX_PROJECT_VERSION_STR ${GMX_PROJECT_VERSION_STR}
219         CACHE STRING "Gromacs version string" FORCE)
220     set(GMX_GIT_HEAD_HASH ${GMX_GIT_HEAD_HASH}${DIRTY_STR}
221         CACHE STRING "Current git HEAD commit object" FORCE)
222     set(GMX_GIT_REMOTE_HASH ${GMX_GIT_REMOTE_HASH}
223         CACHE STRING "Commmit object of the nearest ancestor present in the Gromacs git repository" FORCE)
224     mark_as_advanced(GMX_GIT_HEAD_HASH GMX_GIT_REMOTE_HASH)
225 else()
226     if("${VERSION_CMAKEIN}" STREQUAL "")
227         message(FATAL_ERROR "Missing input parameter VERSION_CMAKEIN!")
228     endif()
229     if("${VERSION_OUT}" STREQUAL "")
230         message(FATAL_ERROR "Missing input parameter VERSION_OUT!")
231     endif()
232     # Generate the output file.
233     configure_file(${VERSION_CMAKEIN} ${VERSION_OUT})
234 endif()