################################################################################################ # Exclude and prepend functionalities function (exclude OUTPUT INPUT) set(EXCLUDES ${ARGN}) foreach(EXCLUDE ${EXCLUDES}) list(REMOVE_ITEM INPUT "${EXCLUDE}") endforeach() set(${OUTPUT} ${INPUT} PARENT_SCOPE) endfunction(exclude) function (prepend OUTPUT PREPEND) set(OUT "") foreach(ITEM ${ARGN}) list(APPEND OUT "${PREPEND}${ITEM}") endforeach() set(${OUTPUT} ${OUT} PARENT_SCOPE) endfunction(prepend) ################################################################################################ # Clears variables from list # Usage: # caffe_clear_vars() macro(caffe_clear_vars) foreach(_var ${ARGN}) unset(${_var}) endforeach() endmacro() ################################################################################################ # Prints list element per line # Usage: # caffe_print_list() function(caffe_print_list) foreach(e ${ARGN}) message(STATUS ${e}) endforeach() endfunction() ################################################################################################ # Reads set of version defines from the header file # Usage: # caffe_parse_header( ..) macro(caffe_parse_header FILENAME FILE_VAR) set(vars_regex "") set(__parnet_scope OFF) set(__add_cache OFF) foreach(name ${ARGN}) if("${name}" STREQUAL "PARENT_SCOPE") set(__parnet_scope ON) elseif("${name}" STREQUAL "CACHE") set(__add_cache ON) elseif(vars_regex) set(vars_regex "${vars_regex}|${name}") else() set(vars_regex "${name}") endif() endforeach() if(EXISTS "${FILENAME}") file(STRINGS "${FILENAME}" ${FILE_VAR} REGEX "#define[ \t]+(${vars_regex})[ \t]+[0-9]+" ) else() unset(${FILE_VAR}) endif() foreach(name ${ARGN}) if(NOT "${name}" STREQUAL "PARENT_SCOPE" AND NOT "${name}" STREQUAL "CACHE") if(${FILE_VAR}) if(${FILE_VAR} MATCHES ".+[ \t]${name}[ \t]+([0-9]+).*") string(REGEX REPLACE ".+[ \t]${name}[ \t]+([0-9]+).*" "\\1" ${name} "${${FILE_VAR}}") else() set(${name} "") endif() if(__add_cache) set(${name} ${${name}} CACHE INTERNAL "${name} parsed from ${FILENAME}" FORCE) elseif(__parnet_scope) set(${name} "${${name}}" PARENT_SCOPE) endif() else() unset(${name} CACHE) endif() endif() endforeach() endmacro() ################################################################################################ # Reads single version define from the header file and parses it # Usage: # caffe_parse_header_single_define( ) function(caffe_parse_header_single_define LIBNAME HDR_PATH VARNAME) set(${LIBNAME}_H "") if(EXISTS "${HDR_PATH}") file(STRINGS "${HDR_PATH}" ${LIBNAME}_H REGEX "^#define[ \t]+${VARNAME}[ \t]+\"[^\"]*\".*$" LIMIT_COUNT 1) endif() if(${LIBNAME}_H) string(REGEX REPLACE "^.*[ \t]${VARNAME}[ \t]+\"([0-9]+).*$" "\\1" ${LIBNAME}_VERSION_MAJOR "${${LIBNAME}_H}") string(REGEX REPLACE "^.*[ \t]${VARNAME}[ \t]+\"[0-9]+\\.([0-9]+).*$" "\\1" ${LIBNAME}_VERSION_MINOR "${${LIBNAME}_H}") string(REGEX REPLACE "^.*[ \t]${VARNAME}[ \t]+\"[0-9]+\\.[0-9]+\\.([0-9]+).*$" "\\1" ${LIBNAME}_VERSION_PATCH "${${LIBNAME}_H}") set(${LIBNAME}_VERSION_MAJOR ${${LIBNAME}_VERSION_MAJOR} ${ARGN} PARENT_SCOPE) set(${LIBNAME}_VERSION_MINOR ${${LIBNAME}_VERSION_MINOR} ${ARGN} PARENT_SCOPE) set(${LIBNAME}_VERSION_PATCH ${${LIBNAME}_VERSION_PATCH} ${ARGN} PARENT_SCOPE) set(${LIBNAME}_VERSION_STRING "${${LIBNAME}_VERSION_MAJOR}.${${LIBNAME}_VERSION_MINOR}.${${LIBNAME}_VERSION_PATCH}" PARENT_SCOPE) # append a TWEAK version if it exists: set(${LIBNAME}_VERSION_TWEAK "") if("${${LIBNAME}_H}" MATCHES "^.*[ \t]${VARNAME}[ \t]+\"[0-9]+\\.[0-9]+\\.[0-9]+\\.([0-9]+).*$") set(${LIBNAME}_VERSION_TWEAK "${CMAKE_MATCH_1}" ${ARGN} PARENT_SCOPE) endif() if(${LIBNAME}_VERSION_TWEAK) set(${LIBNAME}_VERSION_STRING "${${LIBNAME}_VERSION_STRING}.${${LIBNAME}_VERSION_TWEAK}" ${ARGN} PARENT_SCOPE) else() set(${LIBNAME}_VERSION_STRING "${${LIBNAME}_VERSION_STRING}" ${ARGN} PARENT_SCOPE) endif() endif() endfunction() ################################################################################################ # Parses a version string that might have values beyond major, minor, and patch # and set version variables for the library. # Usage: # caffe2_parse_version_str( ) function(caffe2_parse_version_str LIBNAME VERSIONSTR) string(REGEX REPLACE "^([0-9]+).*$" "\\1" ${LIBNAME}_VERSION_MAJOR "${VERSIONSTR}") string(REGEX REPLACE "^[0-9]+\\.([0-9]+).*$" "\\1" ${LIBNAME}_VERSION_MINOR "${VERSIONSTR}") string(REGEX REPLACE "[0-9]+\\.[0-9]+\\.([0-9]+).*$" "\\1" ${LIBNAME}_VERSION_PATCH "${VERSIONSTR}") set(${LIBNAME}_VERSION_MAJOR ${${LIBNAME}_VERSION_MAJOR} ${ARGN} PARENT_SCOPE) set(${LIBNAME}_VERSION_MINOR ${${LIBNAME}_VERSION_MINOR} ${ARGN} PARENT_SCOPE) set(${LIBNAME}_VERSION_PATCH ${${LIBNAME}_VERSION_PATCH} ${ARGN} PARENT_SCOPE) set(${LIBNAME}_VERSION "${${LIBNAME}_VERSION_MAJOR}.${${LIBNAME}_VERSION_MINOR}.${${LIBNAME}_VERSION_PATCH}" PARENT_SCOPE) endfunction() ### # Removes common indentation from a block of text to produce code suitable for # setting to `python -c`, or using with pycmd. This allows multiline code to be # nested nicely in the surrounding code structure. # # This function respsects PYTHON_EXECUTABLE if it defined, otherwise it uses # `python` and hopes for the best. An error will be thrown if it is not found. # # Args: # outvar : variable that will hold the stdout of the python command # text : text to remove indentation from # function(dedent outvar text) # Use PYTHON_EXECUTABLE if it is defined, otherwise default to python if ("${PYTHON_EXECUTABLE}" STREQUAL "") set(_python_exe "python") else() set(_python_exe "${PYTHON_EXECUTABLE}") endif() set(_fixup_cmd "import sys; from textwrap import dedent; print(dedent(sys.stdin.read()))") file(WRITE "${CMAKE_BINARY_DIR}/indented.txt" "${text}") execute_process( COMMAND "${_python_exe}" -c "${_fixup_cmd}" INPUT_FILE "${CMAKE_BINARY_DIR}/indented.txt" RESULT_VARIABLE _dedent_exitcode OUTPUT_VARIABLE _dedent_text) if(NOT ${_dedent_exitcode} EQUAL 0) message(ERROR " Failed to remove indentation from: \n\"\"\"\n${text}\n\"\"\" Python dedent failed with error code: ${_dedent_exitcode}") message(FATAL_ERROR " Python dedent failed with error code: ${_dedent_exitcode}") endif() # Remove supurflous newlines (artifacts of print) string(STRIP "${_dedent_text}" _dedent_text) set(${outvar} "${_dedent_text}" PARENT_SCOPE) endfunction() function(pycmd_no_exit outvar exitcode cmd) # Use PYTHON_EXECUTABLE if it is defined, otherwise default to python if ("${PYTHON_EXECUTABLE}" STREQUAL "") set(_python_exe "python") else() set(_python_exe "${PYTHON_EXECUTABLE}") endif() # run the actual command execute_process( COMMAND "${_python_exe}" -c "${cmd}" RESULT_VARIABLE _exitcode OUTPUT_VARIABLE _output) # Remove supurflous newlines (artifacts of print) string(STRIP "${_output}" _output) set(${outvar} "${_output}" PARENT_SCOPE) set(${exitcode} "${_exitcode}" PARENT_SCOPE) endfunction() ### # Helper function to run `python -c ""` and capture the results of stdout # # Runs a python command and populates an outvar with the result of stdout. # Common indentation in the text of `cmd` is removed before the command is # executed, so the caller does not need to worry about indentation issues. # # This function respsects PYTHON_EXECUTABLE if it defined, otherwise it uses # `python` and hopes for the best. An error will be thrown if it is not found. # # Args: # outvar : variable that will hold the stdout of the python command # cmd : text representing a (possibly multiline) block of python code # function(pycmd outvar cmd) dedent(_dedent_cmd "${cmd}") pycmd_no_exit(_output _exitcode "${_dedent_cmd}") if(NOT ${_exitcode} EQUAL 0) message(ERROR " Failed when running python code: \"\"\"\n${_dedent_cmd}\n\"\"\"") message(FATAL_ERROR " Python command failed with error code: ${_exitcode}") endif() # Remove supurflous newlines (artifacts of print) string(STRIP "${_output}" _output) set(${outvar} "${_output}" PARENT_SCOPE) endfunction() ### # Helper function to print out everything that cmake knows about a target # # Copied from https://stackoverflow.com/questions/32183975/how-to-print-all-the-properties-of-a-target-in-cmake # This isn't called anywhere, but it's very useful when debugging cmake # NOTE: This doesn't work for INTERFACE_LIBRARY or INTERFACE_LINK_LIBRARY targets function(print_target_properties tgt) if(NOT TARGET ${tgt}) message("There is no target named '${tgt}'") return() endif() # Get a list of all cmake properties TODO cache this lazily somehow execute_process(COMMAND cmake --help-property-list OUTPUT_VARIABLE CMAKE_PROPERTY_LIST) STRING(REGEX REPLACE ";" "\\\\;" CMAKE_PROPERTY_LIST "${CMAKE_PROPERTY_LIST}") STRING(REGEX REPLACE "\n" ";" CMAKE_PROPERTY_LIST "${CMAKE_PROPERTY_LIST}") foreach (prop ${CMAKE_PROPERTY_LIST}) string(REPLACE "" "${CMAKE_BUILD_TYPE}" prop ${prop}) get_property(propval TARGET ${tgt} PROPERTY ${prop} SET) if (propval) get_target_property(propval ${tgt} ${prop}) message ("${tgt} ${prop} = ${propval}") endif() endforeach(prop) endfunction(print_target_properties)