title: Cmake Regenerate on variable change
body: <p>Y Part:</p>
  
  <p>I've got a CMake macro that runs an external program and creates project specific variables. What I'd like to have CMake do is to run this script on every build, check if the variables have changed, and iff they have (to prevent full project rebuild when it has not), regenerate a CONFIGURE_FILE header...I'm not really sure how to do this though. (Add custom target rebuilds the header file every time and can't call macros, same with Add Custom Command).</p>
  
  <p>X part:</p>
  
  <p>So I've written the following script for pulling hg version information for use in a c++ project:</p>
  
  <pre><code>macro (ReadProjectRevisionStatus)
  exec_program(hg ${PROJECT_SOURCE_DIR} ARGS paths OUTPUT_VARIABLE ${PROJECT_NAME}_HGPATHS)
  message(STATUS "${PROJECT_NAME}_HGPATHS=${${PROJECT_NAME}_HGPATHS}}")
  if (NOT(${PROJECT_NAME}_HGPATHS STREQUAL ""))
  
  string(REPLACE "\n" ";" ${PROJECT_NAME}_HGPATHS ${${PROJECT_NAME}_HGPATHS})
  
  foreach(HGPATH ${${PROJECT_NAME}_HGPATHS})
  string(SUBSTRING "${HGPATH}" 0 10 HGPATHSTART)
  if (HGPATHSTART MATCHES "default = ")
  string(LENGTH "${HGPATH}" HGPATHLENGTH)
  math(EXPR HGSUBLEN "${HGPATHLENGTH}-10")
  string(SUBSTRING "${HGPATH}" 10 ${HGSUBLEN} ${PROJECT_NAME}_HGREMOTEDIR)
  endif()
  endforeach()
  
  endif()
  
  if (NOT ${PROJECT_NAME}_HGREMOTEDIR)
  message(WARNING "No remote repository set. Will use current direcoty for build number, but this value may be inaccurate.")
  set(${PROJECT_NAME}_HGREMOTEDIR ${PROJECT_SOURCE_DIR})
  else()
  exec_program(hg ${${PROJECT_NAME}_HGREMOTEDIR} ARGS status RETURN_VALUE HGREMOTESTATUSVALUE OUTPUT_VARIABLE NUL)
  if (NOT HGREMOTESTATUSVALUE EQUAL 0)
  message(WARNING "Cannot connect to remote repository at ${${PROJECT_NAME}_HGREMOTEDIR}. Will use current direcoty for build number, but this value may be inaccurate.")
  set(${PROJECT_NAME}_HGREMOTEDIR ${PROJECT_SOURCE_DIR})
  endif()
  endif()
  
  #Identify changeset
  exec_program(hg ${PROJECT_SOURCE_DIR} ARGS "id" "-i" OUTPUT_VARIABLE OUTPUT_VARIABLE ${PROJECT_NAME}_HGHASHCODE)
  
  if (${PROJECT_NAME}_HGHASHCODE MATCHES ".*\\+")
  MESSAGE(STATUS "Node is dirty. Will generate temporary version number...")
  set (${PROJECT_NAME}_HGDIRTY 1)
  string(LENGTH ${${PROJECT_NAME}_HGHASHCODE} HGHASHLEN)
  MATH(EXPR HGHASHLEN "${HGHASHLEN}-1")
  string(SUBSTRING ${${PROJECT_NAME}_HGHASHCODE} 0 ${HGHASHLEN} ${PROJECT_NAME}_HGHASHCODE)
  endif()
  #check if remote repository contains changeset
  exec_program(hg ${${PROJECT_NAME}_HGREMOTEDIR}
  ARGS "log" "-r" "${${PROJECT_NAME}_HGHASHCODE}"
  RETURN_VALUE HGREMOTEHASCHANGESET
  OUTPUT_VARIABLE NUL)
  if (NOT HGREMOTEHASCHANGESET EQUAL 0)
  message(WARNING "Remote repository ${${PROJECT_NAME}_HGREMOTEDIR} does not have changeset ${${PROJECT_NAME}_HGHASHCODE}. Will use current direcoty for build number, but this value may be inaccurate.")
  set(${PROJECT_NAME}_HGREMOTEDIR ${PROJECT_SOURCE_DIR})
  endif()
  
  exec_program(hg ${${PROJECT_NAME}_HGREMOTEDIR}
  ARGS "log" "-r" ${${PROJECT_NAME}_HGHASHCODE} "--template" "{latesttag}"
  OUTPUT_VARIABLE ${PROJECT_NAME}_HGMAJORMINORVERSION)
  
  MESSAGE(STATUS "${PROJECT_NAME}_HGMAJORMINORVERSION=${${PROJECT_NAME}_HGMAJORMINORVERSION}")
  
  exec_program(hg ${${PROJECT_NAME}_HGREMOTEDIR} 
  ARGS "log" "-r" ${${PROJECT_NAME}_HGHASHCODE} "--template" "{latesttagdistance}"
   OUTPUT_VARIABLE ${PROJECT_NAME}_HGBUILDNUMBER)
  
  if (${PROJECT_NAME}_HGDIRTY)
  MATH(EXPR ${PROJECT_NAME}_HGBUILDNUMBER "${${PROJECT_NAME}_HGBUILDNUMBER}+1")
  set(${PROJECT_NAME}_HGHASHCODE "${${PROJECT_NAME}_HGHASHCODE}+")
  endif()
  
  MESSAGE(STATUS "Version=${${PROJECT_NAME}_HGMAJORMINORVERSION}.${${PROJECT_NAME}_HGBUILDNUMBER}.${${PROJECT_NAME}_HGHASHCODE}")
  endmacro()
  </code></pre>
  
  <p>And I currently call it like this.</p>
  
  <pre><code>ReadProjectRevisionStatus()
  CONFIGURE_FILE(${CMAKE_CURRENT_SOURCE_DIR}/main.h.in ${CMAKE_CURRENT_BINARY_DIR}/main.h)
  </code></pre>
  
  <p>Where main.h.in is:</p>
  
  <pre><code>#ifndef MAIN_H
  #define MAIN_H
  #include &lt;string&gt;
  
  const std::string ${PROJECT_NAME}_HGMAJORMINORVERSION = "${${PROJECT_NAME}_HGMAJORMINORVERSION}";
  const std::string ${PROJECT_NAME}_HGBUILDNUMBER = "${${PROJECT_NAME}_HGBUILDNUMBER}";
  const std::string ${PROJECT_NAME}_HGHASHCODE = "${${PROJECT_NAME}_HGHASHCODE}";
  
  const std::string ${PROJECT_NAME}_HG_SHORT_VERSION = 
      ${PROJECT_NAME}_HGMAJORMINORVERSION+"."+
      ${PROJECT_NAME}_HGBUILDNUMBER;
  
  const std::string ${PROJECT_NAME}_HG_VERSION = 
      ${PROJECT_NAME}_HG_SHORT_VERSION + "." +
      ${PROJECT_NAME}_HGHASHCODE;
  
  #endif
  </code></pre>
  
  <p>What I'd like to do is run this macro on every build command (I can use ADD_CUSTOM_TARGET and then set the variables instead of reading them from the project), but I only want to regenerate main.h if the variables have changed (to prevent unnecessary recompilation).</p>
  
  <p>EDIT: Working solution</p>
  
  <p>Made a HGVersion.CMake</p>
  
  <pre><code>cmake_minimum_required (VERSION 2.8)
  
  macro (ReadProjectRevisionStatus)
  message(STATUS PROJECT_SOURCE_DIR=${PROJECT_SOURCE_DIR})
  
  exec_program(hg ${PROJECT_SOURCE_DIR} ARGS paths OUTPUT_VARIABLE ${PROJECT_NAME}_HGPATHS)
  message(STATUS "${PROJECT_NAME}_HGPATHS=${${PROJECT_NAME}_HGPATHS}}")
  if (NOT(${PROJECT_NAME}_HGPATHS STREQUAL ""))
  
  string(REPLACE "\n" ";" ${PROJECT_NAME}_HGPATHS ${${PROJECT_NAME}_HGPATHS})
  
  foreach(HGPATH ${${PROJECT_NAME}_HGPATHS})
  string(SUBSTRING "${HGPATH}" 0 10 HGPATHSTART)
  if (HGPATHSTART MATCHES "default = ")
  string(LENGTH "${HGPATH}" HGPATHLENGTH)
  math(EXPR HGSUBLEN "${HGPATHLENGTH}-10")
  string(SUBSTRING "${HGPATH}" 10 ${HGSUBLEN} ${PROJECT_NAME}_HGREMOTEDIR)
  endif()
  endforeach()
  
  endif()
  
  if (NOT ${PROJECT_NAME}_HGREMOTEDIR)
  message(WARNING "No remote repository set. Will use current direcoty for build number, but this value may be inaccurate.")
  set(${PROJECT_NAME}_HGREMOTEDIR ${PROJECT_SOURCE_DIR})
  else()
  exec_program(hg ${${PROJECT_NAME}_HGREMOTEDIR} ARGS status RETURN_VALUE HGREMOTESTATUSVALUE OUTPUT_VARIABLE NUL)
  if (NOT HGREMOTESTATUSVALUE EQUAL 0)
  message(WARNING "Cannot connect to remote repository at ${${PROJECT_NAME}_HGREMOTEDIR}. Will use current direcoty for build number, but this value may be inaccurate.")
  set(${PROJECT_NAME}_HGREMOTEDIR ${PROJECT_SOURCE_DIR})
  endif()
  endif()
  
  #Identify changeset
  exec_program(hg ${PROJECT_SOURCE_DIR} ARGS "id" "-i" OUTPUT_VARIABLE OUTPUT_VARIABLE ${PROJECT_NAME}_HGHASHCODE)
  
  if (${PROJECT_NAME}_HGHASHCODE MATCHES ".*\\+")
  MESSAGE(STATUS "Node is dirty. Will generate temporary version number...")
  set (${PROJECT_NAME}_HGDIRTY 1)
  string(LENGTH ${${PROJECT_NAME}_HGHASHCODE} HGHASHLEN)
  MATH(EXPR HGHASHLEN "${HGHASHLEN}-1")
  string(SUBSTRING ${${PROJECT_NAME}_HGHASHCODE} 0 ${HGHASHLEN} ${PROJECT_NAME}_HGHASHCODE)
  endif()
  #check if remote repository contains changeset
  exec_program(hg ${${PROJECT_NAME}_HGREMOTEDIR}
  ARGS "log" "-r" "${${PROJECT_NAME}_HGHASHCODE}"
  RETURN_VALUE HGREMOTEHASCHANGESET
  OUTPUT_VARIABLE NUL)
  if (NOT HGREMOTEHASCHANGESET EQUAL 0)
  message(WARNING "Remote repository ${${PROJECT_NAME}_HGREMOTEDIR} does not have changeset ${${PROJECT_NAME}_HGHASHCODE}. Will use current direcoty for build number, but this value may be inaccurate.")
  set(${PROJECT_NAME}_HGREMOTEDIR ${PROJECT_SOURCE_DIR})
  endif()
  
  exec_program(hg ${${PROJECT_NAME}_HGREMOTEDIR}
  ARGS "log" "-r" ${${PROJECT_NAME}_HGHASHCODE} "--template" "{latesttag}"
  OUTPUT_VARIABLE ${PROJECT_NAME}_HGMAJORMINORVERSION)
  
  MESSAGE(STATUS "${PROJECT_NAME}_HGMAJORMINORVERSION=${${PROJECT_NAME}_HGMAJORMINORVERSION}")
  
  exec_program(hg ${${PROJECT_NAME}_HGREMOTEDIR} 
  ARGS "log" "-r" ${${PROJECT_NAME}_HGHASHCODE} "--template" "{latesttagdistance}"
   OUTPUT_VARIABLE ${PROJECT_NAME}_HGBUILDNUMBER)
  
  if (${PROJECT_NAME}_HGDIRTY)
  MATH(EXPR ${PROJECT_NAME}_HGBUILDNUMBER "${${PROJECT_NAME}_HGBUILDNUMBER}+1")
  set(${PROJECT_NAME}_HGHASHCODE "${${PROJECT_NAME}_HGHASHCODE}+")
  endif()
  
  MESSAGE(STATUS "Version=${${PROJECT_NAME}_HGMAJORMINORVERSION}.${${PROJECT_NAME}_HGBUILDNUMBER}.${${PROJECT_NAME}_HGHASHCODE}")
  endmacro()
  message(STATUS "GETTING HG VERSION")
  ReadProjectRevisionStatus()
  CONFIGURE_FILE(${PROJECT_SOURCE_DIR}/main.h.in ${PROJECT_BINARY_DIR}/main.h)
  </code></pre>
  
  <p>In the main cmake file</p>
  
  <pre><code>add_custom_target(
  ${PROJECT_NAME}_hg_version_target
   DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/main.h.in
   COMMAND ${CMAKE_COMMAND}
   ARGS -DPROJECT_SOURCE_DIR=${PROJECT_SOURCE_DIR}
   -DPROJECT_NAME=${PROJECT_NAME}
   -DPROJECT_BINARY_DIR=${PROJECT_BINARY_DIR}
   -P "${CMAKE_CURRENT_SOURCE_DIR}/HGVersion.CMake")
  </code></pre>
  
  <p>and</p>
  
  <p>add_dependencies(${PROJECT_NAME} ${PROJECT_NAME}_hg_version_target)</p>
  
  <p>Works but seems...real messy. Anyone got something better?</p>