tomopy/tomopy

View on GitHub
cmake/Modules/MacroUtilities.cmake

Summary

Maintainability
Test Coverage
# ~~~
# MacroUtilities - useful macros and functions for generic tasks
#
#
# General
# --------------
# function add_feature(<NAME> <DOCSTRING>)
#          Add a  feature, whose activation is specified by the
#          existence of the variable <NAME>, to the list of enabled/disabled
#          features, plus a docstring describing the feature
#
# function print_enabled_features()
#          Print enabled  features plus their docstrings.
#
# ~~~

# * Include guard
if(__${PROJECT_NAME}_macroutilities_isloaded)
  return()
endif()
set(__${PROJECT_NAME}_macroutilities_isloaded YES)

cmake_policy(PUSH)
if(NOT CMAKE_VERSION VERSION_LESS 3.1)
  cmake_policy(SET CMP0054 NEW)
endif()

include(CMakeDependentOption)
include(CMakeParseArguments)

# -----------------------------------------------------------------------
# macro safe_remove_duplicates(<list>) ensures remove_duplicates is only called
# if list has values
#
macro(safe_remove_duplicates _list)
  if(NOT "${${_list}}" STREQUAL "")
    list(REMOVE_DUPLICATES ${_list})
  endif(NOT "${${_list}}" STREQUAL "")
endmacro()

# -----------------------------------------------------------------------
# function - capitalize - make a string capitalized (first letter is capital)
# usage: capitalize("SHARED" CShared) message(STATUS "-- CShared is
# \"${CShared}\"") $ -- CShared is "Shared"
function(capitalize str var)
  # make string lower
  string(TOLOWER "${str}" str)
  string(SUBSTRING "${str}" 0 1 _first)
  string(TOUPPER "${_first}" _first)
  string(SUBSTRING "${str}" 1 -1 _remainder)
  string(CONCAT str "${_first}" "${_remainder}")
  set(${var}
      "${str}"
      PARENT_SCOPE)
endfunction()

# -----------------------------------------------------------------------
# GENERAL
# -----------------------------------------------------------------------
# function add_feature(<NAME> <DOCSTRING>) Add a project feature, whose
# activation is specified by the existence of the variable <NAME>, to the list
# of enabled/disabled features, plus a docstring describing the feature
#
function(ADD_FEATURE _var _description)
  set(EXTRA_DESC "")
  foreach(currentArg ${ARGN})
    if(NOT "${currentArg}" STREQUAL "${_var}" AND NOT "${currentArg}" STREQUAL
                                                  "${_description}")
      set(EXTRA_DESC "${EXTA_DESC}${currentArg}")
    endif()
  endforeach()

  set_property(GLOBAL APPEND PROPERTY PROJECT_FEATURES ${_var})
  # set(${_var} ${${_var}} CACHE INTERNAL "${_description}${EXTRA_DESC}")

  set_property(GLOBAL PROPERTY ${_var}_DESCRIPTION
                               "${_description}${EXTRA_DESC}")
endfunction()

# ------------------------------------------------------------------------------#
# function add_option(<OPTION_NAME> <DOCSRING> <DEFAULT_SETTING> [NO_FEATURE])
# Add an option and add as a feature if NO_FEATURE is not provided
#
function(ADD_OPTION _NAME _MESSAGE _DEFAULT)
  set(_FEATURE ${ARGN})
  option(${_NAME} "${_MESSAGE}" ${_DEFAULT})
  if(NOT "${_FEATURE}" STREQUAL "NO_FEATURE")
    add_feature(${_NAME} "${_MESSAGE}")
  else()
    mark_as_advanced(${_NAME})
  endif()
endfunction(
  ADD_OPTION
  _NAME
  _MESSAGE
  _DEFAULT)

# ------------------------------------------------------------------------------#
# macro CHECKOUT_GIT_SUBMODULE()
#
# Run "git submodule update" if a file in a submodule does not exist
#
# ARGS: RECURSIVE (option) -- add "--recursive" flag RELATIVE_PATH (one value)
# -- typically the relative path to submodule from PROJECT_SOURCE_DIR
# WORKING_DIRECTORY (one value) -- (default: PROJECT_SOURCE_DIR) TEST_FILE (one
# value) -- file to check for (default: CMakeLists.txt) ADDITIONAL_CMDS (many
# value) -- any addition commands to pass
#
function(CHECKOUT_GIT_SUBMODULE)
  # parse args
  cmake_parse_arguments(
    CHECKOUT "RECURSIVE" "RELATIVE_PATH;WORKING_DIRECTORY;TEST_FILE"
    "ADDITIONAL_CMDS" ${ARGN})
  find_package(Git)
  if(NOT Git_FOUND)
    message(
      WARNING
        "Git not found. submodule ${CHECKOUT_RELATIVE_PATH} not checked out")
    return()
  endif()

  if(NOT CHECKOUT_WORKING_DIRECTORY)
    set(CHECKOUT_WORKING_DIRECTORY ${PROJECT_SOURCE_DIR})
  endif(NOT CHECKOUT_WORKING_DIRECTORY)

  if(NOT CHECKOUT_TEST_FILE)
    set(CHECKOUT_TEST_FILE "CMakeLists.txt")
  endif(NOT CHECKOUT_TEST_FILE)

  set(_DIR "${CHECKOUT_WORKING_DIRECTORY}/${CHECKOUT_RELATIVE_PATH}")
  # ensure the (possibly empty) directory exists
  if(NOT EXISTS "${_DIR}")
    message(FATAL_ERROR "submodule directory does not exist")
  endif(NOT EXISTS "${_DIR}")

  # if this file exists --> project has been checked out if not exists --> not
  # been checked out
  set(_TEST_FILE "${_DIR}/${CHECKOUT_TEST_FILE}")

  set(_RECURSE)
  if(CHECKOUT_RECURSIVE)
    set(_RECURSE --recursive)
  endif(CHECKOUT_RECURSIVE)

  # if the module has not been checked out
  if(NOT EXISTS "${_TEST_FILE}")
    # perform the checkout
    execute_process(
      COMMAND ${GIT_EXECUTABLE} submodule update --init ${_RECURSE}
              ${CHECKOUT_ADDITIONAL_CMDS} ${CHECKOUT_RELATIVE_PATH}
      WORKING_DIRECTORY ${CHECKOUT_WORKING_DIRECTORY}
      RESULT_VARIABLE RET)

    # check the return code
    if(RET GREATER 0)
      set(_CMD "${GIT_EXECUTABLE} submodule update --init ${_RECURSE}
                ${CHECKOUT_ADDITIONAL_CMDS} ${CHECKOUT_RELATIVE_PATH}")
      message(STATUS "macro(CHECKOUT_SUBMODULE) failed.")
      message(WARNING "Command: \"${_CMD}\"")
      return()
    endif()
  elseif(NOT SKIP_GIT_UPDATE)
    message(
      STATUS
        "Executing '${GIT_EXECUTABLE} submodule update ${_RECURSE} ${CHECKOUT_RELATIVE_PATH}'... Disable with SKIP_GIT_UPDATE..."
    )
    execute_process(
      COMMAND ${GIT_EXECUTABLE} submodule update ${_RECURSE}
              ${CHECKOUT_RELATIVE_PATH}
      WORKING_DIRECTORY ${CHECKOUT_WORKING_DIRECTORY})
  endif()

endfunction()

# ------------------------------------------------------------------------------#
# function print_enabled_features() Print enabled  features plus their
# docstrings.
#
function(print_enabled_features)
  set(_basemsg "The following features are defined/enabled (+):")
  set(_currentFeatureText "${_basemsg}")
  get_property(_features GLOBAL PROPERTY PROJECT_FEATURES)
  if(NOT "${_features}" STREQUAL "")
    list(REMOVE_DUPLICATES _features)
    list(SORT _features)
  endif()
  foreach(_feature ${_features})
    if(${_feature})
      # add feature to text
      set(_currentFeatureText "${_currentFeatureText}\n     ${_feature}")
      # get description
      get_property(_desc GLOBAL PROPERTY ${_feature}_DESCRIPTION)
      # print description, if not standard ON/OFF, print what is set to
      if(_desc)
        if(NOT "${${_feature}}" STREQUAL "ON" AND NOT "${${_feature}}" STREQUAL
                                                  "TRUE")
          set(_currentFeatureText
              "${_currentFeatureText}: ${_desc} -- [\"${${_feature}}\"]")
        else()
          string(REGEX REPLACE "^USE_" "" _feature_tmp "${_feature}")
          string(TOLOWER "${_feature_tmp}" _feature_tmp_l)
          capitalize("${_feature_tmp}" _feature_tmp_c)
          foreach(_var _feature_tmp _feature_tmp_l _feature_tmp_c)
            set(_ver "${${${_var}}_VERSION}")
            if(NOT "${_ver}" STREQUAL "")
              set(_desc "${_desc} -- [found version ${_ver}]")
              break()
            endif()
            unset(_ver)
          endforeach(_var _feature_tmp _feature_tmp_l _feature_tmp_c)
          set(_currentFeatureText "${_currentFeatureText}: ${_desc}")
        endif()
        set(_desc NOTFOUND)
      endif(_desc)
      # check for subfeatures
      get_property(_subfeatures GLOBAL PROPERTY ${_feature}_FEATURES)
      # remove duplicates and sort if subfeatures exist
      if(NOT "${_subfeatures}" STREQUAL "")
        list(REMOVE_DUPLICATES _subfeatures)
        list(SORT _subfeatures)
      endif()

      # sort enabled and disabled features into lists
      set(_enabled_subfeatures)
      set(_disabled_subfeatures)
      foreach(_subfeature ${_subfeatures})
        if(${_subfeature})
          list(APPEND _enabled_subfeatures ${_subfeature})
        else()
          list(APPEND _disabled_subfeatures ${_subfeature})
        endif()
      endforeach()

      # loop over enabled subfeatures
      foreach(_subfeature ${_enabled_subfeatures})
        # add subfeature to text
        set(_currentFeatureText
            "${_currentFeatureText}\n       + ${_subfeature}")
        # get subfeature description
        get_property(_subdesc GLOBAL
                     PROPERTY ${_feature}_${_subfeature}_DESCRIPTION)
        # print subfeature description. If not standard ON/OFF, print what is
        # set to
        if(_subdesc)
          if(NOT "${${_subfeature}}" STREQUAL "ON" AND NOT "${${_subfeature}}"
                                                       STREQUAL "TRUE")
            set(_currentFeatureText
                "${_currentFeatureText}: ${_subdesc} -- [\"${${_subfeature}}\"]"
            )
          else()
            set(_currentFeatureText "${_currentFeatureText}: ${_subdesc}")
          endif()
          set(_subdesc NOTFOUND)
        endif(_subdesc)
      endforeach(_subfeature)

      # loop over disabled subfeatures
      foreach(_subfeature ${_disabled_subfeatures})
        # add subfeature to text
        set(_currentFeatureText
            "${_currentFeatureText}\n       - ${_subfeature}")
        # get subfeature description
        get_property(_subdesc GLOBAL
                     PROPERTY ${_feature}_${_subfeature}_DESCRIPTION)
        # print subfeature description.
        if(_subdesc)
          set(_currentFeatureText "${_currentFeatureText}: ${_subdesc}")
          set(_subdesc NOTFOUND)
        endif(_subdesc)
      endforeach(_subfeature)

    endif(${_feature})
  endforeach(_feature)

  if(NOT "${_currentFeatureText}" STREQUAL "${_basemsg}")
    message(STATUS "${_currentFeatureText}\n")
  endif()
endfunction()

# ------------------------------------------------------------------------------#
# function print_disabled_features() Print disabled features plus their
# docstrings.
#
function(print_disabled_features)
  set(_basemsg "The following features are NOT defined/enabled (-):")
  set(_currentFeatureText "${_basemsg}")
  get_property(_features GLOBAL PROPERTY PROJECT_FEATURES)
  if(NOT "${_features}" STREQUAL "")
    list(REMOVE_DUPLICATES _features)
    list(SORT _features)
  endif()
  foreach(_feature ${_features})
    if(NOT ${_feature})
      set(_currentFeatureText "${_currentFeatureText}\n     ${_feature}")

      get_property(_desc GLOBAL PROPERTY ${_feature}_DESCRIPTION)

      if(_desc)
        set(_currentFeatureText "${_currentFeatureText}: ${_desc}")
        set(_desc NOTFOUND)
      endif(_desc)
    endif()
  endforeach(_feature)

  if(NOT "${_currentFeatureText}" STREQUAL "${_basemsg}")
    message(STATUS "${_currentFeatureText}\n")
  endif()
endfunction()

# ------------------------------------------------------------------------------#
# function print_features() Print all features plus their docstrings.
#
function(print_features)
  message(STATUS "")
  print_enabled_features()
  print_disabled_features()
endfunction()

# ------------------------------------------------------------------------------#
function(TOMOPY_ADD_LIBRARY _TARGET)
  # basically this makes tomopy_add_library(...) act exactly like
  # add_library(...)
  add_library(${_TARGET} ${ARGN})

  # check the name has the "tomo-" prefix (expected by libtomoConfig.cmake)
  if(NOT "${_TARGET}" MATCHES "^(tomo-)")
    message(
      FATAL_ERROR
        "The name of the target passed to tomopy_add_library MUST start with 'tomo-'"
    )
  endif()

  # create an alias library in the build tree. This allows parent projects
  # adding tomopy as a submodule to be able to always reference the libtomo
  # targets with the libtomo:: namespace
  add_library(libtomo::${_TARGET} ALIAS ${_TARGET})

  # append to global property. Use the namespaced name because when
  # libtomoConfig.cmake imports the targets tomo-accel will not be the name of
  # the target, libtomo::tomo-accel will be the name
  set_property(GLOBAL APPEND PROPERTY libtomo_TARGETS "libtomo::${_TARGET} ")
endfunction()

cmake_policy(POP)