开发者

Have makefile based cmake projects automatically run make rebuild_cache when an environment variable changes

开发者 https://www.devze.com 2023-03-08 06:26 出处:网络
In my cmake configuration several variables depend on an environment variable in order to be set correctly.This environment variable can change and it means that the cache for cmake should be rebuilt.

In my cmake configuration several variables depend on an environment variable in order to be set correctly. This environment variable can change and it means that the cache for cmake should be rebuilt.

My configuration can detect the need for this reconfigure and update the appropriate cache entries when either another call to "cmake " is called or "make rebuild_cache" is called.

However, I would like whenever I run make it to be checked automatically for changes and have the rebuild_cache target run if necessary.

Is thi开发者_StackOverflow中文版s possible?


Make does not have a memory. There is no way for make to "remember" what a given environment variable was set to the last time make was run.

Unless you write the environment variable to a file.

I've never used CMake, so I don't know how to best implement it. But on the "raw" make level, the general idea would be to:

1) write a rule (say, envir_cache) which writes the environment variable to file (named, not so coincidentially, envir_cache), if that file does not yet exist, or the file exists but its contents are different from the value of the environment variable. (Something along the lines of if [ -f envir_cache ] and read cached_var < envir_cache and if [ "${myvar}" != "${cached_var}" ].)

2) make the target rebuild_cache depend on envir_cache.

That way, the rebuild_cache rule will be executed on the first run, and whenever the variable changed between runs.


This is not easily done with CMake, but an example of how to do this as a CMake include modules is shown below. The solution depends on using a custom target that outputs the variables in question to a file and calls the cmake compare_files option to compare the previous file to the verify file and calls cmake to rebuild the cache in the event they don't match.

The solution involves a carefully crafted CMake include module that will recursively call itself to verify the cached values haven't been changed by an Environment variable. If it has, it will perform the rebuild cache step by calling cmake with appropriate arguments as shown below. It is expected that you will call the add_option macro for every variable you want to be able to override using an Environment variable (see examples below):

# Capture the full path to this CMake module file
if(NOT _option_cmake_file)
  set(_option_cmake_file ${CMAKE_CURRENT_LIST_FILE})
endif()

# When this CMake module is called as a script include the option file
if(_option_verify)
  include(${_option_file})
endif()

# add_option macro for adding cached values you want to be able to
# override with an environment variable of the same name
# _name - variable name to use for the cached value
# _type - type of cached variable
# _description - description of cached variable for CMake GUI
# _default - default value if no variable with same name is defined
macro(add_option _name _type _description _default)
  # Define _option_file to be created if not in verify mode
  if(NOT _option_verify)
    set(_option_file ${CMAKE_BINARY_DIR}/${_name}.cmake)
  endif()

  # Determine the source for the alue of the cached variable
  set(_option_output "set(_name ${_name})")
  list(APPEND _option_output "\nset(_type ${_type})")
  list(APPEND _option_output "\nset(_description \"${_description}\")")
  if(DEFINED ENV{${_name}})
    set(${_name} $ENV{${_name}} CACHE ${_type} "${_description}" FORCE)
    list(APPEND _option_output "\nset(${_name} $ENV{${_name}})")
  elseif(${_name})
    set(${_name} ${${_name}} CACHE ${_type} "${_description}" FORCE)
    set(ENV{${_name}} ${${_name}}) # needed to pass from verify back to rebuild_cache
    list(APPEND _option_output "\nset(${_name} ${${_name}})")
  else()
    set(${_name} ${_default} CACHE ${_type} "${_description}" FORCE)
    list(APPEND _option_output "\nset(${_name} ${_default})")
  endif()

  # Create the _option_file (or verify file) containing the values
  # defined above
  execute_process(
    COMMAND ${CMAKE_COMMAND} -E echo ${_option_output}
    OUTPUT_FILE ${_option_output}${_option_verify})

  # If not in verify mode create check target to verify value
  if(NOT _option_verify)
    # Only create parent check-variables target once
    if(NOT TARGET check-variables)
      add_custom_target(check-variables ALL)
    endif()
    # Use this file as custom CMake target to verify variable value
    add_custom_target(check-${_name}
      COMMAND ${CMAKE_COMMAND}
      -D_option_verify:String=-verify
      -D_option_file:Filepath=${_option_file}
      -D_option_sdir:Path=${CMAKE_SOURCE_DIR}
      -D_option_bdir:Path=${CMAKE_BINARY_DIR}
      -P ${_option_cmake_file}
      COMMENT "Checking variable '${_name}' for changes"
      VERBATIM)
    # Add custom target as dependency for parent check-variables target
    add_dependencies(check-variables check-${_name})
  else()
    # Use cmake to compare options file and verify file created above
    execute_process(
      COMMAND ${CMAKE_COMMAND} -E compare_files
        ${_option_file} ${_option_file}${_option_verify}
      OUTPUT_VARIABLE COMPARE_OUTPUT
      ERROR_VARIABLE COMPARE_ERROR
      RESULT_VARIABLE COMPARE_RESULT)
    # Remove verify file
    file(REMOVE ${_option_file}${_option_verify})
    # If compare failed, then call CMAKE to rebuild_cache
    if(NOT COMPARE_RESULT EQUAL 0)
      # Perform the rebuild_cache step
      execute_process(
        COMMAND ${CMAKE_COMMAND} -H${_option_sdir} -B${_option_bdir})
    endif()
  endif()
endmacro()

# In verify mode? then call add_option macro to initiate the process
if(_option_verify)
  # The values below come from the include(_option_file) above
  add_option(${_name} ${_type} "${_description}" ${${_name}})
endif()

If the above CMake module was named add_option.cmake you could use it as follows:

cmake_minimum_required(VERSION 2.8)
project(Example)
include(${PROJECT_SOURCE_DIR}/add_option.cmake)
add_option(MYVAR
  BOOL
  "A boolean cached value that can be overridden by Environment variable"
  ON)
add_option(MYSTR
  STRING
  "A string cached value that can be overridden by Environment variable"
  "some string")
message(STATUS "MYVAR=${MYVAR}")
message(STATUS "MYSTR=${MYSTR}")

With the above CMakeLists.txt file do the following (using Unix Makefiles):

mkdir build
cd build

The following example demonstrates the initial creation of the Unix Makefiles. Note the variables use their default values in this case.

cmake .. -G "Unix Makefiles"
-- The C compiler identification is GNU 4.8.4
-- The CXX compiler identification is GNU 4.8.4
-- Check for working C compiler: /usr/bin/cc
-- Check for working C compiler: /usr/bin/cc -- works
-- Detecting C compiler ABI info
-- Detecting C compiler ABI info - done
-- Check for working CXX compiler: /usr/bin/c++
-- Check for working CXX compiler: /usr/bin/c++ -- works
-- Detecting CXX compiler ABI info
-- Detecting CXX compiler ABI info - done
-- MYVAR=ON
-- MYSTR=some string
-- Configuring done
-- Generating done
-- Build files have been written to: /home/user/example/build

The following example shows how make can invoke the check-variables target and its dependent targets created by the add_option.cmake module above. Notice how no rebuild cache occurs.

make
Scanning dependencies of target check-MYVAR
[ 50%] Checking variable 'MYVAR' for changes
[ 50%] Built target check-MYVAR
Scanning dependencies of target check-MYSTR
[100%] Checking variable 'MYSTR' for changes
[100%] Built target check-MYSTR
Scanning dependencies of target check-variables
[100%] Built target check-variables

The following example shows how an environment variable causes one of the check-variables step to fail and triggers a rebuild cache event. Note the change in the MYVAR value during the cache rebuild process.

make MYVAR=off
[ 50%] Checking variable 'MYVAR' for changes
-- MYVAR=off
-- MYSTR=some string
-- Configuring done
-- Generating done
-- Build files have been written to: /home/user/example/build
[ 50%] Built target check-MYVAR
[100%] Checking variable 'MYSTR' for changes
[100%] Built target check-MYSTR
[100%] Built target check-variables

The following example shows how the variable changed temporarily above is reverted back to the defaults and a different variable is triggered to change its value. Note the MYVAR variable revert back to its default value while the MYSTR variable obtains a new value provided.

make MYSTR="hi mom"
[ 50%] Checking variable 'MYSTR' for changes
-- MYVAR=ON
-- MYSTR=hi mom
-- Configuring done
-- Generating done
-- Build files have been written to: /home/user/example/build
[ 50%] Built target check-MYSTR
[100%] Checking variable 'MYVAR' for changes
[100%] Built target check-MYVAR
[100%] Built target check-variables
0

精彩评论

暂无评论...
验证码 换一张
取 消