Merge branch 'gerrit/release-4-5-patches' into release-4-6
[alexxy/gromacs.git] / cmake / gmxGenerateVersionInfo.cmake
index 165a941d34c7f37370808b3259686f6f623d3296..754806e2c9ede36bd81bc123c412bade2c59961a 100644 (file)
@@ -1,54 +1,54 @@
 # Generate Gromacs development build version information.
 #
-# The script generates version information for a build from a development 
-# source tree based on git repository information. 
+# The script generates version information for a build from a development
+# source tree based on git repository information.
 # It is assumed that by default the script is run in cmake script mode.
-# If *not* called in script mode but used in generating cache variables,  
+# If *not* called in script mode but used in generating cache variables,
 # GEN_VERSION_INFO_INTERNAL has to be set ON.
 #
-# The following variables have to be previously defined: 
+# The following variables have to be previously defined:
 # GIT_EXECUTABLE        - path to git binary
 # GIT_VERSION           - git version (if not defined it's assumed that >=1.5.3)
-# PROJECT_VERSION       - hard-coded version string, should have the following structure: 
-#                       VERSION[-dev-SUFFIX] where the VERSION can have any form and the suffix 
+# PROJECT_VERSION       - hard-coded version string, should have the following structure:
+#                       VERSION[-dev-SUFFIX] where the VERSION can have any form and the suffix
 #                       is optional but should start with -dev
 # PROJECT_SOURCE_DIR    - top level source directory (which has to be in git)
-# VERSION_C_CMAKEIN     - path to the version.c.cmakein file 
+# VERSION_C_CMAKEIN     - path to the version.c.cmakein file
 # VERSION_C_OUT         - path to the version.c output file
 #
-# Output: 
-# i)  Script mode: version.c configured from the input version.c.cmakein using 
-# the variables listed below. 
+# Output:
+# i)  Script mode: version.c configured from the input version.c.cmakein using
+# the variables listed below.
 # ii) Cache variable mode: the varables below are set in cache.
 #
-# GMX_PROJECT_VERSION_STR   - version string 
-# GMX_GIT_HEAD_HASH         - git hash of current local HEAD 
-# GMX_GIT_REMOTE_HASH       - git hash of the first ancestor commit from the 
-#                             main Gromacs repository 
-# 
-# Szilard Pall (pszilard@cbr.su.se) 
+# GMX_PROJECT_VERSION_STR   - version string
+# GMX_GIT_HEAD_HASH         - git hash of current local HEAD
+# GMX_GIT_REMOTE_HASH       - git hash of the first ancestor commit from the
+#                             main Gromacs repository
+#
+# Szilard Pall (pszilard@cbr.su.se)
 
-if(${PROJECT_VERSION} STREQUAL "")
+if("${PROJECT_VERSION}" STREQUAL "")
     message(FATAL_ERROR "PROJECT_VERSION undefined!")
 endif()
 set(VER ${PROJECT_VERSION})
 
-# if we're generating variables for cache unset the variables 
+# if we're generating variables for cache unset the variables
 if(GEN_VERSION_INFO_INTERNAL)
     set(GMX_PROJECT_VERSION_STR)
     set(GMX_GIT_HEAD_HASH)
     set(GMX_GIT_REMOTE_HASH)
 endif()
 
-# bail if the source tree is not in a git repository  
+# bail if the source tree is not in a git repository
 if(NOT EXISTS "${PROJECT_SOURCE_DIR}/.git")
-    message(FATAL_ERROR " Project source directory ${PROJECT_SOURCE_DIR} not in git")
+    message(FATAL_ERROR "Project source directory ${PROJECT_SOURCE_DIR} not in git")
 endif()
 
-# if git executable xists and it's compatible version
-# build the development version string 
+# if git executable exists and it's compatible version
+# build the development version string
 # this should at some point become VERSION_LESS
-if(EXISTS ${GIT_EXECUTABLE} AND NOT ${GIT_VERSION} STRLESS "1.5.1")
+if(EXISTS "${GIT_EXECUTABLE}" AND NOT GIT_VERSION STRLESS "1.5.3")
     # refresh git index 
     execute_process(COMMAND ${GIT_EXECUTABLE} update-index -q --refresh
         WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}
@@ -56,17 +56,23 @@ if(EXISTS ${GIT_EXECUTABLE} AND NOT ${GIT_VERSION} STRLESS "1.5.1")
         OUTPUT_QUIET
         ERROR_VARIABLE EXEC_ERR
         OUTPUT_STRIP_TRAILING_WHITESPACE
-    ) 
+    )
 
-   # get the full hash of the current HEAD 
+    # get the full hash of the current HEAD
     execute_process(COMMAND ${GIT_EXECUTABLE} rev-parse HEAD
         WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}
-        OUTPUT_VARIABLE GMX_GIT_HEAD_HASH
+        OUTPUT_VARIABLE HEAD_HASH
         ERROR_VARIABLE EXEC_ERR
         OUTPUT_STRIP_TRAILING_WHITESPACE
     )
+    set(GMX_GIT_HEAD_HASH ${HEAD_HASH})
     # extract the shortened hash (7 char)
-    string(SUBSTRING ${GMX_GIT_HEAD_HASH} 0 5 HEAD_HASH_SHORT) 
+    execute_process(COMMAND ${GIT_EXECUTABLE} rev-parse --short HEAD
+        WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}
+        OUTPUT_VARIABLE HEAD_HASH_SHORT
+        ERROR_VARIABLE EXEC_ERR
+        OUTPUT_STRIP_TRAILING_WHITESPACE
+    )
 
     # if there are local uncommitted changes, the build gets labeled "dirty"
     execute_process(COMMAND ${GIT_EXECUTABLE} diff-index --name-only HEAD
@@ -74,141 +80,120 @@ if(EXISTS ${GIT_EXECUTABLE} AND NOT ${GIT_VERSION} STRLESS "1.5.1")
         OUTPUT_VARIABLE SRC_LOCAL_CHANGES
         ERROR_VARIABLE EXEC_ERR
         OUTPUT_STRIP_TRAILING_WHITESPACE
-    )   
-    if(NOT ${SRC_LOCAL_CHANGES} STREQUAL "")
+    )
+    if(NOT "${SRC_LOCAL_CHANGES}" STREQUAL "")
         set(DIRTY_STR "-dirty")
         set(GMX_GIT_HEAD_HASH "${GMX_GIT_HEAD_HASH} (dirty)")
     endif()
 
-    # if git is older then 1.5.3 we need to extract the RFC2822 style date 
-    # and massage it, otherwise the ISO 8601 format is more trusworthy
-    # this should at some point become VERSION_LESS
-    if (NOT GIT_VERSION STREQUAL "" AND GIT_VERSION STRLESS "1.5.3")
-        execute_process(COMMAND ${GIT_EXECUTABLE} rev-list -n1 "--pretty=format:%cD" HEAD
-            WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}
-            OUTPUT_VARIABLE HEAD_DATE
-            ERROR_VARIABLE EXEC_ERR
-            OUTPUT_STRIP_TRAILING_WHITESPACE
-        )
-        # date format: day, D Mmm YYYY  -> YYYY-MM-DD
-        # if the day is single sigit need to insert a "0"
-        string(REGEX REPLACE ".*(, )([0-9] )(.*)" "\\10\\2\\3" 
-            HEAD_DATE ${HEAD_DATE})
-        string(REGEX REPLACE ".*, ([0-9][0-9]) ([A-Z][a-z]+) ([0-9]+).*" "\\3\\2\\1" 
-            HEAD_DATE ${HEAD_DATE})
-        string(TOUPPER ${HEAD_DATE} HEAD_DATE)
-        string(REGEX REPLACE "JAN" "01" HEAD_DATE ${HEAD_DATE})
-        string(REGEX REPLACE "FEB" "02" HEAD_DATE ${HEAD_DATE})
-        string(REGEX REPLACE "MAR" "03" HEAD_DATE ${HEAD_DATE})
-        string(REGEX REPLACE "APR" "04" HEAD_DATE ${HEAD_DATE})
-        string(REGEX REPLACE "MAY" "05" HEAD_DATE ${HEAD_DATE})
-        string(REGEX REPLACE "JUN" "06" HEAD_DATE ${HEAD_DATE})
-        string(REGEX REPLACE "JUL" "07" HEAD_DATE ${HEAD_DATE})
-        string(REGEX REPLACE "AUG" "08" HEAD_DATE ${HEAD_DATE})
-        string(REGEX REPLACE "SEP" "09" HEAD_DATE ${HEAD_DATE})
-        string(REGEX REPLACE "OCT" "10" HEAD_DATE ${HEAD_DATE})
-        string(REGEX REPLACE "NOV" "11" HEAD_DATE ${HEAD_DATE})
-        string(REGEX REPLACE "DEC" "12" HEAD_DATE ${HEAD_DATE})
-    else()
-        # get the date of the HEAD commit
-        execute_process(COMMAND ${GIT_EXECUTABLE} rev-list -n1 "--pretty=format:%ci" HEAD
-            WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}
-            OUTPUT_VARIABLE HEAD_DATE
-            ERROR_VARIABLE EXEC_ERR
-            OUTPUT_STRIP_TRAILING_WHITESPACE
-        )
-        string(REGEX REPLACE "\n| " ";" HEAD_DATE ${HEAD_DATE})
-        list(GET HEAD_DATE 2 HEAD_DATE)
-        string(REGEX REPLACE "-" "" HEAD_DATE ${HEAD_DATE})
-    endif()
+    # get the date of the HEAD commit
+    execute_process(COMMAND ${GIT_EXECUTABLE} rev-list -n1 "--pretty=format:%ci" HEAD
+        WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}
+        OUTPUT_VARIABLE HEAD_DATE
+        ERROR_VARIABLE EXEC_ERR
+        OUTPUT_STRIP_TRAILING_WHITESPACE
+    )
+    string(REGEX REPLACE "\n| " ";" HEAD_DATE "${HEAD_DATE}")
+    list(GET HEAD_DATE 2 HEAD_DATE)
+    string(REGEX REPLACE "-" "" HEAD_DATE "${HEAD_DATE}")
 
     # compile the version string suffix
-    set(VERSION_STR_SUFFIX "${HEAD_DATE}-${HEAD_HASH_SHORT}${DIRTY_STR}") 
-    
-    # find the name of the remote which is located on the official gromacs git server
-    execute_process(COMMAND ${GIT_EXECUTABLE} config --get-regexp 
-                    "remote\\..*\\.url" "git\\.gromacs\\.org[:|/]gromacs"
+    set(VERSION_STR_SUFFIX "${HEAD_DATE}-${HEAD_HASH_SHORT}${DIRTY_STR}")
+
+    # find the names of remotes that are located on the official gromacs
+    # git/gerrit servers
+    execute_process(COMMAND ${GIT_EXECUTABLE} config --get-regexp
+                    "remote\\..*\\.url" "\\.gromacs\\.org[:/].*gromacs(\\.git)?$"
         WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}
-        OUTPUT_VARIABLE GMX_REMOTE
+        OUTPUT_VARIABLE GMX_REMOTES
         ERROR_VARIABLE EXEC_ERR
         OUTPUT_STRIP_TRAILING_WHITESPACE
     )
-    # if there's a remote from the gromacs git, try to find ancestor commits of the 
-    # current HEAD from this remote; otherwise, label the buld "unknown"
-    if(GMX_REMOTE STREQUAL "")
+    # if there are remotes from the gromacs git servers, try to find ancestor
+    # commits of the current HEAD from this remote;
+    # otherwise, label the build "unknown"
+    if("${GMX_REMOTES}" STREQUAL "")
         set(VERSION_STR_SUFFIX "${VERSION_STR_SUFFIX}-unknown")
-        set(GMX_GIT_REMOTE_HASH "unknown")        
-    else()         
-        string(REGEX REPLACE "remote\\.(.*)\\.url.*" "\\1" GMX_REMOTE ${GMX_REMOTE})
-        # find the first ancestor in the list provided by rev-list (not 
-        # necessarily the last though) which is in GMX_REMOTE, extract the 
-        # hash and the number of commits HEAD is ahead with 
-        execute_process(COMMAND ${GIT_EXECUTABLE} rev-list --max-count=100 HEAD
+        set(GMX_GIT_REMOTE_HASH "unknown")
+    else()
+        string(REPLACE "\n" ";" GMX_REMOTES ${GMX_REMOTES})
+        # construct a command pipeline that produces a reverse-time-ordered
+        # list of commits and their annotated names in GMX_REMOTES
+        # the max-count limit is there to put an upper bound on the execution time
+        set(BASEREVCOMMAND "COMMAND ${GIT_EXECUTABLE} rev-list --max-count=1000 HEAD")
+        foreach(REMOTE ${GMX_REMOTES})
+            string(REGEX REPLACE "remote\\.(.*)\\.url.*" "\\1" REMOTE ${REMOTE})
+            set(BASEREVCOMMAND "${BASEREVCOMMAND} COMMAND ${GIT_EXECUTABLE} name-rev --stdin --refs=refs/remotes/${REMOTE}/*")
+        endforeach(REMOTE)
+        # this is necessary for CMake to properly split the variable into
+        # parameters for execute_process().
+        string(REPLACE " " ";" BASEREVCOMMAND ${BASEREVCOMMAND})
+        # find the first ancestor in the list provided by rev-list (not
+        # necessarily the last though) which is in GMX_REMOTE, extract the
+        # hash and the number of commits HEAD is ahead with
+        execute_process(${BASEREVCOMMAND}
             WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}
             OUTPUT_VARIABLE ANCESTOR_LIST
         )
-        string(REGEX REPLACE "\n" ";" ANCESTOR_LIST ${ANCESTOR_LIST})
+        string(REGEX REPLACE "\n" ";" ANCESTOR_LIST "${ANCESTOR_LIST}")
 
         set(AHEAD 0)
         set(GMX_GIT_REMOTE_HASH "")
-        foreach(OBJ ${ANCESTOR_LIST})
-            execute_process(COMMAND ${GIT_EXECUTABLE} name-rev --refs=refs/remotes/${GMX_REMOTE}/* ${OBJ}
-                WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}
-                OUTPUT_VARIABLE HASH_AND_REVNAME
-                OUTPUT_STRIP_TRAILING_WHITESPACE
-            )
-            string(REGEX REPLACE "\n" "" HASH_AND_REVNAME ${HASH_AND_REVNAME})
-            string(REGEX REPLACE " " ";" HASH_AND_REVNAME ${HASH_AND_REVNAME})
-            list(GET HASH_AND_REVNAME 0 GMX_GIT_REMOTE_HASH) 
-            list(GET HASH_AND_REVNAME 1 REVNAME)
+        foreach(ANCESTOR ${ANCESTOR_LIST})
+            string(REPLACE "\n" "" HASH_AND_REVNAMES "${ANCESTOR}")
+            string(REPLACE " " ";" HASH_AND_REVNAMES "${HASH_AND_REVNAMES}")
+            list(LENGTH HASH_AND_REVNAMES COUNT)
             # stop and set the hash if we have a hit, otherwise loop and count
             # how far ahead is the local repo
-            if(${REVNAME} MATCHES "remotes/${GMX_REMOTE}/.*")
-                set(GMX_GIT_REMOTE_HASH 
-                        "${GMX_GIT_REMOTE_HASH} (${AHEAD} newer local commits)")
+            if(COUNT GREATER 1)
+                LIST(GET HASH_AND_REVNAMES 0 GMX_GIT_REMOTE_HASH)
                 break()
-            else()
-                math(EXPR AHEAD ${AHEAD}+1)
             endif()
-        endforeach(OBJ)
-        # mark the build "local" if didn't find any commits that are from 
+            math(EXPR AHEAD ${AHEAD}+1)
+        endforeach(ANCESTOR)
+        # mark the build "local" if didn't find any commits that are from
         # remotes/${GMX_REMOTE}/*
-        if(${GMX_GIT_REMOTE_HASH} STREQUAL "")
+        if("${GMX_GIT_REMOTE_HASH}" STREQUAL "")
             set(GMX_GIT_REMOTE_HASH "unknown")
-            set(VERSION_STR_SUFFIX "${VERSION_STR_SUFFIX}-local") 
+            set(VERSION_STR_SUFFIX "${VERSION_STR_SUFFIX}-local")
+        # don't print the remote hash if there are no local commits
+        elseif("${GMX_GIT_REMOTE_HASH}" STREQUAL "${HEAD_HASH}")
+            set(GMX_GIT_REMOTE_HASH "")
+        else()
+            set(GMX_GIT_REMOTE_HASH "${GMX_GIT_REMOTE_HASH} (${AHEAD} newer local commits)")
         endif()
     endif()
 
-    # compile final version string, if there is already a -dev suffix in VER 
+    # compile final version string, if there is already a -dev suffix in VER
     # remove everything after this and replace it with the generated suffix
-    string(REGEX REPLACE "(.*)-dev.*" "\\1" VER ${VER})
+    string(REGEX REPLACE "(.*)-dev.*" "\\1" VER "${VER}")
     set(GMX_PROJECT_VERSION_STR "${VER}-dev-${VERSION_STR_SUFFIX}")
 else()
-    # the version has to be defined - if not we're not using version.h/.c and set 
+    # the version has to be defined - if not we're not using version.h/.c and set
     # the GIT related information to "unknown"
-    message(WARNING " Source tree seems to be a repository, but no compatible git is available, using hard-coded version string")
+    message(WARNING "Source tree seems to be a repository, but no compatible git is available, using hard-coded version string")
     set(GMX_PROJECT_VERSION_STR "${PROJECT_VERSION}")
     set(GMX_GIT_HEAD_HASH "unknown")
     set(GMX_GIT_REMOTE_HASH "unknown")
 endif()
 
 # if we're generating cache variables set these
-# otherwise it's assumed that it's called in script mode to generate version.c 
+# otherwise it's assumed that it's called in script mode to generate version.c
 if(GEN_VERSION_INFO_INTERNAL)
-    set(GMX_PROJECT_VERSION_STR ${GMX_PROJECT_VERSION_STR} 
+    set(GMX_PROJECT_VERSION_STR ${GMX_PROJECT_VERSION_STR}
         CACHE STRING "Gromacs version string" FORCE)
-    set(GMX_GIT_HEAD_HASH ${GMX_GIT_HEAD_HASH}${DIRTY_STR}  
+    set(GMX_GIT_HEAD_HASH ${GMX_GIT_HEAD_HASH}${DIRTY_STR}
         CACHE STRING "Current git HEAD commit object" FORCE)
-    set(GMX_GIT_REMOTE_HASH ${GMX_GIT_REMOTE_HASH} 
+    set(GMX_GIT_REMOTE_HASH ${GMX_GIT_REMOTE_HASH}
         CACHE STRING "Commmit object of the nearest ancestor present in the Gromacs git repository" FORCE)
     mark_as_advanced(GMX_GIT_HEAD_HASH GMX_GIT_REMOTE_HASH)
 else()
-    if(${VERSION_C_CMAKEIN} STREQUAL "")
+    if("${VERSION_C_CMAKEIN}" STREQUAL "")
         message(FATAL_ERROR "Missing input parameter VERSION_C_CMAKEIN!")
     endif()
-    if(${VERSION_C_OUT} STREQUAL "")
+    if("${VERSION_C_OUT}" STREQUAL "")
         message(FATAL_ERROR "Missing input parameter VERSION_C_OUT!")
     endif()
     # generate version.c
-   configure_file(${VERSION_C_CMAKEIN} ${VERSION_C_OUT})    
+    configure_file(${VERSION_C_CMAKEIN} ${VERSION_C_OUT})
 endif()