diff options
102 files changed, 4214 insertions, 2059 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt index 1250a94..d86ae96 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -37,10 +37,10 @@ if("${CMake_SOURCE_DIR}" STREQUAL "${CMAKE_SOURCE_DIR}") endif() # Use most-recent available language dialects with GNU and Clang -if(NOT DEFINED CMAKE_C_STANDARD) +if(NOT DEFINED CMAKE_C_STANDARD AND NOT CMake_NO_C_STANDARD) set(CMAKE_C_STANDARD 11) endif() -if(NOT DEFINED CMAKE_CXX_STANDARD) +if(NOT DEFINED CMAKE_CXX_STANDARD AND NOT CMake_NO_CXX_STANDARD) set(CMAKE_CXX_STANDARD 14) endif() diff --git a/Help/manual/cmake-generator-expressions.7.rst b/Help/manual/cmake-generator-expressions.7.rst index d38cf7e..477a132 100644 --- a/Help/manual/cmake-generator-expressions.7.rst +++ b/Help/manual/cmake-generator-expressions.7.rst @@ -40,10 +40,6 @@ otherwise expands to nothing. Available logical expressions are: -``$<0:...>`` - Empty string (ignores ``...``) -``$<1:...>`` - Content of ``...`` ``$<BOOL:...>`` ``1`` if the ``...`` is true, else ``0`` ``$<AND:?[,?]...>`` @@ -241,6 +237,10 @@ where ``${prop}`` refers to a helper variable:: Available output expressions are: +``$<0:...>`` + Empty string (ignores ``...``) +``$<1:...>`` + Content of ``...`` ``$<JOIN:list,...>`` Joins the list with the content of ``...`` ``$<ANGLE-R>`` diff --git a/Help/manual/cmake-policies.7.rst b/Help/manual/cmake-policies.7.rst index 96f39e6..76ca5d4 100644 --- a/Help/manual/cmake-policies.7.rst +++ b/Help/manual/cmake-policies.7.rst @@ -114,3 +114,4 @@ All Policies /policy/CMP0054 /policy/CMP0055 /policy/CMP0056 + /policy/CMP0057 diff --git a/Help/policy/CMP0057.rst b/Help/policy/CMP0057.rst new file mode 100644 index 0000000..5cf0784 --- /dev/null +++ b/Help/policy/CMP0057.rst @@ -0,0 +1,21 @@ +CMP0057 +------- + +Disallow multiple ``MAIN_DEPENDENCY`` specifications for the same file. + +CMake 3.3 and above no longer allow the same input file to be used +as a ``MAIN_DEPENDENCY`` in more than one custom command. + +Listing the same input file more than once in this context has not been +supported by earlier versions either and would lead to build time issues +but was not diagnosed. + +The ``OLD`` behavior for this policy is to allow using the same input file +in a ``MAIN_DEPENDENCY`` specfication more than once. +The ``NEW`` behavior is to disallow using the same input file in a +``MAIN_DEPENDENCY`` specification more than once. + +This policy was introduced in CMake version 3.3. +CMake version |release| warns when the policy is not set and uses +``OLD`` behavior. Use the :command:`cmake_policy` command to set +it to ``OLD`` or ``NEW`` explicitly. diff --git a/Help/release/dev/FindMatlab-rewrite.rst b/Help/release/dev/FindMatlab-rewrite.rst new file mode 100644 index 0000000..07727b8 --- /dev/null +++ b/Help/release/dev/FindMatlab-rewrite.rst @@ -0,0 +1,7 @@ +FindMatlab-rewrite +------------------ + +* The :module:`FindMatlab` module was completely rewritten. It learned + about versions and components and to find Matlab in a more precise and + multiplatform way. The module now offers APIs to create mex extensions, + documentation, and unit tests. diff --git a/Help/release/dev/main_dependency_diagnostic.rst b/Help/release/dev/main_dependency_diagnostic.rst new file mode 100644 index 0000000..13486ef --- /dev/null +++ b/Help/release/dev/main_dependency_diagnostic.rst @@ -0,0 +1,6 @@ +main_dependency_diagnostic +-------------------------- + +* Listing the same input file as a MAIN_DEPENDENCY of a custom command + can lead to broken build time behavior. This is now diagnosed. + See policy :policy:`CMP0057`. diff --git a/Modules/FindMatlab.cmake b/Modules/FindMatlab.cmake index 73b3a5b..d08423b 100644 --- a/Modules/FindMatlab.cmake +++ b/Modules/FindMatlab.cmake @@ -2,20 +2,208 @@ # FindMatlab # ---------- # -# this module looks for Matlab +# Finds Matlab installations and provides Matlab tools and libraries to cmake. # -# Defines: +# This package first intention is to find the libraries associated with Matlab +# in order to be able to build Matlab extensions (mex files). It can also be +# used: # -# :: +# * run specific commands in Matlab +# * declare Matlab unit test +# * retrieve various information from Matlab (mex extensions, versions and +# release queries, ...) # -# MATLAB_INCLUDE_DIR: include path for mex.h, engine.h -# MATLAB_LIBRARIES: required libraries: libmex, etc -# MATLAB_MEX_LIBRARY: path to libmex.lib -# MATLAB_MX_LIBRARY: path to libmx.lib -# MATLAB_ENG_LIBRARY: path to libeng.lib +# The module supports the following components: +# +# * ``MX_LIBRARY`` and ``ENG_LIBRARY`` respectively the MX and ENG libraries of +# Matlab +# * ``MAIN_PROGRAM`` the Matlab binary program. +# +# .. note:: +# +# The version given to the :command:`find_package` directive is the Matlab +# **version**, which should not be confused with the Matlab *release* name +# (eg. `R2014`). +# The :command:`matlab_get_version_from_release_name` and +# :command:`matlab_get_release_name_from_version` allow a mapping +# from the release name to the version. +# +# The variable :variable:`Matlab_ROOT_DIR` may be specified in order to give +# the path of the desired Matlab version. Otherwise, the behaviour is platform +# specific: +# +# * Windows: The installed versions of Matlab are retrieved from the +# Windows registry +# * OS X: The installed versions of Matlab are given by the MATLAB +# paths in ``/Application``. If no such application is found, it falls back +# to the one that might be accessible from the PATH. +# * Unix: The desired Matlab should be accessible from the PATH. +# +# Additional information is provided when :variable:`MATLAB_FIND_DEBUG` is set. +# When a Matlab binary is found automatically and the ``MATLAB_VERSION`` +# is not given, the version is queried from Matlab directly. +# On Windows, it can make a window running Matlab appear. +# +# The mapping of the release names and the version of Matlab is performed by +# defining pairs (name, version). The variable +# :variable:`MATLAB_ADDITIONAL_VERSIONS` may be provided before the call to +# the :command:`find_package` in order to handle additional versions. +# +# A Matlab scripts can be added to the set of tests using the +# :command:`matlab_add_unit_test`. By default, the Matlab unit test framework +# will be used (>= 2013a) to run this script, but regular ``.m`` files +# returning an exit code can be used as well (0 indicating a success). +# +# Module Input Variables +# ---------------------- +# +# Users or projects may set the following variables to configure the module +# behaviour: +# +# :variable:`Matlab_ROOT_DIR` +# the root of the Matlab installation. +# :variable:`MATLAB_FIND_DEBUG` +# outputs debug information +# :variable:`MATLAB_ADDITIONAL_VERSIONS` +# additional versions of Matlab for the automatic retrieval of the installed +# versions. +# +# Variables defined by the module +# ------------------------------- +# +# Result variables +# ^^^^^^^^^^^^^^^^ +# +# ``Matlab_FOUND`` +# ``TRUE`` if the Matlab installation is found, ``FALSE`` +# otherwise. All variable below are defined if Matlab is found. +# ``Matlab_ROOT_DIR`` +# the final root of the Matlab installation determined by the FindMatlab +# module. +# ``Matlab_MAIN_PROGRAM`` +# the Matlab binary program. Available only if the component ``MAIN_PROGRAM`` +# is given in the :command:`find_package` directive. +# ``Matlab_INCLUDE_DIRS`` +# the path of the Matlab libraries headers +# ``Matlab_MEX_LIBRARY`` +# library for mex, always available. +# ``Matlab_MX_LIBRARY`` +# mx library of Matlab (arrays). Available only if the component +# ``MX_LIBRARY`` has been requested. +# ``Matlab_ENG_LIBRARY`` +# Matlab engine library. Available only if the component ``ENG_LIBRARY`` +# is requested. +# ``Matlab_LIBRARIES`` +# the whole set of libraries of Matlab +# ``Matlab_MEX_COMPILER`` +# the mex compiler of Matlab. Currently not used. +# Available only if the component ``MEX_COMPILER`` is asked +# +# Cached variables +# ^^^^^^^^^^^^^^^^ +# +# ``Matlab_MEX_EXTENSION`` +# the extension of the mex files for the current platform (given by Matlab). +# ``Matlab_ROOT_DIR`` +# the location of the root of the Matlab installation found. If this value +# is changed by the user, the result variables are recomputed. +# +# Provided macros +# --------------- +# +# :command:`matlab_get_version_from_release_name` +# returns the version from the release name +# :command:`matlab_get_release_name_from_version` +# returns the release name from the Matlab version +# +# Provided functions +# ------------------ +# +# :command:`matlab_add_mex` +# adds a target compiling a MEX file. +# :command:`matlab_add_unit_test` +# adds a Matlab unit test file as a test to the project. +# :command:`matlab_extract_all_installed_versions_from_registry` +# parses the registry for all Matlab versions. Available on Windows only. +# The part of the registry parsed is dependent on the host processor +# :command:`matlab_get_all_valid_matlab_roots_from_registry` +# returns all the possible Matlab paths, according to a previously +# given list. Only the existing/accessible paths are kept. This is mainly +# useful for the searching all possible Matlab installation. +# :command:`matlab_get_mex_suffix` +# returns the suffix to be used for the mex files +# (platform/architecture dependant) +# :command:`matlab_get_version_from_matlab_run` +# returns the version of Matlab, given the full directory of the Matlab program. +# +# +# Known issues +# ------------ +# +# **Symbol clash in a MEX target** +# By default, every symbols inside a MEX +# file defined with the command :command:`matlab_add_mex` have hidden +# visibility, except for the entry point. This is the default behaviour of +# the MEX compiler, which lowers the risk of symbol collision between the +# libraries shipped with Matlab, and the libraries to which the MEX file is +# linking to. This is also the default on Windows platforms. +# +# However, this is not sufficient in certain case, where for instance your +# MEX file is linking against libraries that are already loaded by Matlab, +# even if those libraries have different SONAMES. +# A possible solution is to hide the symbols of the libraries to which the +# MEX target is linking to. This can be achieved in GNU GCC compilers with +# the linker option ``-Wl,--exclude-libs,ALL``. +# +# **Tests using GPU resources** +# in case your MEX file is using the GPU and +# in order to be able to run unit tests on this MEX file, the GPU resources +# should be properly released by Matlab. A possible solution is to make +# Matlab aware of the use of the GPU resources in the session, which can be +# performed by a command such as ``D = gpuDevice()`` at the beginning of +# the test script (or via a fixture). +# +# +# Reference +# -------------- +# +# .. variable:: Matlab_ROOT_DIR +# +# The root folder of the Matlab installation. If set before the call to +# :command:`find_package`, the module will look for the components in that +# path. If not set, then an automatic search of Matlab +# will be performed. If set, it should point to a valid version of Matlab. +# +# .. variable:: MATLAB_FIND_DEBUG +# +# If set, the lookup of Matlab and the intermediate configuration steps are +# outputted to the console. +# +# .. variable:: MATLAB_ADDITIONAL_VERSIONS +# +# If set, specifies additional versions of Matlab that may be looked for. +# The variable should be a list of strings, organised by pairs of release +# name and versions, such as follows:: +# +# set(MATLAB_ADDITIONAL_VERSIONS +# "release_name1=corresponding_version1" +# "release_name2=corresponding_version2" +# ... +# ) +# +# Example:: +# +# set(MATLAB_ADDITIONAL_VERSIONS +# "R2013b=8.2" +# "R2013a=8.1" +# "R2012b=8.0") +# +# The order of entries in this list matters when several versions of +# Matlab are installed. The priority is set according to the ordering in +# this list. #============================================================================= -# Copyright 2005-2009 Kitware, Inc. +# Copyright 2014-2015 Raffi Enficiaud, Max Planck Society # # Distributed under the OSI-approved BSD License (the "License"); # see accompanying file Copyright.txt for details. @@ -27,101 +215,1247 @@ # (To distribute this file outside of CMake, substitute the full # License text for the above reference.) -set(MATLAB_FOUND 0) -if(WIN32) - if(${CMAKE_GENERATOR} MATCHES "Visual Studio 6") - set(MATLAB_ROOT "[HKEY_LOCAL_MACHINE\\SOFTWARE\\MathWorks\\MATLAB\\7.0;MATLABROOT]/extern/lib/win32/microsoft/msvc60") +set(_FindMatlab_SELF_DIR "${CMAKE_CURRENT_LIST_DIR}") + +include(FindPackageHandleStandardArgs) +include(CheckCXXCompilerFlag) + + +# The currently supported versions. Other version can be added by the user by +# providing MATLAB_ADDITIONAL_VERSIONS +if(NOT MATLAB_ADDITIONAL_VERSIONS) + set(MATLAB_ADDITIONAL_VERSIONS) +endif() + +set(MATLAB_VERSIONS_MAPPING + "R2014a=8.3" + "R2013b=8.2" + "R2013a=8.1" + "R2012b=8.0" + "R2012a=7.14" + + "R2011b=7.13" + "R2011a=7.12" + "R2010b=7.11" + + ${MATLAB_ADDITIONAL_VERSIONS} + ) + + +# temporary folder for all Matlab runs +set(_matlab_temporary_folder ${CMAKE_BINARY_DIR}/Matlab) + +if(NOT EXISTS "${_matlab_temporary_folder}") + file(MAKE_DIRECTORY "${_matlab_temporary_folder}") +endif() + +#.rst: +# .. command:: matlab_get_version_from_release_name +# +# Returns the version of Matlab (17.58) from a release name (R2017k) +macro (matlab_get_version_from_release_name release_name version_name) + + string(REGEX MATCHALL "${release_name}=([0-9]+\\.?[0-9]*)" _matched ${MATLAB_VERSIONS_MAPPING}) + + set(${version_name} "") + if(NOT _matched STREQUAL "") + set(${version_name} ${CMAKE_MATCH_1}) else() - if(${CMAKE_GENERATOR} MATCHES "Visual Studio 7") - # Assume people are generally using 7.1, - # if using 7.0 need to link to: ../extern/lib/win32/microsoft/msvc70 - set(MATLAB_ROOT "[HKEY_LOCAL_MACHINE\\SOFTWARE\\MathWorks\\MATLAB\\7.0;MATLABROOT]/extern/lib/win32/microsoft/msvc71") - else() - if(${CMAKE_GENERATOR} MATCHES "Borland") - # Same here, there are also: bcc50 and bcc51 directories - set(MATLAB_ROOT "[HKEY_LOCAL_MACHINE\\SOFTWARE\\MathWorks\\MATLAB\\7.0;MATLABROOT]/extern/lib/win32/microsoft/bcc54") - else() - if(MATLAB_FIND_REQUIRED) - message(FATAL_ERROR "Generator not compatible: ${CMAKE_GENERATOR}") - endif() + message(WARNING "The release name ${release_name} is not registered") + endif() + unset(_matched) + +endmacro() + + + + + +#.rst: +# .. command:: matlab_get_release_name_from_version +# +# Returns the release name (R2017k) from the version of Matlab (17.58) +macro (matlab_get_release_name_from_version version release_name) + + set(${release_name} "") + foreach(_var IN LISTS MATLAB_VERSIONS_MAPPING) + string(REGEX MATCHALL "(.+)=${version}" _matched ${_var}) + if(NOT _matched STREQUAL "") + set(${release_name} ${CMAKE_MATCH_1}) + break() + endif() + endforeach(_var) + + unset(_var) + unset(_matched) + if(${release_name} STREQUAL "") + message(WARNING "The version ${version} is not registered") + endif() + +endmacro() + + + + + +# extracts all the supported release names (R2017k...) of Matlab +# internal use +macro(matlab_get_supported_releases list_releases) + set(${list_releases}) + foreach(_var IN LISTS MATLAB_VERSIONS_MAPPING) + string(REGEX MATCHALL "(.+)=([0-9]+\\.?[0-9]*)" _matched ${_var}) + if(NOT _matched STREQUAL "") + list(APPEND ${list_releases} ${CMAKE_MATCH_1}) + endif() + unset(_matched) + unset(CMAKE_MATCH_1) + endforeach(_var) + unset(_var) +endmacro() + + + +# extracts all the supported versions of Matlab +# internal use +macro(matlab_get_supported_versions list_versions) + set(${list_versions}) + foreach(_var IN LISTS MATLAB_VERSIONS_MAPPING) + string(REGEX MATCHALL "(.+)=([0-9]+\\.?[0-9]*)" _matched ${_var}) + if(NOT _matched STREQUAL "") + list(APPEND ${list_versions} ${CMAKE_MATCH_2}) + endif() + unset(_matched) + unset(CMAKE_MATCH_1) + endforeach(_var) + unset(_var) +endmacro() + + +#.rst: +# .. command:: matlab_extract_all_installed_versions_from_registry +# +# This function parses the registry and founds the Matlab versions that are +# installed. The found versions are returned in `matlab_versions`. +# Set `win64` to `TRUE` if the 64 bit version of Matlab should be looked for +# The returned list contains all versions under +# ``HKLM\\SOFTWARE\\Mathworks\\MATLAB`` or an empty list in case an error +# occurred (or nothing found). +# +# .. note:: +# +# Only the versions are provided. No check is made over the existence of the +# installation referenced in the registry, +# +function(matlab_extract_all_installed_versions_from_registry win64 matlab_versions) + + if(NOT CMAKE_HOST_WIN32) + message(FATAL_ERROR "This macro can only be called by a windows host (call to reg.exe") + endif() + + + if(${win64} AND ${CMAKE_HOST_SYSTEM_PROCESSOR} MATCHES "64") + set(APPEND_REG "/reg:64") + else() + set(APPEND_REG "/reg:32") + endif() + + # /reg:64 should be added on 64 bits capable OSs in order to enable the + # redirection of 64 bits applications + execute_process( + COMMAND reg query HKEY_LOCAL_MACHINE\\SOFTWARE\\Mathworks\\MATLAB /f * /k ${APPEND_REG} + RESULT_VARIABLE resultMatlab + OUTPUT_VARIABLE varMatlab + ERROR_VARIABLE errMatlab + INPUT_FILE NUL + ) + + + set(matlabs_from_registry) + if(${resultMatlab} EQUAL 0) + + string( + REGEX MATCHALL "MATLAB\\\\([0-9]+(\\.[0-9]+)?)" + matlab_versions_regex ${varMatlab}) + + foreach(match IN LISTS matlab_versions_regex) + string( + REGEX MATCH "MATLAB\\\\(([0-9]+)(\\.([0-9]+))?)" + current_match ${match}) + + set(_matlab_current_version ${CMAKE_MATCH_1}) + set(current_matlab_version_major ${CMAKE_MATCH_2}) + set(current_matlab_version_minor ${CMAKE_MATCH_4}) + if(NOT current_matlab_version_minor) + set(current_matlab_version_minor "0") endif() + + list(APPEND matlabs_from_registry ${_matlab_current_version}) + unset(_matlab_current_version) + endforeach(match) + + endif() + + if(matlabs_from_registry) + list(REMOVE_DUPLICATES matlabs_from_registry) + list(SORT matlabs_from_registry) + list(REVERSE matlabs_from_registry) + endif() + + set(${matlab_versions} ${matlabs_from_registry} PARENT_SCOPE) + +endfunction() + + + +# (internal) +macro(extract_matlab_versions_from_registry_brute_force matlab_versions) + # get the supported versions + set(matlab_supported_versions) + matlab_get_supported_versions(matlab_supported_versions) + + + # this is a manual population of the versions we want to look for + # this can be done as is, but preferably with the call to + # matlab_get_supported_versions and variable + + # populating the versions we want to look for + # set(matlab_supported_versions) + + # # Matlab 7 + # set(matlab_major 7) + # foreach(current_matlab_minor RANGE 4 20) + # list(APPEND matlab_supported_versions "${matlab_major}.${current_matlab_minor}") + # endforeach(current_matlab_minor) + + # # Matlab 8 + # set(matlab_major 8) + # foreach(current_matlab_minor RANGE 0 5) + # list(APPEND matlab_supported_versions "${matlab_major}.${current_matlab_minor}") + # endforeach(current_matlab_minor) + + # # taking into account the possible additional versions provided by the user + # if(DEFINED MATLAB_ADDITIONAL_VERSIONS) + # list(APPEND matlab_supported_versions MATLAB_ADDITIONAL_VERSIONS) + # endif() + + + # we order from more recent to older + if(matlab_supported_versions) + list(REMOVE_DUPLICATES matlab_supported_versions) + list(SORT matlab_supported_versions) + list(REVERSE matlab_supported_versions) + endif() + + + set(${matlab_versions} ${matlab_supported_versions}) + + +endmacro() + + + + +#.rst: +# .. command:: matlab_get_all_valid_matlab_roots_from_registry +# +# Populates the Matlab root with valid versions of Matlab. +# The returned matlab_roots is organized in pairs +# ``(version_number,matlab_root_path)``. +# +# :: +# +# matlab_get_all_valid_matlab_roots_from_registry( +# matlab_versions +# matlab_roots) +# +# ``matlab_versions`` +# the versions of each of the Matlab installations +# ``matlab_roots`` +# the location of each of the Matlab installations +function(matlab_get_all_valid_matlab_roots_from_registry matlab_versions matlab_roots) + + # The matlab_versions comes either from + # extract_matlab_versions_from_registry_brute_force or + # matlab_extract_all_installed_versions_from_registry. + + + set(_matlab_roots_list ) + foreach(_matlab_current_version ${matlab_versions}) + get_filename_component( + current_MATLAB_ROOT + "[HKEY_LOCAL_MACHINE\\SOFTWARE\\MathWorks\\MATLAB\\${_matlab_current_version};MATLABROOT]" + ABSOLUTE) + + if(EXISTS ${current_MATLAB_ROOT}) + list(APPEND _matlab_roots_list ${_matlab_current_version} ${current_MATLAB_ROOT}) + endif() + + endforeach(_matlab_current_version) + unset(_matlab_current_version) + set(${matlab_roots} ${_matlab_roots_list} PARENT_SCOPE) + unset(_matlab_roots_list) +endfunction() + +#.rst: +# .. command:: matlab_get_mex_suffix +# +# Returns the extension of the mex files (the suffixes). +# This function should not be called before the appropriate Matlab root has +# been found. +# +# :: +# +# matlab_get_mex_suffix( +# matlab_root +# mex_suffix) +# +# ``matlab_root`` +# the root of the Matlab installation +# ``mex_suffix`` +# the variable name in which the suffix will be returned. +function(matlab_get_mex_suffix matlab_root mex_suffix) + + # todo setup the extension properly. Currently I do not know if this is + # sufficient for all win32 distributions. + # there is also CMAKE_EXECUTABLE_SUFFIX that could be tweaked + set(mexext_suffix "") + if(WIN32) + list(APPEND mexext_suffix ".bat") + endif() + + # we first try without suffix, since cmake does not understand a list with + # one empty string element + find_program( + Matlab_MEXEXTENSIONS_PROG + NAMES mexext + PATHS ${matlab_root}/bin + DOC "Matlab MEX extension provider" + NO_DEFAULT_PATH + ) + + foreach(current_mexext_suffix IN LISTS mexext_suffix) + if(NOT DEFINED Matlab_MEXEXTENSIONS_PROG OR NOT Matlab_MEXEXTENSIONS_PROG) + # this call should populate the cache automatically + find_program( + Matlab_MEXEXTENSIONS_PROG + "mexext${current_mexext_suffix}" + PATHS ${matlab_root}/bin + DOC "Matlab MEX extension provider" + NO_DEFAULT_PATH + ) + endif() + endforeach(current_mexext_suffix) + + + # the program has been found? + if((NOT Matlab_MEXEXTENSIONS_PROG) OR (NOT EXISTS ${Matlab_MEXEXTENSIONS_PROG})) + if(MATLAB_FIND_DEBUG) + message(WARNING "[MATLAB] Cannot found mexext program. Matlab root is ${matlab_root}") + endif() + unset(Matlab_MEXEXTENSIONS_PROG CACHE) + return() + endif() + + set(_matlab_mex_extension) + + set(devnull) + if(UNIX) + set(devnull INPUT_FILE /dev/null) + elseif(WIN32) + set(devnull INPUT_FILE NUL) + endif() + + execute_process( + COMMAND ${Matlab_MEXEXTENSIONS_PROG} + OUTPUT_VARIABLE _matlab_mex_extension + ERROR_VARIABLE _matlab_mex_extension_error + ${devnull}) + string(STRIP ${_matlab_mex_extension} _matlab_mex_extension) + + unset(Matlab_MEXEXTENSIONS_PROG CACHE) + set(${mex_suffix} ${_matlab_mex_extension} PARENT_SCOPE) +endfunction() + + + + +#.rst: +# .. command:: matlab_get_version_from_matlab_run +# +# This function runs Matlab program specified on arguments and extracts its +# version. +# +# :: +# +# matlab_get_version_from_matlab_run( +# matlab_binary_path +# matlab_list_versions) +# +# ``matlab_binary_path`` +# the location of the `matlab` binary executable +# ``matlab_list_versions`` +# the version extracted from Matlab +function(matlab_get_version_from_matlab_run matlab_binary_program matlab_list_versions) + + set(${matlab_list_versions} "" PARENT_SCOPE) + + + if(MATLAB_FIND_DEBUG) + message(STATUS "[MATLAB] Determining the version of Matlab from ${matlab_binary_program}") + endif() + + if(EXISTS "${_matlab_temporary_folder}/matlabVersionLog.cmaketmp") + if(MATLAB_FIND_DEBUG) + message(STATUS "[MATLAB] Removing previous ${_matlab_temporary_folder}/matlabVersionLog.cmaketmp file") endif() + file(REMOVE "${_matlab_temporary_folder}/matlabVersionLog.cmaketmp") + endif() + + + # the log file is needed since on windows the command executes in a new + # window and it is not possible to get back the answer of Matlab + # the -wait command is needed on windows, otherwise the call returns + # immediately after the program launches itself. + if(WIN32) + set(_matlab_additional_commands "-wait") endif() - find_library(MATLAB_MEX_LIBRARY - libmex - ${MATLAB_ROOT} + + set(devnull) + if(UNIX) + set(devnull INPUT_FILE /dev/null) + elseif(WIN32) + set(devnull INPUT_FILE NUL) + endif() + + # timeout set to 30 seconds, in case it does not start + # note as said before OUTPUT_VARIABLE cannot be used in a platform + # independent manner however, not setting it would flush the output of Matlab + # in the current console (unix variant) + execute_process( + COMMAND "${matlab_binary_program}" -nosplash -nojvm ${_matlab_additional_commands} -logfile "matlabVersionLog.cmaketmp" -nodesktop -nodisplay -r "version, exit" + OUTPUT_VARIABLE _matlab_version_from_cmd_dummy + RESULT_VARIABLE _matlab_result_version_call + ERROR_VARIABLE _matlab_result_version_call_error + TIMEOUT 30 + WORKING_DIRECTORY "${_matlab_temporary_folder}" + ${devnull} ) - find_library(MATLAB_MX_LIBRARY - libmx - ${MATLAB_ROOT} + + + if(${_matlab_result_version_call}) + if(MATLAB_FIND_DEBUG) + message(WARNING "[MATLAB] Unable to determine the version of Matlab. Matlab call returned with error ${_matlab_result_version_call}.") + endif() + return() + elseif(NOT EXISTS "${_matlab_temporary_folder}/matlabVersionLog.cmaketmp") + if(MATLAB_FIND_DEBUG) + message(WARNING "[MATLAB] Unable to determine the version of Matlab. The log file does not exist.") + endif() + return() + endif() + + # if successful, read back the log + file(READ "${_matlab_temporary_folder}/matlabVersionLog.cmaketmp" _matlab_version_from_cmd) + file(REMOVE "${_matlab_temporary_folder}/matlabVersionLog.cmaketmp") + + set(index -1) + string(FIND ${_matlab_version_from_cmd} "ans" index) + if(index EQUAL -1) + + if(MATLAB_FIND_DEBUG) + message(WARNING "[MATLAB] Cannot find the version of Matlab returned by the run.") + endif() + + else() + set(matlab_list_of_all_versions_tmp) + + string(SUBSTRING ${_matlab_version_from_cmd} ${index} -1 substring_ans) + string( + REGEX MATCHALL "ans[\r\n\t ]*=[\r\n\t ]*([0-9]+(\\.[0-9]+)?)" + matlab_versions_regex + ${substring_ans}) + foreach(match IN LISTS matlab_versions_regex) + string( + REGEX MATCH "ans[\r\n\t ]*=[\r\n\t ]*(([0-9]+)(\\.([0-9]+))?)" + current_match ${match}) + + list(APPEND matlab_list_of_all_versions_tmp ${CMAKE_MATCH_1}) + endforeach() + if(matlab_list_of_all_versions_tmp) + list(REMOVE_DUPLICATES matlab_list_of_all_versions_tmp) + endif() + set(${matlab_list_versions} ${matlab_list_of_all_versions_tmp} PARENT_SCOPE) + + endif() + +endfunction() + + +#.rst: +# .. command:: matlab_add_unit_test +# +# Adds a Matlab unit test to the test set of cmake/ctest. +# This command requires the component ``MAIN_PROGRAM``. +# The unit test uses the Matlab unittest framework (default, available +# starting Matlab 2013b+) except if the option ``NO_UNITTEST_FRAMEWORK`` +# is given. +# +# The function expects one Matlab test script file to be given. +# In the case ``NO_UNITTEST_FRAMEWORK`` is given, the unittest script file +# should contain the script to be run, plus an exit command with the exit +# value. This exit value will be passed to the ctest framework (0 success, +# non 0 failure). Additional arguments accepted by :command:`add_test` can be +# passed through ``TEST_ARGS`` (eg. ``CONFIGURATION <config> ...``). +# +# :: +# +# matlab_add_unit_test( +# NAME <name> +# UNITTEST_FILE matlab_file_containing_unittest.m +# [UNITTEST_PRECOMMAND matlab_command_to_run] +# [TIMEOUT timeout] +# [ADDITIONAL_PATH path1 [path2 ...]] +# [MATLAB_ADDITIONAL_STARTUP_OPTIONS option1 [option2 ...]] +# [TEST_ARGS arg1 [arg2 ...]] +# [NO_UNITTEST_FRAMEWORK] +# ) +# +# The function arguments are: +# +# ``NAME`` +# name of the unittest in ctest. +# ``UNITTEST_FILE`` +# the matlab unittest file. Its path will be automatically +# added to the Matlab path. +# ``UNITTEST_PRECOMMAND`` +# Matlab script command to be ran before the file +# containing the test (eg. GPU device initialisation based on CMake +# variables). +# ``TIMEOUT`` +# the test timeout in seconds. Defaults to 180 seconds as the +# Matlab unit test may hang. +# ``ADDITIONAL_PATH`` +# a list of paths to add to the Matlab path prior to +# running the unit test. +# ``MATLAB_ADDITIONAL_STARTUP_OPTIONS`` +# a list of additional option in order +# to run Matlab from the command line. +# ``TEST_ARGS`` +# Additional options provided to the add_test command. These +# options are added to the default options (eg. "CONFIGURATIONS Release") +# ``NO_UNITTEST_FRAMEWORK`` +# when set, indicates that the test should not +# use the unittest framework of Matlab (available for versions >= R2013a). +# +function(matlab_add_unit_test) + + if(NOT Matlab_MAIN_PROGRAM) + message(FATAL_ERROR "[MATLAB] This functionality needs the MAIN_PROGRAM component (not default)") + endif() + + set(options NO_UNITTEST_FRAMEWORK) + set(oneValueArgs NAME UNITTEST_PRECOMMAND UNITTEST_FILE TIMEOUT) + set(multiValueArgs ADDITIONAL_PATH MATLAB_ADDITIONAL_STARTUP_OPTIONS TEST_ARGS) + + set(prefix _matlab_unittest_prefix) + cmake_parse_arguments(${prefix} "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN} ) + + if(NOT ${prefix}_NAME) + message(FATAL_ERROR "[MATLAB] The Matlab test name cannot be empty") + endif() + + add_test(NAME ${${prefix}_NAME} + COMMAND ${CMAKE_COMMAND} + -Dtest_name=${${prefix}_NAME} + -Dadditional_paths=${${prefix}_ADDITIONAL_PATH} + -Dtest_timeout=${${prefix}_TIMEOUT} + -Doutput_directory=${_matlab_temporary_folder} + -DMatlab_PROGRAM=${Matlab_MAIN_PROGRAM} + -Dno_unittest_framework=${${prefix}_NO_UNITTEST_FRAMEWORK} + -DMatlab_ADDITIONNAL_STARTUP_OPTIONS=${${prefix}_MATLAB_ADDITIONAL_STARTUP_OPTIONS} + -Dunittest_file_to_run=${${prefix}_UNITTEST_FILE} + -Dcmd_to_run_before_test=${${prefix}_UNITTEST_PRECOMMAND} + -P ${_FindMatlab_SELF_DIR}/MatlabTestsRedirect.cmake + ${${prefix}_TEST_ARGS} + ${${prefix}_UNPARSED_ARGUMENTS} + ) +endfunction() + + +#.rst: +# .. command:: matlab_add_mex +# +# Adds a Matlab MEX target. +# This commands compiles the given sources with the current tool-chain in +# order to produce a MEX file. The final name of the produced output may be +# specified, as well as additional link libraries, and a documentation entry +# for the MEX file. Remaining arguments of the call are passed to the +# :command:`add_library` command. +# +# :: +# +# matlab_add_mex( +# NAME <name> +# SRC src1 [src2 ...] +# [OUTPUT_NAME output_name] +# [DOCUMENTATION file.txt] +# [LINK_TO target1 target2 ...] +# [...] +# ) +# +# ``NAME`` +# name of the target. +# ``SRC`` +# list of tje source files. +# ``LINK_TO`` +# a list of additional link dependencies. The target links to ``libmex`` +# by default. If ``Matlab_MX_LIBRARY`` is defined, it also +# links to ``libmx``. +# ``OUTPUT_NAME`` +# if given, overrides the default name. The default name is +# the name of the target without any prefix and +# with ``Matlab_MEX_EXTENSION`` suffix. +# ``DOCUMENTATION`` +# if given, the file ``file.txt`` will be considered as +# being the documentation file for the MEX file. This file is copied into +# the same folder without any processing, with the same name as the final +# mex file, and with extension `.m`. In that case, typing ``help <name>`` +# in Matlab prints the documentation contained in this file. +# +# The documentation file is not processed and should be in the following +# format: +# +# :: +# +# % This is the documentation +# function ret = mex_target_output_name(input1) +# +function(matlab_add_mex ) + + if(NOT WIN32) + # we do not need all this on Windows + # pthread options + check_cxx_compiler_flag(-pthread HAS_MINUS_PTHREAD) + # we should use try_compile instead, the link flags are discarded from + # this compiler_flag function. + #check_cxx_compiler_flag(-Wl,--exclude-libs,ALL HAS_SYMBOL_HIDING_CAPABILITY) + + endif() + + set(oneValueArgs NAME DOCUMENTATION OUTPUT_NAME) + set(multiValueArgs LINK_TO SRC) + + set(prefix _matlab_addmex_prefix) + cmake_parse_arguments(${prefix} "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN} ) + + if(NOT ${prefix}_NAME) + message(FATAL_ERROR "[MATLAB] The MEX target name cannot be empty") + endif() + + if(NOT ${prefix}_OUTPUT_NAME) + set(${prefix}_OUTPUT_NAME ${${prefix}_NAME}) + endif() + + add_library(${${prefix}_NAME} + SHARED + ${${prefix}_SRC} + ${${prefix}_DOCUMENTATION} + ${${prefix}_UNPARSED_ARGUMENTS}) + target_include_directories(${${prefix}_NAME} PRIVATE ${Matlab_INCLUDE_DIRS}) + + if(DEFINED Matlab_MX_LIBRARY) + target_link_libraries(${${prefix}_NAME} ${Matlab_MX_LIBRARY}) + endif() + + target_link_libraries(${${prefix}_NAME} ${Matlab_MEX_LIBRARY} ${${prefix}_LINK_TO}) + set_target_properties(${${prefix}_NAME} + PROPERTIES + PREFIX "" + OUTPUT_NAME ${${prefix}_OUTPUT_NAME} + SUFFIX ".${Matlab_MEX_EXTENSION}") + + + # documentation + if(NOT ${${prefix}_DOCUMENTATION} STREQUAL "") + get_target_property(output_name ${${prefix}_NAME} OUTPUT_NAME) + add_custom_command( + TARGET ${${prefix}_NAME} + PRE_BUILD + COMMAND ${CMAKE_COMMAND} -E copy_if_different ${${prefix}_DOCUMENTATION} $<TARGET_FILE_DIR:${${prefix}_NAME}>/${output_name}.m + COMMENT "Copy ${${prefix}_NAME} documentation file into the output folder" ) - find_library(MATLAB_ENG_LIBRARY - libeng - ${MATLAB_ROOT} + endif() # documentation + + # entry point in the mex file + taking care of visibility and symbol clashes. + if(WIN32) + set_target_properties(${${prefix}_NAME} + PROPERTIES + DEFINE_SYMBOL "DLL_EXPORT_SYM=__declspec(dllexport)") + else() + + if(HAS_MINUS_PTHREAD AND NOT APPLE) + # Apparently, compiling with -pthread generated the proper link flags + # and some defines at compilation + target_compile_options(${${prefix}_NAME} PRIVATE "-pthread") + endif() + + + # if we do not do that, the symbols linked from eg. boost remain weak and + # then clash with the ones defined in the matlab process. So by default + # the symbols are hidden. + # This also means that for shared libraries (like MEX), the entry point + # should be explicitly declared with default visibility, otherwise Matlab + # cannot find the entry point. + # Note that this is particularly meaningful if the MEX wrapper itself + # contains symbols that are clashing with Matlab (that are compiled in the + # MEX file). In order to propagate the visibility options to the libraries + # to which the MEX file is linked against, the -Wl,--exclude-libs,ALL + # option should also be specified. + + set_target_properties(${${prefix}_NAME} + PROPERTIES + CXX_VISIBILITY_PRESET "hidden" + C_VISIBILITY_PRESET "hidden" + VISIBILITY_INLINES_HIDDEN "hidden" ) - find_path(MATLAB_INCLUDE_DIR - "mex.h" - "[HKEY_LOCAL_MACHINE\\SOFTWARE\\MathWorks\\MATLAB\\7.0;MATLABROOT]/extern/include" + # get_target_property( + # _previous_link_flags + # ${${prefix}_NAME} + # LINK_FLAGS) + # if(NOT _previous_link_flags) + # set(_previous_link_flags) + # endif() + + # if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU") + # set_target_properties(${${prefix}_NAME} + # PROPERTIES + # LINK_FLAGS "${_previous_link_flags} -Wl,--exclude-libs,ALL" + # # -Wl,--version-script=${_FindMatlab_SELF_DIR}/MatlabLinuxVisibility.map" + # ) + # elseif("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang") + # # in this case, all other symbols become hidden. + # set_target_properties(${${prefix}_NAME} + # PROPERTIES + # LINK_FLAGS "${_previous_link_flags} -Wl,-exported_symbol,_mexFunction" + # #-Wl,-exported_symbols_list,${_FindMatlab_SELF_DIR}/MatlabOSXVisilibity.map" + # ) + # endif() + + + + set_target_properties(${${prefix}_NAME} + PROPERTIES + DEFINE_SYMBOL "DLL_EXPORT_SYM=__attribute__ ((visibility (\"default\")))" ) -else() - if(CMAKE_SIZEOF_VOID_P EQUAL 4) - # Regular x86 - set(MATLAB_ROOT - /usr/local/matlab-7sp1/bin/glnx86/ - /opt/matlab-7sp1/bin/glnx86/ - $ENV{HOME}/matlab-7sp1/bin/glnx86/ - $ENV{HOME}/redhat-matlab/bin/glnx86/ + + + endif() + +endfunction() + + +# (internal) +# Used to get the version of matlab, using caching. This basically transforms the +# output of the root list, with possible unknown version, to a version +# +function(_Matlab_get_version_from_root matlab_root matlab_known_version matlab_final_version) + + # if the version is not trivial, we query matlab for that + # we keep track of the location of matlab that induced this version + #if(NOT DEFINED Matlab_PROG_VERSION_STRING_AUTO_DETECT) + # set(Matlab_PROG_VERSION_STRING_AUTO_DETECT "" CACHE INTERNAL "internal matlab location for the discovered version") + #endif() + + if(NOT ${matlab_known_version} STREQUAL "NOTFOUND") + # the version is known, we just return it + set(${matlab_final_version} ${matlab_known_version} PARENT_SCOPE) + set(Matlab_VERSION_STRING_INTERNAL ${matlab_known_version} CACHE INTERNAL "Matlab version (automatically determined)" FORCE) + return() + endif() + + # + set(_matlab_current_program ${Matlab_MAIN_PROGRAM}) + + # do we already have a matlab program? + if(NOT _matlab_current_program) + + set(_find_matlab_options) + if(matlab_root AND EXISTS ${matlab_root}) + set(_find_matlab_options PATHS ${matlab_root} ${matlab_root}/bin NO_DEFAULT_PATH) + endif() + + find_program( + _matlab_current_program + matlab + ${_find_matlab_options} + DOC "Matlab main program" ) + endif() + + if(NOT _matlab_current_program OR NOT EXISTS ${_matlab_current_program}) + # if not found, clear the dependent variables + if(MATLAB_FIND_DEBUG) + message(WARNING "[MATLAB] Cannot find the main matlab program under ${matlab_root}") + endif() + set(Matlab_PROG_VERSION_STRING_AUTO_DETECT "" CACHE INTERNAL "internal matlab location for the discovered version" FORCE) + set(Matlab_VERSION_STRING_INTERNAL "" CACHE INTERNAL "internal matlab location for the discovered version" FORCE) + unset(_matlab_current_program) + unset(_matlab_current_program CACHE) + return() + endif() + + # full real path for path comparison + get_filename_component(_matlab_main_real_path_tmp "${_matlab_current_program}" REALPATH) + unset(_matlab_current_program) + unset(_matlab_current_program CACHE) + + # is it the same as the previous one? + if(_matlab_main_real_path_tmp STREQUAL Matlab_PROG_VERSION_STRING_AUTO_DETECT) + set(${matlab_final_version} ${Matlab_VERSION_STRING_INTERNAL} PARENT_SCOPE) + return() + endif() + + # update the location of the program + set(Matlab_PROG_VERSION_STRING_AUTO_DETECT ${_matlab_main_real_path_tmp} CACHE INTERNAL "internal matlab location for the discovered version" FORCE) + + set(matlab_list_of_all_versions) + matlab_get_version_from_matlab_run("${Matlab_PROG_VERSION_STRING_AUTO_DETECT}" matlab_list_of_all_versions) + + list(GET matlab_list_of_all_versions 0 _matlab_version_tmp) + + # set the version into the cache + set(Matlab_VERSION_STRING_INTERNAL ${_matlab_version_tmp} CACHE INTERNAL "Matlab version (automatically determined)" FORCE) + + # warning, just in case several versions found (should not happen) + list(LENGTH matlab_list_of_all_versions list_of_all_versions_length) + if((${list_of_all_versions_length} GREATER 1) AND MATLAB_FIND_DEBUG) + message(WARNING "[MATLAB] Found several versions, taking the first one (versions found ${matlab_list_of_all_versions})") + endif() + + # return the updated value + set(${matlab_final_version} ${Matlab_VERSION_STRING_INTERNAL} PARENT_SCOPE) + +endfunction() + + + + + + + +# ################################### +# Exploring the possible Matlab_ROOTS + +# this variable will get all Matlab installations found in the current system. +set(_matlab_possible_roots) + + + +if(Matlab_ROOT_DIR) + # if the user specifies a possible root, we keep this one + + if(NOT EXISTS ${Matlab_ROOT_DIR}) + # if Matlab_ROOT_DIR specified but erroneous + if(MATLAB_FIND_DEBUG) + message(WARNING "[MATLAB] the specified path for Matlab_ROOT_DIR does not exist (${Matlab_ROOT_DIR})") + endif() else() - # AMD64: - set(MATLAB_ROOT - /usr/local/matlab-7sp1/bin/glnxa64/ - /opt/matlab-7sp1/bin/glnxa64/ - $ENV{HOME}/matlab7_64/bin/glnxa64/ - $ENV{HOME}/matlab-7sp1/bin/glnxa64/ - $ENV{HOME}/redhat-matlab/bin/glnxa64/ - ) + # NOTFOUND indicates the code below to search for the version automatically + if(NOT DEFINED Matlab_VERSION_STRING_INTERNAL) + list(APPEND _matlab_possible_roots "NOTFOUND" ${Matlab_ROOT_DIR}) # empty version + else() + list(APPEND _matlab_possible_roots ${Matlab_VERSION_STRING_INTERNAL} ${Matlab_ROOT_DIR}) # cached version + endif() + endif() + + +else() + + # if the user does not specify the possible installation root, we look for + # one installation using the appropriate heuristics + + if(WIN32) + + # On WIN32, we look for Matlab installation in the registry + # if unsuccessful, we look for all known revision and filter the existing + # ones. + + # testing if we are able to extract the needed information from the registry + set(_matlab_versions_from_registry) + matlab_extract_all_installed_versions_from_registry(CMAKE_CL_64 _matlab_versions_from_registry) + + # the returned list is empty, doing the search on all known versions + if(NOT _matlab_versions_from_registry) + + if(MATLAB_FIND_DEBUG) + message(STATUS "[MATLAB] Search for Matlab from the registry unsuccessful, testing all supported versions") + endif() + + extract_matlab_versions_from_registry_brute_force(_matlab_versions_from_registry) + endif() + + # filtering the results with the registry keys + matlab_get_all_valid_matlab_roots_from_registry("${_matlab_versions_from_registry}" _matlab_possible_roots) + unset(_matlab_versions_from_registry) + + elseif(APPLE) + + # on mac, we look for the /Application paths + # this corresponds to the behaviour on Windows. On Linux, we do not have + # any other guess. + matlab_get_supported_releases(_matlab_releases) + if(MATLAB_FIND_DEBUG) + message(STATUS "[MATLAB] Matlab supported versions ${_matlab_releases}. If more version should be supported " + "the variable MATLAB_ADDITIONAL_VERSIONS can be set according to the documentation") + endif() + + foreach(_matlab_current_release IN LISTS _matlab_releases) + set(_matlab_full_string "/Applications/MATLAB_${_matlab_current_release}.app") + if(EXISTS ${_matlab_full_string}) + set(_matlab_current_version) + matlab_get_version_from_release_name("${_matlab_current_release}" _matlab_current_version) + if(MATLAB_FIND_DEBUG) + message(STATUS "[MATLAB] Found version ${_matlab_current_release} (${_matlab_current_version}) in ${_matlab_full_string}") + endif() + list(APPEND _matlab_possible_roots ${_matlab_current_version} ${_matlab_full_string}) + unset(_matlab_current_version) + endif() + + unset(_matlab_full_string) + endforeach(_matlab_current_release) + + unset(_matlab_current_release) + unset(_matlab_releases) + endif() - find_library(MATLAB_MEX_LIBRARY - mex - ${MATLAB_ROOT} - ) - find_library(MATLAB_MX_LIBRARY - mx - ${MATLAB_ROOT} - ) - find_library(MATLAB_ENG_LIBRARY - eng - ${MATLAB_ROOT} - ) - find_path(MATLAB_INCLUDE_DIR - "mex.h" - "/usr/local/matlab-7sp1/extern/include/" - "/opt/matlab-7sp1/extern/include/" - "$ENV{HOME}/matlab-7sp1/extern/include/" - "$ENV{HOME}/redhat-matlab/extern/include/" - ) endif() -# This is common to UNIX and Win32: -set(MATLAB_LIBRARIES - ${MATLAB_MEX_LIBRARY} - ${MATLAB_MX_LIBRARY} - ${MATLAB_ENG_LIBRARY} + + +list(LENGTH _matlab_possible_roots _numbers_of_matlab_roots) +if(_numbers_of_matlab_roots EQUAL 0) + # if we have not found anything, we fall back on the PATH + + + # At this point, we have no other choice than trying to find it from PATH. + # If set by the user, this wont change + find_program( + _matlab_main_tmp + NAMES matlab) + + + if(_matlab_main_tmp) + # we then populate the list of roots, with empty version + if(MATLAB_FIND_DEBUG) + message(STATUS "[MATLAB] matlab found from PATH: ${_matlab_main_tmp}") + endif() + + # resolve symlinks + get_filename_component(_matlab_current_location "${_matlab_main_tmp}" REALPATH) + + # get the directory (the command below has to be run twice) + # this will be the matlab root + get_filename_component(_matlab_current_location "${_matlab_current_location}" DIRECTORY) + get_filename_component(_matlab_current_location "${_matlab_current_location}" DIRECTORY) # Matlab should be in bin + + list(APPEND _matlab_possible_roots "NOTFOUND" ${_matlab_current_location}) + + unset(_matlab_current_location) + + endif() + unset(_matlab_main_tmp CACHE) + +endif() + + + + + +if(MATLAB_FIND_DEBUG) + message(STATUS "[MATLAB] Matlab root folders are ${_matlab_possible_roots}") +endif() + + + + + +# take the first possible Matlab root +list(LENGTH _matlab_possible_roots _numbers_of_matlab_roots) +set(Matlab_VERSION_STRING "NOTFOUND") +if(_numbers_of_matlab_roots GREATER 0) + list(GET _matlab_possible_roots 0 Matlab_VERSION_STRING) + list(GET _matlab_possible_roots 1 Matlab_ROOT_DIR) + + # adding a warning in case of ambiguity + if(_numbers_of_matlab_roots GREATER 2 AND MATLAB_FIND_DEBUG) + message(WARNING "[MATLAB] Found several distributions of Matlab. Setting the current version to ${Matlab_VERSION_STRING} (located ${Matlab_ROOT_DIR})." + " If this is not the desired behaviour, provide the -DMatlab_ROOT_DIR=... on the command line") + endif() +endif() + + +# check if the root changed against the previous defined one, if so +# clear all the cached variables +if(DEFINED Matlab_ROOT_DIR_LAST_CACHED) + + if(NOT Matlab_ROOT_DIR_LAST_CACHED STREQUAL Matlab_ROOT_DIR) + set(_Matlab_cached_vars + Matlab_INCLUDE_DIRS + Matlab_MEX_LIBRARY + Matlab_MEX_COMPILER + Matlab_MAIN_PROGRAM + Matlab_MX_LIBRARY + Matlab_ENG_LIBRARY + Matlab_MEX_EXTENSION + + # internal + Matlab_MEXEXTENSIONS_PROG + Matlab_ROOT_DIR_LAST_CACHED + #Matlab_PROG_VERSION_STRING_AUTO_DETECT + Matlab_VERSION_STRING_INTERNAL + ) + foreach(_var IN LISTS _Matlab_cached_vars) + if(DEFINED ${_var}) + unset(${_var} CACHE) + endif() + endforeach() + endif() +endif() + +set(Matlab_ROOT_DIR_LAST_CACHED ${Matlab_ROOT_DIR} CACHE INTERNAL "last Matlab root dir location") +set(Matlab_ROOT_DIR ${Matlab_ROOT_DIR} CACHE PATH "Matlab installation root path" FORCE) + +# Fix the version, in case this one is NOTFOUND +_Matlab_get_version_from_root( + "${Matlab_ROOT_DIR}" + ${Matlab_VERSION_STRING} + Matlab_VERSION_STRING ) -if(MATLAB_INCLUDE_DIR AND MATLAB_LIBRARIES) - set(MATLAB_FOUND 1) + + + +if(MATLAB_FIND_DEBUG) + message(STATUS "[MATLAB] Current version is ${Matlab_VERSION_STRING} located ${Matlab_ROOT_DIR}") endif() -mark_as_advanced( - MATLAB_LIBRARIES - MATLAB_MEX_LIBRARY - MATLAB_MX_LIBRARY - MATLAB_ENG_LIBRARY - MATLAB_INCLUDE_DIR - MATLAB_FOUND - MATLAB_ROOT + + +if(Matlab_ROOT_DIR) + file(TO_CMAKE_PATH ${Matlab_ROOT_DIR} Matlab_ROOT_DIR) +endif() + +if(CMAKE_SIZEOF_VOID_P EQUAL 4) + set(_matlab_64Build FALSE) +else() + set(_matlab_64Build TRUE) +endif() + +if(APPLE) + set(_matlab_bin_prefix "mac") # i should be for intel + set(_matlab_bin_suffix_32bits "i") + set(_matlab_bin_suffix_64bits "i64") +elseif(UNIX) + set(_matlab_bin_prefix "gln") + set(_matlab_bin_suffix_32bits "x86") + set(_matlab_bin_suffix_64bits "xa64") +else() + set(_matlab_bin_prefix "win") + set(_matlab_bin_suffix_32bits "32") + set(_matlab_bin_suffix_64bits "64") +endif() + + + +set(MATLAB_INCLUDE_DIR_TO_LOOK ${Matlab_ROOT_DIR}/extern/include) +if(_matlab_64Build) + set(_matlab_current_suffix ${_matlab_bin_suffix_64bits}) +else() + set(_matlab_current_suffix ${_matlab_bin_suffix_32bits}) +endif() + +set(Matlab_BINARIES_DIR + ${Matlab_ROOT_DIR}/bin/${_matlab_bin_prefix}${_matlab_current_suffix}) +set(Matlab_EXTERN_LIBRARY_DIR + ${Matlab_ROOT_DIR}/extern/lib/${_matlab_bin_prefix}${_matlab_current_suffix}) + +if(WIN32) + set(_matlab_lib_dir_for_search ${Matlab_EXTERN_LIBRARY_DIR}/microsoft) + set(_matlab_lib_prefix_for_search "lib") +else() + set(_matlab_lib_dir_for_search ${Matlab_BINARIES_DIR}) + set(_matlab_lib_prefix_for_search "lib") +endif() + +unset(_matlab_64Build) + + +if(NOT DEFINED Matlab_MEX_EXTENSION) + set(_matlab_mex_extension "") + matlab_get_mex_suffix("${Matlab_ROOT_DIR}" _matlab_mex_extension) + + # This variable goes to the cache. + set(Matlab_MEX_EXTENSION ${_matlab_mex_extension} CACHE STRING "Extensions for the mex targets (automatically given by Matlab)") + unset(_matlab_mex_extension) +endif() + + +if(MATLAB_FIND_DEBUG) + message(STATUS "[MATLAB] [DEBUG]_matlab_lib_prefix_for_search = ${_matlab_lib_prefix_for_search} | _matlab_lib_dir_for_search = ${_matlab_lib_dir_for_search}") +endif() + + + +# internal +# This small stub around find_library is to prevent any pollution of CMAKE_FIND_LIBRARY_PREFIXES in the global scope. +# This is the function to be used below instead of the find_library directives. +function(_Matlab_find_library _matlab_library_prefix) + set(CMAKE_FIND_LIBRARY_PREFIXES ${CMAKE_FIND_LIBRARY_PREFIXES} ${_matlab_library_prefix}) + find_library(${ARGN}) +endfunction() + + +set(_matlab_required_variables) + + +# the MEX library/header are required +find_path( + Matlab_INCLUDE_DIRS + mex.h + PATHS ${MATLAB_INCLUDE_DIR_TO_LOOK} + NO_DEFAULT_PATH + ) +list(APPEND _matlab_required_variables Matlab_INCLUDE_DIRS) + +_Matlab_find_library( + ${_matlab_lib_prefix_for_search} + Matlab_MEX_LIBRARY + mex + PATHS ${_matlab_lib_dir_for_search} + NO_DEFAULT_PATH ) + + +list(APPEND _matlab_required_variables Matlab_MEX_LIBRARY) + +# the MEX extension is required +list(APPEND _matlab_required_variables Matlab_MEX_EXTENSION) + +# the matlab root is required +list(APPEND _matlab_required_variables Matlab_ROOT_DIR) + + +# component Mex Compiler +list(FIND Matlab_FIND_COMPONENTS MEX_COMPILER _matlab_find_mex_compiler) +if(_matlab_find_mex_compiler GREATER -1) + find_program( + Matlab_MEX_COMPILER + "mex" + PATHS ${Matlab_BINARIES_DIR} + DOC "Matlab MEX compiler" + NO_DEFAULT_PATH + ) + + if(Matlab_MEX_COMPILER) + set(Matlab_MEX_COMPILER_FOUND TRUE) + endif() +endif() +unset(_matlab_find_mex_compiler) + +# component Matlab program +list(FIND Matlab_FIND_COMPONENTS MAIN_PROGRAM _matlab_find_matlab_program) +if(_matlab_find_matlab_program GREATER -1) + + find_program( + Matlab_MAIN_PROGRAM + matlab + PATHS ${Matlab_ROOT_DIR} ${Matlab_ROOT_DIR}/bin + DOC "Matlab main program" + NO_DEFAULT_PATH + ) + + if(Matlab_MAIN_PROGRAM) + set(Matlab_MAIN_PROGRAM_FOUND TRUE) + endif() + +endif() +unset(_matlab_find_matlab_program) + +# Component MX library +list(FIND Matlab_FIND_COMPONENTS MX_LIBRARY _matlab_find_mx) +if(_matlab_find_mx GREATER -1) + _Matlab_find_library( + ${_matlab_lib_prefix_for_search} + Matlab_MX_LIBRARY + mx + PATHS ${_matlab_lib_dir_for_search} + NO_DEFAULT_PATH + ) + + if(Matlab_MX_LIBRARY) + set(Matlab_MX_LIBRARY_FOUND TRUE) + endif() +endif() +unset(_matlab_find_mx) + + +# Component ENG library +list(FIND Matlab_FIND_COMPONENTS ENG_LIBRARY _matlab_find_eng) +if(_matlab_find_eng GREATER -1) + _Matlab_find_library( + ${_matlab_lib_prefix_for_search} + Matlab_ENG_LIBRARY + eng + PATHS ${_matlab_lib_dir_for_search} + NO_DEFAULT_PATH + ) + if(Matlab_ENG_LIBRARY) + set(Matlab_ENG_LIBRARY_FOUND TRUE) + endif() +endif() +unset(_matlab_find_eng) + + + + + +unset(_matlab_lib_dir_for_search) + + +set(Matlab_LIBRARIES ${Matlab_MEX_LIBRARY} ${Matlab_MX_LIBRARY} ${Matlab_ENG_LIBRARY}) + + +find_package_handle_standard_args( + Matlab + FOUND_VAR Matlab_FOUND + REQUIRED_VARS ${_matlab_required_variables} + VERSION_VAR Matlab_VERSION_STRING + HANDLE_COMPONENTS) + +unset(_matlab_required_variables) +unset(_matlab_bin_prefix) +unset(_matlab_bin_suffix_32bits) +unset(_matlab_bin_suffix_64bits) +unset(_matlab_current_suffix) +unset(_matlab_lib_dir_for_search) +unset(_matlab_lib_prefix_for_search) + +if(Matlab_INCLUDE_DIRS AND Matlab_LIBRARIES) + mark_as_advanced( + #Matlab_LIBRARIES + Matlab_MEX_LIBRARY + Matlab_MX_LIBRARY + Matlab_ENG_LIBRARY + Matlab_INCLUDE_DIRS + Matlab_FOUND + #Matlab_ROOT_DIR + #Matlab_VERSION_STRING + Matlab_MAIN_PROGRAM + #Matlab_MEX_EXTENSION + Matlab_MEXEXTENSIONS_PROG + Matlab_MEX_EXTENSION + #Matlab_BINARIES_DIR + ) +endif() diff --git a/Modules/MatlabTestsRedirect.cmake b/Modules/MatlabTestsRedirect.cmake new file mode 100644 index 0000000..ebccbf6 --- /dev/null +++ b/Modules/MatlabTestsRedirect.cmake @@ -0,0 +1,92 @@ +# This is an undocumented internal helper for the FindMatlab +# module ``matlab_add_unit_test`` command. + +#============================================================================= +# Copyright 2014-2015 Raffi Enficiaud, Max Planck Society +# +# Distributed under the OSI-approved BSD License (the "License"); +# see accompanying file Copyright.txt for details. +# +# This software is distributed WITHOUT ANY WARRANTY; without even the +# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the License for more information. +#============================================================================= +# (To distribute this file outside of CMake, substitute the full +# License text for the above reference.) + + +# Usage: cmake +# -Dtest_timeout=180 +# -Dworking_directory="." +# -Doutput_directory= +# -Dadditional_paths="" +# -Dno_unittest_framework="" +# -DMatlab_PROGRAM=matlab_exe_location +# -DMatlab_ADDITIONNAL_STARTUP_OPTIONS="" +# -Dtest_name=name_of_the_test +# -Dcmd_to_run_before_test="" +# -Dunittest_file_to_run +# -P FindMatlab_TestsRedirect.cmake + +set(Matlab_UNIT_TESTS_CMD -nosplash -nojvm -nodesktop -nodisplay ${Matlab_ADDITIONNAL_STARTUP_OPTIONS}) +if(WIN32) + set(Matlab_UNIT_TESTS_CMD ${Matlab_UNIT_TESTS_CMD} -wait) +endif() + +if(NOT test_timeout) + set(test_timeout 180) +endif() + +if(NOT cmd_to_run_before_test) + set(cmd_to_run_before_test) +endif() + +get_filename_component(unittest_file_directory "${unittest_file_to_run}" DIRECTORY) +get_filename_component(unittest_file_to_run_name "${unittest_file_to_run}" NAME_WE) + +set(concat_string '${unittest_file_directory}') +foreach(s IN LISTS additional_paths) + if(NOT "${s}" STREQUAL "") + set(concat_string "${concat_string}, '${s}'") + endif() +endforeach() + +set(unittest_to_run "runtests('${unittest_file_to_run_name}'), exit(max([ans(1,:).Failed]))") +if(no_unittest_framework) + set(unittest_to_run "try, ${unittest_file_to_run_name}, catch err, disp('An exception has been thrown during the execution'), disp(err), disp(err.stack), exit(1), end, exit(0)") +endif() + +set(Matlab_SCRIPT_TO_RUN + "addpath(${concat_string}), path, ${cmd_to_run_before_test}, ${unittest_to_run}" + ) + +set(Matlab_LOG_FILE "${output_directory}/${test_name}.log") + +set(devnull) +if(UNIX) + set(devnull INPUT_FILE /dev/null) +elseif(WIN32) + set(devnull INPUT_FILE NUL) +endif() + +execute_process( + COMMAND "${Matlab_PROGRAM}" ${Matlab_UNIT_TESTS_CMD} -logfile "${test_name}.log" -r "${Matlab_SCRIPT_TO_RUN}" + RESULT_VARIABLE res + TIMEOUT ${test_timeout} + OUTPUT_QUIET # we do not want the output twice + WORKING_DIRECTORY "${output_directory}" + ${devnull} + ) + +if(NOT EXISTS ${Matlab_LOG_FILE}) + message( FATAL_ERROR "[MATLAB] ERROR: cannot find the log file ${Matlab_LOG_FILE}") +endif() + +# print the output in any case. +file(READ ${Matlab_LOG_FILE} matlab_log_content) +message("Matlab test ${name_of_the_test} output:\n${matlab_log_content}") # if we put FATAL_ERROR here, the file is indented. + + +if(NOT (res EQUAL 0)) + message( FATAL_ERROR "[MATLAB] TEST FAILED" ) +endif() diff --git a/Source/CMakeLists.txt b/Source/CMakeLists.txt index 5b518b8..482bd39 100644 --- a/Source/CMakeLists.txt +++ b/Source/CMakeLists.txt @@ -238,12 +238,16 @@ set(SRCS cmFileTimeComparison.cxx cmFileTimeComparison.h cmGeneratedFileStream.cxx + cmGeneratorExpressionContext.cxx + cmGeneratorExpressionContext.h cmGeneratorExpressionDAGChecker.cxx cmGeneratorExpressionDAGChecker.h cmGeneratorExpressionEvaluator.cxx cmGeneratorExpressionEvaluator.h cmGeneratorExpressionLexer.cxx cmGeneratorExpressionLexer.h + cmGeneratorExpressionNode.cxx + cmGeneratorExpressionNode.h cmGeneratorExpressionParser.cxx cmGeneratorExpressionParser.h cmGeneratorExpression.cxx diff --git a/Source/CMakeVersion.cmake b/Source/CMakeVersion.cmake index ca66478..35ae8b9 100644 --- a/Source/CMakeVersion.cmake +++ b/Source/CMakeVersion.cmake @@ -1,5 +1,5 @@ # CMake version number components. set(CMake_VERSION_MAJOR 3) set(CMake_VERSION_MINOR 2) -set(CMake_VERSION_PATCH 20150312) +set(CMake_VERSION_PATCH 20150317) #set(CMake_VERSION_RC 1) diff --git a/Source/CTest/cmCTestBuildHandler.cxx b/Source/CTest/cmCTestBuildHandler.cxx index fe27e2b..29e07ef 100644 --- a/Source/CTest/cmCTestBuildHandler.cxx +++ b/Source/CTest/cmCTestBuildHandler.cxx @@ -20,6 +20,7 @@ #include "cmGeneratedFileStream.h" #include "cmXMLSafe.h" #include "cmFileTimeComparison.h" +#include "cmAlgorithms.h" //#include <cmsys/RegularExpression.hxx> #include <cmsys/Process.h> diff --git a/Source/CTest/cmCTestRunTest.cxx b/Source/CTest/cmCTestRunTest.cxx index 03131fd..01a7884 100644 --- a/Source/CTest/cmCTestRunTest.cxx +++ b/Source/CTest/cmCTestRunTest.cxx @@ -536,11 +536,26 @@ void cmCTestRunTest::ComputeArguments() } this->TestResult.FullCommandLine = testCommand; + // Print the test command in verbose mode cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT, std::endl << this->Index << ": " << (this->TestHandler->MemCheck?"MemCheck":"Test") << " command: " << testCommand << std::endl); + + // Print any test-specific env vars in verbose mode + if (this->TestProperties->Environment.size()) + { + cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT, this->Index << ": " + << "Environment variables: " << std::endl); + } + for(std::vector<std::string>::const_iterator e = + this->TestProperties->Environment.begin(); + e != this->TestProperties->Environment.end(); ++e) + { + cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT, this->Index << ": " << *e + << std::endl); + } } //---------------------------------------------------------------------- diff --git a/Source/CTest/cmParseJacocoCoverage.cxx b/Source/CTest/cmParseJacocoCoverage.cxx index ec4cfad..31ad9fe 100644 --- a/Source/CTest/cmParseJacocoCoverage.cxx +++ b/Source/CTest/cmParseJacocoCoverage.cxx @@ -78,7 +78,10 @@ class cmParseJacocoCoverage::XMLParser: public cmXMLParser std::string line; FileLinesType& curFileLines = this->Coverage.TotalCoverage[this->CurFileName]; - curFileLines.push_back(-1); + if(fin) + { + curFileLines.push_back(-1); + } while(cmSystemTools::GetLineFromStream(fin, line)) { curFileLines.push_back(-1); diff --git a/Source/CursesDialog/cmCursesMainForm.cxx b/Source/CursesDialog/cmCursesMainForm.cxx index dcd0b6c..d60062e 100644 --- a/Source/CursesDialog/cmCursesMainForm.cxx +++ b/Source/CursesDialog/cmCursesMainForm.cxx @@ -22,6 +22,7 @@ #include "cmCursesDummyWidget.h" #include "cmCursesCacheEntryComposite.h" #include "cmCursesLongMessageForm.h" +#include "cmAlgorithms.h" inline int ctrl(int z) diff --git a/Source/cmAlgorithms.h b/Source/cmAlgorithms.h index b9d7e78..f117475 100644 --- a/Source/cmAlgorithms.h +++ b/Source/cmAlgorithms.h @@ -237,6 +237,11 @@ typename Range::const_iterator cmRemoveIndices(Range& r, InputRange const& rem) { typename InputRange::const_iterator remIt = rem.begin(); typename InputRange::const_iterator remEnd = rem.end(); + const typename Range::iterator rangeEnd = r.end(); + if (remIt == remEnd) + { + return rangeEnd; + } typename Range::iterator writer = r.begin(); std::advance(writer, *remIt); @@ -244,7 +249,6 @@ typename Range::const_iterator cmRemoveIndices(Range& r, InputRange const& rem) typename InputRange::value_type prevRem = *remIt; ++remIt; size_t count = 1; - const typename Range::iterator rangeEnd = r.end(); for ( ; writer != rangeEnd && remIt != remEnd; ++count, ++remIt) { std::advance(pivot, *remIt - prevRem); @@ -261,23 +265,53 @@ typename Range::const_iterator cmRemoveMatching(Range &r, MatchRange const& m) ContainerAlgorithms::BinarySearcher<MatchRange>(m)); } +namespace ContainerAlgorithms { + +template<typename Range, typename T = typename Range::value_type> +struct RemoveDuplicatesAPI +{ + typedef typename Range::const_iterator const_iterator; + typedef typename Range::const_iterator value_type; + + static bool lessThan(value_type a, value_type b) { return *a < *b; } + static value_type uniqueValue(const_iterator a) { return a; } + template<typename It> + static bool valueCompare(It it, const_iterator it2) { return **it != *it2; } +}; + +template<typename Range, typename T> +struct RemoveDuplicatesAPI<Range, T*> +{ + typedef typename Range::const_iterator const_iterator; + typedef T* value_type; + + static bool lessThan(value_type a, value_type b) { return a < b; } + static value_type uniqueValue(const_iterator a) { return *a; } + template<typename It> + static bool valueCompare(It it, const_iterator it2) { return *it != *it2; } +}; + +} + template<typename Range> typename Range::const_iterator cmRemoveDuplicates(Range& r) { - typedef std::vector<typename Range::value_type> UniqueVector; - UniqueVector unique; + typedef typename ContainerAlgorithms::RemoveDuplicatesAPI<Range> API; + typedef typename API::value_type T; + std::vector<T> unique; unique.reserve(r.size()); std::vector<size_t> indices; size_t count = 0; - const typename Range::iterator end = r.end(); + const typename Range::const_iterator end = r.end(); for(typename Range::const_iterator it = r.begin(); it != end; ++it, ++count) { - const typename UniqueVector::iterator low = - std::lower_bound(unique.begin(), unique.end(), *it); - if (low == unique.end() || *low != *it) + const typename std::vector<T>::iterator low = + std::lower_bound(unique.begin(), unique.end(), + API::uniqueValue(it), API::lessThan); + if (low == unique.end() || API::valueCompare(low, it)) { - unique.insert(low, *it); + unique.insert(low, API::uniqueValue(it)); } else { diff --git a/Source/cmCTest.cxx b/Source/cmCTest.cxx index e6d3960..1d0df69 100644 --- a/Source/cmCTest.cxx +++ b/Source/cmCTest.cxx @@ -26,6 +26,7 @@ #include "cmVersionMacros.h" #include "cmCTestCommand.h" #include "cmCTestStartCommand.h" +#include "cmAlgorithms.h" #include "cmCTestBuildHandler.h" #include "cmCTestBuildAndTestHandler.h" diff --git a/Source/cmComputeLinkDepends.cxx b/Source/cmComputeLinkDepends.cxx index be28b2f..6005d5f 100644 --- a/Source/cmComputeLinkDepends.cxx +++ b/Source/cmComputeLinkDepends.cxx @@ -17,6 +17,7 @@ #include "cmMakefile.h" #include "cmTarget.h" #include "cmake.h" +#include "cmAlgorithms.h" #include <cmsys/stl/algorithm> diff --git a/Source/cmComputeLinkInformation.cxx b/Source/cmComputeLinkInformation.cxx index 479da75..b0e0f36 100644 --- a/Source/cmComputeLinkInformation.cxx +++ b/Source/cmComputeLinkInformation.cxx @@ -19,6 +19,7 @@ #include "cmMakefile.h" #include "cmTarget.h" #include "cmake.h" +#include "cmAlgorithms.h" #include <ctype.h> diff --git a/Source/cmCoreTryCompile.cxx b/Source/cmCoreTryCompile.cxx index e9390e4..59efa52 100644 --- a/Source/cmCoreTryCompile.cxx +++ b/Source/cmCoreTryCompile.cxx @@ -14,6 +14,7 @@ #include "cmCacheManager.h" #include "cmLocalGenerator.h" #include "cmGlobalGenerator.h" +#include "cmAlgorithms.h" #include "cmExportTryCompileFileGenerator.h" #include <cmsys/Directory.hxx> diff --git a/Source/cmDependsC.cxx b/Source/cmDependsC.cxx index 6dde349..63d8fa6 100644 --- a/Source/cmDependsC.cxx +++ b/Source/cmDependsC.cxx @@ -15,6 +15,7 @@ #include "cmLocalGenerator.h" #include "cmMakefile.h" #include "cmSystemTools.h" +#include "cmAlgorithms.h" #include <cmsys/FStream.hxx> #include <ctype.h> // isspace diff --git a/Source/cmDocumentation.cxx b/Source/cmDocumentation.cxx index f4e3a75..8c17536 100644 --- a/Source/cmDocumentation.cxx +++ b/Source/cmDocumentation.cxx @@ -14,6 +14,7 @@ #include "cmSystemTools.h" #include "cmVersion.h" #include "cmRST.h" +#include "cmAlgorithms.h" #include <cmsys/Directory.hxx> #include <cmsys/Glob.hxx> diff --git a/Source/cmExportFileGenerator.cxx b/Source/cmExportFileGenerator.cxx index 71728be..b4fad98 100644 --- a/Source/cmExportFileGenerator.cxx +++ b/Source/cmExportFileGenerator.cxx @@ -22,6 +22,7 @@ #include "cmTargetExport.h" #include "cmVersion.h" #include "cmComputeLinkInformation.h" +#include "cmAlgorithms.h" #include <cmsys/auto_ptr.hxx> #include <cmsys/FStream.hxx> diff --git a/Source/cmExportInstallFileGenerator.cxx b/Source/cmExportInstallFileGenerator.cxx index ba1dde2..6d639c9 100644 --- a/Source/cmExportInstallFileGenerator.cxx +++ b/Source/cmExportInstallFileGenerator.cxx @@ -19,6 +19,7 @@ #include "cmInstallExportGenerator.h" #include "cmInstallTargetGenerator.h" #include "cmTargetExport.h" +#include "cmAlgorithms.h" //---------------------------------------------------------------------------- cmExportInstallFileGenerator diff --git a/Source/cmFileCommand.cxx b/Source/cmFileCommand.cxx index 0290c92..ec22ea0 100644 --- a/Source/cmFileCommand.cxx +++ b/Source/cmFileCommand.cxx @@ -16,6 +16,7 @@ #include "cmInstallType.h" #include "cmFileTimeComparison.h" #include "cmCryptoHash.h" +#include "cmAlgorithms.h" #include "cmTimestamp.h" diff --git a/Source/cmFindBase.cxx b/Source/cmFindBase.cxx index ffc641c..f63df61 100644 --- a/Source/cmFindBase.cxx +++ b/Source/cmFindBase.cxx @@ -11,6 +11,8 @@ ============================================================================*/ #include "cmFindBase.h" +#include "cmAlgorithms.h" + cmFindBase::cmFindBase() { this->AlreadyInCache = false; diff --git a/Source/cmFindPackageCommand.cxx b/Source/cmFindPackageCommand.cxx index 26bd4b9..87f9037 100644 --- a/Source/cmFindPackageCommand.cxx +++ b/Source/cmFindPackageCommand.cxx @@ -14,6 +14,7 @@ #include <cmsys/Directory.hxx> #include <cmsys/RegularExpression.hxx> #include <cmsys/Encoding.hxx> +#include "cmAlgorithms.h" #ifdef CMAKE_BUILD_WITH_CMAKE #include "cmVariableWatch.h" diff --git a/Source/cmGeneratorExpression.cxx b/Source/cmGeneratorExpression.cxx index 0a27016..a1c405b 100644 --- a/Source/cmGeneratorExpression.cxx +++ b/Source/cmGeneratorExpression.cxx @@ -14,6 +14,7 @@ #include "cmMakefile.h" #include "cmTarget.h" #include "assert.h" +#include "cmAlgorithms.h" #include "cmGeneratorExpressionEvaluator.h" #include "cmGeneratorExpressionLexer.h" @@ -72,6 +73,19 @@ const char *cmCompiledGeneratorExpression::Evaluate( cmGeneratorExpressionDAGChecker *dagChecker, std::string const& language) const { + cmGeneratorExpressionContext context(mf, config, quiet, headTarget, + currentTarget ? currentTarget : headTarget, + this->EvaluateForBuildsystem, + this->Backtrace, language); + + return this->EvaluateWithContext(context, dagChecker); +} + +//---------------------------------------------------------------------------- +const char* cmCompiledGeneratorExpression::EvaluateWithContext( + cmGeneratorExpressionContext& context, + cmGeneratorExpressionDAGChecker *dagChecker) const +{ if (!this->NeedsEvaluation) { return this->Input.c_str(); @@ -84,20 +98,6 @@ const char *cmCompiledGeneratorExpression::Evaluate( const std::vector<cmGeneratorExpressionEvaluator*>::const_iterator end = this->Evaluators.end(); - cmGeneratorExpressionContext context; - context.Makefile = mf; - context.Config = config; - context.Quiet = quiet; - context.HadError = false; - context.HadContextSensitiveCondition = false; - context.HadHeadSensitiveCondition = false; - context.SourceSensitiveTargets.clear(); - context.HeadTarget = headTarget; - context.EvaluateForBuildsystem = this->EvaluateForBuildsystem; - context.CurrentTarget = currentTarget ? currentTarget : headTarget; - context.Backtrace = this->Backtrace; - context.Language = language; - for ( ; it != end; ++it) { this->Output += (*it)->Evaluate(&context, dagChecker); diff --git a/Source/cmGeneratorExpression.h b/Source/cmGeneratorExpression.h index 55d9691..11c27fd 100644 --- a/Source/cmGeneratorExpression.h +++ b/Source/cmGeneratorExpression.h @@ -24,6 +24,7 @@ class cmMakefile; class cmListFileBacktrace; struct cmGeneratorExpressionEvaluator; +struct cmGeneratorExpressionContext; struct cmGeneratorExpressionDAGChecker; class cmCompiledGeneratorExpression; @@ -131,6 +132,9 @@ public: std::map<std::string, std::string>& mapping); private: + const char* EvaluateWithContext(cmGeneratorExpressionContext& context, + cmGeneratorExpressionDAGChecker *dagChecker) const; + cmCompiledGeneratorExpression(cmListFileBacktrace const& backtrace, const std::string& input); diff --git a/Source/cmGeneratorExpressionContext.cxx b/Source/cmGeneratorExpressionContext.cxx new file mode 100644 index 0000000..947015e --- /dev/null +++ b/Source/cmGeneratorExpressionContext.cxx @@ -0,0 +1,34 @@ +/*============================================================================ + CMake - Cross Platform Makefile Generator + Copyright 2012 Stephen Kelly <steveire@gmail.com> + + Distributed under the OSI-approved BSD License (the "License"); + see accompanying file Copyright.txt for details. + + This software is distributed WITHOUT ANY WARRANTY; without even the + implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the License for more information. +============================================================================*/ + +#include "cmGeneratorExpressionContext.h" + +cmGeneratorExpressionContext::cmGeneratorExpressionContext( + cmMakefile* mf, std::string const& config, + bool quiet, cmTarget const* headTarget, + cmTarget const* currentTarget, + bool evaluateForBuildsystem, + cmListFileBacktrace const& backtrace, + std::string const& language) + : Backtrace(backtrace), + Makefile(mf), + Config(config), + Language(language), + HeadTarget(headTarget), + CurrentTarget(currentTarget), + Quiet(quiet), + HadError(false), + HadContextSensitiveCondition(false), + HadHeadSensitiveCondition(false), + EvaluateForBuildsystem(evaluateForBuildsystem) +{ +} diff --git a/Source/cmGeneratorExpressionContext.h b/Source/cmGeneratorExpressionContext.h new file mode 100644 index 0000000..ed83509 --- /dev/null +++ b/Source/cmGeneratorExpressionContext.h @@ -0,0 +1,54 @@ +/*============================================================================ + CMake - Cross Platform Makefile Generator + Copyright 2012 Stephen Kelly <steveire@gmail.com> + + Distributed under the OSI-approved BSD License (the "License"); + see accompanying file Copyright.txt for details. + + This software is distributed WITHOUT ANY WARRANTY; without even the + implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the License for more information. +============================================================================*/ +#ifndef cmGeneratorExpressionContext_h +#define cmGeneratorExpressionContext_h + +#include "cmListFileCache.h" + +#include <set> +#include <map> +#include <string> + +class cmTarget; + +//---------------------------------------------------------------------------- +struct cmGeneratorExpressionContext +{ + cmGeneratorExpressionContext(cmMakefile* mf, std::string const& config, + bool quiet, cmTarget const* headTarget, + cmTarget const* currentTarget, + bool evaluateForBuildsystem, + cmListFileBacktrace const& backtrace, + std::string const& language); + + + cmListFileBacktrace Backtrace; + std::set<cmTarget*> DependTargets; + std::set<cmTarget const*> AllTargets; + std::set<std::string> SeenTargetProperties; + std::set<cmTarget const*> SourceSensitiveTargets; + std::map<cmTarget const*, std::map<std::string, std::string> > + MaxLanguageStandard; + cmMakefile *Makefile; + std::string Config; + std::string Language; + cmTarget const* HeadTarget; // The target whose property is being evaluated. + cmTarget const* CurrentTarget; // The dependent of HeadTarget which appears + // directly or indirectly in the property. + bool Quiet; + bool HadError; + bool HadContextSensitiveCondition; + bool HadHeadSensitiveCondition; + bool EvaluateForBuildsystem; +}; + +#endif diff --git a/Source/cmGeneratorExpressionDAGChecker.cxx b/Source/cmGeneratorExpressionDAGChecker.cxx index c8b9949..ff8790c 100644 --- a/Source/cmGeneratorExpressionDAGChecker.cxx +++ b/Source/cmGeneratorExpressionDAGChecker.cxx @@ -13,6 +13,7 @@ #include "cmGeneratorExpressionDAGChecker.h" #include "cmMakefile.h" +#include "cmAlgorithms.h" //---------------------------------------------------------------------------- cmGeneratorExpressionDAGChecker::cmGeneratorExpressionDAGChecker( diff --git a/Source/cmGeneratorExpressionEvaluator.cxx b/Source/cmGeneratorExpressionEvaluator.cxx index 756d932..af94bcc 100644 --- a/Source/cmGeneratorExpressionEvaluator.cxx +++ b/Source/cmGeneratorExpressionEvaluator.cxx @@ -18,1900 +18,14 @@ #include "cmLocalGenerator.h" #include "cmGlobalGenerator.h" #include "cmSourceFile.h" +#include "cmAlgorithms.h" #include <cmsys/String.h> #include <assert.h> #include <errno.h> -//---------------------------------------------------------------------------- -static void reportError(cmGeneratorExpressionContext *context, - const std::string &expr, const std::string &result) -{ - context->HadError = true; - if (context->Quiet) - { - return; - } - - std::ostringstream e; - e << "Error evaluating generator expression:\n" - << " " << expr << "\n" - << result; - context->Makefile->GetCMakeInstance() - ->IssueMessage(cmake::FATAL_ERROR, e.str(), - context->Backtrace); -} - -//---------------------------------------------------------------------------- -struct cmGeneratorExpressionNode -{ - enum { - DynamicParameters = 0, - OneOrMoreParameters = -1, - OneOrZeroParameters = -2 - }; - virtual ~cmGeneratorExpressionNode() {} - - virtual bool GeneratesContent() const { return true; } - - virtual bool RequiresLiteralInput() const { return false; } - - virtual bool AcceptsArbitraryContentParameter() const - { return false; } - - virtual int NumExpectedParameters() const { return 1; } - - virtual std::string Evaluate(const std::vector<std::string> ¶meters, - cmGeneratorExpressionContext *context, - const GeneratorExpressionContent *content, - cmGeneratorExpressionDAGChecker *dagChecker - ) const = 0; - - static std::string EvaluateDependentExpression( - std::string const& prop, cmMakefile *makefile, - cmGeneratorExpressionContext *context, - cmTarget const* headTarget, cmTarget const* currentTarget, - cmGeneratorExpressionDAGChecker *dagChecker); -}; - -//---------------------------------------------------------------------------- -std::string cmGeneratorExpressionNode::EvaluateDependentExpression( - std::string const& prop, cmMakefile *makefile, - cmGeneratorExpressionContext *context, - cmTarget const* headTarget, cmTarget const* currentTarget, - cmGeneratorExpressionDAGChecker *dagChecker) -{ - cmGeneratorExpression ge(&context->Backtrace); - cmsys::auto_ptr<cmCompiledGeneratorExpression> cge = ge.Parse(prop); - cge->SetEvaluateForBuildsystem(context->EvaluateForBuildsystem); - std::string result = cge->Evaluate(makefile, - context->Config, - context->Quiet, - headTarget, - currentTarget, - dagChecker, - context->Language); - if (cge->GetHadContextSensitiveCondition()) - { - context->HadContextSensitiveCondition = true; - } - if (cge->GetHadHeadSensitiveCondition()) - { - context->HadHeadSensitiveCondition = true; - } - return result; -} - -//---------------------------------------------------------------------------- -static const struct ZeroNode : public cmGeneratorExpressionNode -{ - ZeroNode() {} - - virtual bool GeneratesContent() const { return false; } - - virtual bool AcceptsArbitraryContentParameter() const { return true; } - - std::string Evaluate(const std::vector<std::string> &, - cmGeneratorExpressionContext *, - const GeneratorExpressionContent *, - cmGeneratorExpressionDAGChecker *) const - { - return std::string(); - } -} zeroNode; - -//---------------------------------------------------------------------------- -static const struct OneNode : public cmGeneratorExpressionNode -{ - OneNode() {} - - virtual bool AcceptsArbitraryContentParameter() const { return true; } - - std::string Evaluate(const std::vector<std::string> ¶meters, - cmGeneratorExpressionContext *, - const GeneratorExpressionContent *, - cmGeneratorExpressionDAGChecker *) const - { - return parameters.front(); - } -} oneNode; - -//---------------------------------------------------------------------------- -static const struct OneNode buildInterfaceNode; - -//---------------------------------------------------------------------------- -static const struct ZeroNode installInterfaceNode; - -//---------------------------------------------------------------------------- -#define BOOLEAN_OP_NODE(OPNAME, OP, SUCCESS_VALUE, FAILURE_VALUE) \ -static const struct OP ## Node : public cmGeneratorExpressionNode \ -{ \ - OP ## Node () {} \ - virtual int NumExpectedParameters() const { return OneOrMoreParameters; } \ - \ - std::string Evaluate(const std::vector<std::string> ¶meters, \ - cmGeneratorExpressionContext *context, \ - const GeneratorExpressionContent *content, \ - cmGeneratorExpressionDAGChecker *) const \ - { \ - std::vector<std::string>::const_iterator it = parameters.begin(); \ - const std::vector<std::string>::const_iterator end = parameters.end(); \ - for ( ; it != end; ++it) \ - { \ - if (*it == #FAILURE_VALUE) \ - { \ - return #FAILURE_VALUE; \ - } \ - else if (*it != #SUCCESS_VALUE) \ - { \ - reportError(context, content->GetOriginalExpression(), \ - "Parameters to $<" #OP "> must resolve to either '0' or '1'."); \ - return std::string(); \ - } \ - } \ - return #SUCCESS_VALUE; \ - } \ -} OPNAME; - -BOOLEAN_OP_NODE(andNode, AND, 1, 0) -BOOLEAN_OP_NODE(orNode, OR, 0, 1) - -#undef BOOLEAN_OP_NODE - -//---------------------------------------------------------------------------- -static const struct NotNode : public cmGeneratorExpressionNode -{ - NotNode() {} - - std::string Evaluate(const std::vector<std::string> ¶meters, - cmGeneratorExpressionContext *context, - const GeneratorExpressionContent *content, - cmGeneratorExpressionDAGChecker *) const - { - if (*parameters.begin() != "0" && *parameters.begin() != "1") - { - reportError(context, content->GetOriginalExpression(), - "$<NOT> parameter must resolve to exactly one '0' or '1' value."); - return std::string(); - } - return *parameters.begin() == "0" ? "1" : "0"; - } -} notNode; - -//---------------------------------------------------------------------------- -static const struct BoolNode : public cmGeneratorExpressionNode -{ - BoolNode() {} - - virtual int NumExpectedParameters() const { return 1; } - - std::string Evaluate(const std::vector<std::string> ¶meters, - cmGeneratorExpressionContext *, - const GeneratorExpressionContent *, - cmGeneratorExpressionDAGChecker *) const - { - return !cmSystemTools::IsOff(parameters.begin()->c_str()) ? "1" : "0"; - } -} boolNode; - -//---------------------------------------------------------------------------- -static const struct StrEqualNode : public cmGeneratorExpressionNode -{ - StrEqualNode() {} - - virtual int NumExpectedParameters() const { return 2; } - - std::string Evaluate(const std::vector<std::string> ¶meters, - cmGeneratorExpressionContext *, - const GeneratorExpressionContent *, - cmGeneratorExpressionDAGChecker *) const - { - return *parameters.begin() == parameters[1] ? "1" : "0"; - } -} strEqualNode; - -//---------------------------------------------------------------------------- -static const struct EqualNode : public cmGeneratorExpressionNode -{ - EqualNode() {} - - virtual int NumExpectedParameters() const { return 2; } - - std::string Evaluate(const std::vector<std::string> ¶meters, - cmGeneratorExpressionContext *context, - const GeneratorExpressionContent *content, - cmGeneratorExpressionDAGChecker *) const - { - char *pEnd; - - int base = 0; - bool flipSign = false; - - const char *lhs = parameters[0].c_str(); - if (cmHasLiteralPrefix(lhs, "0b") || cmHasLiteralPrefix(lhs, "0B")) - { - base = 2; - lhs += 2; - } - if (cmHasLiteralPrefix(lhs, "-0b") || cmHasLiteralPrefix(lhs, "-0B")) - { - base = 2; - lhs += 3; - flipSign = true; - } - if (cmHasLiteralPrefix(lhs, "+0b") || cmHasLiteralPrefix(lhs, "+0B")) - { - base = 2; - lhs += 3; - } - - long lnum = strtol(lhs, &pEnd, base); - if (pEnd == lhs || *pEnd != '\0' || errno == ERANGE) - { - reportError(context, content->GetOriginalExpression(), - "$<EQUAL> parameter " + parameters[0] + " is not a valid integer."); - return std::string(); - } - - if (flipSign) - { - lnum = -lnum; - } - - base = 0; - flipSign = false; - - const char *rhs = parameters[1].c_str(); - if (cmHasLiteralPrefix(rhs, "0b") || cmHasLiteralPrefix(rhs, "0B")) - { - base = 2; - rhs += 2; - } - if (cmHasLiteralPrefix(rhs, "-0b") || cmHasLiteralPrefix(rhs, "-0B")) - { - base = 2; - rhs += 3; - flipSign = true; - } - if (cmHasLiteralPrefix(rhs, "+0b") || cmHasLiteralPrefix(rhs, "+0B")) - { - base = 2; - rhs += 3; - } - - long rnum = strtol(rhs, &pEnd, base); - if (pEnd == rhs || *pEnd != '\0' || errno == ERANGE) - { - reportError(context, content->GetOriginalExpression(), - "$<EQUAL> parameter " + parameters[1] + " is not a valid integer."); - return std::string(); - } - - if (flipSign) - { - rnum = -rnum; - } - - return lnum == rnum ? "1" : "0"; - } -} equalNode; - -//---------------------------------------------------------------------------- -static const struct LowerCaseNode : public cmGeneratorExpressionNode -{ - LowerCaseNode() {} - - bool AcceptsArbitraryContentParameter() const { return true; } - - std::string Evaluate(const std::vector<std::string> ¶meters, - cmGeneratorExpressionContext *, - const GeneratorExpressionContent *, - cmGeneratorExpressionDAGChecker *) const - { - return cmSystemTools::LowerCase(parameters.front()); - } -} lowerCaseNode; - -//---------------------------------------------------------------------------- -static const struct UpperCaseNode : public cmGeneratorExpressionNode -{ - UpperCaseNode() {} - - bool AcceptsArbitraryContentParameter() const { return true; } - - std::string Evaluate(const std::vector<std::string> ¶meters, - cmGeneratorExpressionContext *, - const GeneratorExpressionContent *, - cmGeneratorExpressionDAGChecker *) const - { - return cmSystemTools::UpperCase(parameters.front()); - } -} upperCaseNode; - -//---------------------------------------------------------------------------- -static const struct MakeCIdentifierNode : public cmGeneratorExpressionNode -{ - MakeCIdentifierNode() {} - - bool AcceptsArbitraryContentParameter() const { return true; } - - std::string Evaluate(const std::vector<std::string> ¶meters, - cmGeneratorExpressionContext *, - const GeneratorExpressionContent *, - cmGeneratorExpressionDAGChecker *) const - { - return cmSystemTools::MakeCidentifier(parameters.front()); - } -} makeCIdentifierNode; - -//---------------------------------------------------------------------------- -static const struct Angle_RNode : public cmGeneratorExpressionNode -{ - Angle_RNode() {} - - virtual int NumExpectedParameters() const { return 0; } - - std::string Evaluate(const std::vector<std::string> &, - cmGeneratorExpressionContext *, - const GeneratorExpressionContent *, - cmGeneratorExpressionDAGChecker *) const - { - return ">"; - } -} angle_rNode; - -//---------------------------------------------------------------------------- -static const struct CommaNode : public cmGeneratorExpressionNode -{ - CommaNode() {} - - virtual int NumExpectedParameters() const { return 0; } - - std::string Evaluate(const std::vector<std::string> &, - cmGeneratorExpressionContext *, - const GeneratorExpressionContent *, - cmGeneratorExpressionDAGChecker *) const - { - return ","; - } -} commaNode; - -//---------------------------------------------------------------------------- -static const struct SemicolonNode : public cmGeneratorExpressionNode -{ - SemicolonNode() {} - - virtual int NumExpectedParameters() const { return 0; } - - std::string Evaluate(const std::vector<std::string> &, - cmGeneratorExpressionContext *, - const GeneratorExpressionContent *, - cmGeneratorExpressionDAGChecker *) const - { - return ";"; - } -} semicolonNode; - -//---------------------------------------------------------------------------- -struct CompilerIdNode : public cmGeneratorExpressionNode -{ - CompilerIdNode() {} - - virtual int NumExpectedParameters() const { return OneOrZeroParameters; } - - std::string EvaluateWithLanguage(const std::vector<std::string> ¶meters, - cmGeneratorExpressionContext *context, - const GeneratorExpressionContent *content, - cmGeneratorExpressionDAGChecker *, - const std::string &lang) const - { - const char *compilerId = - context->Makefile->GetSafeDefinition("CMAKE_" + lang + "_COMPILER_ID"); - if (parameters.empty()) - { - return compilerId ? compilerId : ""; - } - static cmsys::RegularExpression compilerIdValidator("^[A-Za-z0-9_]*$"); - if (!compilerIdValidator.find(*parameters.begin())) - { - reportError(context, content->GetOriginalExpression(), - "Expression syntax not recognized."); - return std::string(); - } - if (!compilerId) - { - return parameters.front().empty() ? "1" : "0"; - } - - if (strcmp(parameters.begin()->c_str(), compilerId) == 0) - { - return "1"; - } - - if (cmsysString_strcasecmp(parameters.begin()->c_str(), compilerId) == 0) - { - switch(context->Makefile->GetPolicyStatus(cmPolicies::CMP0044)) - { - case cmPolicies::WARN: - { - std::ostringstream e; - e << context->Makefile->GetPolicies() - ->GetPolicyWarning(cmPolicies::CMP0044); - context->Makefile->GetCMakeInstance() - ->IssueMessage(cmake::AUTHOR_WARNING, - e.str(), context->Backtrace); - } - case cmPolicies::OLD: - return "1"; - case cmPolicies::NEW: - case cmPolicies::REQUIRED_ALWAYS: - case cmPolicies::REQUIRED_IF_USED: - break; - } - } - return "0"; - } -}; - -//---------------------------------------------------------------------------- -static const struct CCompilerIdNode : public CompilerIdNode -{ - CCompilerIdNode() {} - - std::string Evaluate(const std::vector<std::string> ¶meters, - cmGeneratorExpressionContext *context, - const GeneratorExpressionContent *content, - cmGeneratorExpressionDAGChecker *dagChecker) const - { - if (!context->HeadTarget) - { - reportError(context, content->GetOriginalExpression(), - "$<C_COMPILER_ID> may only be used with binary targets. It may " - "not be used with add_custom_command or add_custom_target."); - return std::string(); - } - return this->EvaluateWithLanguage(parameters, context, content, - dagChecker, "C"); - } -} cCompilerIdNode; - -//---------------------------------------------------------------------------- -static const struct CXXCompilerIdNode : public CompilerIdNode -{ - CXXCompilerIdNode() {} - - std::string Evaluate(const std::vector<std::string> ¶meters, - cmGeneratorExpressionContext *context, - const GeneratorExpressionContent *content, - cmGeneratorExpressionDAGChecker *dagChecker) const - { - if (!context->HeadTarget) - { - reportError(context, content->GetOriginalExpression(), - "$<CXX_COMPILER_ID> may only be used with binary targets. It may " - "not be used with add_custom_command or add_custom_target."); - return std::string(); - } - return this->EvaluateWithLanguage(parameters, context, content, - dagChecker, "CXX"); - } -} cxxCompilerIdNode; - -//---------------------------------------------------------------------------- -struct CompilerVersionNode : public cmGeneratorExpressionNode -{ - CompilerVersionNode() {} - - virtual int NumExpectedParameters() const { return OneOrZeroParameters; } - - std::string EvaluateWithLanguage(const std::vector<std::string> ¶meters, - cmGeneratorExpressionContext *context, - const GeneratorExpressionContent *content, - cmGeneratorExpressionDAGChecker *, - const std::string &lang) const - { - const char *compilerVersion = context->Makefile->GetSafeDefinition( - "CMAKE_" + lang + "_COMPILER_VERSION"); - if (parameters.empty()) - { - return compilerVersion ? compilerVersion : ""; - } - - static cmsys::RegularExpression compilerIdValidator("^[0-9\\.]*$"); - if (!compilerIdValidator.find(*parameters.begin())) - { - reportError(context, content->GetOriginalExpression(), - "Expression syntax not recognized."); - return std::string(); - } - if (!compilerVersion) - { - return parameters.front().empty() ? "1" : "0"; - } - - return cmSystemTools::VersionCompare(cmSystemTools::OP_EQUAL, - parameters.begin()->c_str(), - compilerVersion) ? "1" : "0"; - } -}; - -//---------------------------------------------------------------------------- -static const struct CCompilerVersionNode : public CompilerVersionNode -{ - CCompilerVersionNode() {} - - std::string Evaluate(const std::vector<std::string> ¶meters, - cmGeneratorExpressionContext *context, - const GeneratorExpressionContent *content, - cmGeneratorExpressionDAGChecker *dagChecker) const - { - if (!context->HeadTarget) - { - reportError(context, content->GetOriginalExpression(), - "$<C_COMPILER_VERSION> may only be used with binary targets. It " - "may not be used with add_custom_command or add_custom_target."); - return std::string(); - } - return this->EvaluateWithLanguage(parameters, context, content, - dagChecker, "C"); - } -} cCompilerVersionNode; - -//---------------------------------------------------------------------------- -static const struct CxxCompilerVersionNode : public CompilerVersionNode -{ - CxxCompilerVersionNode() {} - - std::string Evaluate(const std::vector<std::string> ¶meters, - cmGeneratorExpressionContext *context, - const GeneratorExpressionContent *content, - cmGeneratorExpressionDAGChecker *dagChecker) const - { - if (!context->HeadTarget) - { - reportError(context, content->GetOriginalExpression(), - "$<CXX_COMPILER_VERSION> may only be used with binary targets. It " - "may not be used with add_custom_command or add_custom_target."); - return std::string(); - } - return this->EvaluateWithLanguage(parameters, context, content, - dagChecker, "CXX"); - } -} cxxCompilerVersionNode; - - -//---------------------------------------------------------------------------- -struct PlatformIdNode : public cmGeneratorExpressionNode -{ - PlatformIdNode() {} - - virtual int NumExpectedParameters() const { return OneOrZeroParameters; } - - std::string Evaluate(const std::vector<std::string> ¶meters, - cmGeneratorExpressionContext *context, - const GeneratorExpressionContent *, - cmGeneratorExpressionDAGChecker *) const - { - const char *platformId = - context->Makefile->GetSafeDefinition("CMAKE_SYSTEM_NAME"); - if (parameters.empty()) - { - return platformId ? platformId : ""; - } - - if (!platformId) - { - return parameters.front().empty() ? "1" : "0"; - } - - if (strcmp(parameters.begin()->c_str(), platformId) == 0) - { - return "1"; - } - return "0"; - } -} platformIdNode; - -//---------------------------------------------------------------------------- -static const struct VersionGreaterNode : public cmGeneratorExpressionNode -{ - VersionGreaterNode() {} - - virtual int NumExpectedParameters() const { return 2; } - - std::string Evaluate(const std::vector<std::string> ¶meters, - cmGeneratorExpressionContext *, - const GeneratorExpressionContent *, - cmGeneratorExpressionDAGChecker *) const - { - return cmSystemTools::VersionCompare(cmSystemTools::OP_GREATER, - parameters.front().c_str(), - parameters[1].c_str()) ? "1" : "0"; - } -} versionGreaterNode; - -//---------------------------------------------------------------------------- -static const struct VersionLessNode : public cmGeneratorExpressionNode -{ - VersionLessNode() {} - - virtual int NumExpectedParameters() const { return 2; } - - std::string Evaluate(const std::vector<std::string> ¶meters, - cmGeneratorExpressionContext *, - const GeneratorExpressionContent *, - cmGeneratorExpressionDAGChecker *) const - { - return cmSystemTools::VersionCompare(cmSystemTools::OP_LESS, - parameters.front().c_str(), - parameters[1].c_str()) ? "1" : "0"; - } -} versionLessNode; - -//---------------------------------------------------------------------------- -static const struct VersionEqualNode : public cmGeneratorExpressionNode -{ - VersionEqualNode() {} - - virtual int NumExpectedParameters() const { return 2; } - - std::string Evaluate(const std::vector<std::string> ¶meters, - cmGeneratorExpressionContext *, - const GeneratorExpressionContent *, - cmGeneratorExpressionDAGChecker *) const - { - return cmSystemTools::VersionCompare(cmSystemTools::OP_EQUAL, - parameters.front().c_str(), - parameters[1].c_str()) ? "1" : "0"; - } -} versionEqualNode; - -//---------------------------------------------------------------------------- -static const struct LinkOnlyNode : public cmGeneratorExpressionNode -{ - LinkOnlyNode() {} - - std::string Evaluate(const std::vector<std::string> ¶meters, - cmGeneratorExpressionContext *, - const GeneratorExpressionContent *, - cmGeneratorExpressionDAGChecker *dagChecker) const - { - if(!dagChecker->GetTransitivePropertiesOnly()) - { - return parameters.front(); - } - return ""; - } -} linkOnlyNode; - -//---------------------------------------------------------------------------- -static const struct ConfigurationNode : public cmGeneratorExpressionNode -{ - ConfigurationNode() {} - - virtual int NumExpectedParameters() const { return 0; } - - std::string Evaluate(const std::vector<std::string> &, - cmGeneratorExpressionContext *context, - const GeneratorExpressionContent *, - cmGeneratorExpressionDAGChecker *) const - { - context->HadContextSensitiveCondition = true; - return context->Config; - } -} configurationNode; - -//---------------------------------------------------------------------------- -static const struct ConfigurationTestNode : public cmGeneratorExpressionNode -{ - ConfigurationTestNode() {} - - virtual int NumExpectedParameters() const { return OneOrZeroParameters; } - - std::string Evaluate(const std::vector<std::string> ¶meters, - cmGeneratorExpressionContext *context, - const GeneratorExpressionContent *content, - cmGeneratorExpressionDAGChecker *) const - { - if (parameters.empty()) - { - return configurationNode.Evaluate(parameters, context, content, 0); - } - static cmsys::RegularExpression configValidator("^[A-Za-z0-9_]*$"); - if (!configValidator.find(*parameters.begin())) - { - reportError(context, content->GetOriginalExpression(), - "Expression syntax not recognized."); - return std::string(); - } - context->HadContextSensitiveCondition = true; - if (context->Config.empty()) - { - return parameters.front().empty() ? "1" : "0"; - } - - if (cmsysString_strcasecmp(parameters.begin()->c_str(), - context->Config.c_str()) == 0) - { - return "1"; - } - - if (context->CurrentTarget - && context->CurrentTarget->IsImported()) - { - const char* loc = 0; - const char* imp = 0; - std::string suffix; - if (context->CurrentTarget->GetMappedConfig(context->Config, - &loc, - &imp, - suffix)) - { - // This imported target has an appropriate location - // for this (possibly mapped) config. - // Check if there is a proper config mapping for the tested config. - std::vector<std::string> mappedConfigs; - std::string mapProp = "MAP_IMPORTED_CONFIG_"; - mapProp += cmSystemTools::UpperCase(context->Config); - if(const char* mapValue = - context->CurrentTarget->GetProperty(mapProp)) - { - cmSystemTools::ExpandListArgument(cmSystemTools::UpperCase(mapValue), - mappedConfigs); - return std::find(mappedConfigs.begin(), mappedConfigs.end(), - cmSystemTools::UpperCase(parameters.front())) - != mappedConfigs.end() ? "1" : "0"; - } - } - } - return "0"; - } -} configurationTestNode; - -static const struct JoinNode : public cmGeneratorExpressionNode -{ - JoinNode() {} - - virtual int NumExpectedParameters() const { return 2; } - - virtual bool AcceptsArbitraryContentParameter() const { return true; } - - std::string Evaluate(const std::vector<std::string> ¶meters, - cmGeneratorExpressionContext *, - const GeneratorExpressionContent *, - cmGeneratorExpressionDAGChecker *) const - { - std::vector<std::string> list; - cmSystemTools::ExpandListArgument(parameters.front(), list); - return cmJoin(list, parameters[1]); - } -} joinNode; - -static const struct CompileLanguageNode : public cmGeneratorExpressionNode -{ - CompileLanguageNode() {} - - virtual int NumExpectedParameters() const { return OneOrZeroParameters; } - - std::string Evaluate(const std::vector<std::string> ¶meters, - cmGeneratorExpressionContext *context, - const GeneratorExpressionContent *content, - cmGeneratorExpressionDAGChecker *dagChecker) const - { - if(context->Language.empty()) - { - reportError(context, content->GetOriginalExpression(), - "$<COMPILE_LANGUAGE:...> may only be used to specify include " - "directories compile definitions, compile options and to evaluate " - "components of the file(GENERATE) command."); - return std::string(); - } - - std::vector<std::string> enabledLanguages; - cmGlobalGenerator* gg - = context->Makefile->GetLocalGenerator()->GetGlobalGenerator(); - gg->GetEnabledLanguages(enabledLanguages); - if (!parameters.empty() && - std::find(enabledLanguages.begin(), enabledLanguages.end(), - parameters.front()) == enabledLanguages.end()) - { - reportError(context, content->GetOriginalExpression(), - "$<COMPILE_LANGUAGE:...> Unknown language."); - return std::string(); - } - - std::string genName = gg->GetName(); - if (genName.find("Visual Studio") != std::string::npos) - { - reportError(context, content->GetOriginalExpression(), - "$<COMPILE_LANGUAGE:...> may not be used with Visual Studio " - "generators."); - return std::string(); - } - else if (genName.find("Xcode") != std::string::npos) - { - if (dagChecker && (dagChecker->EvaluatingCompileDefinitions() - || dagChecker->EvaluatingIncludeDirectories())) - { - reportError(context, content->GetOriginalExpression(), - "$<COMPILE_LANGUAGE:...> may only be used with COMPILE_OPTIONS " - "with the Xcode generator."); - return std::string(); - } - } - else - { - if(genName.find("Makefiles") == std::string::npos && - genName.find("Ninja") == std::string::npos && - genName.find("Watcom WMake") == std::string::npos) - { - reportError(context, content->GetOriginalExpression(), - "$<COMPILE_LANGUAGE:...> not supported for this generator."); - return std::string(); - } - } - if (parameters.empty()) - { - return context->Language; - } - return context->Language == parameters.front() ? "1" : "0"; - } -} languageNode; - -#define TRANSITIVE_PROPERTY_NAME(PROPERTY) \ - , "INTERFACE_" #PROPERTY - -//---------------------------------------------------------------------------- -static const char* targetPropertyTransitiveWhitelist[] = { - 0 - CM_FOR_EACH_TRANSITIVE_PROPERTY_NAME(TRANSITIVE_PROPERTY_NAME) -}; - -#undef TRANSITIVE_PROPERTY_NAME - -template <typename T> -std::string -getLinkedTargetsContent( - std::vector<T> const &libraries, - cmTarget const* target, - cmTarget const* headTarget, - cmGeneratorExpressionContext *context, - cmGeneratorExpressionDAGChecker *dagChecker, - const std::string &interfacePropertyName) -{ - std::string linkedTargetsContent; - std::string sep; - std::string depString; - for (typename std::vector<T>::const_iterator it = libraries.begin(); - it != libraries.end(); ++it) - { - // Broken code can have a target in its own link interface. - // Don't follow such link interface entries so as not to create a - // self-referencing loop. - if (it->Target && it->Target != target) - { - depString += - sep + "$<TARGET_PROPERTY:" + - it->Target->GetName() + "," + interfacePropertyName + ">"; - sep = ";"; - } - } - if(!depString.empty()) - { - linkedTargetsContent = - cmGeneratorExpressionNode::EvaluateDependentExpression(depString, - target->GetMakefile(), context, - headTarget, target, dagChecker); - } - linkedTargetsContent = - cmGeneratorExpression::StripEmptyListElements(linkedTargetsContent); - return linkedTargetsContent; -} - -//---------------------------------------------------------------------------- -static const struct TargetPropertyNode : public cmGeneratorExpressionNode -{ - TargetPropertyNode() {} - - // This node handles errors on parameter count itself. - virtual int NumExpectedParameters() const { return OneOrMoreParameters; } - - std::string Evaluate(const std::vector<std::string> ¶meters, - cmGeneratorExpressionContext *context, - const GeneratorExpressionContent *content, - cmGeneratorExpressionDAGChecker *dagCheckerParent - ) const - { - if (parameters.size() != 1 && parameters.size() != 2) - { - reportError(context, content->GetOriginalExpression(), - "$<TARGET_PROPERTY:...> expression requires one or two parameters"); - return std::string(); - } - static cmsys::RegularExpression propertyNameValidator("^[A-Za-z0-9_]+$"); - - cmTarget const* target = context->HeadTarget; - std::string propertyName = *parameters.begin(); - - if (parameters.size() == 1) - { - context->HadHeadSensitiveCondition = true; - } - if (!target && parameters.size() == 1) - { - reportError(context, content->GetOriginalExpression(), - "$<TARGET_PROPERTY:prop> may only be used with binary targets. " - "It may not be used with add_custom_command or add_custom_target. " - "Specify the target to read a property from using the " - "$<TARGET_PROPERTY:tgt,prop> signature instead."); - return std::string(); - } - - if (parameters.size() == 2) - { - if (parameters.begin()->empty() && parameters[1].empty()) - { - reportError(context, content->GetOriginalExpression(), - "$<TARGET_PROPERTY:tgt,prop> expression requires a non-empty " - "target name and property name."); - return std::string(); - } - if (parameters.begin()->empty()) - { - reportError(context, content->GetOriginalExpression(), - "$<TARGET_PROPERTY:tgt,prop> expression requires a non-empty " - "target name."); - return std::string(); - } - - std::string targetName = parameters.front(); - propertyName = parameters[1]; - if (!cmGeneratorExpression::IsValidTargetName(targetName)) - { - if (!propertyNameValidator.find(propertyName.c_str())) - { - ::reportError(context, content->GetOriginalExpression(), - "Target name and property name not supported."); - return std::string(); - } - ::reportError(context, content->GetOriginalExpression(), - "Target name not supported."); - return std::string(); - } - if(propertyName == "ALIASED_TARGET") - { - if(context->Makefile->IsAlias(targetName)) - { - if(cmTarget* tgt = context->Makefile->FindTargetToUse(targetName)) - { - return tgt->GetName(); - } - } - return ""; - } - target = context->Makefile->FindTargetToUse(targetName); - - if (!target) - { - std::ostringstream e; - e << "Target \"" - << targetName - << "\" not found."; - reportError(context, content->GetOriginalExpression(), e.str()); - return std::string(); - } - context->AllTargets.insert(target); - } - - if (target == context->HeadTarget) - { - // Keep track of the properties seen while processing. - // The evaluation of the LINK_LIBRARIES generator expressions - // will check this to ensure that properties have one consistent - // value for all evaluations. - context->SeenTargetProperties.insert(propertyName); - } - if (propertyName == "SOURCES") - { - context->SourceSensitiveTargets.insert(target); - } - - if (propertyName.empty()) - { - reportError(context, content->GetOriginalExpression(), - "$<TARGET_PROPERTY:...> expression requires a non-empty property " - "name."); - return std::string(); - } - - if (!propertyNameValidator.find(propertyName)) - { - ::reportError(context, content->GetOriginalExpression(), - "Property name not supported."); - return std::string(); - } - - assert(target); - - if (propertyName == "LINKER_LANGUAGE") - { - if (target->LinkLanguagePropagatesToDependents() && - dagCheckerParent && (dagCheckerParent->EvaluatingLinkLibraries() - || dagCheckerParent->EvaluatingSources())) - { - reportError(context, content->GetOriginalExpression(), - "LINKER_LANGUAGE target property can not be used while evaluating " - "link libraries for a static library"); - return std::string(); - } - return target->GetLinkerLanguage(context->Config); - } - - cmGeneratorExpressionDAGChecker dagChecker(context->Backtrace, - target->GetName(), - propertyName, - content, - dagCheckerParent); - - switch (dagChecker.Check()) - { - case cmGeneratorExpressionDAGChecker::SELF_REFERENCE: - dagChecker.ReportError(context, content->GetOriginalExpression()); - return std::string(); - case cmGeneratorExpressionDAGChecker::CYCLIC_REFERENCE: - // No error. We just skip cyclic references. - return std::string(); - case cmGeneratorExpressionDAGChecker::ALREADY_SEEN: - for (size_t i = 1; - i < cmArraySize(targetPropertyTransitiveWhitelist); - ++i) - { - if (targetPropertyTransitiveWhitelist[i] == propertyName) - { - // No error. We're not going to find anything new here. - return std::string(); - } - } - case cmGeneratorExpressionDAGChecker::DAG: - break; - } - - const char *prop = target->GetProperty(propertyName); - - if (dagCheckerParent) - { - if (dagCheckerParent->EvaluatingLinkLibraries()) - { -#define TRANSITIVE_PROPERTY_COMPARE(PROPERTY) \ - (#PROPERTY == propertyName || "INTERFACE_" #PROPERTY == propertyName) || - if (CM_FOR_EACH_TRANSITIVE_PROPERTY_NAME(TRANSITIVE_PROPERTY_COMPARE) - false) - { - reportError(context, content->GetOriginalExpression(), - "$<TARGET_PROPERTY:...> expression in link libraries " - "evaluation depends on target property which is transitive " - "over the link libraries, creating a recursion."); - return std::string(); - } -#undef TRANSITIVE_PROPERTY_COMPARE - - if(!prop) - { - return std::string(); - } - } - else - { -#define ASSERT_TRANSITIVE_PROPERTY_METHOD(METHOD) \ - dagCheckerParent->METHOD () || - - assert( - CM_FOR_EACH_TRANSITIVE_PROPERTY_METHOD( - ASSERT_TRANSITIVE_PROPERTY_METHOD) - false); -#undef ASSERT_TRANSITIVE_PROPERTY_METHOD - } - } - - std::string linkedTargetsContent; - - std::string interfacePropertyName; - bool isInterfaceProperty = false; - -#define POPULATE_INTERFACE_PROPERTY_NAME(prop) \ - if (propertyName == #prop) \ - { \ - interfacePropertyName = "INTERFACE_" #prop; \ - } \ - else if (propertyName == "INTERFACE_" #prop) \ - { \ - interfacePropertyName = "INTERFACE_" #prop; \ - isInterfaceProperty = true; \ - } \ - else - - CM_FOR_EACH_TRANSITIVE_PROPERTY_NAME(POPULATE_INTERFACE_PROPERTY_NAME) - // Note that the above macro terminates with an else - /* else */ if (cmHasLiteralPrefix(propertyName.c_str(), - "COMPILE_DEFINITIONS_")) - { - cmPolicies::PolicyStatus polSt = - context->Makefile->GetPolicyStatus(cmPolicies::CMP0043); - if (polSt == cmPolicies::WARN || polSt == cmPolicies::OLD) - { - interfacePropertyName = "INTERFACE_COMPILE_DEFINITIONS"; - } - } -#undef POPULATE_INTERFACE_PROPERTY_NAME - cmTarget const* headTarget = context->HeadTarget && isInterfaceProperty - ? context->HeadTarget : target; - - if(isInterfaceProperty) - { - if(cmTarget::LinkInterfaceLibraries const* iface = - target->GetLinkInterfaceLibraries(context->Config, headTarget, true)) - { - linkedTargetsContent = - getLinkedTargetsContent(iface->Libraries, target, - headTarget, - context, &dagChecker, - interfacePropertyName); - } - } - else if(!interfacePropertyName.empty()) - { - if(cmTarget::LinkImplementationLibraries const* impl = - target->GetLinkImplementationLibraries(context->Config)) - { - linkedTargetsContent = - getLinkedTargetsContent(impl->Libraries, target, - target, - context, &dagChecker, - interfacePropertyName); - } - } - - if (!prop) - { - if (target->IsImported() - || target->GetType() == cmTarget::INTERFACE_LIBRARY) - { - return linkedTargetsContent; - } - if (target->IsLinkInterfaceDependentBoolProperty(propertyName, - context->Config)) - { - context->HadContextSensitiveCondition = true; - return target->GetLinkInterfaceDependentBoolProperty( - propertyName, - context->Config) ? "1" : "0"; - } - if (target->IsLinkInterfaceDependentStringProperty(propertyName, - context->Config)) - { - context->HadContextSensitiveCondition = true; - const char *propContent = - target->GetLinkInterfaceDependentStringProperty( - propertyName, - context->Config); - return propContent ? propContent : ""; - } - if (target->IsLinkInterfaceDependentNumberMinProperty(propertyName, - context->Config)) - { - context->HadContextSensitiveCondition = true; - const char *propContent = - target->GetLinkInterfaceDependentNumberMinProperty( - propertyName, - context->Config); - return propContent ? propContent : ""; - } - if (target->IsLinkInterfaceDependentNumberMaxProperty(propertyName, - context->Config)) - { - context->HadContextSensitiveCondition = true; - const char *propContent = - target->GetLinkInterfaceDependentNumberMaxProperty( - propertyName, - context->Config); - return propContent ? propContent : ""; - } - - return linkedTargetsContent; - } - - if (!target->IsImported() - && dagCheckerParent && !dagCheckerParent->EvaluatingLinkLibraries()) - { - if (target->IsLinkInterfaceDependentNumberMinProperty(propertyName, - context->Config)) - { - context->HadContextSensitiveCondition = true; - const char *propContent = - target->GetLinkInterfaceDependentNumberMinProperty( - propertyName, - context->Config); - return propContent ? propContent : ""; - } - if (target->IsLinkInterfaceDependentNumberMaxProperty(propertyName, - context->Config)) - { - context->HadContextSensitiveCondition = true; - const char *propContent = - target->GetLinkInterfaceDependentNumberMaxProperty( - propertyName, - context->Config); - return propContent ? propContent : ""; - } - } - if(!interfacePropertyName.empty()) - { - std::string result = this->EvaluateDependentExpression(prop, - context->Makefile, context, - headTarget, target, &dagChecker); - if (!linkedTargetsContent.empty()) - { - result += (result.empty() ? "" : ";") + linkedTargetsContent; - } - return result; - } - return prop; - } -} targetPropertyNode; - -//---------------------------------------------------------------------------- -static const struct TargetNameNode : public cmGeneratorExpressionNode -{ - TargetNameNode() {} - - virtual bool GeneratesContent() const { return true; } - - virtual bool AcceptsArbitraryContentParameter() const { return true; } - virtual bool RequiresLiteralInput() const { return true; } - - std::string Evaluate(const std::vector<std::string> ¶meters, - cmGeneratorExpressionContext *, - const GeneratorExpressionContent *, - cmGeneratorExpressionDAGChecker *) const - { - return parameters.front(); - } - - virtual int NumExpectedParameters() const { return 1; } - -} targetNameNode; - -//---------------------------------------------------------------------------- -static const struct TargetObjectsNode : public cmGeneratorExpressionNode -{ - TargetObjectsNode() {} - - std::string Evaluate(const std::vector<std::string> ¶meters, - cmGeneratorExpressionContext *context, - const GeneratorExpressionContent *content, - cmGeneratorExpressionDAGChecker *) const - { - if (!context->EvaluateForBuildsystem) - { - std::ostringstream e; - e << "The evaluation of the TARGET_OBJECTS generator expression " - "is only suitable for consumption by CMake. It is not suitable " - "for writing out elsewhere."; - reportError(context, content->GetOriginalExpression(), e.str()); - return std::string(); - } - - std::string tgtName = parameters.front(); - cmGeneratorTarget* gt = - context->Makefile->FindGeneratorTargetToUse(tgtName); - if (!gt) - { - std::ostringstream e; - e << "Objects of target \"" << tgtName - << "\" referenced but no such target exists."; - reportError(context, content->GetOriginalExpression(), e.str()); - return std::string(); - } - if (gt->GetType() != cmTarget::OBJECT_LIBRARY) - { - std::ostringstream e; - e << "Objects of target \"" << tgtName - << "\" referenced but is not an OBJECT library."; - reportError(context, content->GetOriginalExpression(), e.str()); - return std::string(); - } - - std::vector<cmSourceFile const*> objectSources; - gt->GetObjectSources(objectSources, context->Config); - std::map<cmSourceFile const*, std::string> mapping; - - for(std::vector<cmSourceFile const*>::const_iterator it - = objectSources.begin(); it != objectSources.end(); ++it) - { - mapping[*it]; - } - - gt->LocalGenerator->ComputeObjectFilenames(mapping, gt); - - std::string obj_dir = gt->ObjectDirectory; - std::string result; - const char* sep = ""; - for(std::vector<cmSourceFile const*>::const_iterator it - = objectSources.begin(); it != objectSources.end(); ++it) - { - // Find the object file name corresponding to this source file. - std::map<cmSourceFile const*, std::string>::const_iterator - map_it = mapping.find(*it); - // It must exist because we populated the mapping just above. - assert(!map_it->second.empty()); - result += sep; - std::string objFile = obj_dir + map_it->second; - cmSourceFile* sf = context->Makefile->GetOrCreateSource(objFile, true); - sf->SetObjectLibrary(tgtName); - sf->SetProperty("EXTERNAL_OBJECT", "1"); - result += objFile; - sep = ";"; - } - return result; - } -} targetObjectsNode; - -//---------------------------------------------------------------------------- -static const struct CompileFeaturesNode : public cmGeneratorExpressionNode -{ - CompileFeaturesNode() {} - - virtual int NumExpectedParameters() const { return OneOrMoreParameters; } - - std::string Evaluate(const std::vector<std::string> ¶meters, - cmGeneratorExpressionContext *context, - const GeneratorExpressionContent *content, - cmGeneratorExpressionDAGChecker *dagChecker) const - { - cmTarget const* target = context->HeadTarget; - if (!target) - { - reportError(context, content->GetOriginalExpression(), - "$<COMPILE_FEATURE> may only be used with binary targets. It may " - "not be used with add_custom_command or add_custom_target."); - return std::string(); - } - context->HadHeadSensitiveCondition = true; - - typedef std::map<std::string, std::vector<std::string> > LangMap; - static LangMap availableFeatures; - - LangMap testedFeatures; - - for (std::vector<std::string>::const_iterator it = parameters.begin(); - it != parameters.end(); ++it) - { - std::string error; - std::string lang; - if (!context->Makefile->CompileFeatureKnown(context->HeadTarget, - *it, lang, &error)) - { - reportError(context, content->GetOriginalExpression(), error); - return std::string(); - } - testedFeatures[lang].push_back(*it); - - if (availableFeatures.find(lang) == availableFeatures.end()) - { - const char* featuresKnown - = context->Makefile->CompileFeaturesAvailable(lang, &error); - if (!featuresKnown) - { - reportError(context, content->GetOriginalExpression(), error); - return std::string(); - } - cmSystemTools::ExpandListArgument(featuresKnown, - availableFeatures[lang]); - } - } - - bool evalLL = dagChecker && dagChecker->EvaluatingLinkLibraries(); - - std::string result; - - for (LangMap::const_iterator lit = testedFeatures.begin(); - lit != testedFeatures.end(); ++lit) - { - std::vector<std::string> const& langAvailable - = availableFeatures[lit->first]; - const char* standardDefault = context->Makefile - ->GetDefinition("CMAKE_" + lit->first + "_STANDARD_DEFAULT"); - for (std::vector<std::string>::const_iterator it = lit->second.begin(); - it != lit->second.end(); ++it) - { - if (std::find(langAvailable.begin(), langAvailable.end(), *it) - == langAvailable.end()) - { - return "0"; - } - if (standardDefault && !*standardDefault) - { - // This compiler has no notion of language standard levels. - // All features known for the language are always available. - continue; - } - if (!context->Makefile->HaveStandardAvailable(target, - lit->first, *it)) - { - if (evalLL) - { - const char* l = target->GetProperty(lit->first + "_STANDARD"); - if (!l) - { - l = standardDefault; - } - assert(l); - context->MaxLanguageStandard[target][lit->first] = l; - } - else - { - return "0"; - } - } - } - } - return "1"; - } -} compileFeaturesNode; - -//---------------------------------------------------------------------------- -static const char* targetPolicyWhitelist[] = { - 0 -#define TARGET_POLICY_STRING(POLICY) \ - , #POLICY - - CM_FOR_EACH_TARGET_POLICY(TARGET_POLICY_STRING) - -#undef TARGET_POLICY_STRING -}; - -cmPolicies::PolicyStatus statusForTarget(cmTarget const* tgt, - const char *policy) -{ -#define RETURN_POLICY(POLICY) \ - if (strcmp(policy, #POLICY) == 0) \ - { \ - return tgt->GetPolicyStatus ## POLICY (); \ - } \ - - CM_FOR_EACH_TARGET_POLICY(RETURN_POLICY) - -#undef RETURN_POLICY - - assert(0 && "Unreachable code. Not a valid policy"); - return cmPolicies::WARN; -} - -cmPolicies::PolicyID policyForString(const char *policy_id) -{ -#define RETURN_POLICY_ID(POLICY_ID) \ - if (strcmp(policy_id, #POLICY_ID) == 0) \ - { \ - return cmPolicies:: POLICY_ID; \ - } \ - - CM_FOR_EACH_TARGET_POLICY(RETURN_POLICY_ID) - -#undef RETURN_POLICY_ID - - assert(0 && "Unreachable code. Not a valid policy"); - return cmPolicies::CMP0002; -} - -//---------------------------------------------------------------------------- -static const struct TargetPolicyNode : public cmGeneratorExpressionNode -{ - TargetPolicyNode() {} - - virtual int NumExpectedParameters() const { return 1; } - - std::string Evaluate(const std::vector<std::string> ¶meters, - cmGeneratorExpressionContext *context , - const GeneratorExpressionContent *content, - cmGeneratorExpressionDAGChecker *) const - { - if (!context->HeadTarget) - { - reportError(context, content->GetOriginalExpression(), - "$<TARGET_POLICY:prop> may only be used with binary targets. It " - "may not be used with add_custom_command or add_custom_target."); - return std::string(); - } - - context->HadContextSensitiveCondition = true; - context->HadHeadSensitiveCondition = true; - - for (size_t i = 1; i < cmArraySize(targetPolicyWhitelist); ++i) - { - const char *policy = targetPolicyWhitelist[i]; - if (parameters.front() == policy) - { - cmMakefile *mf = context->HeadTarget->GetMakefile(); - switch(statusForTarget(context->HeadTarget, policy)) - { - case cmPolicies::WARN: - mf->IssueMessage(cmake::AUTHOR_WARNING, - mf->GetPolicies()-> - GetPolicyWarning(policyForString(policy))); - case cmPolicies::REQUIRED_IF_USED: - case cmPolicies::REQUIRED_ALWAYS: - case cmPolicies::OLD: - return "0"; - case cmPolicies::NEW: - return "1"; - } - } - } - reportError(context, content->GetOriginalExpression(), - "$<TARGET_POLICY:prop> may only be used with a limited number of " - "policies. Currently it may be used with the following policies:\n" - -#define STRINGIFY_HELPER(X) #X -#define STRINGIFY(X) STRINGIFY_HELPER(X) - -#define TARGET_POLICY_LIST_ITEM(POLICY) \ - " * " STRINGIFY(POLICY) "\n" - - CM_FOR_EACH_TARGET_POLICY(TARGET_POLICY_LIST_ITEM) - -#undef TARGET_POLICY_LIST_ITEM - ); - return std::string(); - } - -} targetPolicyNode; - -//---------------------------------------------------------------------------- -static const struct InstallPrefixNode : public cmGeneratorExpressionNode -{ - InstallPrefixNode() {} - - virtual bool GeneratesContent() const { return true; } - virtual int NumExpectedParameters() const { return 0; } - - std::string Evaluate(const std::vector<std::string> &, - cmGeneratorExpressionContext *context, - const GeneratorExpressionContent *content, - cmGeneratorExpressionDAGChecker *) const - { - reportError(context, content->GetOriginalExpression(), - "INSTALL_PREFIX is a marker for install(EXPORT) only. It " - "should never be evaluated."); - return std::string(); - } - -} installPrefixNode; - -//---------------------------------------------------------------------------- -class ArtifactNameTag; -class ArtifactLinkerTag; -class ArtifactSonameTag; -class ArtifactPdbTag; - -class ArtifactPathTag; -class ArtifactDirTag; -class ArtifactNameTag; - -//---------------------------------------------------------------------------- -template<typename ArtifactT> -struct TargetFilesystemArtifactResultCreator -{ - static std::string Create(cmTarget* target, - cmGeneratorExpressionContext *context, - const GeneratorExpressionContent *content); -}; - -//---------------------------------------------------------------------------- -template<> -struct TargetFilesystemArtifactResultCreator<ArtifactSonameTag> -{ - static std::string Create(cmTarget* target, - cmGeneratorExpressionContext *context, - const GeneratorExpressionContent *content) - { - // The target soname file (.so.1). - if(target->IsDLLPlatform()) - { - ::reportError(context, content->GetOriginalExpression(), - "TARGET_SONAME_FILE is not allowed " - "for DLL target platforms."); - return std::string(); - } - if(target->GetType() != cmTarget::SHARED_LIBRARY) - { - ::reportError(context, content->GetOriginalExpression(), - "TARGET_SONAME_FILE is allowed only for " - "SHARED libraries."); - return std::string(); - } - std::string result = target->GetDirectory(context->Config); - result += "/"; - result += target->GetSOName(context->Config); - return result; - } -}; - -//---------------------------------------------------------------------------- -template<> -struct TargetFilesystemArtifactResultCreator<ArtifactPdbTag> -{ - static std::string Create(cmTarget* target, - cmGeneratorExpressionContext *context, - const GeneratorExpressionContent *content) - { - std::string language = target->GetLinkerLanguage(context->Config); - - std::string pdbSupportVar = "CMAKE_" + language + "_LINKER_SUPPORTS_PDB"; - - if(!context->Makefile->IsOn(pdbSupportVar)) - { - ::reportError(context, content->GetOriginalExpression(), - "TARGET_PDB_FILE is not supported by the target linker."); - return std::string(); - } - - cmTarget::TargetType targetType = target->GetType(); - - if(targetType != cmTarget::SHARED_LIBRARY && - targetType != cmTarget::MODULE_LIBRARY && - targetType != cmTarget::EXECUTABLE) - { - ::reportError(context, content->GetOriginalExpression(), - "TARGET_PDB_FILE is allowed only for " - "targets with linker created artifacts."); - return std::string(); - } - - std::string result = target->GetPDBDirectory(context->Config); - result += "/"; - result += target->GetPDBName(context->Config); - return result; - } -}; - -//---------------------------------------------------------------------------- -template<> -struct TargetFilesystemArtifactResultCreator<ArtifactLinkerTag> -{ - static std::string Create(cmTarget* target, - cmGeneratorExpressionContext *context, - const GeneratorExpressionContent *content) - { - // The file used to link to the target (.so, .lib, .a). - if(!target->IsLinkable()) - { - ::reportError(context, content->GetOriginalExpression(), - "TARGET_LINKER_FILE is allowed only for libraries and " - "executables with ENABLE_EXPORTS."); - return std::string(); - } - return target->GetFullPath(context->Config, - target->HasImportLibrary()); - } -}; - -//---------------------------------------------------------------------------- -template<> -struct TargetFilesystemArtifactResultCreator<ArtifactNameTag> -{ - static std::string Create(cmTarget* target, - cmGeneratorExpressionContext *context, - const GeneratorExpressionContent *) - { - return target->GetFullPath(context->Config, false, true); - } -}; - - -//---------------------------------------------------------------------------- -template<typename ArtifactT> -struct TargetFilesystemArtifactResultGetter -{ - static std::string Get(const std::string &result); -}; - -//---------------------------------------------------------------------------- -template<> -struct TargetFilesystemArtifactResultGetter<ArtifactNameTag> -{ - static std::string Get(const std::string &result) - { return cmSystemTools::GetFilenameName(result); } -}; - -//---------------------------------------------------------------------------- -template<> -struct TargetFilesystemArtifactResultGetter<ArtifactDirTag> -{ - static std::string Get(const std::string &result) - { return cmSystemTools::GetFilenamePath(result); } -}; - -//---------------------------------------------------------------------------- -template<> -struct TargetFilesystemArtifactResultGetter<ArtifactPathTag> -{ - static std::string Get(const std::string &result) - { return result; } -}; - -//---------------------------------------------------------------------------- -template<typename ArtifactT, typename ComponentT> -struct TargetFilesystemArtifact : public cmGeneratorExpressionNode -{ - TargetFilesystemArtifact() {} - - virtual int NumExpectedParameters() const { return 1; } - - std::string Evaluate(const std::vector<std::string> ¶meters, - cmGeneratorExpressionContext *context, - const GeneratorExpressionContent *content, - cmGeneratorExpressionDAGChecker *dagChecker) const - { - // Lookup the referenced target. - std::string name = *parameters.begin(); - - if (!cmGeneratorExpression::IsValidTargetName(name)) - { - ::reportError(context, content->GetOriginalExpression(), - "Expression syntax not recognized."); - return std::string(); - } - cmTarget* target = context->Makefile->FindTargetToUse(name); - if(!target) - { - ::reportError(context, content->GetOriginalExpression(), - "No target \"" + name + "\""); - return std::string(); - } - if(target->GetType() >= cmTarget::OBJECT_LIBRARY && - target->GetType() != cmTarget::UNKNOWN_LIBRARY) - { - ::reportError(context, content->GetOriginalExpression(), - "Target \"" + name + "\" is not an executable or library."); - return std::string(); - } - if (dagChecker && (dagChecker->EvaluatingLinkLibraries(name.c_str()) - || (dagChecker->EvaluatingSources() - && name == dagChecker->TopTarget()))) - { - ::reportError(context, content->GetOriginalExpression(), - "Expressions which require the linker language may not " - "be used while evaluating link libraries"); - return std::string(); - } - context->DependTargets.insert(target); - context->AllTargets.insert(target); - - std::string result = - TargetFilesystemArtifactResultCreator<ArtifactT>::Create( - target, - context, - content); - if (context->HadError) - { - return std::string(); - } - return - TargetFilesystemArtifactResultGetter<ComponentT>::Get(result); - } -}; - -//---------------------------------------------------------------------------- -template<typename ArtifactT> -struct TargetFilesystemArtifactNodeGroup -{ - TargetFilesystemArtifactNodeGroup() - { - } - - TargetFilesystemArtifact<ArtifactT, ArtifactPathTag> File; - TargetFilesystemArtifact<ArtifactT, ArtifactNameTag> FileName; - TargetFilesystemArtifact<ArtifactT, ArtifactDirTag> FileDir; -}; - -//---------------------------------------------------------------------------- -static const -TargetFilesystemArtifactNodeGroup<ArtifactNameTag> targetNodeGroup; - -static const -TargetFilesystemArtifactNodeGroup<ArtifactLinkerTag> targetLinkerNodeGroup; - -static const -TargetFilesystemArtifactNodeGroup<ArtifactSonameTag> targetSoNameNodeGroup; - -static const -TargetFilesystemArtifactNodeGroup<ArtifactPdbTag> targetPdbNodeGroup; - -//---------------------------------------------------------------------------- -static const -cmGeneratorExpressionNode* GetNode(const std::string &identifier) -{ - typedef std::map<std::string, const cmGeneratorExpressionNode*> NodeMap; - static NodeMap nodeMap; - if (nodeMap.empty()) - { - nodeMap["0"] = &zeroNode; - nodeMap["1"] = &oneNode; - nodeMap["AND"] = &andNode; - nodeMap["OR"] = &orNode; - nodeMap["NOT"] = ¬Node; - nodeMap["C_COMPILER_ID"] = &cCompilerIdNode; - nodeMap["CXX_COMPILER_ID"] = &cxxCompilerIdNode; - nodeMap["VERSION_GREATER"] = &versionGreaterNode; - nodeMap["VERSION_LESS"] = &versionLessNode; - nodeMap["VERSION_EQUAL"] = &versionEqualNode; - nodeMap["C_COMPILER_VERSION"] = &cCompilerVersionNode; - nodeMap["CXX_COMPILER_VERSION"] = &cxxCompilerVersionNode; - nodeMap["PLATFORM_ID"] = &platformIdNode; - nodeMap["COMPILE_FEATURES"] = &compileFeaturesNode; - nodeMap["CONFIGURATION"] = &configurationNode; - nodeMap["CONFIG"] = &configurationTestNode; - nodeMap["TARGET_FILE"] = &targetNodeGroup.File; - nodeMap["TARGET_LINKER_FILE"] = &targetLinkerNodeGroup.File; - nodeMap["TARGET_SONAME_FILE"] = &targetSoNameNodeGroup.File; - nodeMap["TARGET_PDB_FILE"] = &targetPdbNodeGroup.File; - nodeMap["TARGET_FILE_NAME"] = &targetNodeGroup.FileName; - nodeMap["TARGET_LINKER_FILE_NAME"] = &targetLinkerNodeGroup.FileName; - nodeMap["TARGET_SONAME_FILE_NAME"] = &targetSoNameNodeGroup.FileName; - nodeMap["TARGET_PDB_FILE_NAME"] = &targetPdbNodeGroup.FileName; - nodeMap["TARGET_FILE_DIR"] = &targetNodeGroup.FileDir; - nodeMap["TARGET_LINKER_FILE_DIR"] = &targetLinkerNodeGroup.FileDir; - nodeMap["TARGET_SONAME_FILE_DIR"] = &targetSoNameNodeGroup.FileDir; - nodeMap["TARGET_PDB_FILE_DIR"] = &targetPdbNodeGroup.FileDir; - nodeMap["STREQUAL"] = &strEqualNode; - nodeMap["EQUAL"] = &equalNode; - nodeMap["LOWER_CASE"] = &lowerCaseNode; - nodeMap["UPPER_CASE"] = &upperCaseNode; - nodeMap["MAKE_C_IDENTIFIER"] = &makeCIdentifierNode; - nodeMap["BOOL"] = &boolNode; - nodeMap["ANGLE-R"] = &angle_rNode; - nodeMap["COMMA"] = &commaNode; - nodeMap["SEMICOLON"] = &semicolonNode; - nodeMap["TARGET_PROPERTY"] = &targetPropertyNode; - nodeMap["TARGET_NAME"] = &targetNameNode; - nodeMap["TARGET_OBJECTS"] = &targetObjectsNode; - nodeMap["TARGET_POLICY"] = &targetPolicyNode; - nodeMap["BUILD_INTERFACE"] = &buildInterfaceNode; - nodeMap["INSTALL_INTERFACE"] = &installInterfaceNode; - nodeMap["INSTALL_PREFIX"] = &installPrefixNode; - nodeMap["JOIN"] = &joinNode; - nodeMap["LINK_ONLY"] = &linkOnlyNode; - nodeMap["COMPILE_LANGUAGE"] = &languageNode; - } - NodeMap::const_iterator i = nodeMap.find(identifier); - if (i == nodeMap.end()) - { - return 0; - } - return i->second; - -} +#include "cmGeneratorExpressionNode.h" //---------------------------------------------------------------------------- GeneratorExpressionContent::GeneratorExpressionContent( @@ -2000,7 +114,8 @@ std::string GeneratorExpressionContent::Evaluate( } } - const cmGeneratorExpressionNode *node = GetNode(identifier); + const cmGeneratorExpressionNode *node = + cmGeneratorExpressionNode::GetNode(identifier); if (!node) { diff --git a/Source/cmGeneratorExpressionEvaluator.h b/Source/cmGeneratorExpressionEvaluator.h index b1fec0b..7c1bd8c 100644 --- a/Source/cmGeneratorExpressionEvaluator.h +++ b/Source/cmGeneratorExpressionEvaluator.h @@ -16,37 +16,10 @@ #include <string> #include "cmListFileCache.h" +#include "cmGeneratorExpressionContext.h" class cmTarget; -//---------------------------------------------------------------------------- -struct cmGeneratorExpressionContext -{ - cmGeneratorExpressionContext() - : Backtrace(NULL) - { - } - - cmListFileBacktrace Backtrace; - std::set<cmTarget*> DependTargets; - std::set<cmTarget const*> AllTargets; - std::set<std::string> SeenTargetProperties; - std::set<cmTarget const*> SourceSensitiveTargets; - std::map<cmTarget const*, std::map<std::string, std::string> > - MaxLanguageStandard; - cmMakefile *Makefile; - std::string Config; - std::string Language; - cmTarget const* HeadTarget; // The target whose property is being evaluated. - cmTarget const* CurrentTarget; // The dependent of HeadTarget which appears - // directly or indirectly in the property. - bool Quiet; - bool HadError; - bool HadContextSensitiveCondition; - bool HadHeadSensitiveCondition; - bool EvaluateForBuildsystem; -}; - struct cmGeneratorExpressionDAGChecker; struct cmGeneratorExpressionNode; diff --git a/Source/cmGeneratorExpressionNode.cxx b/Source/cmGeneratorExpressionNode.cxx new file mode 100644 index 0000000..673dcb9 --- /dev/null +++ b/Source/cmGeneratorExpressionNode.cxx @@ -0,0 +1,1870 @@ +/*============================================================================ + CMake - Cross Platform Makefile Generator + Copyright 2012 Stephen Kelly <steveire@gmail.com> + + Distributed under the OSI-approved BSD License (the "License"); + see accompanying file Copyright.txt for details. + + This software is distributed WITHOUT ANY WARRANTY; without even the + implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the License for more information. +============================================================================*/ + +#include "cmGeneratorExpressionNode.h" +#include "cmGlobalGenerator.h" +#include "cmAlgorithms.h" + +//---------------------------------------------------------------------------- +std::string cmGeneratorExpressionNode::EvaluateDependentExpression( + std::string const& prop, cmMakefile *makefile, + cmGeneratorExpressionContext *context, + cmTarget const* headTarget, cmTarget const* currentTarget, + cmGeneratorExpressionDAGChecker *dagChecker) +{ + cmGeneratorExpression ge(&context->Backtrace); + cmsys::auto_ptr<cmCompiledGeneratorExpression> cge = ge.Parse(prop); + cge->SetEvaluateForBuildsystem(context->EvaluateForBuildsystem); + std::string result = cge->Evaluate(makefile, + context->Config, + context->Quiet, + headTarget, + currentTarget, + dagChecker, + context->Language); + if (cge->GetHadContextSensitiveCondition()) + { + context->HadContextSensitiveCondition = true; + } + if (cge->GetHadHeadSensitiveCondition()) + { + context->HadHeadSensitiveCondition = true; + } + return result; +} + +//---------------------------------------------------------------------------- +static const struct ZeroNode : public cmGeneratorExpressionNode +{ + ZeroNode() {} + + virtual bool GeneratesContent() const { return false; } + + virtual bool AcceptsArbitraryContentParameter() const { return true; } + + std::string Evaluate(const std::vector<std::string> &, + cmGeneratorExpressionContext *, + const GeneratorExpressionContent *, + cmGeneratorExpressionDAGChecker *) const + { + return std::string(); + } +} zeroNode; + +//---------------------------------------------------------------------------- +static const struct OneNode : public cmGeneratorExpressionNode +{ + OneNode() {} + + virtual bool AcceptsArbitraryContentParameter() const { return true; } + + std::string Evaluate(const std::vector<std::string> ¶meters, + cmGeneratorExpressionContext *, + const GeneratorExpressionContent *, + cmGeneratorExpressionDAGChecker *) const + { + return parameters.front(); + } +} oneNode; + +//---------------------------------------------------------------------------- +static const struct OneNode buildInterfaceNode; + +//---------------------------------------------------------------------------- +static const struct ZeroNode installInterfaceNode; + +//---------------------------------------------------------------------------- +#define BOOLEAN_OP_NODE(OPNAME, OP, SUCCESS_VALUE, FAILURE_VALUE) \ +static const struct OP ## Node : public cmGeneratorExpressionNode \ +{ \ + OP ## Node () {} \ + virtual int NumExpectedParameters() const { return OneOrMoreParameters; } \ + \ + std::string Evaluate(const std::vector<std::string> ¶meters, \ + cmGeneratorExpressionContext *context, \ + const GeneratorExpressionContent *content, \ + cmGeneratorExpressionDAGChecker *) const \ + { \ + std::vector<std::string>::const_iterator it = parameters.begin(); \ + const std::vector<std::string>::const_iterator end = parameters.end(); \ + for ( ; it != end; ++it) \ + { \ + if (*it == #FAILURE_VALUE) \ + { \ + return #FAILURE_VALUE; \ + } \ + else if (*it != #SUCCESS_VALUE) \ + { \ + reportError(context, content->GetOriginalExpression(), \ + "Parameters to $<" #OP "> must resolve to either '0' or '1'."); \ + return std::string(); \ + } \ + } \ + return #SUCCESS_VALUE; \ + } \ +} OPNAME; + +BOOLEAN_OP_NODE(andNode, AND, 1, 0) +BOOLEAN_OP_NODE(orNode, OR, 0, 1) + +#undef BOOLEAN_OP_NODE + +//---------------------------------------------------------------------------- +static const struct NotNode : public cmGeneratorExpressionNode +{ + NotNode() {} + + std::string Evaluate(const std::vector<std::string> ¶meters, + cmGeneratorExpressionContext *context, + const GeneratorExpressionContent *content, + cmGeneratorExpressionDAGChecker *) const + { + if (*parameters.begin() != "0" && *parameters.begin() != "1") + { + reportError(context, content->GetOriginalExpression(), + "$<NOT> parameter must resolve to exactly one '0' or '1' value."); + return std::string(); + } + return *parameters.begin() == "0" ? "1" : "0"; + } +} notNode; + +//---------------------------------------------------------------------------- +static const struct BoolNode : public cmGeneratorExpressionNode +{ + BoolNode() {} + + virtual int NumExpectedParameters() const { return 1; } + + std::string Evaluate(const std::vector<std::string> ¶meters, + cmGeneratorExpressionContext *, + const GeneratorExpressionContent *, + cmGeneratorExpressionDAGChecker *) const + { + return !cmSystemTools::IsOff(parameters.begin()->c_str()) ? "1" : "0"; + } +} boolNode; + +//---------------------------------------------------------------------------- +static const struct StrEqualNode : public cmGeneratorExpressionNode +{ + StrEqualNode() {} + + virtual int NumExpectedParameters() const { return 2; } + + std::string Evaluate(const std::vector<std::string> ¶meters, + cmGeneratorExpressionContext *, + const GeneratorExpressionContent *, + cmGeneratorExpressionDAGChecker *) const + { + return *parameters.begin() == parameters[1] ? "1" : "0"; + } +} strEqualNode; + +//---------------------------------------------------------------------------- +static const struct EqualNode : public cmGeneratorExpressionNode +{ + EqualNode() {} + + virtual int NumExpectedParameters() const { return 2; } + + std::string Evaluate(const std::vector<std::string> ¶meters, + cmGeneratorExpressionContext *context, + const GeneratorExpressionContent *content, + cmGeneratorExpressionDAGChecker *) const + { + char *pEnd; + + int base = 0; + bool flipSign = false; + + const char *lhs = parameters[0].c_str(); + if (cmHasLiteralPrefix(lhs, "0b") || cmHasLiteralPrefix(lhs, "0B")) + { + base = 2; + lhs += 2; + } + if (cmHasLiteralPrefix(lhs, "-0b") || cmHasLiteralPrefix(lhs, "-0B")) + { + base = 2; + lhs += 3; + flipSign = true; + } + if (cmHasLiteralPrefix(lhs, "+0b") || cmHasLiteralPrefix(lhs, "+0B")) + { + base = 2; + lhs += 3; + } + + long lnum = strtol(lhs, &pEnd, base); + if (pEnd == lhs || *pEnd != '\0' || errno == ERANGE) + { + reportError(context, content->GetOriginalExpression(), + "$<EQUAL> parameter " + parameters[0] + " is not a valid integer."); + return std::string(); + } + + if (flipSign) + { + lnum = -lnum; + } + + base = 0; + flipSign = false; + + const char *rhs = parameters[1].c_str(); + if (cmHasLiteralPrefix(rhs, "0b") || cmHasLiteralPrefix(rhs, "0B")) + { + base = 2; + rhs += 2; + } + if (cmHasLiteralPrefix(rhs, "-0b") || cmHasLiteralPrefix(rhs, "-0B")) + { + base = 2; + rhs += 3; + flipSign = true; + } + if (cmHasLiteralPrefix(rhs, "+0b") || cmHasLiteralPrefix(rhs, "+0B")) + { + base = 2; + rhs += 3; + } + + long rnum = strtol(rhs, &pEnd, base); + if (pEnd == rhs || *pEnd != '\0' || errno == ERANGE) + { + reportError(context, content->GetOriginalExpression(), + "$<EQUAL> parameter " + parameters[1] + " is not a valid integer."); + return std::string(); + } + + if (flipSign) + { + rnum = -rnum; + } + + return lnum == rnum ? "1" : "0"; + } +} equalNode; + +//---------------------------------------------------------------------------- +static const struct LowerCaseNode : public cmGeneratorExpressionNode +{ + LowerCaseNode() {} + + bool AcceptsArbitraryContentParameter() const { return true; } + + std::string Evaluate(const std::vector<std::string> ¶meters, + cmGeneratorExpressionContext *, + const GeneratorExpressionContent *, + cmGeneratorExpressionDAGChecker *) const + { + return cmSystemTools::LowerCase(parameters.front()); + } +} lowerCaseNode; + +//---------------------------------------------------------------------------- +static const struct UpperCaseNode : public cmGeneratorExpressionNode +{ + UpperCaseNode() {} + + bool AcceptsArbitraryContentParameter() const { return true; } + + std::string Evaluate(const std::vector<std::string> ¶meters, + cmGeneratorExpressionContext *, + const GeneratorExpressionContent *, + cmGeneratorExpressionDAGChecker *) const + { + return cmSystemTools::UpperCase(parameters.front()); + } +} upperCaseNode; + +//---------------------------------------------------------------------------- +static const struct MakeCIdentifierNode : public cmGeneratorExpressionNode +{ + MakeCIdentifierNode() {} + + bool AcceptsArbitraryContentParameter() const { return true; } + + std::string Evaluate(const std::vector<std::string> ¶meters, + cmGeneratorExpressionContext *, + const GeneratorExpressionContent *, + cmGeneratorExpressionDAGChecker *) const + { + return cmSystemTools::MakeCidentifier(parameters.front()); + } +} makeCIdentifierNode; + +//---------------------------------------------------------------------------- +static const struct Angle_RNode : public cmGeneratorExpressionNode +{ + Angle_RNode() {} + + virtual int NumExpectedParameters() const { return 0; } + + std::string Evaluate(const std::vector<std::string> &, + cmGeneratorExpressionContext *, + const GeneratorExpressionContent *, + cmGeneratorExpressionDAGChecker *) const + { + return ">"; + } +} angle_rNode; + +//---------------------------------------------------------------------------- +static const struct CommaNode : public cmGeneratorExpressionNode +{ + CommaNode() {} + + virtual int NumExpectedParameters() const { return 0; } + + std::string Evaluate(const std::vector<std::string> &, + cmGeneratorExpressionContext *, + const GeneratorExpressionContent *, + cmGeneratorExpressionDAGChecker *) const + { + return ","; + } +} commaNode; + +//---------------------------------------------------------------------------- +static const struct SemicolonNode : public cmGeneratorExpressionNode +{ + SemicolonNode() {} + + virtual int NumExpectedParameters() const { return 0; } + + std::string Evaluate(const std::vector<std::string> &, + cmGeneratorExpressionContext *, + const GeneratorExpressionContent *, + cmGeneratorExpressionDAGChecker *) const + { + return ";"; + } +} semicolonNode; + +//---------------------------------------------------------------------------- +struct CompilerIdNode : public cmGeneratorExpressionNode +{ + CompilerIdNode() {} + + virtual int NumExpectedParameters() const { return OneOrZeroParameters; } + + std::string EvaluateWithLanguage(const std::vector<std::string> ¶meters, + cmGeneratorExpressionContext *context, + const GeneratorExpressionContent *content, + cmGeneratorExpressionDAGChecker *, + const std::string &lang) const + { + const char *compilerId = + context->Makefile->GetSafeDefinition("CMAKE_" + lang + "_COMPILER_ID"); + if (parameters.empty()) + { + return compilerId ? compilerId : ""; + } + static cmsys::RegularExpression compilerIdValidator("^[A-Za-z0-9_]*$"); + if (!compilerIdValidator.find(*parameters.begin())) + { + reportError(context, content->GetOriginalExpression(), + "Expression syntax not recognized."); + return std::string(); + } + if (!compilerId) + { + return parameters.front().empty() ? "1" : "0"; + } + + if (strcmp(parameters.begin()->c_str(), compilerId) == 0) + { + return "1"; + } + + if (cmsysString_strcasecmp(parameters.begin()->c_str(), compilerId) == 0) + { + switch(context->Makefile->GetPolicyStatus(cmPolicies::CMP0044)) + { + case cmPolicies::WARN: + { + std::ostringstream e; + e << context->Makefile->GetPolicies() + ->GetPolicyWarning(cmPolicies::CMP0044); + context->Makefile->GetCMakeInstance() + ->IssueMessage(cmake::AUTHOR_WARNING, + e.str(), context->Backtrace); + } + case cmPolicies::OLD: + return "1"; + case cmPolicies::NEW: + case cmPolicies::REQUIRED_ALWAYS: + case cmPolicies::REQUIRED_IF_USED: + break; + } + } + return "0"; + } +}; + +//---------------------------------------------------------------------------- +static const struct CCompilerIdNode : public CompilerIdNode +{ + CCompilerIdNode() {} + + std::string Evaluate(const std::vector<std::string> ¶meters, + cmGeneratorExpressionContext *context, + const GeneratorExpressionContent *content, + cmGeneratorExpressionDAGChecker *dagChecker) const + { + if (!context->HeadTarget) + { + reportError(context, content->GetOriginalExpression(), + "$<C_COMPILER_ID> may only be used with binary targets. It may " + "not be used with add_custom_command or add_custom_target."); + return std::string(); + } + return this->EvaluateWithLanguage(parameters, context, content, + dagChecker, "C"); + } +} cCompilerIdNode; + +//---------------------------------------------------------------------------- +static const struct CXXCompilerIdNode : public CompilerIdNode +{ + CXXCompilerIdNode() {} + + std::string Evaluate(const std::vector<std::string> ¶meters, + cmGeneratorExpressionContext *context, + const GeneratorExpressionContent *content, + cmGeneratorExpressionDAGChecker *dagChecker) const + { + if (!context->HeadTarget) + { + reportError(context, content->GetOriginalExpression(), + "$<CXX_COMPILER_ID> may only be used with binary targets. It may " + "not be used with add_custom_command or add_custom_target."); + return std::string(); + } + return this->EvaluateWithLanguage(parameters, context, content, + dagChecker, "CXX"); + } +} cxxCompilerIdNode; + +//---------------------------------------------------------------------------- +struct CompilerVersionNode : public cmGeneratorExpressionNode +{ + CompilerVersionNode() {} + + virtual int NumExpectedParameters() const { return OneOrZeroParameters; } + + std::string EvaluateWithLanguage(const std::vector<std::string> ¶meters, + cmGeneratorExpressionContext *context, + const GeneratorExpressionContent *content, + cmGeneratorExpressionDAGChecker *, + const std::string &lang) const + { + const char *compilerVersion = context->Makefile->GetSafeDefinition( + "CMAKE_" + lang + "_COMPILER_VERSION"); + if (parameters.empty()) + { + return compilerVersion ? compilerVersion : ""; + } + + static cmsys::RegularExpression compilerIdValidator("^[0-9\\.]*$"); + if (!compilerIdValidator.find(*parameters.begin())) + { + reportError(context, content->GetOriginalExpression(), + "Expression syntax not recognized."); + return std::string(); + } + if (!compilerVersion) + { + return parameters.front().empty() ? "1" : "0"; + } + + return cmSystemTools::VersionCompare(cmSystemTools::OP_EQUAL, + parameters.begin()->c_str(), + compilerVersion) ? "1" : "0"; + } +}; + +//---------------------------------------------------------------------------- +static const struct CCompilerVersionNode : public CompilerVersionNode +{ + CCompilerVersionNode() {} + + std::string Evaluate(const std::vector<std::string> ¶meters, + cmGeneratorExpressionContext *context, + const GeneratorExpressionContent *content, + cmGeneratorExpressionDAGChecker *dagChecker) const + { + if (!context->HeadTarget) + { + reportError(context, content->GetOriginalExpression(), + "$<C_COMPILER_VERSION> may only be used with binary targets. It " + "may not be used with add_custom_command or add_custom_target."); + return std::string(); + } + return this->EvaluateWithLanguage(parameters, context, content, + dagChecker, "C"); + } +} cCompilerVersionNode; + +//---------------------------------------------------------------------------- +static const struct CxxCompilerVersionNode : public CompilerVersionNode +{ + CxxCompilerVersionNode() {} + + std::string Evaluate(const std::vector<std::string> ¶meters, + cmGeneratorExpressionContext *context, + const GeneratorExpressionContent *content, + cmGeneratorExpressionDAGChecker *dagChecker) const + { + if (!context->HeadTarget) + { + reportError(context, content->GetOriginalExpression(), + "$<CXX_COMPILER_VERSION> may only be used with binary targets. It " + "may not be used with add_custom_command or add_custom_target."); + return std::string(); + } + return this->EvaluateWithLanguage(parameters, context, content, + dagChecker, "CXX"); + } +} cxxCompilerVersionNode; + + +//---------------------------------------------------------------------------- +struct PlatformIdNode : public cmGeneratorExpressionNode +{ + PlatformIdNode() {} + + virtual int NumExpectedParameters() const { return OneOrZeroParameters; } + + std::string Evaluate(const std::vector<std::string> ¶meters, + cmGeneratorExpressionContext *context, + const GeneratorExpressionContent *, + cmGeneratorExpressionDAGChecker *) const + { + const char *platformId = + context->Makefile->GetSafeDefinition("CMAKE_SYSTEM_NAME"); + if (parameters.empty()) + { + return platformId ? platformId : ""; + } + + if (!platformId) + { + return parameters.front().empty() ? "1" : "0"; + } + + if (strcmp(parameters.begin()->c_str(), platformId) == 0) + { + return "1"; + } + return "0"; + } +} platformIdNode; + +//---------------------------------------------------------------------------- +static const struct VersionGreaterNode : public cmGeneratorExpressionNode +{ + VersionGreaterNode() {} + + virtual int NumExpectedParameters() const { return 2; } + + std::string Evaluate(const std::vector<std::string> ¶meters, + cmGeneratorExpressionContext *, + const GeneratorExpressionContent *, + cmGeneratorExpressionDAGChecker *) const + { + return cmSystemTools::VersionCompare(cmSystemTools::OP_GREATER, + parameters.front().c_str(), + parameters[1].c_str()) ? "1" : "0"; + } +} versionGreaterNode; + +//---------------------------------------------------------------------------- +static const struct VersionLessNode : public cmGeneratorExpressionNode +{ + VersionLessNode() {} + + virtual int NumExpectedParameters() const { return 2; } + + std::string Evaluate(const std::vector<std::string> ¶meters, + cmGeneratorExpressionContext *, + const GeneratorExpressionContent *, + cmGeneratorExpressionDAGChecker *) const + { + return cmSystemTools::VersionCompare(cmSystemTools::OP_LESS, + parameters.front().c_str(), + parameters[1].c_str()) ? "1" : "0"; + } +} versionLessNode; + +//---------------------------------------------------------------------------- +static const struct VersionEqualNode : public cmGeneratorExpressionNode +{ + VersionEqualNode() {} + + virtual int NumExpectedParameters() const { return 2; } + + std::string Evaluate(const std::vector<std::string> ¶meters, + cmGeneratorExpressionContext *, + const GeneratorExpressionContent *, + cmGeneratorExpressionDAGChecker *) const + { + return cmSystemTools::VersionCompare(cmSystemTools::OP_EQUAL, + parameters.front().c_str(), + parameters[1].c_str()) ? "1" : "0"; + } +} versionEqualNode; + +//---------------------------------------------------------------------------- +static const struct LinkOnlyNode : public cmGeneratorExpressionNode +{ + LinkOnlyNode() {} + + std::string Evaluate(const std::vector<std::string> ¶meters, + cmGeneratorExpressionContext *, + const GeneratorExpressionContent *, + cmGeneratorExpressionDAGChecker *dagChecker) const + { + if(!dagChecker->GetTransitivePropertiesOnly()) + { + return parameters.front(); + } + return ""; + } +} linkOnlyNode; + +//---------------------------------------------------------------------------- +static const struct ConfigurationNode : public cmGeneratorExpressionNode +{ + ConfigurationNode() {} + + virtual int NumExpectedParameters() const { return 0; } + + std::string Evaluate(const std::vector<std::string> &, + cmGeneratorExpressionContext *context, + const GeneratorExpressionContent *, + cmGeneratorExpressionDAGChecker *) const + { + context->HadContextSensitiveCondition = true; + return context->Config; + } +} configurationNode; + +//---------------------------------------------------------------------------- +static const struct ConfigurationTestNode : public cmGeneratorExpressionNode +{ + ConfigurationTestNode() {} + + virtual int NumExpectedParameters() const { return OneOrZeroParameters; } + + std::string Evaluate(const std::vector<std::string> ¶meters, + cmGeneratorExpressionContext *context, + const GeneratorExpressionContent *content, + cmGeneratorExpressionDAGChecker *) const + { + if (parameters.empty()) + { + return configurationNode.Evaluate(parameters, context, content, 0); + } + static cmsys::RegularExpression configValidator("^[A-Za-z0-9_]*$"); + if (!configValidator.find(*parameters.begin())) + { + reportError(context, content->GetOriginalExpression(), + "Expression syntax not recognized."); + return std::string(); + } + context->HadContextSensitiveCondition = true; + if (context->Config.empty()) + { + return parameters.front().empty() ? "1" : "0"; + } + + if (cmsysString_strcasecmp(parameters.begin()->c_str(), + context->Config.c_str()) == 0) + { + return "1"; + } + + if (context->CurrentTarget + && context->CurrentTarget->IsImported()) + { + const char* loc = 0; + const char* imp = 0; + std::string suffix; + if (context->CurrentTarget->GetMappedConfig(context->Config, + &loc, + &imp, + suffix)) + { + // This imported target has an appropriate location + // for this (possibly mapped) config. + // Check if there is a proper config mapping for the tested config. + std::vector<std::string> mappedConfigs; + std::string mapProp = "MAP_IMPORTED_CONFIG_"; + mapProp += cmSystemTools::UpperCase(context->Config); + if(const char* mapValue = + context->CurrentTarget->GetProperty(mapProp)) + { + cmSystemTools::ExpandListArgument(cmSystemTools::UpperCase(mapValue), + mappedConfigs); + return std::find(mappedConfigs.begin(), mappedConfigs.end(), + cmSystemTools::UpperCase(parameters.front())) + != mappedConfigs.end() ? "1" : "0"; + } + } + } + return "0"; + } +} configurationTestNode; + +static const struct JoinNode : public cmGeneratorExpressionNode +{ + JoinNode() {} + + virtual int NumExpectedParameters() const { return 2; } + + virtual bool AcceptsArbitraryContentParameter() const { return true; } + + std::string Evaluate(const std::vector<std::string> ¶meters, + cmGeneratorExpressionContext *, + const GeneratorExpressionContent *, + cmGeneratorExpressionDAGChecker *) const + { + std::vector<std::string> list; + cmSystemTools::ExpandListArgument(parameters.front(), list); + return cmJoin(list, parameters[1]); + } +} joinNode; + +static const struct CompileLanguageNode : public cmGeneratorExpressionNode +{ + CompileLanguageNode() {} + + virtual int NumExpectedParameters() const { return OneOrZeroParameters; } + + std::string Evaluate(const std::vector<std::string> ¶meters, + cmGeneratorExpressionContext *context, + const GeneratorExpressionContent *content, + cmGeneratorExpressionDAGChecker *dagChecker) const + { + if(context->Language.empty()) + { + reportError(context, content->GetOriginalExpression(), + "$<COMPILE_LANGUAGE:...> may only be used to specify include " + "directories compile definitions, compile options and to evaluate " + "components of the file(GENERATE) command."); + return std::string(); + } + + std::vector<std::string> enabledLanguages; + cmGlobalGenerator* gg + = context->Makefile->GetLocalGenerator()->GetGlobalGenerator(); + gg->GetEnabledLanguages(enabledLanguages); + if (!parameters.empty() && + std::find(enabledLanguages.begin(), enabledLanguages.end(), + parameters.front()) == enabledLanguages.end()) + { + reportError(context, content->GetOriginalExpression(), + "$<COMPILE_LANGUAGE:...> Unknown language."); + return std::string(); + } + std::string genName = gg->GetName(); + if (genName.find("Visual Studio") != std::string::npos) + { + reportError(context, content->GetOriginalExpression(), + "$<COMPILE_LANGUAGE:...> may not be used with Visual Studio " + "generators."); + return std::string(); + } + else if (genName.find("Xcode") != std::string::npos) + { + if (dagChecker && (dagChecker->EvaluatingCompileDefinitions() + || dagChecker->EvaluatingIncludeDirectories())) + { + reportError(context, content->GetOriginalExpression(), + "$<COMPILE_LANGUAGE:...> may only be used with COMPILE_OPTIONS " + "with the Xcode generator."); + return std::string(); + } + } + else + { + if(genName.find("Makefiles") == std::string::npos && + genName.find("Ninja") == std::string::npos && + genName.find("Watcom WMake") == std::string::npos) + { + reportError(context, content->GetOriginalExpression(), + "$<COMPILE_LANGUAGE:...> not supported for this generator."); + return std::string(); + } + } + if (parameters.empty()) + { + return context->Language; + } + return context->Language == parameters.front() ? "1" : "0"; + } +} languageNode; + +#define TRANSITIVE_PROPERTY_NAME(PROPERTY) \ + , "INTERFACE_" #PROPERTY + +//---------------------------------------------------------------------------- +static const char* targetPropertyTransitiveWhitelist[] = { + 0 + CM_FOR_EACH_TRANSITIVE_PROPERTY_NAME(TRANSITIVE_PROPERTY_NAME) +}; + +#undef TRANSITIVE_PROPERTY_NAME + +template <typename T> +std::string +getLinkedTargetsContent( + std::vector<T> const &libraries, + cmTarget const* target, + cmTarget const* headTarget, + cmGeneratorExpressionContext *context, + cmGeneratorExpressionDAGChecker *dagChecker, + const std::string &interfacePropertyName) +{ + std::string linkedTargetsContent; + std::string sep; + std::string depString; + for (typename std::vector<T>::const_iterator it = libraries.begin(); + it != libraries.end(); ++it) + { + // Broken code can have a target in its own link interface. + // Don't follow such link interface entries so as not to create a + // self-referencing loop. + if (it->Target && it->Target != target) + { + depString += + sep + "$<TARGET_PROPERTY:" + + it->Target->GetName() + "," + interfacePropertyName + ">"; + sep = ";"; + } + } + if(!depString.empty()) + { + linkedTargetsContent = + cmGeneratorExpressionNode::EvaluateDependentExpression(depString, + target->GetMakefile(), context, + headTarget, target, dagChecker); + } + linkedTargetsContent = + cmGeneratorExpression::StripEmptyListElements(linkedTargetsContent); + return linkedTargetsContent; +} + +//---------------------------------------------------------------------------- +static const struct TargetPropertyNode : public cmGeneratorExpressionNode +{ + TargetPropertyNode() {} + + // This node handles errors on parameter count itself. + virtual int NumExpectedParameters() const { return OneOrMoreParameters; } + + std::string Evaluate(const std::vector<std::string> ¶meters, + cmGeneratorExpressionContext *context, + const GeneratorExpressionContent *content, + cmGeneratorExpressionDAGChecker *dagCheckerParent + ) const + { + if (parameters.size() != 1 && parameters.size() != 2) + { + reportError(context, content->GetOriginalExpression(), + "$<TARGET_PROPERTY:...> expression requires one or two parameters"); + return std::string(); + } + static cmsys::RegularExpression propertyNameValidator("^[A-Za-z0-9_]+$"); + + cmTarget const* target = context->HeadTarget; + std::string propertyName = *parameters.begin(); + + if (parameters.size() == 1) + { + context->HadHeadSensitiveCondition = true; + } + if (!target && parameters.size() == 1) + { + reportError(context, content->GetOriginalExpression(), + "$<TARGET_PROPERTY:prop> may only be used with binary targets. " + "It may not be used with add_custom_command or add_custom_target. " + "Specify the target to read a property from using the " + "$<TARGET_PROPERTY:tgt,prop> signature instead."); + return std::string(); + } + + if (parameters.size() == 2) + { + if (parameters.begin()->empty() && parameters[1].empty()) + { + reportError(context, content->GetOriginalExpression(), + "$<TARGET_PROPERTY:tgt,prop> expression requires a non-empty " + "target name and property name."); + return std::string(); + } + if (parameters.begin()->empty()) + { + reportError(context, content->GetOriginalExpression(), + "$<TARGET_PROPERTY:tgt,prop> expression requires a non-empty " + "target name."); + return std::string(); + } + + std::string targetName = parameters.front(); + propertyName = parameters[1]; + if (!cmGeneratorExpression::IsValidTargetName(targetName)) + { + if (!propertyNameValidator.find(propertyName.c_str())) + { + ::reportError(context, content->GetOriginalExpression(), + "Target name and property name not supported."); + return std::string(); + } + ::reportError(context, content->GetOriginalExpression(), + "Target name not supported."); + return std::string(); + } + if(propertyName == "ALIASED_TARGET") + { + if(context->Makefile->IsAlias(targetName)) + { + if(cmTarget* tgt = context->Makefile->FindTargetToUse(targetName)) + { + return tgt->GetName(); + } + } + return ""; + } + target = context->Makefile->FindTargetToUse(targetName); + + if (!target) + { + std::ostringstream e; + e << "Target \"" + << targetName + << "\" not found."; + reportError(context, content->GetOriginalExpression(), e.str()); + return std::string(); + } + context->AllTargets.insert(target); + } + + if (target == context->HeadTarget) + { + // Keep track of the properties seen while processing. + // The evaluation of the LINK_LIBRARIES generator expressions + // will check this to ensure that properties have one consistent + // value for all evaluations. + context->SeenTargetProperties.insert(propertyName); + } + if (propertyName == "SOURCES") + { + context->SourceSensitiveTargets.insert(target); + } + + if (propertyName.empty()) + { + reportError(context, content->GetOriginalExpression(), + "$<TARGET_PROPERTY:...> expression requires a non-empty property " + "name."); + return std::string(); + } + + if (!propertyNameValidator.find(propertyName)) + { + ::reportError(context, content->GetOriginalExpression(), + "Property name not supported."); + return std::string(); + } + + assert(target); + + if (propertyName == "LINKER_LANGUAGE") + { + if (target->LinkLanguagePropagatesToDependents() && + dagCheckerParent && (dagCheckerParent->EvaluatingLinkLibraries() + || dagCheckerParent->EvaluatingSources())) + { + reportError(context, content->GetOriginalExpression(), + "LINKER_LANGUAGE target property can not be used while evaluating " + "link libraries for a static library"); + return std::string(); + } + return target->GetLinkerLanguage(context->Config); + } + + cmGeneratorExpressionDAGChecker dagChecker(context->Backtrace, + target->GetName(), + propertyName, + content, + dagCheckerParent); + + switch (dagChecker.Check()) + { + case cmGeneratorExpressionDAGChecker::SELF_REFERENCE: + dagChecker.ReportError(context, content->GetOriginalExpression()); + return std::string(); + case cmGeneratorExpressionDAGChecker::CYCLIC_REFERENCE: + // No error. We just skip cyclic references. + return std::string(); + case cmGeneratorExpressionDAGChecker::ALREADY_SEEN: + for (size_t i = 1; + i < cmArraySize(targetPropertyTransitiveWhitelist); + ++i) + { + if (targetPropertyTransitiveWhitelist[i] == propertyName) + { + // No error. We're not going to find anything new here. + return std::string(); + } + } + case cmGeneratorExpressionDAGChecker::DAG: + break; + } + + const char *prop = target->GetProperty(propertyName); + + if (dagCheckerParent) + { + if (dagCheckerParent->EvaluatingLinkLibraries()) + { +#define TRANSITIVE_PROPERTY_COMPARE(PROPERTY) \ + (#PROPERTY == propertyName || "INTERFACE_" #PROPERTY == propertyName) || + if (CM_FOR_EACH_TRANSITIVE_PROPERTY_NAME(TRANSITIVE_PROPERTY_COMPARE) + false) + { + reportError(context, content->GetOriginalExpression(), + "$<TARGET_PROPERTY:...> expression in link libraries " + "evaluation depends on target property which is transitive " + "over the link libraries, creating a recursion."); + return std::string(); + } +#undef TRANSITIVE_PROPERTY_COMPARE + + if(!prop) + { + return std::string(); + } + } + else + { +#define ASSERT_TRANSITIVE_PROPERTY_METHOD(METHOD) \ + dagCheckerParent->METHOD () || + + assert( + CM_FOR_EACH_TRANSITIVE_PROPERTY_METHOD( + ASSERT_TRANSITIVE_PROPERTY_METHOD) + false); +#undef ASSERT_TRANSITIVE_PROPERTY_METHOD + } + } + + std::string linkedTargetsContent; + + std::string interfacePropertyName; + bool isInterfaceProperty = false; + +#define POPULATE_INTERFACE_PROPERTY_NAME(prop) \ + if (propertyName == #prop) \ + { \ + interfacePropertyName = "INTERFACE_" #prop; \ + } \ + else if (propertyName == "INTERFACE_" #prop) \ + { \ + interfacePropertyName = "INTERFACE_" #prop; \ + isInterfaceProperty = true; \ + } \ + else + + CM_FOR_EACH_TRANSITIVE_PROPERTY_NAME(POPULATE_INTERFACE_PROPERTY_NAME) + // Note that the above macro terminates with an else + /* else */ if (cmHasLiteralPrefix(propertyName.c_str(), + "COMPILE_DEFINITIONS_")) + { + cmPolicies::PolicyStatus polSt = + context->Makefile->GetPolicyStatus(cmPolicies::CMP0043); + if (polSt == cmPolicies::WARN || polSt == cmPolicies::OLD) + { + interfacePropertyName = "INTERFACE_COMPILE_DEFINITIONS"; + } + } +#undef POPULATE_INTERFACE_PROPERTY_NAME + cmTarget const* headTarget = context->HeadTarget && isInterfaceProperty + ? context->HeadTarget : target; + + if(isInterfaceProperty) + { + if(cmTarget::LinkInterfaceLibraries const* iface = + target->GetLinkInterfaceLibraries(context->Config, headTarget, true)) + { + linkedTargetsContent = + getLinkedTargetsContent(iface->Libraries, target, + headTarget, + context, &dagChecker, + interfacePropertyName); + } + } + else if(!interfacePropertyName.empty()) + { + if(cmTarget::LinkImplementationLibraries const* impl = + target->GetLinkImplementationLibraries(context->Config)) + { + linkedTargetsContent = + getLinkedTargetsContent(impl->Libraries, target, + target, + context, &dagChecker, + interfacePropertyName); + } + } + + if (!prop) + { + if (target->IsImported() + || target->GetType() == cmTarget::INTERFACE_LIBRARY) + { + return linkedTargetsContent; + } + if (target->IsLinkInterfaceDependentBoolProperty(propertyName, + context->Config)) + { + context->HadContextSensitiveCondition = true; + return target->GetLinkInterfaceDependentBoolProperty( + propertyName, + context->Config) ? "1" : "0"; + } + if (target->IsLinkInterfaceDependentStringProperty(propertyName, + context->Config)) + { + context->HadContextSensitiveCondition = true; + const char *propContent = + target->GetLinkInterfaceDependentStringProperty( + propertyName, + context->Config); + return propContent ? propContent : ""; + } + if (target->IsLinkInterfaceDependentNumberMinProperty(propertyName, + context->Config)) + { + context->HadContextSensitiveCondition = true; + const char *propContent = + target->GetLinkInterfaceDependentNumberMinProperty( + propertyName, + context->Config); + return propContent ? propContent : ""; + } + if (target->IsLinkInterfaceDependentNumberMaxProperty(propertyName, + context->Config)) + { + context->HadContextSensitiveCondition = true; + const char *propContent = + target->GetLinkInterfaceDependentNumberMaxProperty( + propertyName, + context->Config); + return propContent ? propContent : ""; + } + + return linkedTargetsContent; + } + + if (!target->IsImported() + && dagCheckerParent && !dagCheckerParent->EvaluatingLinkLibraries()) + { + if (target->IsLinkInterfaceDependentNumberMinProperty(propertyName, + context->Config)) + { + context->HadContextSensitiveCondition = true; + const char *propContent = + target->GetLinkInterfaceDependentNumberMinProperty( + propertyName, + context->Config); + return propContent ? propContent : ""; + } + if (target->IsLinkInterfaceDependentNumberMaxProperty(propertyName, + context->Config)) + { + context->HadContextSensitiveCondition = true; + const char *propContent = + target->GetLinkInterfaceDependentNumberMaxProperty( + propertyName, + context->Config); + return propContent ? propContent : ""; + } + } + if(!interfacePropertyName.empty()) + { + std::string result = this->EvaluateDependentExpression(prop, + context->Makefile, context, + headTarget, target, &dagChecker); + if (!linkedTargetsContent.empty()) + { + result += (result.empty() ? "" : ";") + linkedTargetsContent; + } + return result; + } + return prop; + } +} targetPropertyNode; + +//---------------------------------------------------------------------------- +static const struct TargetNameNode : public cmGeneratorExpressionNode +{ + TargetNameNode() {} + + virtual bool GeneratesContent() const { return true; } + + virtual bool AcceptsArbitraryContentParameter() const { return true; } + virtual bool RequiresLiteralInput() const { return true; } + + std::string Evaluate(const std::vector<std::string> ¶meters, + cmGeneratorExpressionContext *, + const GeneratorExpressionContent *, + cmGeneratorExpressionDAGChecker *) const + { + return parameters.front(); + } + + virtual int NumExpectedParameters() const { return 1; } + +} targetNameNode; + +//---------------------------------------------------------------------------- +static const struct TargetObjectsNode : public cmGeneratorExpressionNode +{ + TargetObjectsNode() {} + + std::string Evaluate(const std::vector<std::string> ¶meters, + cmGeneratorExpressionContext *context, + const GeneratorExpressionContent *content, + cmGeneratorExpressionDAGChecker *) const + { + if (!context->EvaluateForBuildsystem) + { + std::ostringstream e; + e << "The evaluation of the TARGET_OBJECTS generator expression " + "is only suitable for consumption by CMake. It is not suitable " + "for writing out elsewhere."; + reportError(context, content->GetOriginalExpression(), e.str()); + return std::string(); + } + + std::string tgtName = parameters.front(); + cmGeneratorTarget* gt = + context->Makefile->FindGeneratorTargetToUse(tgtName); + if (!gt) + { + std::ostringstream e; + e << "Objects of target \"" << tgtName + << "\" referenced but no such target exists."; + reportError(context, content->GetOriginalExpression(), e.str()); + return std::string(); + } + if (gt->GetType() != cmTarget::OBJECT_LIBRARY) + { + std::ostringstream e; + e << "Objects of target \"" << tgtName + << "\" referenced but is not an OBJECT library."; + reportError(context, content->GetOriginalExpression(), e.str()); + return std::string(); + } + + std::vector<cmSourceFile const*> objectSources; + gt->GetObjectSources(objectSources, context->Config); + std::map<cmSourceFile const*, std::string> mapping; + + for(std::vector<cmSourceFile const*>::const_iterator it + = objectSources.begin(); it != objectSources.end(); ++it) + { + mapping[*it]; + } + + gt->LocalGenerator->ComputeObjectFilenames(mapping, gt); + + std::string obj_dir = gt->ObjectDirectory; + std::string result; + const char* sep = ""; + for(std::vector<cmSourceFile const*>::const_iterator it + = objectSources.begin(); it != objectSources.end(); ++it) + { + // Find the object file name corresponding to this source file. + std::map<cmSourceFile const*, std::string>::const_iterator + map_it = mapping.find(*it); + // It must exist because we populated the mapping just above. + assert(!map_it->second.empty()); + result += sep; + std::string objFile = obj_dir + map_it->second; + cmSourceFile* sf = context->Makefile->GetOrCreateSource(objFile, true); + sf->SetObjectLibrary(tgtName); + sf->SetProperty("EXTERNAL_OBJECT", "1"); + result += objFile; + sep = ";"; + } + return result; + } +} targetObjectsNode; + +//---------------------------------------------------------------------------- +static const struct CompileFeaturesNode : public cmGeneratorExpressionNode +{ + CompileFeaturesNode() {} + + virtual int NumExpectedParameters() const { return OneOrMoreParameters; } + + std::string Evaluate(const std::vector<std::string> ¶meters, + cmGeneratorExpressionContext *context, + const GeneratorExpressionContent *content, + cmGeneratorExpressionDAGChecker *dagChecker) const + { + cmTarget const* target = context->HeadTarget; + if (!target) + { + reportError(context, content->GetOriginalExpression(), + "$<COMPILE_FEATURE> may only be used with binary targets. It may " + "not be used with add_custom_command or add_custom_target."); + return std::string(); + } + context->HadHeadSensitiveCondition = true; + + typedef std::map<std::string, std::vector<std::string> > LangMap; + static LangMap availableFeatures; + + LangMap testedFeatures; + + for (std::vector<std::string>::const_iterator it = parameters.begin(); + it != parameters.end(); ++it) + { + std::string error; + std::string lang; + if (!context->Makefile->CompileFeatureKnown(context->HeadTarget, + *it, lang, &error)) + { + reportError(context, content->GetOriginalExpression(), error); + return std::string(); + } + testedFeatures[lang].push_back(*it); + + if (availableFeatures.find(lang) == availableFeatures.end()) + { + const char* featuresKnown + = context->Makefile->CompileFeaturesAvailable(lang, &error); + if (!featuresKnown) + { + reportError(context, content->GetOriginalExpression(), error); + return std::string(); + } + cmSystemTools::ExpandListArgument(featuresKnown, + availableFeatures[lang]); + } + } + + bool evalLL = dagChecker && dagChecker->EvaluatingLinkLibraries(); + + std::string result; + + for (LangMap::const_iterator lit = testedFeatures.begin(); + lit != testedFeatures.end(); ++lit) + { + std::vector<std::string> const& langAvailable + = availableFeatures[lit->first]; + const char* standardDefault = context->Makefile + ->GetDefinition("CMAKE_" + lit->first + "_STANDARD_DEFAULT"); + for (std::vector<std::string>::const_iterator it = lit->second.begin(); + it != lit->second.end(); ++it) + { + if (std::find(langAvailable.begin(), langAvailable.end(), *it) + == langAvailable.end()) + { + return "0"; + } + if (standardDefault && !*standardDefault) + { + // This compiler has no notion of language standard levels. + // All features known for the language are always available. + continue; + } + if (!context->Makefile->HaveStandardAvailable(target, + lit->first, *it)) + { + if (evalLL) + { + const char* l = target->GetProperty(lit->first + "_STANDARD"); + if (!l) + { + l = standardDefault; + } + assert(l); + context->MaxLanguageStandard[target][lit->first] = l; + } + else + { + return "0"; + } + } + } + } + return "1"; + } +} compileFeaturesNode; + +//---------------------------------------------------------------------------- +static const char* targetPolicyWhitelist[] = { + 0 +#define TARGET_POLICY_STRING(POLICY) \ + , #POLICY + + CM_FOR_EACH_TARGET_POLICY(TARGET_POLICY_STRING) + +#undef TARGET_POLICY_STRING +}; + +cmPolicies::PolicyStatus statusForTarget(cmTarget const* tgt, + const char *policy) +{ +#define RETURN_POLICY(POLICY) \ + if (strcmp(policy, #POLICY) == 0) \ + { \ + return tgt->GetPolicyStatus ## POLICY (); \ + } \ + + CM_FOR_EACH_TARGET_POLICY(RETURN_POLICY) + +#undef RETURN_POLICY + + assert(0 && "Unreachable code. Not a valid policy"); + return cmPolicies::WARN; +} + +cmPolicies::PolicyID policyForString(const char *policy_id) +{ +#define RETURN_POLICY_ID(POLICY_ID) \ + if (strcmp(policy_id, #POLICY_ID) == 0) \ + { \ + return cmPolicies:: POLICY_ID; \ + } \ + + CM_FOR_EACH_TARGET_POLICY(RETURN_POLICY_ID) + +#undef RETURN_POLICY_ID + + assert(0 && "Unreachable code. Not a valid policy"); + return cmPolicies::CMP0002; +} + +//---------------------------------------------------------------------------- +static const struct TargetPolicyNode : public cmGeneratorExpressionNode +{ + TargetPolicyNode() {} + + virtual int NumExpectedParameters() const { return 1; } + + std::string Evaluate(const std::vector<std::string> ¶meters, + cmGeneratorExpressionContext *context , + const GeneratorExpressionContent *content, + cmGeneratorExpressionDAGChecker *) const + { + if (!context->HeadTarget) + { + reportError(context, content->GetOriginalExpression(), + "$<TARGET_POLICY:prop> may only be used with binary targets. It " + "may not be used with add_custom_command or add_custom_target."); + return std::string(); + } + + context->HadContextSensitiveCondition = true; + context->HadHeadSensitiveCondition = true; + + for (size_t i = 1; i < cmArraySize(targetPolicyWhitelist); ++i) + { + const char *policy = targetPolicyWhitelist[i]; + if (parameters.front() == policy) + { + cmMakefile *mf = context->HeadTarget->GetMakefile(); + switch(statusForTarget(context->HeadTarget, policy)) + { + case cmPolicies::WARN: + mf->IssueMessage(cmake::AUTHOR_WARNING, + mf->GetPolicies()-> + GetPolicyWarning(policyForString(policy))); + case cmPolicies::REQUIRED_IF_USED: + case cmPolicies::REQUIRED_ALWAYS: + case cmPolicies::OLD: + return "0"; + case cmPolicies::NEW: + return "1"; + } + } + } + reportError(context, content->GetOriginalExpression(), + "$<TARGET_POLICY:prop> may only be used with a limited number of " + "policies. Currently it may be used with the following policies:\n" + +#define STRINGIFY_HELPER(X) #X +#define STRINGIFY(X) STRINGIFY_HELPER(X) + +#define TARGET_POLICY_LIST_ITEM(POLICY) \ + " * " STRINGIFY(POLICY) "\n" + + CM_FOR_EACH_TARGET_POLICY(TARGET_POLICY_LIST_ITEM) + +#undef TARGET_POLICY_LIST_ITEM + ); + return std::string(); + } + +} targetPolicyNode; + +//---------------------------------------------------------------------------- +static const struct InstallPrefixNode : public cmGeneratorExpressionNode +{ + InstallPrefixNode() {} + + virtual bool GeneratesContent() const { return true; } + virtual int NumExpectedParameters() const { return 0; } + + std::string Evaluate(const std::vector<std::string> &, + cmGeneratorExpressionContext *context, + const GeneratorExpressionContent *content, + cmGeneratorExpressionDAGChecker *) const + { + reportError(context, content->GetOriginalExpression(), + "INSTALL_PREFIX is a marker for install(EXPORT) only. It " + "should never be evaluated."); + return std::string(); + } + +} installPrefixNode; + +//---------------------------------------------------------------------------- +class ArtifactNameTag; +class ArtifactLinkerTag; +class ArtifactSonameTag; +class ArtifactPdbTag; + +class ArtifactPathTag; +class ArtifactDirTag; +class ArtifactNameTag; + +//---------------------------------------------------------------------------- +template<typename ArtifactT> +struct TargetFilesystemArtifactResultCreator +{ + static std::string Create(cmTarget* target, + cmGeneratorExpressionContext *context, + const GeneratorExpressionContent *content); +}; + +//---------------------------------------------------------------------------- +template<> +struct TargetFilesystemArtifactResultCreator<ArtifactSonameTag> +{ + static std::string Create(cmTarget* target, + cmGeneratorExpressionContext *context, + const GeneratorExpressionContent *content) + { + // The target soname file (.so.1). + if(target->IsDLLPlatform()) + { + ::reportError(context, content->GetOriginalExpression(), + "TARGET_SONAME_FILE is not allowed " + "for DLL target platforms."); + return std::string(); + } + if(target->GetType() != cmTarget::SHARED_LIBRARY) + { + ::reportError(context, content->GetOriginalExpression(), + "TARGET_SONAME_FILE is allowed only for " + "SHARED libraries."); + return std::string(); + } + std::string result = target->GetDirectory(context->Config); + result += "/"; + result += target->GetSOName(context->Config); + return result; + } +}; + +//---------------------------------------------------------------------------- +template<> +struct TargetFilesystemArtifactResultCreator<ArtifactPdbTag> +{ + static std::string Create(cmTarget* target, + cmGeneratorExpressionContext *context, + const GeneratorExpressionContent *content) + { + std::string language = target->GetLinkerLanguage(context->Config); + + std::string pdbSupportVar = "CMAKE_" + language + "_LINKER_SUPPORTS_PDB"; + + if(!context->Makefile->IsOn(pdbSupportVar)) + { + ::reportError(context, content->GetOriginalExpression(), + "TARGET_PDB_FILE is not supported by the target linker."); + return std::string(); + } + + cmTarget::TargetType targetType = target->GetType(); + + if(targetType != cmTarget::SHARED_LIBRARY && + targetType != cmTarget::MODULE_LIBRARY && + targetType != cmTarget::EXECUTABLE) + { + ::reportError(context, content->GetOriginalExpression(), + "TARGET_PDB_FILE is allowed only for " + "targets with linker created artifacts."); + return std::string(); + } + + std::string result = target->GetPDBDirectory(context->Config); + result += "/"; + result += target->GetPDBName(context->Config); + return result; + } +}; + +//---------------------------------------------------------------------------- +template<> +struct TargetFilesystemArtifactResultCreator<ArtifactLinkerTag> +{ + static std::string Create(cmTarget* target, + cmGeneratorExpressionContext *context, + const GeneratorExpressionContent *content) + { + // The file used to link to the target (.so, .lib, .a). + if(!target->IsLinkable()) + { + ::reportError(context, content->GetOriginalExpression(), + "TARGET_LINKER_FILE is allowed only for libraries and " + "executables with ENABLE_EXPORTS."); + return std::string(); + } + return target->GetFullPath(context->Config, + target->HasImportLibrary()); + } +}; + +//---------------------------------------------------------------------------- +template<> +struct TargetFilesystemArtifactResultCreator<ArtifactNameTag> +{ + static std::string Create(cmTarget* target, + cmGeneratorExpressionContext *context, + const GeneratorExpressionContent *) + { + return target->GetFullPath(context->Config, false, true); + } +}; + + +//---------------------------------------------------------------------------- +template<typename ArtifactT> +struct TargetFilesystemArtifactResultGetter +{ + static std::string Get(const std::string &result); +}; + +//---------------------------------------------------------------------------- +template<> +struct TargetFilesystemArtifactResultGetter<ArtifactNameTag> +{ + static std::string Get(const std::string &result) + { return cmSystemTools::GetFilenameName(result); } +}; + +//---------------------------------------------------------------------------- +template<> +struct TargetFilesystemArtifactResultGetter<ArtifactDirTag> +{ + static std::string Get(const std::string &result) + { return cmSystemTools::GetFilenamePath(result); } +}; + +//---------------------------------------------------------------------------- +template<> +struct TargetFilesystemArtifactResultGetter<ArtifactPathTag> +{ + static std::string Get(const std::string &result) + { return result; } +}; + +//---------------------------------------------------------------------------- +template<typename ArtifactT, typename ComponentT> +struct TargetFilesystemArtifact : public cmGeneratorExpressionNode +{ + TargetFilesystemArtifact() {} + + virtual int NumExpectedParameters() const { return 1; } + + std::string Evaluate(const std::vector<std::string> ¶meters, + cmGeneratorExpressionContext *context, + const GeneratorExpressionContent *content, + cmGeneratorExpressionDAGChecker *dagChecker) const + { + // Lookup the referenced target. + std::string name = *parameters.begin(); + + if (!cmGeneratorExpression::IsValidTargetName(name)) + { + ::reportError(context, content->GetOriginalExpression(), + "Expression syntax not recognized."); + return std::string(); + } + cmTarget* target = context->Makefile->FindTargetToUse(name); + if(!target) + { + ::reportError(context, content->GetOriginalExpression(), + "No target \"" + name + "\""); + return std::string(); + } + if(target->GetType() >= cmTarget::OBJECT_LIBRARY && + target->GetType() != cmTarget::UNKNOWN_LIBRARY) + { + ::reportError(context, content->GetOriginalExpression(), + "Target \"" + name + "\" is not an executable or library."); + return std::string(); + } + if (dagChecker && (dagChecker->EvaluatingLinkLibraries(name.c_str()) + || (dagChecker->EvaluatingSources() + && name == dagChecker->TopTarget()))) + { + ::reportError(context, content->GetOriginalExpression(), + "Expressions which require the linker language may not " + "be used while evaluating link libraries"); + return std::string(); + } + context->DependTargets.insert(target); + context->AllTargets.insert(target); + + std::string result = + TargetFilesystemArtifactResultCreator<ArtifactT>::Create( + target, + context, + content); + if (context->HadError) + { + return std::string(); + } + return + TargetFilesystemArtifactResultGetter<ComponentT>::Get(result); + } +}; + +//---------------------------------------------------------------------------- +template<typename ArtifactT> +struct TargetFilesystemArtifactNodeGroup +{ + TargetFilesystemArtifactNodeGroup() + { + } + + TargetFilesystemArtifact<ArtifactT, ArtifactPathTag> File; + TargetFilesystemArtifact<ArtifactT, ArtifactNameTag> FileName; + TargetFilesystemArtifact<ArtifactT, ArtifactDirTag> FileDir; +}; + +//---------------------------------------------------------------------------- +static const +TargetFilesystemArtifactNodeGroup<ArtifactNameTag> targetNodeGroup; + +static const +TargetFilesystemArtifactNodeGroup<ArtifactLinkerTag> targetLinkerNodeGroup; + +static const +TargetFilesystemArtifactNodeGroup<ArtifactSonameTag> targetSoNameNodeGroup; + +static const +TargetFilesystemArtifactNodeGroup<ArtifactPdbTag> targetPdbNodeGroup; + +//---------------------------------------------------------------------------- +const cmGeneratorExpressionNode* +cmGeneratorExpressionNode::GetNode(const std::string &identifier) +{ + typedef std::map<std::string, const cmGeneratorExpressionNode*> NodeMap; + static NodeMap nodeMap; + if (nodeMap.empty()) + { + nodeMap["0"] = &zeroNode; + nodeMap["1"] = &oneNode; + nodeMap["AND"] = &andNode; + nodeMap["OR"] = &orNode; + nodeMap["NOT"] = ¬Node; + nodeMap["C_COMPILER_ID"] = &cCompilerIdNode; + nodeMap["CXX_COMPILER_ID"] = &cxxCompilerIdNode; + nodeMap["VERSION_GREATER"] = &versionGreaterNode; + nodeMap["VERSION_LESS"] = &versionLessNode; + nodeMap["VERSION_EQUAL"] = &versionEqualNode; + nodeMap["C_COMPILER_VERSION"] = &cCompilerVersionNode; + nodeMap["CXX_COMPILER_VERSION"] = &cxxCompilerVersionNode; + nodeMap["PLATFORM_ID"] = &platformIdNode; + nodeMap["COMPILE_FEATURES"] = &compileFeaturesNode; + nodeMap["CONFIGURATION"] = &configurationNode; + nodeMap["CONFIG"] = &configurationTestNode; + nodeMap["TARGET_FILE"] = &targetNodeGroup.File; + nodeMap["TARGET_LINKER_FILE"] = &targetLinkerNodeGroup.File; + nodeMap["TARGET_SONAME_FILE"] = &targetSoNameNodeGroup.File; + nodeMap["TARGET_PDB_FILE"] = &targetPdbNodeGroup.File; + nodeMap["TARGET_FILE_NAME"] = &targetNodeGroup.FileName; + nodeMap["TARGET_LINKER_FILE_NAME"] = &targetLinkerNodeGroup.FileName; + nodeMap["TARGET_SONAME_FILE_NAME"] = &targetSoNameNodeGroup.FileName; + nodeMap["TARGET_PDB_FILE_NAME"] = &targetPdbNodeGroup.FileName; + nodeMap["TARGET_FILE_DIR"] = &targetNodeGroup.FileDir; + nodeMap["TARGET_LINKER_FILE_DIR"] = &targetLinkerNodeGroup.FileDir; + nodeMap["TARGET_SONAME_FILE_DIR"] = &targetSoNameNodeGroup.FileDir; + nodeMap["TARGET_PDB_FILE_DIR"] = &targetPdbNodeGroup.FileDir; + nodeMap["STREQUAL"] = &strEqualNode; + nodeMap["EQUAL"] = &equalNode; + nodeMap["LOWER_CASE"] = &lowerCaseNode; + nodeMap["UPPER_CASE"] = &upperCaseNode; + nodeMap["MAKE_C_IDENTIFIER"] = &makeCIdentifierNode; + nodeMap["BOOL"] = &boolNode; + nodeMap["ANGLE-R"] = &angle_rNode; + nodeMap["COMMA"] = &commaNode; + nodeMap["SEMICOLON"] = &semicolonNode; + nodeMap["TARGET_PROPERTY"] = &targetPropertyNode; + nodeMap["TARGET_NAME"] = &targetNameNode; + nodeMap["TARGET_OBJECTS"] = &targetObjectsNode; + nodeMap["TARGET_POLICY"] = &targetPolicyNode; + nodeMap["BUILD_INTERFACE"] = &buildInterfaceNode; + nodeMap["INSTALL_INTERFACE"] = &installInterfaceNode; + nodeMap["INSTALL_PREFIX"] = &installPrefixNode; + nodeMap["JOIN"] = &joinNode; + nodeMap["LINK_ONLY"] = &linkOnlyNode; + nodeMap["COMPILE_LANGUAGE"] = &languageNode; + } + NodeMap::const_iterator i = nodeMap.find(identifier); + if (i == nodeMap.end()) + { + return 0; + } + return i->second; +} + +//---------------------------------------------------------------------------- +void reportError(cmGeneratorExpressionContext *context, + const std::string &expr, const std::string &result) +{ + context->HadError = true; + if (context->Quiet) + { + return; + } + + std::ostringstream e; + e << "Error evaluating generator expression:\n" + << " " << expr << "\n" + << result; + context->Makefile->GetCMakeInstance() + ->IssueMessage(cmake::FATAL_ERROR, e.str(), + context->Backtrace); +} diff --git a/Source/cmGeneratorExpressionNode.h b/Source/cmGeneratorExpressionNode.h new file mode 100644 index 0000000..847a00a --- /dev/null +++ b/Source/cmGeneratorExpressionNode.h @@ -0,0 +1,70 @@ +/*============================================================================ + CMake - Cross Platform Makefile Generator + Copyright 2012 Stephen Kelly <steveire@gmail.com> + + Distributed under the OSI-approved BSD License (the "License"); + see accompanying file Copyright.txt for details. + + This software is distributed WITHOUT ANY WARRANTY; without even the + implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the License for more information. +============================================================================*/ +#ifndef cmGeneratorExpressionNode_h +#define cmGeneratorExpressionNode_h + +#include "cmMakefile.h" + +#include "cmGeneratorExpressionEvaluator.h" +#include "cmGeneratorExpressionParser.h" +#include "cmGeneratorExpressionDAGChecker.h" +#include "cmGeneratorExpression.h" +#include "cmLocalGenerator.h" +#include "cmSourceFile.h" + +#include <cmsys/String.h> + +#include <assert.h> +#include <errno.h> + +#include "cmListFileCache.h" + +//---------------------------------------------------------------------------- +struct cmGeneratorExpressionNode +{ + enum { + DynamicParameters = 0, + OneOrMoreParameters = -1, + OneOrZeroParameters = -2 + }; + virtual ~cmGeneratorExpressionNode() {} + + virtual bool GeneratesContent() const { return true; } + + virtual bool RequiresLiteralInput() const { return false; } + + virtual bool AcceptsArbitraryContentParameter() const + { return false; } + + virtual int NumExpectedParameters() const { return 1; } + + virtual std::string Evaluate(const std::vector<std::string> ¶meters, + cmGeneratorExpressionContext *context, + const GeneratorExpressionContent *content, + cmGeneratorExpressionDAGChecker *dagChecker + ) const = 0; + + static std::string EvaluateDependentExpression( + std::string const& prop, cmMakefile *makefile, + cmGeneratorExpressionContext *context, + cmTarget const* headTarget, cmTarget const* currentTarget, + cmGeneratorExpressionDAGChecker *dagChecker); + + static const cmGeneratorExpressionNode* GetNode( + const std::string &identifier); +}; + +//---------------------------------------------------------------------------- +void reportError(cmGeneratorExpressionContext *context, + const std::string &expr, const std::string &result); + +#endif diff --git a/Source/cmGeneratorTarget.cxx b/Source/cmGeneratorTarget.cxx index b7b2eff..e0af47a 100644 --- a/Source/cmGeneratorTarget.cxx +++ b/Source/cmGeneratorTarget.cxx @@ -20,6 +20,7 @@ #include "cmGeneratorExpressionDAGChecker.h" #include "cmComputeLinkInformation.h" #include "cmCustomCommandGenerator.h" +#include "cmAlgorithms.h" #include <queue> diff --git a/Source/cmGetCMakePropertyCommand.cxx b/Source/cmGetCMakePropertyCommand.cxx index fd18596..85aa31f 100644 --- a/Source/cmGetCMakePropertyCommand.cxx +++ b/Source/cmGetCMakePropertyCommand.cxx @@ -14,6 +14,7 @@ #include "cmGlobalGenerator.h" #include "cmLocalGenerator.h" #include "cmake.h" +#include "cmAlgorithms.h" // cmGetCMakePropertyCommand bool cmGetCMakePropertyCommand diff --git a/Source/cmGlobalGenerator.cxx b/Source/cmGlobalGenerator.cxx index 36395aa..8123c99 100644 --- a/Source/cmGlobalGenerator.cxx +++ b/Source/cmGlobalGenerator.cxx @@ -32,6 +32,7 @@ #include "cmGeneratorExpressionEvaluationFile.h" #include "cmExportBuildFileGenerator.h" #include "cmCPackPropertiesGenerator.h" +#include "cmAlgorithms.h" #include <cmsys/Directory.hxx> #include <cmsys/FStream.hxx> @@ -1717,6 +1718,19 @@ int cmGlobalGenerator::Build( std::string outputBuffer; std::string* outputPtr = &outputBuffer; + std::vector<std::string> makeCommand; + this->GenerateBuildCommand(makeCommand, makeCommandCSTR, projectName, + bindir, target, config, fast, verbose, + nativeOptions); + + // Workaround to convince VCExpress.exe to produce output. + if (outputflag == cmSystemTools::OUTPUT_PASSTHROUGH && + !makeCommand.empty() && cmSystemTools::LowerCase( + cmSystemTools::GetFilenameName(makeCommand[0])) == "vcexpress.exe") + { + outputflag = cmSystemTools::OUTPUT_NORMAL; + } + // should we do a clean first? if (clean) { @@ -1743,10 +1757,6 @@ int cmGlobalGenerator::Build( } // now build - std::vector<std::string> makeCommand; - this->GenerateBuildCommand(makeCommand, makeCommandCSTR, projectName, - bindir, target, config, fast, verbose, - nativeOptions); std::string makeCommandStr = cmSystemTools::PrintSingleCommand(makeCommand); output += "\nRun Build Command:"; output += makeCommandStr; diff --git a/Source/cmGlobalUnixMakefileGenerator3.cxx b/Source/cmGlobalUnixMakefileGenerator3.cxx index 5701564..7648813 100644 --- a/Source/cmGlobalUnixMakefileGenerator3.cxx +++ b/Source/cmGlobalUnixMakefileGenerator3.cxx @@ -18,6 +18,7 @@ #include "cmSourceFile.h" #include "cmTarget.h" #include "cmGeneratorTarget.h" +#include "cmAlgorithms.h" cmGlobalUnixMakefileGenerator3::cmGlobalUnixMakefileGenerator3() { diff --git a/Source/cmGlobalVisualStudio10Generator.cxx b/Source/cmGlobalVisualStudio10Generator.cxx index 7df2073..18d40e1 100644 --- a/Source/cmGlobalVisualStudio10Generator.cxx +++ b/Source/cmGlobalVisualStudio10Generator.cxx @@ -17,6 +17,7 @@ #include "cmVisualStudioSlnData.h" #include "cmVisualStudioSlnParser.h" #include "cmake.h" +#include "cmAlgorithms.h" static const char vs10generatorName[] = "Visual Studio 10 2010"; diff --git a/Source/cmGlobalVisualStudio11Generator.cxx b/Source/cmGlobalVisualStudio11Generator.cxx index 3013200..ed828b6 100644 --- a/Source/cmGlobalVisualStudio11Generator.cxx +++ b/Source/cmGlobalVisualStudio11Generator.cxx @@ -12,6 +12,7 @@ #include "cmGlobalVisualStudio11Generator.h" #include "cmLocalVisualStudio10Generator.h" #include "cmMakefile.h" +#include "cmAlgorithms.h" static const char vs11generatorName[] = "Visual Studio 11 2012"; diff --git a/Source/cmGlobalVisualStudio12Generator.cxx b/Source/cmGlobalVisualStudio12Generator.cxx index 2bc9379..c2e6f47 100644 --- a/Source/cmGlobalVisualStudio12Generator.cxx +++ b/Source/cmGlobalVisualStudio12Generator.cxx @@ -12,6 +12,7 @@ #include "cmGlobalVisualStudio12Generator.h" #include "cmLocalVisualStudio10Generator.h" #include "cmMakefile.h" +#include "cmAlgorithms.h" static const char vs12generatorName[] = "Visual Studio 12 2013"; diff --git a/Source/cmGlobalVisualStudio14Generator.cxx b/Source/cmGlobalVisualStudio14Generator.cxx index fe702c0..b551c65 100644 --- a/Source/cmGlobalVisualStudio14Generator.cxx +++ b/Source/cmGlobalVisualStudio14Generator.cxx @@ -12,6 +12,7 @@ #include "cmGlobalVisualStudio14Generator.h" #include "cmLocalVisualStudio10Generator.h" #include "cmMakefile.h" +#include "cmAlgorithms.h" static const char vs14generatorName[] = "Visual Studio 14 2015"; diff --git a/Source/cmInstalledFile.cxx b/Source/cmInstalledFile.cxx index 4b53752..8c52b48 100644 --- a/Source/cmInstalledFile.cxx +++ b/Source/cmInstalledFile.cxx @@ -12,6 +12,7 @@ #include "cmInstalledFile.h" #include "cmSystemTools.h" #include "cmMakefile.h" +#include "cmAlgorithms.h" //---------------------------------------------------------------------------- cmInstalledFile::cmInstalledFile(): @@ -29,6 +30,16 @@ cmInstalledFile::~cmInstalledFile() } } +cmInstalledFile::Property::Property() +{ + +} + +cmInstalledFile::Property::~Property() +{ + cmDeleteAll(this->ValueExpressions); +} + //---------------------------------------------------------------------------- void cmInstalledFile::SetName(cmMakefile* mf, const std::string& name) { diff --git a/Source/cmInstalledFile.h b/Source/cmInstalledFile.h index cdb0866..3af90a7 100644 --- a/Source/cmInstalledFile.h +++ b/Source/cmInstalledFile.h @@ -13,7 +13,6 @@ #define cmInstalledFile_h #include "cmGeneratorExpression.h" -#include "cmAlgorithms.h" /** \class cmInstalledFile * \brief Represents a file intended for installation. @@ -32,15 +31,8 @@ public: struct Property { - Property() - { - - } - - ~Property() - { - cmDeleteAll(this->ValueExpressions); - } + Property(); + ~Property(); ExpressionVectorType ValueExpressions; }; diff --git a/Source/cmListCommand.cxx b/Source/cmListCommand.cxx index 17617aa..d18269d 100644 --- a/Source/cmListCommand.cxx +++ b/Source/cmListCommand.cxx @@ -12,6 +12,7 @@ #include "cmListCommand.h" #include <cmsys/RegularExpression.hxx> #include <cmsys/SystemTools.hxx> +#include "cmAlgorithms.h" #include <stdlib.h> // required for atoi #include <ctype.h> diff --git a/Source/cmLocalGenerator.cxx b/Source/cmLocalGenerator.cxx index 37cc2c6..e1998e4 100644 --- a/Source/cmLocalGenerator.cxx +++ b/Source/cmLocalGenerator.cxx @@ -25,6 +25,7 @@ #include "cmCustomCommandGenerator.h" #include "cmVersion.h" #include "cmake.h" +#include "cmAlgorithms.h" #if defined(CMAKE_BUILD_WITH_CMAKE) # define CM_LG_ENCODE_OBJECT_NAMES diff --git a/Source/cmLocalUnixMakefileGenerator3.cxx b/Source/cmLocalUnixMakefileGenerator3.cxx index 5550070..8938e67 100644 --- a/Source/cmLocalUnixMakefileGenerator3.cxx +++ b/Source/cmLocalUnixMakefileGenerator3.cxx @@ -20,6 +20,7 @@ #include "cmVersion.h" #include "cmFileTimeComparison.h" #include "cmCustomCommandGenerator.h" +#include "cmAlgorithms.h" // Include dependency scanners for supported languages. Only the // C/C++ scanner is needed for bootstrapping CMake. diff --git a/Source/cmLocalVisualStudio7Generator.cxx b/Source/cmLocalVisualStudio7Generator.cxx index f53f825..c4abeb2 100644 --- a/Source/cmLocalVisualStudio7Generator.cxx +++ b/Source/cmLocalVisualStudio7Generator.cxx @@ -391,6 +391,13 @@ cmVS7FlagTable cmLocalVisualStudio7GeneratorFortranFlagTable[] = {"OptimizeForProcessor", "QxT", "", "codeExclusivelyCore2Duo", 0}, {"OptimizeForProcessor", "QxO", "", "codeExclusivelyCore2StreamingSIMD", 0}, {"OptimizeForProcessor", "QxS", "", "codeExclusivelyCore2StreamingSIMD4", 0}, + {"OpenMP", "Qopenmp", "", "OpenMPParallelCode", 0}, + {"OpenMP", "Qopenmp-stubs", "", "OpenMPSequentialCode", 0}, + {"Traceback", "traceback", "", "true", 0}, + {"Traceback", "notraceback", "", "false", 0}, + {"FloatingPointExceptionHandling", "fpe:0", "", "fpe0", 0}, + {"FloatingPointExceptionHandling", "fpe:1", "", "fpe1", 0}, + {"FloatingPointExceptionHandling", "fpe:3", "", "fpe3", 0}, {"ModulePath", "module:", "", "", cmVS7FlagTable::UserValueRequired}, diff --git a/Source/cmMacroCommand.cxx b/Source/cmMacroCommand.cxx index 12c8576..b7cbae6 100644 --- a/Source/cmMacroCommand.cxx +++ b/Source/cmMacroCommand.cxx @@ -12,6 +12,7 @@ #include "cmMacroCommand.h" #include "cmake.h" +#include "cmAlgorithms.h" // define the class for macro commands class cmMacroHelperCommand : public cmCommand diff --git a/Source/cmMakeDepend.cxx b/Source/cmMakeDepend.cxx index 31bbb73..a6d4e58 100644 --- a/Source/cmMakeDepend.cxx +++ b/Source/cmMakeDepend.cxx @@ -12,6 +12,7 @@ #include "cmMakeDepend.h" #include "cmSystemTools.h" #include "cmGeneratorExpression.h" +#include "cmAlgorithms.h" #include <cmsys/RegularExpression.hxx> #include <cmsys/FStream.hxx> diff --git a/Source/cmMakefile.cxx b/Source/cmMakefile.cxx index ccfe2b1..eeb9c72 100644 --- a/Source/cmMakefile.cxx +++ b/Source/cmMakefile.cxx @@ -30,6 +30,7 @@ #include "cmInstallGenerator.h" #include "cmTestGenerator.h" #include "cmDefinitions.h" +#include "cmAlgorithms.h" #include "cmake.h" #include <stdlib.h> // required for atoi @@ -985,6 +986,33 @@ cmMakefile::AddCustomCommandToOutput(const std::vector<std::string>& outputs, } else { + std::ostringstream e; + cmake::MessageType messageType = cmake::AUTHOR_WARNING; + bool issueMessage = false; + + switch(this->GetPolicyStatus(cmPolicies::CMP0057)) + { + case cmPolicies::WARN: + e << (this->GetPolicies()-> + GetPolicyWarning(cmPolicies::CMP0057)) << "\n"; + issueMessage = true; + case cmPolicies::OLD: + break; + case cmPolicies::NEW: + case cmPolicies::REQUIRED_IF_USED: + case cmPolicies::REQUIRED_ALWAYS: + issueMessage = true; + messageType = cmake::FATAL_ERROR; + break; + } + + if(issueMessage) + { + e << "\"" << main_dependency << "\" can only be specified as a " + "custom command MAIN_DEPENDENCY once."; + IssueMessage(messageType, e.str()); + } + // The existing custom command is different. We need to // generate a rule file for this new command. file = 0; diff --git a/Source/cmNinjaNormalTargetGenerator.cxx b/Source/cmNinjaNormalTargetGenerator.cxx index c352c1a..155a30e 100644 --- a/Source/cmNinjaNormalTargetGenerator.cxx +++ b/Source/cmNinjaNormalTargetGenerator.cxx @@ -19,6 +19,7 @@ #include "cmOSXBundleGenerator.h" #include "cmGeneratorTarget.h" #include "cmCustomCommandGenerator.h" +#include "cmAlgorithms.h" #include <assert.h> #include <algorithm> diff --git a/Source/cmOrderDirectories.cxx b/Source/cmOrderDirectories.cxx index 23f8526..a612437 100644 --- a/Source/cmOrderDirectories.cxx +++ b/Source/cmOrderDirectories.cxx @@ -14,6 +14,7 @@ #include "cmGlobalGenerator.h" #include "cmSystemTools.h" #include "cmake.h" +#include "cmAlgorithms.h" #include <assert.h> diff --git a/Source/cmOutputRequiredFilesCommand.cxx b/Source/cmOutputRequiredFilesCommand.cxx index 5016493..df531e7 100644 --- a/Source/cmOutputRequiredFilesCommand.cxx +++ b/Source/cmOutputRequiredFilesCommand.cxx @@ -11,6 +11,7 @@ ============================================================================*/ #include "cmOutputRequiredFilesCommand.h" #include "cmMakeDepend.h" +#include "cmAlgorithms.h" #include <cmsys/FStream.hxx> class cmLBDepend : public cmMakeDepend diff --git a/Source/cmPolicies.cxx b/Source/cmPolicies.cxx index 3a48101..720030b 100644 --- a/Source/cmPolicies.cxx +++ b/Source/cmPolicies.cxx @@ -4,6 +4,7 @@ #include "cmSourceFile.h" #include "cmVersion.h" #include "cmVersionMacros.h" +#include "cmAlgorithms.h" #include <map> #include <set> #include <queue> @@ -374,6 +375,11 @@ cmPolicies::cmPolicies() CMP0056, "CMP0056", "Honor link flags in try_compile() source-file signature.", 3,2,0, cmPolicies::WARN); + + this->DefinePolicy( + CMP0057, "CMP0057", + "Disallow multiple MAIN_DEPENDENCY specifications for the same file.", + 3,3,0, cmPolicies::WARN); } cmPolicies::~cmPolicies() diff --git a/Source/cmPolicies.h b/Source/cmPolicies.h index c393c2f..854b132 100644 --- a/Source/cmPolicies.h +++ b/Source/cmPolicies.h @@ -113,6 +113,8 @@ public: /// or keywords when unquoted. CMP0055, ///< Strict checking for break() command. CMP0056, ///< Honor link flags in try_compile() source-file signature. + CMP0057, ///< Disallow multiple MAIN_DEPENDENCY specifications + /// for the same file. /** \brief Always the last entry. * diff --git a/Source/cmQtAutoGenerators.cxx b/Source/cmQtAutoGenerators.cxx index 844d708..42c18f7 100644 --- a/Source/cmQtAutoGenerators.cxx +++ b/Source/cmQtAutoGenerators.cxx @@ -16,6 +16,7 @@ #include "cmMakefile.h" #include "cmSourceFile.h" #include "cmSystemTools.h" +#include "cmAlgorithms.h" #if defined(_WIN32) && !defined(__CYGWIN__) # include "cmLocalVisualStudioGenerator.h" diff --git a/Source/cmSearchPath.cxx b/Source/cmSearchPath.cxx index 1e777ab..045c82e 100644 --- a/Source/cmSearchPath.cxx +++ b/Source/cmSearchPath.cxx @@ -12,6 +12,7 @@ #include "cmSearchPath.h" #include "cmFindCommon.h" +#include "cmAlgorithms.h" //---------------------------------------------------------------------------- cmSearchPath::cmSearchPath(cmFindCommon* findCmd) diff --git a/Source/cmSourceFileLocation.cxx b/Source/cmSourceFileLocation.cxx index b81951d..9d67c1e 100644 --- a/Source/cmSourceFileLocation.cxx +++ b/Source/cmSourceFileLocation.cxx @@ -15,6 +15,7 @@ #include "cmLocalGenerator.h" #include "cmGlobalGenerator.h" #include "cmSystemTools.h" +#include "cmAlgorithms.h" #include "assert.h" diff --git a/Source/cmTarget.cxx b/Source/cmTarget.cxx index 7a6ad8b..b70f60d 100644 --- a/Source/cmTarget.cxx +++ b/Source/cmTarget.cxx @@ -19,6 +19,7 @@ #include "cmListFileCache.h" #include "cmGeneratorExpression.h" #include "cmGeneratorExpressionDAGChecker.h" +#include "cmAlgorithms.h" #include <cmsys/RegularExpression.hxx> #include <map> #include <set> diff --git a/Source/cmTargetCompileDefinitionsCommand.cxx b/Source/cmTargetCompileDefinitionsCommand.cxx index dc19720..394a166 100644 --- a/Source/cmTargetCompileDefinitionsCommand.cxx +++ b/Source/cmTargetCompileDefinitionsCommand.cxx @@ -11,6 +11,8 @@ ============================================================================*/ #include "cmTargetCompileDefinitionsCommand.h" +#include "cmAlgorithms.h" + bool cmTargetCompileDefinitionsCommand ::InitialPass(std::vector<std::string> const& args, cmExecutionStatus &) { diff --git a/Source/cmTargetCompileFeaturesCommand.cxx b/Source/cmTargetCompileFeaturesCommand.cxx index 6ebc31e..823afa1 100644 --- a/Source/cmTargetCompileFeaturesCommand.cxx +++ b/Source/cmTargetCompileFeaturesCommand.cxx @@ -11,6 +11,8 @@ ============================================================================*/ #include "cmTargetCompileFeaturesCommand.h" +#include "cmAlgorithms.h" + bool cmTargetCompileFeaturesCommand::InitialPass( std::vector<std::string> const& args, cmExecutionStatus &) diff --git a/Source/cmTargetCompileOptionsCommand.cxx b/Source/cmTargetCompileOptionsCommand.cxx index 8c6fc06..a85153d 100644 --- a/Source/cmTargetCompileOptionsCommand.cxx +++ b/Source/cmTargetCompileOptionsCommand.cxx @@ -11,6 +11,8 @@ ============================================================================*/ #include "cmTargetCompileOptionsCommand.h" +#include "cmAlgorithms.h" + bool cmTargetCompileOptionsCommand ::InitialPass(std::vector<std::string> const& args, cmExecutionStatus &) { diff --git a/Source/cmake.cxx b/Source/cmake.cxx index 80e90a8..11196e4 100644 --- a/Source/cmake.cxx +++ b/Source/cmake.cxx @@ -20,6 +20,7 @@ #include "cmSourceFile.h" #include "cmTest.h" #include "cmDocumentationFormatter.h" +#include "cmAlgorithms.h" #if defined(CMAKE_BUILD_WITH_CMAKE) # include "cmGraphVizWriter.h" diff --git a/Source/cmakemain.cxx b/Source/cmakemain.cxx index e0bd55b..ac73ad0 100644 --- a/Source/cmakemain.cxx +++ b/Source/cmakemain.cxx @@ -24,6 +24,7 @@ #include "cmGlobalGenerator.h" #include "cmLocalGenerator.h" #include "cmMakefile.h" +#include "cmAlgorithms.h" #include <cmsys/Encoding.hxx> #ifdef CMAKE_BUILD_WITH_CMAKE diff --git a/Source/cmcmd.cxx b/Source/cmcmd.cxx index 7d67bd8..9f2ea46 100644 --- a/Source/cmcmd.cxx +++ b/Source/cmcmd.cxx @@ -15,6 +15,7 @@ #include "cmGlobalGenerator.h" #include "cmQtAutoGenerators.h" #include "cmVersion.h" +#include "cmAlgorithms.h" #if defined(CMAKE_BUILD_WITH_CMAKE) # include "cmDependsFortran.h" // For -E cmake_copy_f90_mod callback. diff --git a/Source/kwsys/hashtable.hxx.in b/Source/kwsys/hashtable.hxx.in index 307f6bc..7e7dc42 100644 --- a/Source/kwsys/hashtable.hxx.in +++ b/Source/kwsys/hashtable.hxx.in @@ -423,7 +423,7 @@ static const unsigned long _stl_prime_list[_stl_num_primes] = return &_stl_prime_list[0]; } -inline size_t _stl_next_prime(size_t __n) +static inline size_t _stl_next_prime(size_t __n) { const unsigned long* __first = get_stl_prime_list(); const unsigned long* __last = get_stl_prime_list() + (int)_stl_num_primes; diff --git a/Tests/CMakeLists.txt b/Tests/CMakeLists.txt index 08765de..40bea51 100644 --- a/Tests/CMakeLists.txt +++ b/Tests/CMakeLists.txt @@ -1256,6 +1256,12 @@ ${CMake_BINARY_DIR}/bin/cmake -DDIR=dev -P ${CMake_SOURCE_DIR}/Utilities/Release add_subdirectory(FindJsonCpp) endif() + # Matlab module + if(CMake_TEST_FindMatlab) + ADD_TEST_MACRO(FindMatlab.basic_checks ${CMAKE_CTEST_COMMAND} -C $<CONFIGURATION>) + ADD_TEST_MACRO(FindMatlab.versions_checks ${CMAKE_CTEST_COMMAND} -C $<CONFIGURATION>) + endif() + find_package(GTK2 QUIET) if(GTK2_FOUND) add_subdirectory(FindGTK2) @@ -2409,6 +2415,8 @@ ${CMake_BINARY_DIR}/bin/cmake -DDIR=dev -P ${CMake_SOURCE_DIR}/Utilities/Release "${CMake_BINARY_DIR}/Testing/JacocoCoverage/DartConfiguration.tcl") file(COPY "${CMake_SOURCE_DIR}/Tests/JacocoCoverage/Coverage" DESTINATION "${CMake_BINARY_DIR}/Testing/JacocoCoverage") + configure_file("${CMake_BINARY_DIR}/Testing/JacocoCoverage/Coverage/target/site/jacoco.xml.in" + "${CMake_BINARY_DIR}/Testing/JacocoCoverage/Coverage/target/site/jacoco.xml") add_test(NAME CTestJacocoCoverage COMMAND cmake -E chdir ${CMake_BINARY_DIR}/Testing/JacocoCoverage @@ -2559,6 +2567,17 @@ ${CMake_BINARY_DIR}/bin/cmake -DDIR=dev -P ${CMake_SOURCE_DIR}/Utilities/Release --output-log "${CMake_BINARY_DIR}/Tests/CTestTestParallel/testOutput.log" ) + configure_file("${CMake_SOURCE_DIR}/Tests/CTestTestVerboseOutput/test.cmake.in" + "${CMake_BINARY_DIR}/Tests/CTestTestVerboseOutput/test.cmake" @ONLY ESCAPE_QUOTES) + add_test(CTestTestVerboseOutput ${CMAKE_CTEST_COMMAND} + -S "${CMake_BINARY_DIR}/Tests/CTestTestVerboseOutput/test.cmake" -VV + --output-log "${CMake_BINARY_DIR}/Tests/CTestTestVerboseOutput/testOutput.log" + -C "\${CTestTest_CONFIG}" + ) + set_property(TEST CTestTestVerboseOutput PROPERTY PASS_REGULAR_EXPRESSION + "Environment variables:.*foo=bar.*this=that" + ) + configure_file( "${CMake_SOURCE_DIR}/Tests/CTestTestSkipReturnCode/test.cmake.in" "${CMake_BINARY_DIR}/Tests/CTestTestSkipReturnCode/test.cmake" diff --git a/Tests/CPackComponentsForAll/RunCPackVerifyResult.cmake b/Tests/CPackComponentsForAll/RunCPackVerifyResult.cmake index c7ec709..9261d11 100644 --- a/Tests/CPackComponentsForAll/RunCPackVerifyResult.cmake +++ b/Tests/CPackComponentsForAll/RunCPackVerifyResult.cmake @@ -158,37 +158,45 @@ if(CPackGen MATCHES "RPM") ERROR_QUIET OUTPUT_STRIP_TRAILING_WHITESPACE) + execute_process(COMMAND ${RPM_EXECUTABLE} -pqa ${check_file} + RESULT_VARIABLE check_package_architecture_result + OUTPUT_VARIABLE check_package_architecture + ERROR_QUIET + OUTPUT_STRIP_TRAILING_WHITESPACE) + execute_process(COMMAND ${RPM_EXECUTABLE} -pql ${check_file} OUTPUT_VARIABLE check_package_content ERROR_QUIET OUTPUT_STRIP_TRAILING_WHITESPACE) + set(whitespaces "[\\t\\n\\r ]*") + if(check_file_libraries_match) set(check_file_match_expected_summary ".*${CPACK_RPM_libraries_PACKAGE_SUMMARY}.*") set(check_file_match_expected_description ".*${CPACK_RPM_libraries_PACKAGE_DESCRIPTION}.*") - set(check_file_match_expected_relocation_path "Relocations : ${CPACK_PACKAGING_INSTALL_PREFIX} ${CPACK_PACKAGING_INSTALL_PREFIX}/${CMAKE_INSTALL_LIBDIR}") - set(check_file_match_expected_architecture "Architecture: ${CPACK_RPM_applications_PACKAGE_ARCHITECTURE}") + set(check_file_match_expected_relocation_path "Relocations${whitespaces}:${whitespaces}${CPACK_PACKAGING_INSTALL_PREFIX}${whitespaces}${CPACK_PACKAGING_INSTALL_PREFIX}/${CMAKE_INSTALL_LIBDIR}") + set(check_file_match_expected_architecture "") # we don't explicitly set this value so it is different on each platform - ignore it set(spec_regex "*libraries*") set(check_content_list "^/usr/foo/bar\n/usr/foo/bar/lib.*\n/usr/foo/bar/lib.*/libmylib.a$") elseif(check_file_headers_match) set(check_file_match_expected_summary ".*${CPACK_RPM_PACKAGE_SUMMARY}.*") set(check_file_match_expected_description ".*${CPACK_COMPONENT_HEADERS_DESCRIPTION}.*") - set(check_file_match_expected_relocation_path "Relocations : ${CPACK_PACKAGING_INSTALL_PREFIX} ${CPACK_PACKAGING_INSTALL_PREFIX}/${CMAKE_INSTALL_INCLUDEDIR}") - set(check_file_match_expected_architecture "Architecture: ${CPACK_RPM_libraries_PACKAGE_ARCHITECTURE}") + set(check_file_match_expected_relocation_path "Relocations${whitespaces}:${whitespaces}${CPACK_PACKAGING_INSTALL_PREFIX}${whitespaces}${CPACK_PACKAGING_INSTALL_PREFIX}/${CMAKE_INSTALL_INCLUDEDIR}") + set(check_file_match_expected_architecture "noarch") set(spec_regex "*headers*") set(check_content_list "^/usr/foo/bar\n/usr/foo/bar/include\n/usr/foo/bar/include/mylib.h$") elseif(check_file_applications_match) set(check_file_match_expected_summary ".*${CPACK_RPM_PACKAGE_SUMMARY}.*") set(check_file_match_expected_description ".*${CPACK_COMPONENT_APPLICATIONS_DESCRIPTION}.*") - set(check_file_match_expected_relocation_path "Relocations : ${CPACK_PACKAGING_INSTALL_PREFIX} ${CPACK_PACKAGING_INSTALL_PREFIX}/${CMAKE_INSTALL_BINDIR}") - set(check_file_match_expected_architecture "Architecture: ${CPACK_RPM_headers_PACKAGE_ARCHITECTURE}") + set(check_file_match_expected_relocation_path "Relocations${whitespaces}:${whitespaces}${CPACK_PACKAGING_INSTALL_PREFIX}${whitespaces}${CPACK_PACKAGING_INSTALL_PREFIX}/${CMAKE_INSTALL_BINDIR}.*") + set(check_file_match_expected_architecture "armv7hf") set(spec_regex "*applications*") set(check_content_list "^/usr/foo/bar\n/usr/foo/bar/bin\n/usr/foo/bar/bin/mylibapp$") elseif(check_file_Unspecified_match) set(check_file_match_expected_summary ".*${CPACK_RPM_PACKAGE_SUMMARY}.*") set(check_file_match_expected_description ".*DESCRIPTION.*") - set(check_file_match_expected_relocation_path "Relocations : ${CPACK_PACKAGING_INSTALL_PREFIX} ${CPACK_PACKAGING_INSTALL_PREFIX}/${CMAKE_INSTALL_BINDIR}") - set(check_file_match_expected_architecture "Architecture: ${CPACK_RPM_Unspecified_PACKAGE_ARCHITECTURE}") + set(check_file_match_expected_relocation_path "Relocations${whitespaces}:${whitespaces}${CPACK_PACKAGING_INSTALL_PREFIX}${whitespaces}${CPACK_PACKAGING_INSTALL_PREFIX}/${CMAKE_INSTALL_BINDIR}") + set(check_file_match_expected_architecture "") # we don't explicitly set this value so it is different on each platform - ignore it set(spec_regex "*Unspecified*") set(check_content_list "^/usr/foo/bar /usr/foo/bar/bin @@ -228,9 +236,22 @@ if(CPackGen MATCHES "RPM") message(FATAL_ERROR "error: '${check_file}' rpm package relocation path does not match expected value - regex '${check_file_match_expected_relocation_path}'; RPM output: '${check_file_content}'; generated spec file: '${spec_file_content}'") endif() - string(REGEX MATCH ${check_file_match_expected_architecture} check_file_match_architecture ${check_file_content}) - if (NOT check_file_match_architecture) + ####################### + # test package architecture + ####################### + string(REGEX MATCH "Architecture${whitespaces}:" check_info_contains_arch ${check_file_content}) + if(check_info_contains_arch) # test for rpm versions that contain architecture in package info (e.g. 4.11.x) + string(REGEX MATCH "Architecture${whitespaces}:${whitespaces}${check_file_match_expected_architecture}" check_file_match_architecture ${check_file_content}) + if(NOT check_file_match_architecture) message(FATAL_ERROR "error: '${check_file}' Architecture does not match expected value - '${check_file_match_expected_architecture}'; RPM output: '${check_file_content}'; generated spec file: '${spec_file_content}'") + endif() + elseif(NOT check_package_architecture_result) # test result only on platforms that support -pqa rpm query + # test for rpm versions that do not contain architecture in package info (e.g. 4.8.x) + string(REGEX MATCH ".*${check_file_match_expected_architecture}" check_file_match_architecture "${check_package_architecture}") + if(NOT check_file_match_architecture) + message(FATAL_ERROR "error: '${check_file}' Architecture does not match expected value - '${check_file_match_expected_architecture}'; RPM output: '${check_package_architecture}'; generated spec file: '${spec_file_content}'") + endif() + # else rpm version too old (e.g. 4.4.x) - skip test endif() ####################### diff --git a/Tests/CTestTestVerboseOutput/CMakeLists.txt b/Tests/CTestTestVerboseOutput/CMakeLists.txt new file mode 100644 index 0000000..4cdd29c --- /dev/null +++ b/Tests/CTestTestVerboseOutput/CMakeLists.txt @@ -0,0 +1,11 @@ +cmake_minimum_required (VERSION 2.6) +project(CTestTestVerboseOutput) +include(CTest) + +add_executable(nop nop.c) + +add_test(NAME TestWithProperties COMMAND nop) +set_property(TEST TestWithProperties PROPERTY ENVIRONMENT + "foo=bar" + "this=that" +) diff --git a/Tests/CTestTestVerboseOutput/CTestConfig.cmake b/Tests/CTestTestVerboseOutput/CTestConfig.cmake new file mode 100644 index 0000000..4f96c79 --- /dev/null +++ b/Tests/CTestTestVerboseOutput/CTestConfig.cmake @@ -0,0 +1,7 @@ +set(CTEST_PROJECT_NAME "CTestTestVerboseOutput") +set(CTEST_NIGHTLY_START_TIME "21:00:00 EDT") +set(CTEST_DART_SERVER_VERSION "2") +set(CTEST_DROP_METHOD "http") +set(CTEST_DROP_SITE "open.cdash.org") +set(CTEST_DROP_LOCATION "/submit.php?project=PublicDashboard") +set(CTEST_DROP_SITE_CDASH TRUE) diff --git a/Tests/CTestTestVerboseOutput/nop.c b/Tests/CTestTestVerboseOutput/nop.c new file mode 100644 index 0000000..f8b643a --- /dev/null +++ b/Tests/CTestTestVerboseOutput/nop.c @@ -0,0 +1,4 @@ +int main() +{ + return 0; +} diff --git a/Tests/CTestTestVerboseOutput/test.cmake.in b/Tests/CTestTestVerboseOutput/test.cmake.in new file mode 100644 index 0000000..7f49548 --- /dev/null +++ b/Tests/CTestTestVerboseOutput/test.cmake.in @@ -0,0 +1,20 @@ +cmake_minimum_required(VERSION 2.4) + +# Settings: +set(CTEST_DASHBOARD_ROOT "@CMake_BINARY_DIR@/Tests/CTestTest") +set(CTEST_SITE "@SITE@") +set(CTEST_BUILD_NAME "CTestTest-@BUILDNAME@-VerboseOutput") + +set(CTEST_SOURCE_DIRECTORY "@CMake_SOURCE_DIR@/Tests/CTestTestVerboseOutput") +set(CTEST_BINARY_DIRECTORY "@CMake_BINARY_DIR@/Tests/CTestTestVerboseOutput") +set(CTEST_CMAKE_GENERATOR "@CMAKE_GENERATOR@") +set(CTEST_CMAKE_GENERATOR_PLATFORM "@CMAKE_GENERATOR_PLATFORM@") +set(CTEST_CMAKE_GENERATOR_TOOLSET "@CMAKE_GENERATOR_TOOLSET@") +set(CTEST_BUILD_CONFIGURATION "$ENV{CMAKE_CONFIG_TYPE}") +set(CTEST_COVERAGE_COMMAND "@COVERAGE_COMMAND@") +set(CTEST_NOTES_FILES "${CTEST_SCRIPT_DIRECTORY}/${CTEST_SCRIPT_NAME}") + +CTEST_START(Experimental) +CTEST_CONFIGURE(BUILD "${CTEST_BINARY_DIRECTORY}") +CTEST_BUILD(BUILD "${CTEST_BINARY_DIRECTORY}") +CTEST_TEST(BUILD "${CTEST_BINARY_DIRECTORY}") diff --git a/Tests/FindMatlab/basic_checks/CMakeLists.txt b/Tests/FindMatlab/basic_checks/CMakeLists.txt new file mode 100644 index 0000000..acf71ea --- /dev/null +++ b/Tests/FindMatlab/basic_checks/CMakeLists.txt @@ -0,0 +1,57 @@ + +cmake_minimum_required (VERSION 2.8.12) +enable_testing() +project(basic_checks) + +set(MATLAB_FIND_DEBUG TRUE) + +# the success of the following command is dependent on the current configuration: +# - on 32bits builds (cmake is building with 32 bits), it looks for 32 bits Matlab +# - on 64bits builds (cmake is building with 64 bits), it looks for 64 bits Matlab +find_package(Matlab REQUIRED COMPONENTS MX_LIBRARY MAIN_PROGRAM) + + + +matlab_add_mex( + # target name + NAME cmake_matlab_test_wrapper1 + # output name + OUTPUT_NAME cmake_matlab_mex1 + SRC ${CMAKE_CURRENT_SOURCE_DIR}/../matlab_wrapper1.cpp + DOCUMENTATION ${CMAKE_CURRENT_SOURCE_DIR}/../help_text1.m.txt + ) + + +matlab_add_unit_test( + NAME ${PROJECT_NAME}_matlabtest-1 + TIMEOUT 30 + UNITTEST_FILE ${CMAKE_CURRENT_SOURCE_DIR}/../cmake_matlab_unit_tests1.m + ADDITIONAL_PATH $<TARGET_FILE_DIR:cmake_matlab_test_wrapper1> + ) + +matlab_add_unit_test( + NAME ${PROJECT_NAME}_matlabtest-2 + TIMEOUT 15 + UNITTEST_FILE ${CMAKE_CURRENT_SOURCE_DIR}/../cmake_matlab_unit_tests_timeout.m + ADDITIONAL_PATH $<TARGET_FILE_DIR:cmake_matlab_test_wrapper1> + ) +set_tests_properties(${PROJECT_NAME}_matlabtest-2 PROPERTIES WILL_FAIL TRUE) + + +# testing the test without the unittest framework of Matlab +matlab_add_unit_test( + NAME ${PROJECT_NAME}_matlabtest-3 + TIMEOUT 30 + NO_UNITTEST_FRAMEWORK + UNITTEST_FILE ${CMAKE_CURRENT_SOURCE_DIR}/../cmake_matlab_unit_tests2.m + ADDITIONAL_PATH $<TARGET_FILE_DIR:cmake_matlab_test_wrapper1> + ) + +matlab_add_unit_test( + NAME ${PROJECT_NAME}_matlabtest-4 + TIMEOUT 30 + NO_UNITTEST_FRAMEWORK + UNITTEST_FILE ${CMAKE_CURRENT_SOURCE_DIR}/../cmake_matlab_unit_tests3.m + ADDITIONAL_PATH $<TARGET_FILE_DIR:cmake_matlab_test_wrapper1> + ) +set_tests_properties(${PROJECT_NAME}_matlabtest-4 PROPERTIES WILL_FAIL TRUE) diff --git a/Tests/FindMatlab/cmake_matlab_unit_tests1.m b/Tests/FindMatlab/cmake_matlab_unit_tests1.m new file mode 100644 index 0000000..2371c3a --- /dev/null +++ b/Tests/FindMatlab/cmake_matlab_unit_tests1.m @@ -0,0 +1,33 @@ + +classdef cmake_matlab_unit_tests1 < matlab.unittest.TestCase + % some simple unit test for CMake Matlab wrapper + properties + end + + methods (Test) + function testDummyCall(testCase) + % very simple call test + cmake_matlab_mex1(rand(3,3)); + end + + function testDummyCall2(testCase) + % very simple call test 2 + ret = cmake_matlab_mex1(rand(3,3)); + testCase.verifyEqual(size(ret), size(rand(3,3))); + + testCase.verifyEqual(size(cmake_matlab_mex1(rand(4,3))), [4,3] ); + end + + function testFailTest(testCase) + testCase.verifyError(@() cmake_matlab_mex1(10), 'cmake_matlab:configuration'); + testCase.verifyError(@() cmake_matlab_mex1([10]), 'cmake_matlab:configuration'); + end + + function testHelpContent(testCase) + % testing the help feature + testCase.verifySubstring(evalc('help cmake_matlab_mex1'), 'Dummy matlab extension in cmake'); + end + + + end +end diff --git a/Tests/FindMatlab/cmake_matlab_unit_tests2.m b/Tests/FindMatlab/cmake_matlab_unit_tests2.m new file mode 100644 index 0000000..7a8a342 --- /dev/null +++ b/Tests/FindMatlab/cmake_matlab_unit_tests2.m @@ -0,0 +1,6 @@ + +ret = cmake_matlab_mex1(rand(3,3)); + +if(size(ret) ~= size(rand(3,3))) + error('Dimension mismatch!'); +end diff --git a/Tests/FindMatlab/cmake_matlab_unit_tests3.m b/Tests/FindMatlab/cmake_matlab_unit_tests3.m new file mode 100644 index 0000000..2639325 --- /dev/null +++ b/Tests/FindMatlab/cmake_matlab_unit_tests3.m @@ -0,0 +1,5 @@ + +cmake_matlab_mex1(10); + +% should not reach this point +exit(0); diff --git a/Tests/FindMatlab/cmake_matlab_unit_tests_timeout.m b/Tests/FindMatlab/cmake_matlab_unit_tests_timeout.m new file mode 100644 index 0000000..11d5e9e --- /dev/null +++ b/Tests/FindMatlab/cmake_matlab_unit_tests_timeout.m @@ -0,0 +1,16 @@ + +classdef cmake_matlab_unit_tests_timeout < matlab.unittest.TestCase + % timeout tests + + properties + end + + methods (Test) + function testCallHangsShouldBeTimedOut(testCase) + cmake_matlab_mex1(rand(3,3)); + disp('Will now wait.'); + disp('Testing the cmake Matlab package timeout - do not kill'); + pause(20); % supposed to be killed after 15s + end + end +end diff --git a/Tests/FindMatlab/help_text1.m.txt b/Tests/FindMatlab/help_text1.m.txt new file mode 100644 index 0000000..a924355 --- /dev/null +++ b/Tests/FindMatlab/help_text1.m.txt @@ -0,0 +1,2 @@ +% Dummy matlab extension in cmake +function ret = cmake_matlab_mex1(X) diff --git a/Tests/FindMatlab/matlab_wrapper1.cpp b/Tests/FindMatlab/matlab_wrapper1.cpp new file mode 100644 index 0000000..4149bb9 --- /dev/null +++ b/Tests/FindMatlab/matlab_wrapper1.cpp @@ -0,0 +1,26 @@ + +// simple workaround to some compiler specific problems +// see http://stackoverflow.com/questions/22367516/mex-compile-error-unknown-type-name-char16-t/23281916#23281916 +#include <algorithm> + +#include "mex.h" + +// this test should return a matrix of 10 x 10 and should check some of the arguments + +void mexFunction(const int nlhs, mxArray *plhs[], const int nrhs, const mxArray *prhs[]) +{ + if(nrhs != 1) + { + mexErrMsgTxt("Incorrect arguments"); + } + + size_t dim1 = mxGetM(prhs[0]); + size_t dim2 = mxGetN(prhs[0]); + + if(dim1 == 1 || dim2 == 1) + { + mexErrMsgIdAndTxt("cmake_matlab:configuration", "Incorrect arguments"); + } + + plhs[0] = mxCreateNumericMatrix(dim1, dim2, mxGetClassID(prhs[0]), mxREAL); +} diff --git a/Tests/FindMatlab/versions_checks/CMakeLists.txt b/Tests/FindMatlab/versions_checks/CMakeLists.txt new file mode 100644 index 0000000..5d20685 --- /dev/null +++ b/Tests/FindMatlab/versions_checks/CMakeLists.txt @@ -0,0 +1,52 @@ + +cmake_minimum_required (VERSION 2.8.12) +enable_testing() +project(versions_checks) + +set(MATLAB_FIND_DEBUG TRUE) +set(MATLAB_ADDITIONAL_VERSIONS + "dummy=14.9") + +# the success of the following command is dependent on the current configuration +# in this case, we are only interested in the version macros +find_package(Matlab) + + + +if(NOT COMMAND matlab_get_version_from_release_name) + message(FATAL_ERROR "The macro matlab_get_version_from_release_name should be defined") +endif() + +if(NOT COMMAND matlab_get_release_name_from_version) + message(FATAL_ERROR "The macro matlab_get_release_name_from_version should be defined") +endif() + + +# matlab_get_release_name_from_version +matlab_get_release_name_from_version("7.13" release_name) +if(NOT release_name STREQUAL "R2011b") + message(FATAL_ERROR "version 7.13 does not give release R2011b : '${release_name}' != R2011b") +endif() + +matlab_get_release_name_from_version("14.9" release_name) +if(NOT release_name STREQUAL "dummy") + message(FATAL_ERROR "version 14.9 does not give release dummy : '${release_name}' != dummy") +endif() + +matlab_get_release_name_from_version("14.10" release_name) +if(NOT release_name STREQUAL "") + message(FATAL_ERROR "version 14.10 does not give empty release: '${release_name}' != ''") +endif() + + +# matlab_get_version_from_release_name +matlab_get_version_from_release_name("R2011a" version) +if(NOT version STREQUAL "7.12") + message(FATAL_ERROR "Release R2011a does not give version 7.12 : '${version}' != 7.12") +endif() + +matlab_get_version_from_release_name("dummy" version) +#message(FATAL_ERROR "versionversion = ${version}") +if(NOT version STREQUAL "14.9") + message(FATAL_ERROR "Release dummy does not give version 14.9 : '${version}' != 14.9") +endif() diff --git a/Tests/JacocoCoverage/Coverage/target/site/jacoco.xml b/Tests/JacocoCoverage/Coverage/target/site/jacoco.xml.in index 49c3e87..49c3e87 100644 --- a/Tests/JacocoCoverage/Coverage/target/site/jacoco.xml +++ b/Tests/JacocoCoverage/Coverage/target/site/jacoco.xml.in diff --git a/Tests/RunCMake/CMP0057/CMP0057-NEW-result.txt b/Tests/RunCMake/CMP0057/CMP0057-NEW-result.txt new file mode 100644 index 0000000..d00491f --- /dev/null +++ b/Tests/RunCMake/CMP0057/CMP0057-NEW-result.txt @@ -0,0 +1 @@ +1 diff --git a/Tests/RunCMake/CMP0057/CMP0057-NEW-stderr.txt b/Tests/RunCMake/CMP0057/CMP0057-NEW-stderr.txt new file mode 100644 index 0000000..9607d54 --- /dev/null +++ b/Tests/RunCMake/CMP0057/CMP0057-NEW-stderr.txt @@ -0,0 +1,4 @@ +CMake Error at CMP0057-NEW.cmake:8 \(add_custom_command\): + "input.txt" can only be specified as a custom command MAIN_DEPENDENCY once. +Call Stack \(most recent call first\): + CMakeLists.txt:3 \(include\) diff --git a/Tests/RunCMake/CMP0057/CMP0057-NEW.cmake b/Tests/RunCMake/CMP0057/CMP0057-NEW.cmake new file mode 100644 index 0000000..22dbfb3 --- /dev/null +++ b/Tests/RunCMake/CMP0057/CMP0057-NEW.cmake @@ -0,0 +1,13 @@ +cmake_policy(SET CMP0057 NEW) + +add_custom_command(OUTPUT out1 + COMMAND ${CMAKE_COMMAND} -E echo out1 + MAIN_DEPENDENCY input.txt +) + +add_custom_command(OUTPUT out2 + COMMAND ${CMAKE_COMMAND} -E echo out2 + MAIN_DEPENDENCY input.txt +) + +add_custom_target(mytarget1 ALL DEPENDS out1 out2) diff --git a/Tests/RunCMake/CMP0057/CMP0057-OLD.cmake b/Tests/RunCMake/CMP0057/CMP0057-OLD.cmake new file mode 100644 index 0000000..ccf4fcb --- /dev/null +++ b/Tests/RunCMake/CMP0057/CMP0057-OLD.cmake @@ -0,0 +1,13 @@ +cmake_policy(SET CMP0057 OLD) + +add_custom_command(OUTPUT out1 + COMMAND ${CMAKE_COMMAND} -E echo out1 + MAIN_DEPENDENCY input.txt +) + +add_custom_command(OUTPUT out2 + COMMAND ${CMAKE_COMMAND} -E echo out2 + MAIN_DEPENDENCY input.txt +) + +add_custom_target(mytarget1 ALL DEPENDS out1 out2) diff --git a/Tests/RunCMake/CMP0057/CMP0057-WARN-stderr.txt b/Tests/RunCMake/CMP0057/CMP0057-WARN-stderr.txt new file mode 100644 index 0000000..da3a1cb --- /dev/null +++ b/Tests/RunCMake/CMP0057/CMP0057-WARN-stderr.txt @@ -0,0 +1,9 @@ +CMake Warning \(dev\) at CMP0057-WARN.cmake:6 \(add_custom_command\): + Policy CMP0057 is not set: Disallow multiple MAIN_DEPENDENCY specifications + for the same file. Run "cmake --help-policy CMP0057" for policy details. + Use the cmake_policy command to set the policy and suppress this warning. + + "input.txt" can only be specified as a custom command MAIN_DEPENDENCY once. +Call Stack \(most recent call first\): + CMakeLists.txt:3 \(include\) +This warning is for project developers. Use -Wno-dev to suppress it. diff --git a/Tests/RunCMake/CMP0057/CMP0057-WARN.cmake b/Tests/RunCMake/CMP0057/CMP0057-WARN.cmake new file mode 100644 index 0000000..1837968 --- /dev/null +++ b/Tests/RunCMake/CMP0057/CMP0057-WARN.cmake @@ -0,0 +1,11 @@ +add_custom_command(OUTPUT out1 + COMMAND ${CMAKE_COMMAND} -E echo out1 + MAIN_DEPENDENCY input.txt +) + +add_custom_command(OUTPUT out2 + COMMAND ${CMAKE_COMMAND} -E echo out2 + MAIN_DEPENDENCY input.txt +) + +add_custom_target(mytarget1 ALL DEPENDS out1 out2) diff --git a/Tests/RunCMake/CMP0057/CMP0057-once_is_ok.cmake b/Tests/RunCMake/CMP0057/CMP0057-once_is_ok.cmake new file mode 100644 index 0000000..8ce02f9 --- /dev/null +++ b/Tests/RunCMake/CMP0057/CMP0057-once_is_ok.cmake @@ -0,0 +1,8 @@ +cmake_policy(SET CMP0057 NEW) + +add_custom_command(OUTPUT out1 + COMMAND ${CMAKE_COMMAND} -E echo out1 + MAIN_DEPENDENCY input.txt +) + +add_custom_target(mytarget1 ALL DEPENDS out1) diff --git a/Tests/RunCMake/CMP0057/CMakeLists.txt b/Tests/RunCMake/CMP0057/CMakeLists.txt new file mode 100644 index 0000000..ef2163c --- /dev/null +++ b/Tests/RunCMake/CMP0057/CMakeLists.txt @@ -0,0 +1,3 @@ +cmake_minimum_required(VERSION 3.1) +project(${RunCMake_TEST} NONE) +include(${RunCMake_TEST}.cmake) diff --git a/Tests/RunCMake/CMP0057/RunCMakeTest.cmake b/Tests/RunCMake/CMP0057/RunCMakeTest.cmake new file mode 100644 index 0000000..f79235f --- /dev/null +++ b/Tests/RunCMake/CMP0057/RunCMakeTest.cmake @@ -0,0 +1,7 @@ +include(RunCMake) + +run_cmake(CMP0057-OLD) +run_cmake(CMP0057-NEW) +run_cmake(CMP0057-WARN) + +run_cmake(CMP0057-once_is_ok) diff --git a/Tests/RunCMake/CMP0057/input.txt b/Tests/RunCMake/CMP0057/input.txt new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/Tests/RunCMake/CMP0057/input.txt diff --git a/Tests/RunCMake/CMakeLists.txt b/Tests/RunCMake/CMakeLists.txt index 9f1256e..7cbc9fe 100644 --- a/Tests/RunCMake/CMakeLists.txt +++ b/Tests/RunCMake/CMakeLists.txt @@ -63,6 +63,7 @@ add_RunCMake_test(CMP0051) add_RunCMake_test(CMP0053) add_RunCMake_test(CMP0054) add_RunCMake_test(CMP0055) +add_RunCMake_test(CMP0057) add_RunCMake_test(CTest) if(NOT CMake_TEST_EXTERNAL_CMAKE) @@ -210,3 +211,8 @@ if(RPMBUILD_EXECUTABLE) endif() add_RunCMake_test(COMPILE_LANGUAGE-genex) + +# Matlab module related tests +if(CMake_TEST_FindMatlab) + add_RunCMake_test(FindMatlab) +endif() diff --git a/Tests/RunCMake/FindMatlab/CMakeLists.txt b/Tests/RunCMake/FindMatlab/CMakeLists.txt new file mode 100644 index 0000000..1b9a957 --- /dev/null +++ b/Tests/RunCMake/FindMatlab/CMakeLists.txt @@ -0,0 +1,3 @@ + +cmake_minimum_required(VERSION 2.8.12) +include(${RunCMake_TEST}.cmake) diff --git a/Tests/RunCMake/FindMatlab/MatlabTest1-result.txt b/Tests/RunCMake/FindMatlab/MatlabTest1-result.txt new file mode 100644 index 0000000..d00491f --- /dev/null +++ b/Tests/RunCMake/FindMatlab/MatlabTest1-result.txt @@ -0,0 +1 @@ +1 diff --git a/Tests/RunCMake/FindMatlab/MatlabTest1-stderr.txt b/Tests/RunCMake/FindMatlab/MatlabTest1-stderr.txt new file mode 100644 index 0000000..95a787f --- /dev/null +++ b/Tests/RunCMake/FindMatlab/MatlabTest1-stderr.txt @@ -0,0 +1,2 @@ +CMake Error at .*FindMatlab.cmake:[0-9]+ \(message\): + \[MATLAB\] This functionality needs the MAIN_PROGRAM component \(not default\) diff --git a/Tests/RunCMake/FindMatlab/MatlabTest1.cmake b/Tests/RunCMake/FindMatlab/MatlabTest1.cmake new file mode 100644 index 0000000..1cbc1c2 --- /dev/null +++ b/Tests/RunCMake/FindMatlab/MatlabTest1.cmake @@ -0,0 +1,22 @@ + +cmake_minimum_required (VERSION 2.8.12) +enable_testing() +project(test_should_fail) + +find_package(Matlab REQUIRED COMPONENTS MX_LIBRARY) + +matlab_add_mex( + # target name + NAME cmake_matlab_test_wrapper1 + # output name + OUTPUT_NAME cmake_matlab_mex1 + SRC ${CMAKE_CURRENT_SOURCE_DIR}/matlab_wrapper1.cpp + ) + +# this command should raise a FATAL_ERROR, component MAIN_PROGRAM is missing +matlab_add_unit_test( + NAME ${PROJECT_NAME}_matlabtest-1 + TIMEOUT 1 + UNITTEST_FILE ${CMAKE_CURRENT_SOURCE_DIR}/cmake_matlab_unit_tests2.m + ADDITIONAL_PATH $<TARGET_FILE_DIR:cmake_matlab_test_wrapper1> + ) diff --git a/Tests/RunCMake/FindMatlab/RunCMakeTest.cmake b/Tests/RunCMake/FindMatlab/RunCMakeTest.cmake new file mode 100644 index 0000000..33dbb77 --- /dev/null +++ b/Tests/RunCMake/FindMatlab/RunCMakeTest.cmake @@ -0,0 +1,3 @@ + +include(RunCMake) +run_cmake(MatlabTest1) diff --git a/Tests/RunCMake/FindMatlab/cmake_matlab_unit_tests2.m b/Tests/RunCMake/FindMatlab/cmake_matlab_unit_tests2.m new file mode 100644 index 0000000..7a8a342 --- /dev/null +++ b/Tests/RunCMake/FindMatlab/cmake_matlab_unit_tests2.m @@ -0,0 +1,6 @@ + +ret = cmake_matlab_mex1(rand(3,3)); + +if(size(ret) ~= size(rand(3,3))) + error('Dimension mismatch!'); +end diff --git a/Tests/RunCMake/FindMatlab/matlab_wrapper1.cpp b/Tests/RunCMake/FindMatlab/matlab_wrapper1.cpp new file mode 100644 index 0000000..4149bb9 --- /dev/null +++ b/Tests/RunCMake/FindMatlab/matlab_wrapper1.cpp @@ -0,0 +1,26 @@ + +// simple workaround to some compiler specific problems +// see http://stackoverflow.com/questions/22367516/mex-compile-error-unknown-type-name-char16-t/23281916#23281916 +#include <algorithm> + +#include "mex.h" + +// this test should return a matrix of 10 x 10 and should check some of the arguments + +void mexFunction(const int nlhs, mxArray *plhs[], const int nrhs, const mxArray *prhs[]) +{ + if(nrhs != 1) + { + mexErrMsgTxt("Incorrect arguments"); + } + + size_t dim1 = mxGetM(prhs[0]); + size_t dim2 = mxGetN(prhs[0]); + + if(dim1 == 1 || dim2 == 1) + { + mexErrMsgIdAndTxt("cmake_matlab:configuration", "Incorrect arguments"); + } + + plhs[0] = mxCreateNumericMatrix(dim1, dim2, mxGetClassID(prhs[0]), mxREAL); +} @@ -267,9 +267,11 @@ CMAKE_CXX_SOURCES="\ cmInstallDirectoryGenerator \ cmGeneratedFileStream \ cmGeneratorTarget \ + cmGeneratorExpressionContext \ cmGeneratorExpressionDAGChecker \ cmGeneratorExpressionEvaluator \ cmGeneratorExpressionLexer \ + cmGeneratorExpressionNode \ cmGeneratorExpressionParser \ cmGeneratorExpression \ cmGlobalGenerator \ |