diff options
87 files changed, 2143 insertions, 300 deletions
diff --git a/Help/generator/Ninja.rst b/Help/generator/Ninja.rst index ef0e28b..3bbd9dc 100644 --- a/Help/generator/Ninja.rst +++ b/Help/generator/Ninja.rst @@ -21,3 +21,13 @@ are generated: ``sub/dir/package`` Runs the package step in the subdirectory, if any. + +Fortran Support +^^^^^^^^^^^^^^^ + +The ``Ninja`` generator conditionally supports Fortran when the ``ninja`` +tool has the required features. As of this version of CMake the needed +features have not been integrated into upstream Ninja. Kitware maintains +a branch of Ninja with the required features on `github.com/Kitware/ninja`_. + +.. _`github.com/Kitware/ninja`: https://github.com/Kitware/ninja/tree/features-for-fortran#readme diff --git a/Help/manual/cmake-server.7.rst b/Help/manual/cmake-server.7.rst index 00ffcd1..b8a425c 100644 --- a/Help/manual/cmake-server.7.rst +++ b/Help/manual/cmake-server.7.rst @@ -186,6 +186,14 @@ Example:: ]== CMake Server ==] +Type "signal" +^^^^^^^^^^^^^ + +The server can send signals when it detects changes in the system state. Signals +are of type "signal", have an empty "cookie" and "inReplyTo" field and always +have a "name" set to show which signal was sent. + + Specific Message Types ---------------------- @@ -240,3 +248,129 @@ which will result in a response type "reply":: ]== CMake Server ==] indicating that the server is ready for action. + + +Type "globalSettings" +^^^^^^^^^^^^^^^^^^^^^ + +This request can be sent after the initial handshake. It will return a +JSON structure with information on cmake state. + +Example:: + + [== CMake Server ==[ + {"type":"globalSettings"} + ]== CMake Server ==] + +which will result in a response type "reply":: + + [== CMake Server ==[ + { + "buildDirectory": "/tmp/test-build", + "capabilities": { + "generators": [ + { + "extraGenerators": [], + "name": "Watcom WMake", + "platformSupport": false, + "toolsetSupport": false + }, + <...> + ], + "serverMode": false, + "version": { + "isDirty": false, + "major": 3, + "minor": 6, + "patch": 20160830, + "string": "3.6.20160830-gd6abad", + "suffix": "gd6abad" + } + }, + "checkSystemVars": false, + "cookie": "", + "extraGenerator": "", + "generator": "Ninja", + "debugOutput": false, + "inReplyTo": "globalSettings", + "sourceDirectory": "/home/code/cmake", + "trace": false, + "traceExpand": false, + "type": "reply", + "warnUninitialized": false, + "warnUnused": false, + "warnUnusedCli": true + } + ]== CMake Server ==] + + +Type "setGlobalSettings" +^^^^^^^^^^^^^^^^^^^^^^^^ + +This request can be sent to change the global settings attributes. Unknown +attributes are going to be ignored. Read-only attributes reported by +"globalSettings" are all capabilities, buildDirectory, generator, +extraGenerator and sourceDirectory. Any attempt to set these will be ignored, +too. + +All other settings will be changed. + +The server will respond with an empty reply message or an error. + +Example:: + + [== CMake Server ==[ + {"type":"setGlobalSettings","debugOutput":true} + ]== CMake Server ==] + +CMake will reply to this with:: + + [== CMake Server ==[ + {"inReplyTo":"setGlobalSettings","type":"reply"} + ]== CMake Server ==] + + +Type "configure" +^^^^^^^^^^^^^^^^ + +This request will configure a project for build. + +To configure a build directory already containing cmake files, it is enough to +set "buildDirectory" via "setGlobalSettings". To create a fresh build directory +you also need to set "currentGenerator" and "sourceDirectory" via "setGlobalSettings" +in addition to "buildDirectory". + +You may a list of strings to "configure" via the "cacheArguments" key. These +strings will be interpreted similar to command line arguments related to +cache handling that are passed to the cmake command line client. + +Example:: + + [== CMake Server ==[ + {"type":"configure", "cacheArguments":["-Dsomething=else"]} + ]== CMake Server ==] + +CMake will reply like this (after reporting progress for some time):: + + [== CMake Server ==[ + {"cookie":"","inReplyTo":"configure","type":"reply"} + ]== CMake Server ==] + + +Type "compute" +^^^^^^^^^^^^^^ + +This requist will generate build system files in the build directory and +is only available after a project was successfully "configure"d. + +Example:: + + [== CMake Server ==[ + {"type":"compute"} + ]== CMake Server ==] + +CMake will reply (after reporting progress information):: + + [== CMake Server ==[ + {"cookie":"","inReplyTo":"compute","type":"reply"} + ]== CMake Server ==] diff --git a/Help/manual/cmake-variables.7.rst b/Help/manual/cmake-variables.7.rst index 2d2a0b6..b74f867 100644 --- a/Help/manual/cmake-variables.7.rst +++ b/Help/manual/cmake-variables.7.rst @@ -397,6 +397,7 @@ Variables for Languages /variable/CMAKE_LANG_SOURCE_FILE_EXTENSIONS /variable/CMAKE_LANG_STANDARD_INCLUDE_DIRECTORIES /variable/CMAKE_LANG_STANDARD_LIBRARIES + /variable/CMAKE_Swift_LANGUAGE_VERSION /variable/CMAKE_USER_MAKE_RULES_OVERRIDE_LANG Variables for CTest diff --git a/Help/release/dev/CheckFortranSourceCompiles-custom-ext.rst b/Help/release/dev/CheckFortranSourceCompiles-custom-ext.rst new file mode 100644 index 0000000..bf62812 --- /dev/null +++ b/Help/release/dev/CheckFortranSourceCompiles-custom-ext.rst @@ -0,0 +1,6 @@ +CheckFortranSourceCompiles-custom-ext +------------------------------------- + +* The :module:`CheckFortranSourceCompiles` module macro + ``CHECK_Fortran_SOURCE_COMPILES`` gained a ``SRC_EXT`` option + to specify a custom test Fortran source file extension. diff --git a/Help/release/dev/FindMatlab-simulink.rst b/Help/release/dev/FindMatlab-simulink.rst new file mode 100644 index 0000000..cd25412 --- /dev/null +++ b/Help/release/dev/FindMatlab-simulink.rst @@ -0,0 +1,4 @@ +FindMatlab-simulink +------------------- + +* The :module:`FindMatlab` module learned to find a SIMULINK component. diff --git a/Help/release/dev/cpack-deb-package-description-fallback.rst b/Help/release/dev/cpack-deb-package-description-fallback.rst deleted file mode 100644 index 71ca821..0000000 --- a/Help/release/dev/cpack-deb-package-description-fallback.rst +++ /dev/null @@ -1,15 +0,0 @@ -cpack-deb-package-description-fallback --------------------------------------- - -* The :module:`CPackDeb` module gained a new - :variable:`CPACK_DEBIAN_<COMPONENT>_PACKAGE_DESCRIPTION` - variable for component-specific package descriptions. - -* The :module:`CPackDeb` module changed its package description - override rules to match :module:`CPackRPM` module behavior. - If the :variable:`CPACK_PACKAGE_DESCRIPTION_FILE` variable is set to - a non-default location then it is preferred to the - :variable:`CPACK_PACKAGE_DESCRIPTION_SUMMARY` variable. - This is a behavior change from previous versions but produces - more consistent and expected behavior. - See :variable:`CPACK_DEBIAN_PACKAGE_DESCRIPTION`. diff --git a/Help/release/dev/ninja-fortran.rst b/Help/release/dev/ninja-fortran.rst new file mode 100644 index 0000000..612b1ff --- /dev/null +++ b/Help/release/dev/ninja-fortran.rst @@ -0,0 +1,6 @@ +ninja-fortran +------------- + +* The :generator:`Ninja` generator learned to conditionally support + Fortran when using a ``ninja`` tool that has the necessary features. + See generator documentation for details. diff --git a/Help/release/dev/xcode-swift-version.rst b/Help/release/dev/xcode-swift-version.rst new file mode 100644 index 0000000..5dff23c --- /dev/null +++ b/Help/release/dev/xcode-swift-version.rst @@ -0,0 +1,6 @@ +xcode-swift-version +------------------- + +* The :generator:`Xcode` generator's rudimentary Swift language support + learned to honor a new :variable:`CMAKE_Swift_LANGUAGE_VERSION` variable + to tell Xcode what version of Swift is used by the source. diff --git a/Help/variable/CMAKE_Swift_LANGUAGE_VERSION.rst b/Help/variable/CMAKE_Swift_LANGUAGE_VERSION.rst new file mode 100644 index 0000000..50121e2 --- /dev/null +++ b/Help/variable/CMAKE_Swift_LANGUAGE_VERSION.rst @@ -0,0 +1,5 @@ +CMAKE_Swift_LANGUAGE_VERSION +---------------------------- + +Set to the Swift language version number. If not set, the legacy "2.3" +version is assumed. diff --git a/Modules/CMakeDetermineCompilerId.cmake b/Modules/CMakeDetermineCompilerId.cmake index 64d9bed..3d1ca6d 100644 --- a/Modules/CMakeDetermineCompilerId.cmake +++ b/Modules/CMakeDetermineCompilerId.cmake @@ -266,6 +266,11 @@ Id flags: ${testflags} else() set(id_toolset "") endif() + if("${lang}" STREQUAL "Swift") + set(id_lang_version "SWIFT_VERSION = 2.3;") + else() + set(id_lang_version "") + endif() if(CMAKE_OSX_DEPLOYMENT_TARGET) set(id_deployment_target "MACOSX_DEPLOYMENT_TARGET = \"${CMAKE_OSX_DEPLOYMENT_TARGET}\";") diff --git a/Modules/CPackDeb.cmake b/Modules/CPackDeb.cmake index 423bb00..1a7b923 100644 --- a/Modules/CPackDeb.cmake +++ b/Modules/CPackDeb.cmake @@ -156,18 +156,16 @@ # * Default : :code:`CPACK_PACKAGE_CONTACT` # # .. variable:: CPACK_DEBIAN_PACKAGE_DESCRIPTION -# CPACK_DEBIAN_<COMPONENT>_PACKAGE_DESCRIPTION +# CPACK_COMPONENT_<COMPONENT>_DESCRIPTION # # The Debian package description # # * Mandatory : YES # * Default : # -# - :variable:`CPACK_COMPONENT_<compName>_DESCRIPTION` (component based installers only) if set, -# - :variable:`CPACK_PACKAGE_DESCRIPTION_FILE` if set to non default location, -# - :variable:`CPACK_PACKAGE_DESCRIPTION_SUMMARY` if set, -# - :variable:`CPACK_PACKAGE_DESCRIPTION_FILE` default value if set, -# - or "no package description available" +# - :variable:`CPACK_DEBIAN_PACKAGE_DESCRIPTION` if set or +# - :variable:`CPACK_PACKAGE_DESCRIPTION_SUMMARY` +# # # .. variable:: CPACK_DEBIAN_PACKAGE_SECTION # CPACK_DEBIAN_<COMPONENT>_PACKAGE_SECTION @@ -831,23 +829,24 @@ function(cpack_deb_prepare_package_vars) endif() # Description: (mandatory) - if(CPACK_DEB_PACKAGE_COMPONENT) - if(CPACK_DEBIAN_${_local_component_name}_PACKAGE_DESCRIPTION) - set(CPACK_DEBIAN_PACKAGE_DESCRIPTION "${CPACK_DEBIAN_${_local_component_name}_PACKAGE_DESCRIPTION}") - elseif(CPACK_COMPONENT_${_local_component_name}_DESCRIPTION) - set(CPACK_DEBIAN_PACKAGE_DESCRIPTION "${CPACK_COMPONENT_${_local_component_name}_DESCRIPTION}") + if(NOT CPACK_DEB_PACKAGE_COMPONENT) + if(NOT CPACK_DEBIAN_PACKAGE_DESCRIPTION) + if(NOT CPACK_PACKAGE_DESCRIPTION_SUMMARY) + message(FATAL_ERROR "CPackDeb: Debian package requires a summary for a package, set CPACK_PACKAGE_DESCRIPTION_SUMMARY or CPACK_DEBIAN_PACKAGE_DESCRIPTION") + endif() + set(CPACK_DEBIAN_PACKAGE_DESCRIPTION ${CPACK_PACKAGE_DESCRIPTION_SUMMARY}) endif() - endif() - - if(NOT CPACK_DEBIAN_PACKAGE_DESCRIPTION) - if(CPACK_PACKAGE_DESCRIPTION_FILE AND NOT "${CPACK_PACKAGE_DESCRIPTION_FILE}" STREQUAL "${CMAKE_ROOT}/Templates/CPack.GenericDescription.txt") - file(READ "${CPACK_PACKAGE_DESCRIPTION_FILE}" CPACK_DEBIAN_PACKAGE_DESCRIPTION) - elseif(CPACK_PACKAGE_DESCRIPTION_SUMMARY) - set(CPACK_DEBIAN_PACKAGE_DESCRIPTION "${CPACK_PACKAGE_DESCRIPTION_SUMMARY}") - elseif(CPACK_PACKAGE_DESCRIPTION_FILE) # use default package description file content - file(READ "${CPACK_PACKAGE_DESCRIPTION_FILE}" CPACK_DEBIAN_PACKAGE_DESCRIPTION) - else() - set(CPACK_DEBIAN_PACKAGE_DESCRIPTION "no package description available") + else() + set(component_description_var CPACK_COMPONENT_${_local_component_name}_DESCRIPTION) + + # component description overrides package description + if(${component_description_var}) + set(CPACK_DEBIAN_PACKAGE_DESCRIPTION ${${component_description_var}}) + elseif(NOT CPACK_DEBIAN_PACKAGE_DESCRIPTION) + if(NOT CPACK_PACKAGE_DESCRIPTION_SUMMARY) + message(FATAL_ERROR "CPackDeb: Debian package requires a summary for a package, set CPACK_PACKAGE_DESCRIPTION_SUMMARY or CPACK_DEBIAN_PACKAGE_DESCRIPTION or ${component_description_var}") + endif() + set(CPACK_DEBIAN_PACKAGE_DESCRIPTION ${CPACK_PACKAGE_DESCRIPTION_SUMMARY}) endif() endif() diff --git a/Modules/CPackNSIS.cmake b/Modules/CPackNSIS.cmake index db5984a..4693ce5 100644 --- a/Modules/CPackNSIS.cmake +++ b/Modules/CPackNSIS.cmake @@ -96,6 +96,11 @@ # Contact information for questions and comments about the installation # process. # +# .. variable:: CPACK_NSIS_<compName>_INSTALL_DIRECTORY +# +# Custom install directory for the specified component <compName> instead +# of $INSTDIR. +# # .. variable:: CPACK_NSIS_CREATE_ICONS_EXTRA # # Additional NSIS commands for creating start menu shortcuts. diff --git a/Modules/CheckFortranSourceCompiles.cmake b/Modules/CheckFortranSourceCompiles.cmake index 0bdcffa..967b830 100644 --- a/Modules/CheckFortranSourceCompiles.cmake +++ b/Modules/CheckFortranSourceCompiles.cmake @@ -4,7 +4,8 @@ # # Check if given Fortran source compiles and links into an executable:: # -# CHECK_Fortran_SOURCE_COMPILES(<code> <var> [FAIL_REGEX <fail-regex>]) +# CHECK_Fortran_SOURCE_COMPILES(<code> <var> [FAIL_REGEX <fail-regex>] +# [SRC_EXT <ext>]) # # The arguments are: # @@ -13,8 +14,10 @@ # ``<var>`` # Variable to store whether the source code compiled. # Will be created as an internal cache variable. -# ``<fail-regex>`` +# ``FAIL_REGEX <fail-regex>`` # Fail if test output matches this regex. +# ``SRC_EXT <ext>`` +# Use source extension ``.<ext>`` instead of the default ``.F``. # # The following variables may be set before calling this macro to modify # the way the check is run:: @@ -43,9 +46,10 @@ macro(CHECK_Fortran_SOURCE_COMPILES SOURCE VAR) if(NOT DEFINED "${VAR}") set(_FAIL_REGEX) + set(_SRC_EXT) set(_key) foreach(arg ${ARGN}) - if("${arg}" MATCHES "^(FAIL_REGEX)$") + if("${arg}" MATCHES "^(FAIL_REGEX|SRC_EXT)$") set(_key "${arg}") elseif(_key) list(APPEND _${_key} "${arg}") @@ -53,6 +57,9 @@ macro(CHECK_Fortran_SOURCE_COMPILES SOURCE VAR) message(FATAL_ERROR "Unknown argument:\n ${arg}\n") endif() endforeach() + if(NOT _SRC_EXT) + set(_SRC_EXT F) + endif() set(MACRO_CHECK_FUNCTION_DEFINITIONS "-D${VAR} ${CMAKE_REQUIRED_FLAGS}") if(CMAKE_REQUIRED_LIBRARIES) @@ -67,7 +74,7 @@ macro(CHECK_Fortran_SOURCE_COMPILES SOURCE VAR) else() set(CHECK_Fortran_SOURCE_COMPILES_ADD_INCLUDES) endif() - file(WRITE "${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeTmp/src.F" + file(WRITE "${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeTmp/src.${_SRC_EXT}" "${SOURCE}\n") if(NOT CMAKE_REQUIRED_QUIET) @@ -75,7 +82,7 @@ macro(CHECK_Fortran_SOURCE_COMPILES SOURCE VAR) endif() try_compile(${VAR} ${CMAKE_BINARY_DIR} - ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeTmp/src.F + ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeTmp/src.${_SRC_EXT} COMPILE_DEFINITIONS ${CMAKE_REQUIRED_DEFINITIONS} ${CHECK_Fortran_SOURCE_COMPILES_ADD_LIBRARIES} CMAKE_FLAGS -DCOMPILE_DEFINITIONS:STRING=${MACRO_CHECK_FUNCTION_DEFINITIONS} diff --git a/Modules/Compiler/GNU-Fortran.cmake b/Modules/Compiler/GNU-Fortran.cmake index fc848ac..94dc275 100644 --- a/Modules/Compiler/GNU-Fortran.cmake +++ b/Modules/Compiler/GNU-Fortran.cmake @@ -1,6 +1,9 @@ include(Compiler/GNU) __compiler_gnu(Fortran) +set(CMAKE_Fortran_PREPROCESS_SOURCE + "<CMAKE_Fortran_COMPILER> -cpp <DEFINES> <INCLUDES> <FLAGS> -E <SOURCE> -o <PREPROCESSED_SOURCE>") + set(CMAKE_Fortran_FORMAT_FIXED_FLAG "-ffixed-form") set(CMAKE_Fortran_FORMAT_FREE_FLAG "-ffree-form") diff --git a/Modules/Compiler/Intel-Fortran.cmake b/Modules/Compiler/Intel-Fortran.cmake index ef7aa3a..a132055 100644 --- a/Modules/Compiler/Intel-Fortran.cmake +++ b/Modules/Compiler/Intel-Fortran.cmake @@ -7,3 +7,6 @@ set(CMAKE_Fortran_FORMAT_FREE_FLAG "-free") set(CMAKE_Fortran_CREATE_PREPROCESSED_SOURCE "<CMAKE_Fortran_COMPILER> <DEFINES> <INCLUDES> <FLAGS> -E <SOURCE> > <PREPROCESSED_SOURCE>") set(CMAKE_Fortran_CREATE_ASSEMBLY_SOURCE "<CMAKE_Fortran_COMPILER> <DEFINES> <INCLUDES> <FLAGS> -S <SOURCE> -o <ASSEMBLY_SOURCE>") + +set(CMAKE_Fortran_PREPROCESS_SOURCE + "<CMAKE_Fortran_COMPILER> -fpp <DEFINES> <INCLUDES> <FLAGS> -E <SOURCE> > <PREPROCESSED_SOURCE>") diff --git a/Modules/Compiler/SunPro-Fortran.cmake b/Modules/Compiler/SunPro-Fortran.cmake index a0e07d4..6607926 100644 --- a/Modules/Compiler/SunPro-Fortran.cmake +++ b/Modules/Compiler/SunPro-Fortran.cmake @@ -18,5 +18,8 @@ string(APPEND CMAKE_Fortran_FLAGS_RELWITHDEBINFO_INIT " -g -xO2 -DNDEBUG") set(CMAKE_Fortran_MODDIR_FLAG "-moddir=") set(CMAKE_Fortran_MODPATH_FLAG "-M") +set(CMAKE_Fortran_PREPROCESS_SOURCE + "<CMAKE_Fortran_COMPILER> <DEFINES> <INCLUDES> <FLAGS> -F <SOURCE> -o <PREPROCESSED_SOURCE>") + set(CMAKE_Fortran_CREATE_PREPROCESSED_SOURCE "<CMAKE_Fortran_COMPILER> <DEFINES> <INCLUDES> <FLAGS> -F <SOURCE> -o <PREPROCESSED_SOURCE>") set(CMAKE_Fortran_CREATE_ASSEMBLY_SOURCE "<CMAKE_Fortran_COMPILER> <DEFINES> <INCLUDES> <FLAGS> -S <SOURCE> -o <ASSEMBLY_SOURCE>") diff --git a/Modules/CompilerId/Xcode-3.pbxproj.in b/Modules/CompilerId/Xcode-3.pbxproj.in index 20f3da3..22ad4f6 100644 --- a/Modules/CompilerId/Xcode-3.pbxproj.in +++ b/Modules/CompilerId/Xcode-3.pbxproj.in @@ -84,6 +84,7 @@ CONFIGURATION_BUILD_DIR = "$(BUILD_DIR)"; SYMROOT = .; @id_toolset@ + @id_lang_version@ @id_deployment_target@ @id_sdkroot@ }; diff --git a/Modules/FindBoost.cmake b/Modules/FindBoost.cmake index 1f03841..b3e8db9 100644 --- a/Modules/FindBoost.cmake +++ b/Modules/FindBoost.cmake @@ -742,6 +742,21 @@ function(_Boost_COMPONENT_DEPENDENCIES component _ret) set(_Boost_THREAD_DEPENDENCIES chrono system date_time atomic) set(_Boost_WAVE_DEPENDENCIES filesystem system serialization thread chrono date_time atomic) set(_Boost_WSERIALIZATION_DEPENDENCIES serialization) + elseif(NOT Boost_VERSION VERSION_LESS 106200 AND Boost_VERSION VERSION_LESS 106300) + set(_Boost_CHRONO_DEPENDENCIES system) + set(_Boost_CONTEXT_DEPENDENCIES thread chrono system date_time) + set(_Boost_COROUTINE_DEPENDENCIES context system) + set(_Boost_FIBER_DEPENDENCIES context thread chrono system date_time) + set(_Boost_FILESYSTEM_DEPENDENCIES system) + set(_Boost_IOSTREAMS_DEPENDENCIES regex) + set(_Boost_LOG_DEPENDENCIES date_time log_setup system filesystem thread regex chrono atomic) + set(_Boost_MATH_DEPENDENCIES math_c99 math_c99f math_c99l math_tr1 math_tr1f math_tr1l atomic) + set(_Boost_MPI_DEPENDENCIES serialization) + set(_Boost_MPI_PYTHON_DEPENDENCIES python mpi serialization) + set(_Boost_RANDOM_DEPENDENCIES system) + set(_Boost_THREAD_DEPENDENCIES chrono system date_time atomic) + set(_Boost_WAVE_DEPENDENCIES filesystem system serialization thread chrono date_time atomic) + set(_Boost_WSERIALIZATION_DEPENDENCIES serialization) else() message(WARNING "Imported targets not available for Boost version ${Boost_VERSION}") set(_Boost_IMPORTED_TARGETS FALSE) @@ -777,6 +792,7 @@ function(_Boost_COMPONENT_HEADERS component _hdrs) set(_Boost_COROUTINE_HEADERS "boost/coroutine/all.hpp") set(_Boost_EXCEPTION_HEADERS "boost/exception/exception.hpp") set(_Boost_DATE_TIME_HEADERS "boost/date_time/date.hpp") + set(_Boost_FIBER_HEADERS "boost/fiber/all.hpp") set(_Boost_FILESYSTEM_HEADERS "boost/filesystem/path.hpp") set(_Boost_GRAPH_HEADERS "boost/graph/adjacency_list.hpp") set(_Boost_GRAPH_PARALLEL_HEADERS "boost/graph/adjacency_list.hpp") diff --git a/Modules/FindMatlab.cmake b/Modules/FindMatlab.cmake index c813f8f..d016848 100644 --- a/Modules/FindMatlab.cmake +++ b/Modules/FindMatlab.cmake @@ -18,6 +18,8 @@ # * ``MX_LIBRARY``, ``ENG_LIBRARY`` and ``MAT_LIBRARY``: respectively the MX, # ENG and MAT libraries of Matlab # * ``MAIN_PROGRAM`` the Matlab binary program. +# * ``MEX_COMPILER`` the MEX compiler. +# * ``SIMULINK`` the Simulink environment. # # .. note:: # @@ -223,6 +225,7 @@ set(_FindMatlab_SELF_DIR "${CMAKE_CURRENT_LIST_DIR}") include(FindPackageHandleStandardArgs) include(CheckCXXCompilerFlag) +include(CheckCCompilerFlag) # The currently supported versions. Other version can be added by the user by @@ -819,12 +822,13 @@ endfunction() # 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. +# :command:`add_library` or :command:`add_executable` command. # # :: # # matlab_add_mex( # NAME <name> +# [EXECUTABLE | MODULE | SHARED] # SRC src1 [src2 ...] # [OUTPUT_NAME output_name] # [DOCUMENTATION file.txt] @@ -835,7 +839,7 @@ endfunction() # ``NAME`` # name of the target. # ``SRC`` -# list of tje source files. +# list of 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 @@ -851,6 +855,10 @@ endfunction() # mex file, and with extension `.m`. In that case, typing ``help <name>`` # in Matlab prints the documentation contained in this file. # +# ``MODULE`` or ``SHARED`` may be given to specify the type of library to be +# created. ``EXECUTABLE`` may be given to create an executable instead of +# a library. If no type is given explicitly, the type is ``SHARED``. +# # The documentation file is not processed and should be in the following # format: # @@ -859,18 +867,23 @@ endfunction() # % This is the documentation # function ret = mex_target_output_name(input1) # -function(matlab_add_mex ) +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) + if(CMAKE_CXX_COMPILER_LOADED) + check_cxx_compiler_flag(-pthread HAS_MINUS_PTHREAD) + elseif(CMAKE_C_COMPILER_LOADED) + check_c_compiler_flag(-pthread HAS_MINUS_PTHREAD) + endif() # 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(options EXECUTABLE MODULE SHARED) set(oneValueArgs NAME DOCUMENTATION OUTPUT_NAME) set(multiValueArgs LINK_TO SRC) @@ -885,11 +898,25 @@ function(matlab_add_mex ) set(${prefix}_OUTPUT_NAME ${${prefix}_NAME}) endif() - add_library(${${prefix}_NAME} - SHARED + if(${prefix}_EXECUTABLE) + add_executable(${${prefix}_NAME} ${${prefix}_SRC} ${${prefix}_DOCUMENTATION} ${${prefix}_UNPARSED_ARGUMENTS}) + else() + if(${prefix}_MODULE) + set(type MODULE) + else() + set(type SHARED) + endif() + + add_library(${${prefix}_NAME} + ${type} + ${${prefix}_SRC} + ${${prefix}_DOCUMENTATION} + ${${prefix}_UNPARSED_ARGUMENTS}) + endif() + target_include_directories(${${prefix}_NAME} PRIVATE ${Matlab_INCLUDE_DIRS}) if(DEFINED Matlab_MX_LIBRARY) @@ -1463,6 +1490,21 @@ if(_matlab_find_mat GREATER -1) endif() unset(_matlab_find_mat) +# Component Simulink +list(FIND Matlab_FIND_COMPONENTS SIMULINK _matlab_find_simulink) +if(_matlab_find_simulink GREATER -1) + find_path( + Matlab_SIMULINK_INCLUDE_DIR + simstruc.h + PATHS "${Matlab_ROOT_DIR}/simulink/include" + NO_DEFAULT_PATH + ) + if(Matlab_SIMULINK_INCLUDE_DIR) + set(Matlab_SIMULINK_FOUND TRUE) + list(APPEND Matlab_INCLUDE_DIRS "${Matlab_SIMULINK_INCLUDE_DIR}") + endif() +endif() +unset(_matlab_find_simulink) unset(_matlab_lib_dir_for_search) diff --git a/Modules/FindOpenSSL.cmake b/Modules/FindOpenSSL.cmake index 7ddd783..034b0e6 100644 --- a/Modules/FindOpenSSL.cmake +++ b/Modules/FindOpenSSL.cmake @@ -142,15 +142,15 @@ if(WIN32 AND NOT CYGWIN) if(OPENSSL_USE_STATIC_LIBS) set(_OPENSSL_PATH_SUFFIXES - "lib" - "VC/static" "lib/VC/static" + "VC/static" + "lib" ) else() set(_OPENSSL_PATH_SUFFIXES - "lib" - "VC" "lib/VC" + "VC" + "lib" ) endif () @@ -227,8 +227,8 @@ if(WIN32 AND NOT CYGWIN) NAMES_PER_DIR ${_OPENSSL_ROOT_HINTS_AND_PATHS} PATH_SUFFIXES - "lib" "lib/MinGW" + "lib" ) find_library(SSL_EAY @@ -237,8 +237,8 @@ if(WIN32 AND NOT CYGWIN) NAMES_PER_DIR ${_OPENSSL_ROOT_HINTS_AND_PATHS} PATH_SUFFIXES - "lib" "lib/MinGW" + "lib" ) mark_as_advanced(SSL_EAY LIB_EAY) diff --git a/Modules/Platform/Darwin-Initialize.cmake b/Modules/Platform/Darwin-Initialize.cmake index 91c2cf3..427909d 100644 --- a/Modules/Platform/Darwin-Initialize.cmake +++ b/Modules/Platform/Darwin-Initialize.cmake @@ -125,8 +125,10 @@ if(CMAKE_OSX_SYSROOT) set(_CMAKE_OSX_SYSROOT_ORIG "") endif() set(_CMAKE_OSX_SYSROOT_PATH "${CMAKE_OSX_SYSROOT}") - else() - # Transform the sdk name into a path. + endif() + + if(CMAKE_OSX_SYSROOT) + # Transform the (maybe unversioned) sysroot into a versioned path. execute_process( COMMAND xcodebuild -sdk ${CMAKE_OSX_SYSROOT} -version Path OUTPUT_VARIABLE _stdout diff --git a/Source/CMakeVersion.cmake b/Source/CMakeVersion.cmake index 27ca0ca..9eb99ea 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 6) -set(CMake_VERSION_PATCH 20160923) +set(CMake_VERSION_PATCH 20160927) #set(CMake_VERSION_RC 1) diff --git a/Source/CPack/cmCPackNSISGenerator.cxx b/Source/CPack/cmCPackNSISGenerator.cxx index 2db94f1..b49da7f 100644 --- a/Source/CPack/cmCPackNSISGenerator.cxx +++ b/Source/CPack/cmCPackNSISGenerator.cxx @@ -71,14 +71,26 @@ int cmCPackNSISGenerator::PackageFiles() std::ostringstream str; std::vector<std::string>::const_iterator it; for (it = files.begin(); it != files.end(); ++it) { + std::string outputDir = "$INSTDIR"; std::string fileN = cmSystemTools::RelativePath(toplevel.c_str(), it->c_str()); if (!this->Components.empty()) { + const std::string::size_type pos = fileN.find('/'); + + // Use the custom component install directory if we have one + if (pos != std::string::npos) { + const std::string componentName = fileN.substr(0, pos); + outputDir = CustomComponentInstallDirectory(componentName); + } else { + outputDir = CustomComponentInstallDirectory(fileN); + } + // Strip off the component part of the path. - fileN = fileN.substr(fileN.find('/') + 1, std::string::npos); + fileN = fileN.substr(pos + 1, std::string::npos); } std::replace(fileN.begin(), fileN.end(), '/', '\\'); - str << " Delete \"$INSTDIR\\" << fileN << "\"" << std::endl; + + str << " Delete \"" << outputDir << "\\" << fileN << "\"" << std::endl; } cmCPackLogger(cmCPackLog::LOG_DEBUG, "Uninstall Files: " << str.str() << std::endl); @@ -108,7 +120,12 @@ int cmCPackNSISGenerator::PackageFiles() } } std::replace(fileN.begin(), fileN.end(), '/', '\\'); - dstr << " RMDir \"$INSTDIR\\" << fileN << "\"" << std::endl; + + const std::string componentOutputDir = + CustomComponentInstallDirectory(componentName); + + dstr << " RMDir \"" << componentOutputDir << "\\" << fileN << "\"" + << std::endl; if (!componentName.empty()) { this->Components[componentName].Directories.push_back(fileN); } @@ -650,7 +667,10 @@ std::string cmCPackNSISGenerator::CreateComponentDescription( } componentCode += " SectionIn" + out.str() + "\n"; } - componentCode += " SetOutPath \"$INSTDIR\"\n"; + + const std::string componentOutputDir = + CustomComponentInstallDirectory(component->Name); + componentCode += " SetOutPath \"" + componentOutputDir + "\"\n"; // Create the actual installation commands if (component->IsDownloaded) { @@ -796,13 +816,13 @@ std::string cmCPackNSISGenerator::CreateComponentDescription( ++pathIt) { path = *pathIt; std::replace(path.begin(), path.end(), '/', '\\'); - macrosOut << " Delete \"$INSTDIR\\" << path << "\"\n"; + macrosOut << " Delete \"" << componentOutputDir << "\\" << path << "\"\n"; } for (pathIt = component->Directories.begin(); pathIt != component->Directories.end(); ++pathIt) { path = *pathIt; std::replace(path.begin(), path.end(), '/', '\\'); - macrosOut << " RMDir \"$INSTDIR\\" << path << "\"\n"; + macrosOut << " RMDir \"" << componentOutputDir << "\\" << path << "\"\n"; } macrosOut << " noremove_" << component->Name << ":\n"; macrosOut << "!macroend\n"; @@ -914,6 +934,15 @@ std::string cmCPackNSISGenerator::CreateComponentGroupDescription( return code; } +std::string cmCPackNSISGenerator::CustomComponentInstallDirectory( + const std::string& componentName) +{ + const char* outputDir = + this->GetOption("CPACK_NSIS_" + componentName + "_INSTALL_DIRECTORY"); + const std::string componentOutputDir = (outputDir ? outputDir : "$INSTDIR"); + return componentOutputDir; +} + std::string cmCPackNSISGenerator::TranslateNewlines(std::string str) { cmSystemTools::ReplaceString(str, "\n", "$\\r$\\n"); diff --git a/Source/CPack/cmCPackNSISGenerator.h b/Source/CPack/cmCPackNSISGenerator.h index ae03e6b..bd7d752 100644 --- a/Source/CPack/cmCPackNSISGenerator.h +++ b/Source/CPack/cmCPackNSISGenerator.h @@ -84,6 +84,11 @@ protected: std::string CreateComponentGroupDescription(cmCPackComponentGroup* group, std::ostream& macrosOut); + /// Returns the custom install directory if available for the specified + /// component, otherwise $INSTDIR is returned. + std::string CustomComponentInstallDirectory( + const std::string& componentName); + /// Translations any newlines found in the string into \\r\\n, so that the /// resulting string can be used within NSIS. static std::string TranslateNewlines(std::string str); diff --git a/Source/Checks/cm_cxx_auto_ptr.cxx b/Source/Checks/cm_cxx_auto_ptr.cxx new file mode 100644 index 0000000..d3100fd --- /dev/null +++ b/Source/Checks/cm_cxx_auto_ptr.cxx @@ -0,0 +1,18 @@ +#include <memory> + +std::auto_ptr<int> get_auto_ptr() +{ + std::auto_ptr<int> ptr; + ptr = std::auto_ptr<int>(new int(0)); + return ptr; +} + +int use_auto_ptr(std::auto_ptr<int> ptr) +{ + return *ptr; +} + +int main() +{ + return use_auto_ptr(get_auto_ptr()); +} diff --git a/Source/Checks/cm_cxx_features.cmake b/Source/Checks/cm_cxx_features.cmake index c6a532f..80c9f3b 100644 --- a/Source/Checks/cm_cxx_features.cmake +++ b/Source/Checks/cm_cxx_features.cmake @@ -32,6 +32,7 @@ function(cm_check_cxx_feature name) endfunction() if(CMAKE_CXX_STANDARD) + cm_check_cxx_feature(auto_ptr) cm_check_cxx_feature(make_unique) if(CMake_HAVE_CXX_MAKE_UNIQUE) set(CMake_HAVE_CXX_UNIQUE_PTR 1) diff --git a/Source/QtDialog/CMakeSetup.cxx b/Source/QtDialog/CMakeSetup.cxx index 4de4bef..52809b0 100644 --- a/Source/QtDialog/CMakeSetup.cxx +++ b/Source/QtDialog/CMakeSetup.cxx @@ -109,11 +109,13 @@ int main(int argc, char** argv) QTextCodec::setCodecForLocale(utf8_codec); #endif +#if QT_VERSION < 0x050000 // clean out standard Qt paths for plugins, which we don't use anyway // when creating Mac bundles, it potentially causes problems foreach (QString p, QApplication::libraryPaths()) { QApplication::removeLibraryPath(p); } +#endif // tell the cmake library where cmake is QDir cmExecDir(QApplication::applicationDirPath()); diff --git a/Source/cmCTest.cxx b/Source/cmCTest.cxx index 6523e3e..9b5248e 100644 --- a/Source/cmCTest.cxx +++ b/Source/cmCTest.cxx @@ -1506,22 +1506,17 @@ std::string cmCTest::Base64EncodeFile(std::string const& file) | std::ios::binary #endif ); - unsigned char* file_buffer = new unsigned char[len + 1]; - ifs.read(reinterpret_cast<char*>(file_buffer), len); + std::vector<char> file_buffer(len + 1); + ifs.read(&file_buffer[0], len); ifs.close(); - unsigned char* encoded_buffer = new unsigned char[(len * 3) / 2 + 5]; + std::vector<char> encoded_buffer((len * 3) / 2 + 5); - size_t const rlen = cmsysBase64_Encode(file_buffer, len, encoded_buffer, 1); + size_t const rlen = cmsysBase64_Encode( + reinterpret_cast<unsigned char*>(&file_buffer[0]), len, + reinterpret_cast<unsigned char*>(&encoded_buffer[0]), 1); - std::string base64 = ""; - for (size_t i = 0; i < rlen; i++) { - base64 += encoded_buffer[i]; - } - delete[] file_buffer; - delete[] encoded_buffer; - - return base64; + return std::string(&encoded_buffer[0], rlen); } bool cmCTest::SubmitExtraFiles(const VectorOfStrings& files) @@ -2795,48 +2790,42 @@ bool cmCTest::CompressString(std::string& str) int ret; z_stream strm; - unsigned char* in = - reinterpret_cast<unsigned char*>(const_cast<char*>(str.c_str())); - // zlib makes the guarantee that this is the maximum output size - int outSize = - static_cast<int>(static_cast<double>(str.size()) * 1.001 + 13.0); - unsigned char* out = new unsigned char[outSize]; - strm.zalloc = Z_NULL; strm.zfree = Z_NULL; strm.opaque = Z_NULL; ret = deflateInit(&strm, -1); // default compression level if (ret != Z_OK) { - delete[] out; return false; } + unsigned char* in = + reinterpret_cast<unsigned char*>(const_cast<char*>(str.c_str())); + // zlib makes the guarantee that this is the maximum output size + int outSize = + static_cast<int>(static_cast<double>(str.size()) * 1.001 + 13.0); + std::vector<unsigned char> out(outSize); + strm.avail_in = static_cast<uInt>(str.size()); strm.next_in = in; strm.avail_out = outSize; - strm.next_out = out; + strm.next_out = &out[0]; ret = deflate(&strm, Z_FINISH); if (ret == Z_STREAM_ERROR || ret != Z_STREAM_END) { cmCTestLog(this, ERROR_MESSAGE, "Error during gzip compression." << std::endl); - delete[] out; return false; } (void)deflateEnd(&strm); // Now base64 encode the resulting binary string - unsigned char* base64EncodedBuffer = new unsigned char[(outSize * 3) / 2]; + std::vector<unsigned char> base64EncodedBuffer((outSize * 3) / 2); size_t rlen = - cmsysBase64_Encode(out, strm.total_out, base64EncodedBuffer, 1); - - str = ""; - str.append(reinterpret_cast<char*>(base64EncodedBuffer), rlen); + cmsysBase64_Encode(&out[0], strm.total_out, &base64EncodedBuffer[0], 1); - delete[] base64EncodedBuffer; - delete[] out; + str.assign(reinterpret_cast<char*>(&base64EncodedBuffer[0]), rlen); return true; } diff --git a/Source/cmConfigure.cmake.h.in b/Source/cmConfigure.cmake.h.in index 8365367..8a1e81f 100644 --- a/Source/cmConfigure.cmake.h.in +++ b/Source/cmConfigure.cmake.h.in @@ -30,6 +30,7 @@ #cmakedefine CMAKE_USE_MACH_PARSER #cmakedefine CMAKE_USE_LIBUV #cmakedefine CMAKE_ENCODING_UTF8 +#cmakedefine CMake_HAVE_CXX_AUTO_PTR #cmakedefine CMake_HAVE_CXX_MAKE_UNIQUE #cmakedefine CMake_HAVE_CXX_NULLPTR #cmakedefine CMake_HAVE_CXX_OVERRIDE diff --git a/Source/cmGlobalGenerator.cxx b/Source/cmGlobalGenerator.cxx index 4772474..95747f2 100644 --- a/Source/cmGlobalGenerator.cxx +++ b/Source/cmGlobalGenerator.cxx @@ -319,6 +319,12 @@ void cmGlobalGenerator::FindMakeProgram(cmMakefile* mf) } } +bool cmGlobalGenerator::CheckLanguages( + std::vector<std::string> const& /* languages */, cmMakefile* /* mf */) const +{ + return true; +} + // enable the given language // // The following files are loaded in this order: @@ -428,6 +434,10 @@ void cmGlobalGenerator::EnableLanguage( // find and make sure CMAKE_MAKE_PROGRAM is defined this->FindMakeProgram(mf); + if (!this->CheckLanguages(languages, mf)) { + return; + } + // try and load the CMakeSystem.cmake if it is there std::string fpath = rootBin; bool const readCMakeSystem = !mf->GetDefinition("CMAKE_SYSTEM_LOADED"); diff --git a/Source/cmGlobalGenerator.h b/Source/cmGlobalGenerator.h index f7b2e59..7bc389d 100644 --- a/Source/cmGlobalGenerator.h +++ b/Source/cmGlobalGenerator.h @@ -381,6 +381,8 @@ protected: void SetLanguageEnabledFlag(const std::string& l, cmMakefile* mf); void SetLanguageEnabledMaps(const std::string& l, cmMakefile* mf); void FillExtensionToLanguageMap(const std::string& l, cmMakefile* mf); + virtual bool CheckLanguages(std::vector<std::string> const& languages, + cmMakefile* mf) const; virtual void PrintCompilerAdvice(std::ostream& os, std::string const& lang, const char* envVar) const; diff --git a/Source/cmGlobalNinjaGenerator.cxx b/Source/cmGlobalNinjaGenerator.cxx index b913621..81690e7 100644 --- a/Source/cmGlobalNinjaGenerator.cxx +++ b/Source/cmGlobalNinjaGenerator.cxx @@ -14,6 +14,7 @@ #include "cmAlgorithms.h" #include "cmDocumentationEntry.h" +#include "cmFortranParser.h" #include "cmGeneratedFileStream.h" #include "cmGeneratorExpressionEvaluationFile.h" #include "cmGeneratorTarget.h" @@ -26,8 +27,12 @@ #include "cmTarget.h" #include "cmTargetDepend.h" #include "cmVersion.h" +#include "cm_auto_ptr.hxx" #include "cmake.h" +#include "cm_jsoncpp_reader.h" +#include "cm_jsoncpp_writer.h" + #include <algorithm> #include <ctype.h> #include <functional> @@ -473,6 +478,9 @@ cmGlobalNinjaGenerator::cmGlobalNinjaGenerator(cmake* cm) , UsingGCCOnWindows(false) , ComputingUnknownDependencies(false) , PolicyCMP0058(cmPolicies::WARN) + , NinjaSupportsConsolePool(false) + , NinjaSupportsImplicitOuts(false) + , NinjaSupportsDyndeps(0) { #ifdef _WIN32 cm->GetState()->SetWindowsShell(true); @@ -558,15 +566,81 @@ void cmGlobalNinjaGenerator::FindMakeProgram(cmMakefile* mf) cmSystemTools::RunSingleCommand(command, &version, CM_NULLPTR, CM_NULLPTR, CM_NULLPTR, cmSystemTools::OUTPUT_NONE); this->NinjaVersion = cmSystemTools::TrimWhitespace(version); + this->CheckNinjaFeatures(); } } +void cmGlobalNinjaGenerator::CheckNinjaFeatures() +{ + this->NinjaSupportsConsolePool = !cmSystemTools::VersionCompare( + cmSystemTools::OP_LESS, this->NinjaVersion.c_str(), + RequiredNinjaVersionForConsolePool().c_str()); + this->NinjaSupportsImplicitOuts = !cmSystemTools::VersionCompare( + cmSystemTools::OP_LESS, this->NinjaVersion.c_str(), + this->RequiredNinjaVersionForImplicitOuts().c_str()); + { + // Our ninja branch adds ".dyndep-#" to its version number, + // where '#' is a feature-specific version number. Extract it. + static std::string const k_DYNDEP_ = ".dyndep-"; + std::string::size_type pos = this->NinjaVersion.find(k_DYNDEP_); + if (pos != std::string::npos) { + const char* fv = this->NinjaVersion.c_str() + pos + k_DYNDEP_.size(); + cmSystemTools::StringToULong(fv, &this->NinjaSupportsDyndeps); + } + } +} + +bool cmGlobalNinjaGenerator::CheckLanguages( + std::vector<std::string> const& languages, cmMakefile* mf) const +{ + if (std::find(languages.begin(), languages.end(), "Fortran") != + languages.end()) { + return this->CheckFortran(mf); + } + return true; +} + +bool cmGlobalNinjaGenerator::CheckFortran(cmMakefile* mf) const +{ + if (this->NinjaSupportsDyndeps == 1) { + return true; + } + + std::ostringstream e; + if (this->NinjaSupportsDyndeps == 0) { + /* clang-format off */ + e << + "The Ninja generator does not support Fortran using Ninja version\n" + " " + this->NinjaVersion + "\n" + "due to lack of required features. " + "Kitware has implemented the required features but as of this version " + "of CMake they have not been integrated to upstream ninja. " + "Pending integration, Kitware maintains a branch at:\n" + " https://github.com/Kitware/ninja/tree/features-for-fortran#readme\n" + "with the required features. " + "One may build ninja from that branch to get support for Fortran." + ; + /* clang-format on */ + } else { + /* clang-format off */ + e << + "The Ninja generator in this version of CMake does not support Fortran " + "using Ninja version\n" + " " + this->NinjaVersion + "\n" + "because its 'dyndep' feature version is " << + this->NinjaSupportsDyndeps << ". " + "This version of CMake is aware only of 'dyndep' feature version 1." + ; + /* clang-format on */ + } + mf->IssueMessage(cmake::FATAL_ERROR, e.str()); + cmSystemTools::SetFatalErrorOccured(); + return false; +} + void cmGlobalNinjaGenerator::EnableLanguage( std::vector<std::string> const& langs, cmMakefile* mf, bool optional) { - if (std::find(langs.begin(), langs.end(), "Fortran") != langs.end()) { - cmSystemTools::Error("The Ninja generator does not support Fortran yet."); - } this->cmGlobalGenerator::EnableLanguage(langs, mf, optional); for (std::vector<std::string>::const_iterator l = langs.begin(); l != langs.end(); ++l) { @@ -1301,16 +1375,12 @@ std::string cmGlobalNinjaGenerator::ninjaCmd() const bool cmGlobalNinjaGenerator::SupportsConsolePool() const { - return !cmSystemTools::VersionCompare( - cmSystemTools::OP_LESS, this->NinjaVersion.c_str(), - RequiredNinjaVersionForConsolePool().c_str()); + return this->NinjaSupportsConsolePool; } bool cmGlobalNinjaGenerator::SupportsImplicitOuts() const { - return !cmSystemTools::VersionCompare( - cmSystemTools::OP_LESS, this->NinjaVersion.c_str(), - this->RequiredNinjaVersionForImplicitOuts().c_str()); + return this->NinjaSupportsImplicitOuts; } void cmGlobalNinjaGenerator::WriteTargetClean(std::ostream& os) @@ -1378,3 +1448,421 @@ void cmGlobalNinjaGenerator::StripNinjaOutputPathPrefixAsSuffix( EnsureTrailingSlash(path); cmStripSuffixIfExists(path, this->OutputPathPrefix); } + +/* + +We use the following approach to support Fortran. Each target already +has a <target>.dir/ directory used to hold intermediate files for CMake. +For each target, a FortranDependInfo.json file is generated by CMake with +information about include directories, module directories, and the locations +the per-target directories for target dependencies. + +Compilation of source files within a target is split into the following steps: + +1. Preprocess all sources, scan preprocessed output for module dependencies. + This step is done with independent build statements for each source, + and can therefore be done in parallel. + + rule Fortran_PREPROCESS + depfile = $DEP_FILE + command = gfortran -cpp $DEFINES $INCLUDES $FLAGS -E $in -o $out && + cmake -E cmake_ninja_depends \ + --tdi=FortranDependInfo.json --pp=$out --dep=$DEP_FILE \ + --obj=$OBJ_FILE --ddi=$DYNDEP_INTERMEDIATE_FILE + + build src.f90-pp.f90 | src.f90-pp.f90.ddi: Fortran_PREPROCESS src.f90 + OBJ_FILE = src.f90.o + DEP_FILE = src.f90-pp.f90.d + DYNDEP_INTERMEDIATE_FILE = src.f90-pp.f90.ddi + + The ``cmake -E cmake_ninja_depends`` tool reads the preprocessed output + and generates the ninja depfile for preprocessor dependencies. It also + generates a "ddi" file (in a format private to CMake) that lists the + object file that compilation will produce along with the module names + it provides and/or requires. The "ddi" file is an implicit output + because it should not appear in "$out" but is generated by the rule. + +2. Consolidate the per-source module dependencies saved in the "ddi" + files from all sources to produce a ninja "dyndep" file, ``Fortran.dd``. + + rule Fortran_DYNDEP + command = cmake -E cmake_ninja_dyndep \ + --tdi=FortranDependInfo.json --dd=$out $in + + build Fortran.dd: Fortran_DYNDEP src1.f90-pp.f90.ddi src2.f90-pp.f90.ddi + + The ``cmake -E cmake_ninja_dyndep`` tool reads the "ddi" files from all + sources in the target and the ``FortranModules.json`` files from targets + on which the target depends. It computes dependency edges on compilations + that require modules to those that provide the modules. This information + is placed in the ``Fortran.dd`` file for ninja to load later. It also + writes the expected location of modules provided by this target into + ``FortranModules.json`` for use by dependent targets. + +3. Compile all sources after loading dynamically discovered dependencies + of the compilation build statements from their ``dyndep`` bindings. + + rule Fortran_COMPILE + command = gfortran $INCLUDES $FLAGS -c $in -o $out + + build src1.f90.o: Fortran_COMPILE src1.f90-pp.f90 || Fortran.dd + dyndep = Fortran.dd + + The "dyndep" binding tells ninja to load dynamically discovered + dependency information from ``Fortran.dd``. This adds information + such as: + + build src1.f90.o | mod1.mod: dyndep + restat = 1 + + This tells ninja that ``mod1.mod`` is an implicit output of compiling + the object file ``src1.f90.o``. The ``restat`` binding tells it that + the timestamp of the output may not always change. Additionally: + + build src2.f90.o: dyndep | mod1.mod + + This tells ninja that ``mod1.mod`` is a dependency of compiling the + object file ``src2.f90.o``. This ensures that ``src1.f90.o`` and + ``mod1.mod`` will always be up to date before ``src2.f90.o`` is built + (because the latter consumes the module). +*/ + +int cmcmd_cmake_ninja_depends(std::vector<std::string>::const_iterator argBeg, + std::vector<std::string>::const_iterator argEnd) +{ + std::string arg_tdi; + std::string arg_pp; + std::string arg_dep; + std::string arg_obj; + std::string arg_ddi; + for (std::vector<std::string>::const_iterator a = argBeg; a != argEnd; ++a) { + std::string const& arg = *a; + if (cmHasLiteralPrefix(arg, "--tdi=")) { + arg_tdi = arg.substr(6); + } else if (cmHasLiteralPrefix(arg, "--pp=")) { + arg_pp = arg.substr(5); + } else if (cmHasLiteralPrefix(arg, "--dep=")) { + arg_dep = arg.substr(6); + } else if (cmHasLiteralPrefix(arg, "--obj=")) { + arg_obj = arg.substr(6); + } else if (cmHasLiteralPrefix(arg, "--ddi=")) { + arg_ddi = arg.substr(6); + } else { + cmSystemTools::Error("-E cmake_ninja_depends unknown argument: ", + arg.c_str()); + return 1; + } + } + if (arg_tdi.empty()) { + cmSystemTools::Error("-E cmake_ninja_depends requires value for --tdi="); + return 1; + } + if (arg_pp.empty()) { + cmSystemTools::Error("-E cmake_ninja_depends requires value for --pp="); + return 1; + } + if (arg_dep.empty()) { + cmSystemTools::Error("-E cmake_ninja_depends requires value for --dep="); + return 1; + } + if (arg_obj.empty()) { + cmSystemTools::Error("-E cmake_ninja_depends requires value for --obj="); + return 1; + } + if (arg_ddi.empty()) { + cmSystemTools::Error("-E cmake_ninja_depends requires value for --ddi="); + return 1; + } + + std::vector<std::string> includes; + { + Json::Value tdio; + Json::Value const& tdi = tdio; + { + cmsys::ifstream tdif(arg_tdi.c_str(), std::ios::in | std::ios::binary); + Json::Reader reader; + if (!reader.parse(tdif, tdio, false)) { + cmSystemTools::Error("-E cmake_ninja_depends failed to parse ", + arg_tdi.c_str(), + reader.getFormattedErrorMessages().c_str()); + return 1; + } + } + + Json::Value const& tdi_include_dirs = tdi["include-dirs"]; + if (tdi_include_dirs.isArray()) { + for (Json::Value::const_iterator i = tdi_include_dirs.begin(); + i != tdi_include_dirs.end(); ++i) { + includes.push_back(i->asString()); + } + } + } + + cmFortranSourceInfo info; + std::set<std::string> defines; + cmFortranParser parser(includes, defines, info); + if (!cmFortranParser_FilePush(&parser, arg_pp.c_str())) { + cmSystemTools::Error("-E cmake_ninja_depends failed to open ", + arg_pp.c_str()); + return 1; + } + if (cmFortran_yyparse(parser.Scanner) != 0) { + // Failed to parse the file. + return 1; + } + + { + cmGeneratedFileStream depfile(arg_dep.c_str()); + depfile << cmSystemTools::ConvertToUnixOutputPath(arg_pp) << ":"; + for (std::set<std::string>::iterator i = info.Includes.begin(); + i != info.Includes.end(); ++i) { + depfile << " \\\n " << cmSystemTools::ConvertToUnixOutputPath(*i); + } + depfile << "\n"; + } + + Json::Value ddi(Json::objectValue); + ddi["object"] = arg_obj; + + Json::Value& ddi_provides = ddi["provides"] = Json::arrayValue; + for (std::set<std::string>::iterator i = info.Provides.begin(); + i != info.Provides.end(); ++i) { + ddi_provides.append(*i); + } + Json::Value& ddi_requires = ddi["requires"] = Json::arrayValue; + for (std::set<std::string>::iterator i = info.Requires.begin(); + i != info.Requires.end(); ++i) { + // Require modules not provided in the same source. + if (!info.Provides.count(*i)) { + ddi_requires.append(*i); + } + } + + cmGeneratedFileStream ddif(arg_ddi.c_str()); + ddif << ddi; + if (!ddif) { + cmSystemTools::Error("-E cmake_ninja_depends failed to write ", + arg_ddi.c_str()); + return 1; + } + return 0; +} + +struct cmFortranObjectInfo +{ + std::string Object; + std::vector<std::string> Provides; + std::vector<std::string> Requires; +}; + +bool cmGlobalNinjaGenerator::WriteDyndepFile( + std::string const& dir_top_src, std::string const& dir_top_bld, + std::string const& dir_cur_src, std::string const& dir_cur_bld, + std::string const& arg_dd, std::vector<std::string> const& arg_ddis, + std::string const& module_dir, + std::vector<std::string> const& linked_target_dirs) +{ + // Setup path conversions. + { + cmState::Snapshot snapshot = + this->GetCMakeInstance()->GetCurrentSnapshot(); + snapshot.GetDirectory().SetCurrentSource(dir_cur_src); + snapshot.GetDirectory().SetCurrentBinary(dir_cur_bld); + snapshot.GetDirectory().SetRelativePathTopSource(dir_top_src.c_str()); + snapshot.GetDirectory().SetRelativePathTopBinary(dir_top_bld.c_str()); + CM_AUTO_PTR<cmMakefile> mfd(new cmMakefile(this, snapshot)); + CM_AUTO_PTR<cmLocalNinjaGenerator> lgd(static_cast<cmLocalNinjaGenerator*>( + this->CreateLocalGenerator(mfd.get()))); + this->Makefiles.push_back(mfd.release()); + this->LocalGenerators.push_back(lgd.release()); + } + + std::vector<cmFortranObjectInfo> objects; + for (std::vector<std::string>::const_iterator ddii = arg_ddis.begin(); + ddii != arg_ddis.end(); ++ddii) { + // Load the ddi file and compute the module file paths it provides. + Json::Value ddio; + Json::Value const& ddi = ddio; + cmsys::ifstream ddif(ddii->c_str(), std::ios::in | std::ios::binary); + Json::Reader reader; + if (!reader.parse(ddif, ddio, false)) { + cmSystemTools::Error("-E cmake_ninja_dyndep failed to parse ", + ddii->c_str(), + reader.getFormattedErrorMessages().c_str()); + return false; + } + + cmFortranObjectInfo info; + info.Object = ddi["object"].asString(); + Json::Value const& ddi_provides = ddi["provides"]; + if (ddi_provides.isArray()) { + for (Json::Value::const_iterator i = ddi_provides.begin(); + i != ddi_provides.end(); ++i) { + info.Provides.push_back(i->asString()); + } + } + Json::Value const& ddi_requires = ddi["requires"]; + if (ddi_requires.isArray()) { + for (Json::Value::const_iterator i = ddi_requires.begin(); + i != ddi_requires.end(); ++i) { + info.Requires.push_back(i->asString()); + } + } + objects.push_back(info); + } + + // Map from module name to module file path, if known. + std::map<std::string, std::string> mod_files; + + // Populate the module map with those provided by linked targets first. + for (std::vector<std::string>::const_iterator di = + linked_target_dirs.begin(); + di != linked_target_dirs.end(); ++di) { + std::string const ltmn = *di + "/FortranModules.json"; + Json::Value ltm; + cmsys::ifstream ltmf(ltmn.c_str(), std::ios::in | std::ios::binary); + Json::Reader reader; + if (ltmf && !reader.parse(ltmf, ltm, false)) { + cmSystemTools::Error("-E cmake_ninja_dyndep failed to parse ", + di->c_str(), + reader.getFormattedErrorMessages().c_str()); + return false; + } + if (ltm.isObject()) { + for (Json::Value::iterator i = ltm.begin(); i != ltm.end(); ++i) { + mod_files[i.key().asString()] = i->asString(); + } + } + } + + // Extend the module map with those provided by this target. + // We do this after loading the modules provided by linked targets + // in case we have one of the same name that must be preferred. + Json::Value tm = Json::objectValue; + for (std::vector<cmFortranObjectInfo>::iterator oi = objects.begin(); + oi != objects.end(); ++oi) { + for (std::vector<std::string>::iterator i = oi->Provides.begin(); + i != oi->Provides.end(); ++i) { + std::string const mod = module_dir + *i + ".mod"; + mod_files[*i] = mod; + tm[*i] = mod; + } + } + + cmGeneratedFileStream ddf(arg_dd.c_str()); + ddf << "ninja_dyndep_version = 1.0\n"; + + for (std::vector<cmFortranObjectInfo>::iterator oi = objects.begin(); + oi != objects.end(); ++oi) { + std::string const ddComment; + std::string const ddRule = "dyndep"; + cmNinjaDeps ddOutputs; + cmNinjaDeps ddImplicitOuts; + cmNinjaDeps ddExplicitDeps; + cmNinjaDeps ddImplicitDeps; + cmNinjaDeps ddOrderOnlyDeps; + cmNinjaVars ddVars; + + ddOutputs.push_back(oi->Object); + for (std::vector<std::string>::iterator i = oi->Provides.begin(); + i != oi->Provides.end(); ++i) { + ddImplicitOuts.push_back(this->ConvertToNinjaPath(mod_files[*i])); + } + for (std::vector<std::string>::iterator i = oi->Requires.begin(); + i != oi->Requires.end(); ++i) { + std::map<std::string, std::string>::iterator m = mod_files.find(*i); + if (m != mod_files.end()) { + ddImplicitDeps.push_back(this->ConvertToNinjaPath(m->second)); + } + } + if (!oi->Provides.empty()) { + ddVars["restat"] = "1"; + } + + this->WriteBuild(ddf, ddComment, ddRule, ddOutputs, ddImplicitOuts, + ddExplicitDeps, ddImplicitDeps, ddOrderOnlyDeps, ddVars); + } + + // Store the map of modules provided by this target in a file for + // use by dependents that reference this target in linked-target-dirs. + std::string const target_mods_file = + cmSystemTools::GetFilenamePath(arg_dd) + "/FortranModules.json"; + cmGeneratedFileStream tmf(target_mods_file.c_str()); + tmf << tm; + + return true; +} + +int cmcmd_cmake_ninja_dyndep(std::vector<std::string>::const_iterator argBeg, + std::vector<std::string>::const_iterator argEnd) +{ + std::string arg_dd; + std::string arg_tdi; + std::vector<std::string> arg_ddis; + for (std::vector<std::string>::const_iterator a = argBeg; a != argEnd; ++a) { + std::string const& arg = *a; + if (cmHasLiteralPrefix(arg, "--tdi=")) { + arg_tdi = arg.substr(6); + } else if (cmHasLiteralPrefix(arg, "--dd=")) { + arg_dd = arg.substr(5); + } else if (!cmHasLiteralPrefix(arg, "--") && + cmHasLiteralSuffix(arg, ".ddi")) { + arg_ddis.push_back(arg); + } else { + cmSystemTools::Error("-E cmake_ninja_dyndep unknown argument: ", + arg.c_str()); + return 1; + } + } + if (arg_tdi.empty()) { + cmSystemTools::Error("-E cmake_ninja_dyndep requires value for --tdi="); + return 1; + } + if (arg_dd.empty()) { + cmSystemTools::Error("-E cmake_ninja_dyndep requires value for --dd="); + return 1; + } + + Json::Value tdio; + Json::Value const& tdi = tdio; + { + cmsys::ifstream tdif(arg_tdi.c_str(), std::ios::in | std::ios::binary); + Json::Reader reader; + if (!reader.parse(tdif, tdio, false)) { + cmSystemTools::Error("-E cmake_ninja_dyndep failed to parse ", + arg_tdi.c_str(), + reader.getFormattedErrorMessages().c_str()); + return 1; + } + } + + std::string const dir_cur_bld = tdi["dir-cur-bld"].asString(); + std::string const dir_cur_src = tdi["dir-cur-src"].asString(); + std::string const dir_top_bld = tdi["dir-top-bld"].asString(); + std::string const dir_top_src = tdi["dir-top-src"].asString(); + std::string module_dir = tdi["module-dir"].asString(); + if (!module_dir.empty()) { + module_dir += "/"; + } + std::vector<std::string> linked_target_dirs; + Json::Value const& tdi_linked_target_dirs = tdi["linked-target-dirs"]; + if (tdi_linked_target_dirs.isArray()) { + for (Json::Value::const_iterator i = tdi_linked_target_dirs.begin(); + i != tdi_linked_target_dirs.end(); ++i) { + linked_target_dirs.push_back(i->asString()); + } + } + + cmake cm; + cm.SetHomeDirectory(dir_top_src); + cm.SetHomeOutputDirectory(dir_top_bld); + CM_AUTO_PTR<cmGlobalNinjaGenerator> ggd( + static_cast<cmGlobalNinjaGenerator*>(cm.CreateGlobalGenerator("Ninja"))); + if (!ggd.get() || + !ggd->WriteDyndepFile(dir_top_src, dir_top_bld, dir_cur_src, dir_cur_bld, + arg_dd, arg_ddis, module_dir, + linked_target_dirs)) { + return 1; + } + return 0; +} diff --git a/Source/cmGlobalNinjaGenerator.h b/Source/cmGlobalNinjaGenerator.h index 0201685..76430a0 100644 --- a/Source/cmGlobalNinjaGenerator.h +++ b/Source/cmGlobalNinjaGenerator.h @@ -347,6 +347,15 @@ public: bool HasOutputPathPrefix() const { return !this->OutputPathPrefix.empty(); } void StripNinjaOutputPathPrefixAsSuffix(std::string& path); + bool WriteDyndepFile(std::string const& dir_top_src, + std::string const& dir_top_bld, + std::string const& dir_cur_src, + std::string const& dir_cur_bld, + std::string const& arg_dd, + std::vector<std::string> const& arg_ddis, + std::string const& module_dir, + std::vector<std::string> const& linked_target_dirs); + protected: void Generate() CM_OVERRIDE; @@ -355,6 +364,10 @@ protected: private: std::string GetEditCacheCommand() const CM_OVERRIDE; void FindMakeProgram(cmMakefile* mf) CM_OVERRIDE; + void CheckNinjaFeatures(); + bool CheckLanguages(std::vector<std::string> const& languages, + cmMakefile* mf) const CM_OVERRIDE; + bool CheckFortran(cmMakefile* mf) const; void OpenBuildFileStream(); void CloseBuildFileStream(); @@ -439,6 +452,9 @@ private: std::string NinjaCommand; std::string NinjaVersion; + bool NinjaSupportsConsolePool; + bool NinjaSupportsImplicitOuts; + unsigned long NinjaSupportsDyndeps; private: void InitOutputPathPrefix(); diff --git a/Source/cmGlobalXCodeGenerator.cxx b/Source/cmGlobalXCodeGenerator.cxx index 997f46c..0d5de06 100644 --- a/Source/cmGlobalXCodeGenerator.cxx +++ b/Source/cmGlobalXCodeGenerator.cxx @@ -2996,6 +2996,15 @@ bool cmGlobalXCodeGenerator::CreateXCodeObjects( buildSettings->AddAttribute("GCC_VERSION", this->CreateString(this->GeneratorToolset)); } + if (this->GetLanguageEnabled("Swift")) { + std::string swiftVersion = "2.3"; + if (const char* vers = this->CurrentMakefile->GetDefinition( + "CMAKE_Swift_LANGUAGE_VERSION")) { + swiftVersion = vers; + } + buildSettings->AddAttribute("SWIFT_VERSION", + this->CreateString(swiftVersion)); + } std::string symroot = root->GetCurrentBinaryDirectory(); symroot += "/build"; diff --git a/Source/cmNinjaTargetGenerator.cxx b/Source/cmNinjaTargetGenerator.cxx index fb09bfe..f88eb7b 100644 --- a/Source/cmNinjaTargetGenerator.cxx +++ b/Source/cmNinjaTargetGenerator.cxx @@ -29,6 +29,8 @@ #include "cmSystemTools.h" #include "cmake.h" +#include "cm_jsoncpp_writer.h" + #include <algorithm> #include <iterator> #include <map> @@ -93,6 +95,31 @@ std::string cmNinjaTargetGenerator::LanguageCompilerRule( cmGlobalNinjaGenerator::EncodeRuleName(this->GeneratorTarget->GetName()); } +std::string cmNinjaTargetGenerator::LanguagePreprocessRule( + std::string const& lang) const +{ + return lang + "_PREPROCESS__" + + cmGlobalNinjaGenerator::EncodeRuleName(this->GeneratorTarget->GetName()); +} + +bool cmNinjaTargetGenerator::NeedExplicitPreprocessing( + std::string const& lang) const +{ + return lang == "Fortran"; +} + +std::string cmNinjaTargetGenerator::LanguageDyndepRule( + const std::string& lang) const +{ + return lang + "_DYNDEP__" + + cmGlobalNinjaGenerator::EncodeRuleName(this->GeneratorTarget->GetName()); +} + +bool cmNinjaTargetGenerator::NeedDyndep(std::string const& lang) const +{ + return lang == "Fortran"; +} + std::string cmNinjaTargetGenerator::OrderDependsTargetForTarget() { return "cmake_order_depends_target_" + this->GetTargetName(); @@ -229,6 +256,64 @@ std::string cmNinjaTargetGenerator::GetObjectFilePath( return path; } +std::string cmNinjaTargetGenerator::GetPreprocessedFilePath( + cmSourceFile const* source) const +{ + // Choose an extension to compile already-preprocessed source. + std::string ppExt = source->GetExtension(); + if (cmHasLiteralPrefix(ppExt, "F")) { + // Some Fortran compilers automatically enable preprocessing for + // upper-case extensions. Since the source is already preprocessed, + // use a lower-case extension. + ppExt = cmSystemTools::LowerCase(ppExt); + } + if (ppExt == "fpp") { + // Some Fortran compilers automatically enable preprocessing for + // the ".fpp" extension. Since the source is already preprocessed, + // use the ".f" extension. + ppExt = "f"; + } + + // Take the object file name and replace the extension. + std::string const& objName = this->GeneratorTarget->GetObjectName(source); + std::string const& objExt = + this->GetGlobalGenerator()->GetLanguageOutputExtension(*source); + assert(objName.size() >= objExt.size()); + std::string const ppName = + objName.substr(0, objName.size() - objExt.size()) + "-pp." + ppExt; + + std::string path = this->LocalGenerator->GetHomeRelativeOutputPath(); + if (!path.empty()) + path += "/"; + path += this->LocalGenerator->GetTargetDirectory(this->GeneratorTarget); + path += "/"; + path += ppName; + return path; +} + +std::string cmNinjaTargetGenerator::GetDyndepFilePath( + std::string const& lang) const +{ + std::string path = this->LocalGenerator->GetHomeRelativeOutputPath(); + if (!path.empty()) + path += "/"; + path += this->LocalGenerator->GetTargetDirectory(this->GeneratorTarget); + path += "/"; + path += lang; + path += ".dd"; + return path; +} + +std::string cmNinjaTargetGenerator::GetTargetDependInfoPath( + std::string const& lang) const +{ + std::string path = this->Makefile->GetCurrentBinaryDirectory(); + path += "/"; + path += this->LocalGenerator->GetTargetDirectory(this->GeneratorTarget); + path += "/" + lang + "DependInfo.json"; + return path; +} + std::string cmNinjaTargetGenerator::GetTargetOutputDir() const { std::string dir = this->GeneratorTarget->GetDirectory(this->GetConfigName()); @@ -311,6 +396,10 @@ void cmNinjaTargetGenerator::WriteCompileRule(const std::string& lang) vars.ObjectDir = "$OBJECT_DIR"; vars.ObjectFileDir = "$OBJECT_FILE_DIR"; + // For some cases we do an explicit preprocessor invocation. + bool const explicitPP = this->NeedExplicitPreprocessing(lang); + bool const needDyndep = this->NeedDyndep(lang); + cmMakefile* mf = this->GetMakefile(); std::string flags = "$FLAGS"; @@ -331,7 +420,9 @@ void cmNinjaTargetGenerator::WriteCompileRule(const std::string& lang) std::string deptype; std::string depfile; std::string cldeps; - if (this->NeedDepTypeMSVC(lang)) { + if (explicitPP) { + // The explicit preprocessing step will handle dependency scanning. + } else if (this->NeedDepTypeMSVC(lang)) { deptype = "msvc"; depfile = ""; flags += " /showIncludes"; @@ -371,6 +462,109 @@ void cmNinjaTargetGenerator::WriteCompileRule(const std::string& lang) vars.Flags = flags.c_str(); vars.DependencyFile = depfile.c_str(); + std::string const tdi = this->GetLocalGenerator()->ConvertToOutputFormat( + ConvertToNinjaPath(this->GetTargetDependInfoPath(lang)), + cmLocalGenerator::SHELL); + + if (explicitPP) { + // Lookup the explicit preprocessing rule. + std::string const ppVar = "CMAKE_" + lang + "_PREPROCESS_SOURCE"; + std::string const ppCmd = + this->GetMakefile()->GetRequiredDefinition(ppVar); + + // Explicit preprocessing always uses a depfile. + std::string const ppDeptype = ""; // no deps= for multiple outputs + std::string const ppDepfile = "$DEP_FILE"; + + cmLocalGenerator::RuleVariables ppVars; + ppVars.RuleLauncher = vars.RuleLauncher; + ppVars.CMTarget = vars.CMTarget; + ppVars.Language = vars.Language; + ppVars.Object = "$out"; // for RULE_LAUNCH_COMPILE + ppVars.PreprocessedSource = "$out"; + ppVars.DependencyFile = ppDepfile.c_str(); + + // Preprocessing uses the original source, + // compilation uses preprocessed output. + ppVars.Source = vars.Source; + vars.Source = "$in"; + + // Preprocessing and compilation use the same flags. + ppVars.Flags = vars.Flags; + + // Move preprocessor definitions to the preprocessor rule. + ppVars.Defines = vars.Defines; + vars.Defines = ""; + + // Copy include directories to the preprocessor rule. The Fortran + // compilation rule still needs them for the INCLUDE directive. + ppVars.Includes = vars.Includes; + + // Rule for preprocessing source file. + std::vector<std::string> ppCmds; + cmSystemTools::ExpandListArgument(ppCmd, ppCmds); + + for (std::vector<std::string>::iterator i = ppCmds.begin(); + i != ppCmds.end(); ++i) { + this->GetLocalGenerator()->ExpandRuleVariables(*i, ppVars); + } + + // Run CMake dependency scanner on preprocessed output. + std::string const cmake = this->GetLocalGenerator()->ConvertToOutputFormat( + cmSystemTools::GetCMakeCommand(), cmLocalGenerator::SHELL); + ppCmds.push_back( + cmake + " -E cmake_ninja_depends" + " --tdi=" + + tdi + " --pp=$out" + " --dep=$DEP_FILE" + + (needDyndep ? " --obj=$OBJ_FILE --ddi=$DYNDEP_INTERMEDIATE_FILE" : "")); + + std::string const ppCmdLine = + this->GetLocalGenerator()->BuildCommandLine(ppCmds); + + // Write the rule for preprocessing file of the given language. + std::ostringstream ppComment; + ppComment << "Rule for preprocessing " << lang << " files."; + std::ostringstream ppDesc; + ppDesc << "Building " << lang << " preprocessed $out"; + this->GetGlobalGenerator()->AddRule(this->LanguagePreprocessRule(lang), + ppCmdLine, ppDesc.str(), + ppComment.str(), ppDepfile, ppDeptype, + /*rspfile*/ "", + /*rspcontent*/ "", + /*restat*/ "", + /*generator*/ false); + } + + if (needDyndep) { + // Write the rule for ninja dyndep file generation. + std::vector<std::string> ddCmds; + + // Run CMake dependency scanner on preprocessed output. + std::string const cmake = this->GetLocalGenerator()->ConvertToOutputFormat( + cmSystemTools::GetCMakeCommand(), cmLocalGenerator::SHELL); + ddCmds.push_back(cmake + " -E cmake_ninja_dyndep" + " --tdi=" + + tdi + " --dd=$out" + " $in"); + + std::string const ddCmdLine = + this->GetLocalGenerator()->BuildCommandLine(ddCmds); + + std::ostringstream ddComment; + ddComment << "Rule to generate ninja dyndep files for " << lang << "."; + std::ostringstream ddDesc; + ddDesc << "Generating " << lang << " dyndep file $out"; + this->GetGlobalGenerator()->AddRule( + this->LanguageDyndepRule(lang), ddCmdLine, ddDesc.str(), ddComment.str(), + /*depfile*/ "", + /*deps*/ "", + /*rspfile*/ "", + /*rspcontent*/ "", + /*restat*/ "", + /*generator*/ false); + } + // Rule for compiling object file. const std::string cmdVar = std::string("CMAKE_") + lang + "_COMPILE_OBJECT"; std::string compileCmd = mf->GetRequiredDefinition(cmdVar); @@ -519,6 +713,25 @@ void cmNinjaTargetGenerator::WriteObjectBuildStatements() this->WriteObjectBuildStatement(*si, !orderOnlyDeps.empty()); } + if (!this->DDIFiles.empty()) { + std::string const ddComment; + std::string const ddRule = this->LanguageDyndepRule("Fortran"); + cmNinjaDeps ddOutputs; + cmNinjaDeps ddImplicitOuts; + cmNinjaDeps const& ddExplicitDeps = this->DDIFiles; + cmNinjaDeps ddImplicitDeps; + cmNinjaDeps ddOrderOnlyDeps; + cmNinjaVars ddVars; + + this->WriteTargetDependInfo("Fortran"); + + ddOutputs.push_back(this->GetDyndepFilePath("Fortran")); + + this->GetGlobalGenerator()->WriteBuild( + this->GetBuildFileStream(), ddComment, ddRule, ddOutputs, ddImplicitOuts, + ddExplicitDeps, ddImplicitDeps, ddOrderOnlyDeps, ddVars); + } + this->GetBuildFileStream() << "\n"; } @@ -589,6 +802,89 @@ void cmNinjaTargetGenerator::WriteObjectBuildStatement( orderOnlyDeps); } + // For some cases we need to generate a ninja dyndep file. + bool const needDyndep = this->NeedDyndep(language); + + // For some cases we do an explicit preprocessor invocation. + bool const explicitPP = this->NeedExplicitPreprocessing(language); + if (explicitPP) { + std::string const ppComment; + std::string const ppRule = this->LanguagePreprocessRule(language); + cmNinjaDeps ppOutputs; + cmNinjaDeps ppImplicitOuts; + cmNinjaDeps ppExplicitDeps; + cmNinjaDeps ppImplicitDeps; + cmNinjaDeps ppOrderOnlyDeps; + cmNinjaVars ppVars; + + std::string const ppFileName = + this->ConvertToNinjaPath(this->GetPreprocessedFilePath(source)); + ppOutputs.push_back(ppFileName); + + // Move compilation dependencies to the preprocessing build statement. + std::swap(ppExplicitDeps, explicitDeps); + std::swap(ppImplicitDeps, implicitDeps); + std::swap(ppOrderOnlyDeps, orderOnlyDeps); + std::swap(ppVars["IN_ABS"], vars["IN_ABS"]); + + // The actual compilation will now use the preprocessed source. + explicitDeps.push_back(ppFileName); + + // Preprocessing and compilation use the same flags. + ppVars["FLAGS"] = vars["FLAGS"]; + + // Move preprocessor definitions to the preprocessor build statement. + std::swap(ppVars["DEFINES"], vars["DEFINES"]); + + // Copy include directories to the preprocessor build statement. The + // Fortran compilation build statement still needs them for the INCLUDE + // directive. + ppVars["INCLUDES"] = vars["INCLUDES"]; + + // Prepend source file's original directory as an include directory + // so e.g. Fortran INCLUDE statements can look for files in it. + std::vector<std::string> sourceDirectory; + sourceDirectory.push_back( + cmSystemTools::GetParentDirectory(source->GetFullPath())); + + std::string sourceDirectoryFlag = this->LocalGenerator->GetIncludeFlags( + sourceDirectory, this->GeneratorTarget, language, false, false, + this->GetConfigName()); + + vars["INCLUDES"] = sourceDirectoryFlag + " " + vars["INCLUDES"]; + + // Explicit preprocessing always uses a depfile. + ppVars["DEP_FILE"] = + cmGlobalNinjaGenerator::EncodeDepfileSpace(ppFileName + ".d"); + // The actual compilation does not need a depfile because it + // depends on the already-preprocessed source. + vars.erase("DEP_FILE"); + + if (needDyndep) { + // Tell dependency scanner the object file that will result from + // compiling the preprocessed source. + ppVars["OBJ_FILE"] = objectFileName; + + // Tell dependency scanner where to store dyndep intermediate results. + std::string const ddiFile = ppFileName + ".ddi"; + ppVars["DYNDEP_INTERMEDIATE_FILE"] = ddiFile; + ppImplicitOuts.push_back(ddiFile); + this->DDIFiles.push_back(ddiFile); + } + + this->addPoolNinjaVariable("JOB_POOL_COMPILE", this->GetGeneratorTarget(), + ppVars); + + this->GetGlobalGenerator()->WriteBuild( + this->GetBuildFileStream(), ppComment, ppRule, ppOutputs, ppImplicitOuts, + ppExplicitDeps, ppImplicitDeps, ppOrderOnlyDeps, ppVars); + } + if (needDyndep) { + std::string const dyndep = this->GetDyndepFilePath(language); + orderOnlyDeps.push_back(dyndep); + vars["dyndep"] = dyndep; + } + EnsureParentDirectoryExists(objectFileName); vars["OBJECT_DIR"] = this->GetLocalGenerator()->ConvertToOutputFormat( @@ -622,6 +918,49 @@ void cmNinjaTargetGenerator::WriteObjectBuildStatement( } } +void cmNinjaTargetGenerator::WriteTargetDependInfo(std::string const& lang) +{ + Json::Value tdi(Json::objectValue); + tdi["language"] = lang; + tdi["compiler-id"] = + this->Makefile->GetSafeDefinition("CMAKE_" + lang + "_COMPILER_ID"); + + if (lang == "Fortran") { + std::string mod_dir = this->GeneratorTarget->GetFortranModuleDirectory( + this->Makefile->GetHomeOutputDirectory()); + if (mod_dir.empty()) { + mod_dir = this->Makefile->GetCurrentBinaryDirectory(); + } + tdi["module-dir"] = mod_dir; + } + + tdi["dir-cur-bld"] = this->Makefile->GetCurrentBinaryDirectory(); + tdi["dir-cur-src"] = this->Makefile->GetCurrentSourceDirectory(); + tdi["dir-top-bld"] = this->Makefile->GetHomeOutputDirectory(); + tdi["dir-top-src"] = this->Makefile->GetHomeDirectory(); + + Json::Value& tdi_include_dirs = tdi["include-dirs"] = Json::arrayValue; + std::vector<std::string> includes; + this->LocalGenerator->GetIncludeDirectories(includes, this->GeneratorTarget, + lang, this->GetConfigName()); + for (std::vector<std::string>::iterator i = includes.begin(); + i != includes.end(); ++i) { + tdi_include_dirs.append(*i); + } + + Json::Value& tdi_linked_target_dirs = tdi["linked-target-dirs"] = + Json::arrayValue; + std::vector<std::string> linked = this->GetLinkedTargetDirectories(); + for (std::vector<std::string>::iterator i = linked.begin(); + i != linked.end(); ++i) { + tdi_linked_target_dirs.append(*i); + } + + std::string const tdin = this->GetTargetDependInfoPath(lang); + cmGeneratedFileStream tdif(tdin.c_str()); + tdif << tdi; +} + void cmNinjaTargetGenerator::ExportObjectCompileCommand( std::string const& language, std::string const& sourceFileName, std::string const& objectDir, std::string const& objectFileName, diff --git a/Source/cmNinjaTargetGenerator.h b/Source/cmNinjaTargetGenerator.h index 2b26788..f5e6511 100644 --- a/Source/cmNinjaTargetGenerator.h +++ b/Source/cmNinjaTargetGenerator.h @@ -70,6 +70,10 @@ protected: cmMakefile* GetMakefile() const { return this->Makefile; } std::string LanguageCompilerRule(const std::string& lang) const; + std::string LanguagePreprocessRule(std::string const& lang) const; + bool NeedExplicitPreprocessing(std::string const& lang) const; + std::string LanguageDyndepRule(std::string const& lang) const; + bool NeedDyndep(std::string const& lang) const; std::string OrderDependsTargetForTarget(); @@ -107,6 +111,15 @@ protected: /// @return the object file path for the given @a source. std::string GetObjectFilePath(cmSourceFile const* source) const; + /// @return the preprocessed source file path for the given @a source. + std::string GetPreprocessedFilePath(cmSourceFile const* source) const; + + /// @return the dyndep file path for this target. + std::string GetDyndepFilePath(std::string const& lang) const; + + /// @return the target dependency scanner info file path + std::string GetTargetDependInfoPath(std::string const& lang) const; + /// @return the file path where the target named @a name is generated. std::string GetTargetFilePath(const std::string& name) const; @@ -118,6 +131,7 @@ protected: void WriteObjectBuildStatements(); void WriteObjectBuildStatement(cmSourceFile const* source, bool writeOrderDependsTargetForTarget); + void WriteTargetDependInfo(std::string const& lang); void ExportObjectCompileCommand( std::string const& language, std::string const& sourceFileName, @@ -161,6 +175,7 @@ private: cmLocalNinjaGenerator* LocalGenerator; /// List of object files for this target. cmNinjaDeps Objects; + cmNinjaDeps DDIFiles; // TODO: Make per-language. std::vector<cmCustomCommand const*> CustomCommands; cmNinjaDeps ExtraFiles; }; diff --git a/Source/cmServer.cxx b/Source/cmServer.cxx index 56cd7ba..46c1946 100644 --- a/Source/cmServer.cxx +++ b/Source/cmServer.cxx @@ -14,6 +14,7 @@ #include "cmServer.h" #include "cmServerConnection.h" +#include "cmServerDictionary.h" #include "cmServerProtocol.h" #include "cmSystemTools.h" #include "cmVersionMacros.h" @@ -28,19 +29,6 @@ #include <iostream> #include <memory> -static const std::string kTYPE_KEY = "type"; -static const std::string kCOOKIE_KEY = "cookie"; -static const std::string kREPLY_TO_KEY = "inReplyTo"; -static const std::string kERROR_MESSAGE_KEY = "errorMessage"; - -static const std::string kERROR_TYPE = "error"; -static const std::string kREPLY_TYPE = "reply"; -static const std::string kPROGRESS_TYPE = "progress"; -static const std::string kMESSAGE_TYPE = "message"; - -static const std::string kSTART_MAGIC = "[== CMake Server ==["; -static const std::string kEND_MAGIC = "]== CMake Server ==]"; - class cmServer::DebugInfo { public: @@ -135,8 +123,9 @@ void cmServer::RegisterProtocol(cmServerProtocol* protocol) [version](cmServerProtocol* p) { return p->ProtocolVersion() == version; }); - if (it == this->SupportedProtocols.end()) + if (it == this->SupportedProtocols.end()) { this->SupportedProtocols.push_back(protocol); + } } void cmServer::PrintHello() const @@ -144,16 +133,16 @@ void cmServer::PrintHello() const Json::Value hello = Json::objectValue; hello[kTYPE_KEY] = "hello"; - Json::Value& protocolVersions = hello["supportedProtocolVersions"] = + Json::Value& protocolVersions = hello[kSUPPORTED_PROTOCOL_VERSIONS] = Json::arrayValue; for (auto const& proto : this->SupportedProtocols) { auto version = proto->ProtocolVersion(); Json::Value tmp = Json::objectValue; - tmp["major"] = version.first; - tmp["minor"] = version.second; + tmp[kMAJOR_KEY] = version.first; + tmp[kMINOR_KEY] = version.second; if (proto->IsExperimental()) { - tmp["experimental"] = true; + tmp[kIS_EXPERIMENTAL_KEY] = true; } protocolVersions.append(tmp); } @@ -193,31 +182,44 @@ void cmServer::reportMessage(const char* msg, const char* title, cmServerResponse cmServer::SetProtocolVersion(const cmServerRequest& request) { - if (request.Type != "handshake") - return request.ReportError("Waiting for type \"handshake\"."); + if (request.Type != kHANDSHAKE_TYPE) { + return request.ReportError("Waiting for type \"" + kHANDSHAKE_TYPE + + "\"."); + } - Json::Value requestedProtocolVersion = request.Data["protocolVersion"]; - if (requestedProtocolVersion.isNull()) - return request.ReportError( - "\"protocolVersion\" is required for \"handshake\"."); + Json::Value requestedProtocolVersion = request.Data[kPROTOCOL_VERSION_KEY]; + if (requestedProtocolVersion.isNull()) { + return request.ReportError("\"" + kPROTOCOL_VERSION_KEY + + "\" is required for \"" + kHANDSHAKE_TYPE + + "\"."); + } - if (!requestedProtocolVersion.isObject()) - return request.ReportError("\"protocolVersion\" must be a JSON object."); + if (!requestedProtocolVersion.isObject()) { + return request.ReportError("\"" + kPROTOCOL_VERSION_KEY + + "\" must be a JSON object."); + } - Json::Value majorValue = requestedProtocolVersion["major"]; - if (!majorValue.isInt()) - return request.ReportError("\"major\" must be set and an integer."); + Json::Value majorValue = requestedProtocolVersion[kMAJOR_KEY]; + if (!majorValue.isInt()) { + return request.ReportError("\"" + kMAJOR_KEY + + "\" must be set and an integer."); + } - Json::Value minorValue = requestedProtocolVersion["minor"]; - if (!minorValue.isNull() && !minorValue.isInt()) - return request.ReportError("\"minor\" must be unset or an integer."); + Json::Value minorValue = requestedProtocolVersion[kMINOR_KEY]; + if (!minorValue.isNull() && !minorValue.isInt()) { + return request.ReportError("\"" + kMINOR_KEY + + "\" must be unset or an integer."); + } const int major = majorValue.asInt(); const int minor = minorValue.isNull() ? -1 : minorValue.asInt(); - if (major < 0) - return request.ReportError("\"major\" must be >= 0."); - if (!minorValue.isNull() && minor < 0) - return request.ReportError("\"minor\" must be >= 0 when set."); + if (major < 0) { + return request.ReportError("\"" + kMAJOR_KEY + "\" must be >= 0."); + } + if (!minorValue.isNull() && minor < 0) { + return request.ReportError("\"" + kMINOR_KEY + + "\" must be >= 0 when set."); + } this->Protocol = this->FindMatchingProtocol(this->SupportedProtocols, major, minor); @@ -226,7 +228,7 @@ cmServerResponse cmServer::SetProtocolVersion(const cmServerRequest& request) } std::string errorMessage; - if (!this->Protocol->Activate(request, &errorMessage)) { + if (!this->Protocol->Activate(this, request, &errorMessage)) { this->Protocol = CM_NULLPTR; return request.ReportError("Failed to activate protocol version: " + errorMessage); @@ -290,12 +292,15 @@ cmServerProtocol* cmServer::FindMatchingProtocol( cmServerProtocol* bestMatch = nullptr; for (auto protocol : protocols) { auto version = protocol->ProtocolVersion(); - if (major != version.first) + if (major != version.first) { continue; - if (minor == version.second) + } + if (minor == version.second) { return protocol; - if (!bestMatch || bestMatch->ProtocolVersion().second < version.second) + } + if (!bestMatch || bestMatch->ProtocolVersion().second < version.second) { bestMatch = protocol; + } } return minor < 0 ? bestMatch : nullptr; } @@ -311,10 +316,10 @@ void cmServer::WriteProgress(const cmServerRequest& request, int min, obj[kTYPE_KEY] = kPROGRESS_TYPE; obj[kREPLY_TO_KEY] = request.Type; obj[kCOOKIE_KEY] = request.Cookie; - obj["progressMessage"] = message; - obj["progressMinimum"] = min; - obj["progressMaximum"] = max; - obj["progressCurrent"] = current; + obj[kPROGRESS_MESSAGE_KEY] = message; + obj[kPROGRESS_MINIMUM_KEY] = min; + obj[kPROGRESS_MAXIMUM_KEY] = max; + obj[kPROGRESS_CURRENT_KEY] = current; this->WriteJsonObject(obj, nullptr); } @@ -323,16 +328,17 @@ void cmServer::WriteMessage(const cmServerRequest& request, const std::string& message, const std::string& title) const { - if (message.empty()) + if (message.empty()) { return; + } Json::Value obj = Json::objectValue; obj[kTYPE_KEY] = kMESSAGE_TYPE; obj[kREPLY_TO_KEY] = request.Type; obj[kCOOKIE_KEY] = request.Cookie; - obj["message"] = message; + obj[kMESSAGE_KEY] = message; if (!title.empty()) { - obj["title"] = title; + obj[kTITLE_KEY] = title; } WriteJsonObject(obj, nullptr); @@ -349,6 +355,19 @@ void cmServer::WriteParseError(const std::string& message) const this->WriteJsonObject(obj, nullptr); } +void cmServer::WriteSignal(const std::string& name, + const Json::Value& data) const +{ + assert(data.isObject()); + Json::Value obj = data; + obj[kTYPE_KEY] = kSIGNAL_TYPE; + obj[kREPLY_TO_KEY] = ""; + obj[kCOOKIE_KEY] = ""; + obj[kNAME_KEY] = name; + + WriteJsonObject(obj, nullptr); +} + void cmServer::WriteResponse(const cmServerResponse& response, const DebugInfo* debug) const { diff --git a/Source/cmServer.h b/Source/cmServer.h index dde5333..849e5c5 100644 --- a/Source/cmServer.h +++ b/Source/cmServer.h @@ -63,6 +63,7 @@ private: void WriteResponse(const cmServerResponse& response, const DebugInfo* debug) const; void WriteParseError(const std::string& message) const; + void WriteSignal(const std::string& name, const Json::Value& obj) const; void WriteJsonObject(Json::Value const& jsonValue, const DebugInfo* debug) const; @@ -95,6 +96,7 @@ private: mutable bool Writing = false; - friend class cmServerRequest; friend class cmServerConnection; + friend class cmServerProtocol; + friend class cmServerRequest; }; diff --git a/Source/cmServerConnection.cxx b/Source/cmServerConnection.cxx index 398e250..0b3db9f 100644 --- a/Source/cmServerConnection.cxx +++ b/Source/cmServerConnection.cxx @@ -13,15 +13,14 @@ #include "cmServerConnection.h" +#include "cmServerDictionary.h" + #include <cmServer.h> #include <assert.h> namespace { -static const std::string kSTART_MAGIC = "[== CMake Server ==["; -static const std::string kEND_MAGIC = "]== CMake Server ==]"; - struct write_req_t { uv_write_t req; @@ -147,8 +146,9 @@ void cmServerConnection::ReadData(const std::string& data) } std::string line = this->RawReadBuffer.substr(0, needle); const auto ls = line.size(); - if (ls > 1 && line.at(ls - 1) == '\r') + if (ls > 1 && line.at(ls - 1) == '\r') { line.erase(ls - 1, 1); + } this->RawReadBuffer.erase(this->RawReadBuffer.begin(), this->RawReadBuffer.begin() + static_cast<long>(needle) + 1); diff --git a/Source/cmServerDictionary.h b/Source/cmServerDictionary.h new file mode 100644 index 0000000..fc1b44d --- /dev/null +++ b/Source/cmServerDictionary.h @@ -0,0 +1,62 @@ +/*============================================================================ + CMake - Cross Platform Makefile Generator + Copyright 2016 Tobias Hunger <tobias.hunger@qt.io> + + 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. +============================================================================*/ + +#pragma once + +#include <string> + +// Vocabulary: + +static const std::string kCOMPUTE_TYPE = "compute"; +static const std::string kCONFIGURE_TYPE = "configure"; +static const std::string kERROR_TYPE = "error"; +static const std::string kGLOBAL_SETTINGS_TYPE = "globalSettings"; +static const std::string kHANDSHAKE_TYPE = "handshake"; +static const std::string kMESSAGE_TYPE = "message"; +static const std::string kPROGRESS_TYPE = "progress"; +static const std::string kREPLY_TYPE = "reply"; +static const std::string kSET_GLOBAL_SETTINGS_TYPE = "setGlobalSettings"; +static const std::string kSIGNAL_TYPE = "signal"; + +static const std::string kBUILD_DIRECTORY_KEY = "buildDirectory"; +static const std::string kCACHE_ARGUMENTS_KEY = "cacheArguments"; +static const std::string kCAPABILITIES_KEY = "capabilities"; +static const std::string kCHECK_SYSTEM_VARS_KEY = "checkSystemVars"; +static const std::string kCOOKIE_KEY = "cookie"; +static const std::string kDEBUG_OUTPUT_KEY = "debugOutput"; +static const std::string kERROR_MESSAGE_KEY = "errorMessage"; +static const std::string kEXTRA_GENERATOR_KEY = "extraGenerator"; +static const std::string kGENERATOR_KEY = "generator"; +static const std::string kIS_EXPERIMENTAL_KEY = "isExperimental"; +static const std::string kMAJOR_KEY = "major"; +static const std::string kMESSAGE_KEY = "message"; +static const std::string kMINOR_KEY = "minor"; +static const std::string kNAME_KEY = "name"; +static const std::string kPROGRESS_CURRENT_KEY = "progressCurrent"; +static const std::string kPROGRESS_MAXIMUM_KEY = "progressMaximum"; +static const std::string kPROGRESS_MESSAGE_KEY = "progressMessage"; +static const std::string kPROGRESS_MINIMUM_KEY = "progressMinimum"; +static const std::string kPROTOCOL_VERSION_KEY = "protocolVersion"; +static const std::string kREPLY_TO_KEY = "inReplyTo"; +static const std::string kSOURCE_DIRECTORY_KEY = "sourceDirectory"; +static const std::string kSUPPORTED_PROTOCOL_VERSIONS = + "supportedProtocolVersions"; +static const std::string kTITLE_KEY = "title"; +static const std::string kTRACE_EXPAND_KEY = "traceExpand"; +static const std::string kTRACE_KEY = "trace"; +static const std::string kTYPE_KEY = "type"; +static const std::string kWARN_UNINITIALIZED_KEY = "warnUninitialized"; +static const std::string kWARN_UNUSED_CLI_KEY = "warnUnusedCli"; +static const std::string kWARN_UNUSED_KEY = "warnUnused"; + +static const std::string kSTART_MAGIC = "[== CMake Server ==["; +static const std::string kEND_MAGIC = "]== CMake Server ==]"; diff --git a/Source/cmServerProtocol.cxx b/Source/cmServerProtocol.cxx index 26942d3..134edf3 100644 --- a/Source/cmServerProtocol.cxx +++ b/Source/cmServerProtocol.cxx @@ -13,24 +13,19 @@ #include "cmServerProtocol.h" #include "cmExternalMakefileProjectGenerator.h" +#include "cmGlobalGenerator.h" #include "cmServer.h" +#include "cmServerDictionary.h" #include "cmSystemTools.h" #include "cmake.h" +#include "cmServerDictionary.h" + #if defined(CMAKE_BUILD_WITH_CMAKE) #include "cm_jsoncpp_reader.h" #include "cm_jsoncpp_value.h" #endif -// Vocabulary: - -static const std::string kBUILD_DIRECTORY_KEY = "buildDirectory"; -static const std::string kCOOKIE_KEY = "cookie"; -static const std::string kEXTRA_GENERATOR_KEY = "extraGenerator"; -static const std::string kGENERATOR_KEY = "generator"; -static const std::string kSOURCE_DIRECTORY_KEY = "sourceDirectory"; -static const std::string kTYPE_KEY = "type"; - cmServerRequest::cmServerRequest(cmServer* server, const std::string& t, const std::string& c, const Json::Value& d) : Type(t) @@ -103,10 +98,10 @@ bool cmServerResponse::IsError() const std::string cmServerResponse::ErrorMessage() const { - if (this->m_Payload == PAYLOAD_ERROR) + if (this->m_Payload == PAYLOAD_ERROR) { return this->m_ErrorMessage; - else - return std::string(); + } + return std::string(); } Json::Value cmServerResponse::Data() const @@ -115,16 +110,28 @@ Json::Value cmServerResponse::Data() const return this->m_Data; } -bool cmServerProtocol::Activate(const cmServerRequest& request, +bool cmServerProtocol::Activate(cmServer* server, + const cmServerRequest& request, std::string* errorMessage) { + assert(server); + this->m_Server = server; this->m_CMakeInstance = std::make_unique<cmake>(); const bool result = this->DoActivate(request, errorMessage); - if (!result) + if (!result) { this->m_CMakeInstance = CM_NULLPTR; + } return result; } +void cmServerProtocol::SendSignal(const std::string& name, + const Json::Value& data) const +{ + if (this->m_Server) { + this->m_Server->WriteSignal(name, data); + } +} + cmake* cmServerProtocol::CMakeInstance() const { return this->m_CMakeInstance.get(); @@ -151,17 +158,19 @@ bool cmServerProtocol1_0::DoActivate(const cmServerRequest& request, std::string extraGenerator = request.Data[kEXTRA_GENERATOR_KEY].asString(); if (buildDirectory.empty()) { - if (errorMessage) + if (errorMessage) { *errorMessage = std::string("\"") + kBUILD_DIRECTORY_KEY + "\" is missing."; + } return false; } cmake* cm = CMakeInstance(); if (cmSystemTools::PathExists(buildDirectory)) { if (!cmSystemTools::FileIsDirectory(buildDirectory)) { - if (errorMessage) + if (errorMessage) { *errorMessage = std::string("\"") + kBUILD_DIRECTORY_KEY + "\" exists but is not a directory."; + } return false; } @@ -173,18 +182,20 @@ bool cmServerProtocol1_0::DoActivate(const cmServerRequest& request, const std::string cachedGenerator = std::string(state->GetCacheEntryValue("CMAKE_GENERATOR")); if (cachedGenerator.empty() && generator.empty()) { - if (errorMessage) + if (errorMessage) { *errorMessage = std::string("\"") + kGENERATOR_KEY + "\" is required but unset."; + } return false; } if (generator.empty()) { generator = cachedGenerator; } if (generator != cachedGenerator) { - if (errorMessage) + if (errorMessage) { *errorMessage = std::string("\"") + kGENERATOR_KEY + "\" set but incompatible with configured generator."; + } return false; } @@ -193,9 +204,10 @@ bool cmServerProtocol1_0::DoActivate(const cmServerRequest& request, std::string(state->GetCacheEntryValue("CMAKE_EXTRA_GENERATOR")); if (!cachedExtraGenerator.empty() && !extraGenerator.empty() && cachedExtraGenerator != extraGenerator) { - if (errorMessage) + if (errorMessage) { *errorMessage = std::string("\"") + kEXTRA_GENERATOR_KEY + "\" is set but incompatible with configured extra generator."; + } return false; } if (extraGenerator.empty()) { @@ -207,9 +219,10 @@ bool cmServerProtocol1_0::DoActivate(const cmServerRequest& request, std::string(state->GetCacheEntryValue("CMAKE_HOME_DIRECTORY")); if (!cachedSourceDirectory.empty() && !sourceDirectory.empty() && cachedSourceDirectory != sourceDirectory) { - if (errorMessage) + if (errorMessage) { *errorMessage = std::string("\"") + kSOURCE_DIRECTORY_KEY + "\" is set but incompatible with configured source directory."; + } return false; } if (sourceDirectory.empty()) { @@ -219,21 +232,24 @@ bool cmServerProtocol1_0::DoActivate(const cmServerRequest& request, } if (sourceDirectory.empty()) { - if (errorMessage) + if (errorMessage) { *errorMessage = std::string("\"") + kSOURCE_DIRECTORY_KEY + "\" is unset but required."; + } return false; } if (!cmSystemTools::FileIsDirectory(sourceDirectory)) { - if (errorMessage) + if (errorMessage) { *errorMessage = std::string("\"") + kSOURCE_DIRECTORY_KEY + "\" is not a directory."; + } return false; } if (generator.empty()) { - if (errorMessage) + if (errorMessage) { *errorMessage = std::string("\"") + kGENERATOR_KEY + "\" is unset but required."; + } return false; } @@ -243,10 +259,11 @@ bool cmServerProtocol1_0::DoActivate(const cmServerRequest& request, cmGlobalGenerator* gg = cm->CreateGlobalGenerator(fullGeneratorName); if (!gg) { - if (errorMessage) + if (errorMessage) { *errorMessage = std::string("Could not set up the requested combination of \"") + kGENERATOR_KEY + "\" and \"" + kEXTRA_GENERATOR_KEY + "\""; + } return false; } @@ -263,6 +280,19 @@ const cmServerResponse cmServerProtocol1_0::Process( { assert(this->m_State >= STATE_ACTIVE); + if (request.Type == kCOMPUTE_TYPE) { + return this->ProcessCompute(request); + } + if (request.Type == kCONFIGURE_TYPE) { + return this->ProcessConfigure(request); + } + if (request.Type == kGLOBAL_SETTINGS_TYPE) { + return this->ProcessGlobalSettings(request); + } + if (request.Type == kSET_GLOBAL_SETTINGS_TYPE) { + return this->ProcessSetGlobalSettings(request); + } + return request.ReportError("Unknown command!"); } @@ -270,3 +300,179 @@ bool cmServerProtocol1_0::IsExperimental() const { return true; } + +cmServerResponse cmServerProtocol1_0::ProcessCompute( + const cmServerRequest& request) +{ + if (this->m_State > STATE_CONFIGURED) { + return request.ReportError("This build system was already generated."); + } + if (this->m_State < STATE_CONFIGURED) { + return request.ReportError("This project was not configured yet."); + } + + cmake* cm = this->CMakeInstance(); + int ret = cm->Generate(); + + if (ret < 0) { + return request.ReportError("Failed to compute build system."); + } + m_State = STATE_COMPUTED; + return request.Reply(Json::Value()); +} + +cmServerResponse cmServerProtocol1_0::ProcessConfigure( + const cmServerRequest& request) +{ + if (this->m_State == STATE_INACTIVE) { + return request.ReportError("This instance is inactive."); + } + + // Make sure the types of cacheArguments matches (if given): + std::vector<std::string> cacheArgs; + bool cacheArgumentsError = false; + const Json::Value passedArgs = request.Data[kCACHE_ARGUMENTS_KEY]; + if (!passedArgs.isNull()) { + if (passedArgs.isString()) { + cacheArgs.push_back(passedArgs.asString()); + } else if (passedArgs.isArray()) { + for (auto i = passedArgs.begin(); i != passedArgs.end(); ++i) { + if (!i->isString()) { + cacheArgumentsError = true; + break; + } + cacheArgs.push_back(i->asString()); + } + } else { + cacheArgumentsError = true; + } + } + if (cacheArgumentsError) { + request.ReportError( + "cacheArguments must be unset, a string or an array of strings."); + } + + cmake* cm = this->CMakeInstance(); + std::string sourceDir = cm->GetHomeDirectory(); + const std::string buildDir = cm->GetHomeOutputDirectory(); + + if (buildDir.empty()) { + return request.ReportError( + "No build directory set via setGlobalSettings."); + } + + if (cm->LoadCache(buildDir)) { + // build directory has been set up before + const char* cachedSourceDir = + cm->GetState()->GetInitializedCacheValue("CMAKE_HOME_DIRECTORY"); + if (!cachedSourceDir) { + return request.ReportError("No CMAKE_HOME_DIRECTORY found in cache."); + } + if (sourceDir.empty()) { + sourceDir = std::string(cachedSourceDir); + cm->SetHomeDirectory(sourceDir); + } + + const char* cachedGenerator = + cm->GetState()->GetInitializedCacheValue("CMAKE_GENERATOR"); + if (cachedGenerator) { + cmGlobalGenerator* gen = cm->GetGlobalGenerator(); + if (gen && gen->GetName() != cachedGenerator) { + return request.ReportError("Configured generator does not match with " + "CMAKE_GENERATOR found in cache."); + } + } + } else { + // build directory has not been set up before + if (sourceDir.empty()) { + return request.ReportError("No sourceDirectory set via " + "setGlobalSettings and no cache found in " + "buildDirectory."); + } + } + + if (cm->AddCMakePaths() != 1) { + return request.ReportError("Failed to set CMake paths."); + } + + if (!cm->SetCacheArgs(cacheArgs)) { + return request.ReportError("cacheArguments could not be set."); + } + + int ret = cm->Configure(); + if (ret < 0) { + return request.ReportError("Configuration failed."); + } + m_State = STATE_CONFIGURED; + return request.Reply(Json::Value()); +} + +cmServerResponse cmServerProtocol1_0::ProcessGlobalSettings( + const cmServerRequest& request) +{ + cmake* cm = this->CMakeInstance(); + Json::Value obj = Json::objectValue; + + // Capabilities information: + obj[kCAPABILITIES_KEY] = cm->ReportCapabilitiesJson(true); + + obj[kDEBUG_OUTPUT_KEY] = cm->GetDebugOutput(); + obj[kTRACE_KEY] = cm->GetTrace(); + obj[kTRACE_EXPAND_KEY] = cm->GetTraceExpand(); + obj[kWARN_UNINITIALIZED_KEY] = cm->GetWarnUninitialized(); + obj[kWARN_UNUSED_KEY] = cm->GetWarnUnused(); + obj[kWARN_UNUSED_CLI_KEY] = cm->GetWarnUnusedCli(); + obj[kCHECK_SYSTEM_VARS_KEY] = cm->GetCheckSystemVars(); + + obj[kSOURCE_DIRECTORY_KEY] = cm->GetHomeDirectory(); + obj[kBUILD_DIRECTORY_KEY] = cm->GetHomeOutputDirectory(); + + // Currently used generator: + cmGlobalGenerator* gen = cm->GetGlobalGenerator(); + obj[kGENERATOR_KEY] = gen ? gen->GetName() : std::string(); + obj[kEXTRA_GENERATOR_KEY] = + gen ? gen->GetExtraGeneratorName() : std::string(); + + return request.Reply(obj); +} + +static void setBool(const cmServerRequest& request, const std::string& key, + std::function<void(bool)> setter) +{ + if (request.Data[key].isNull()) { + return; + } + setter(request.Data[key].asBool()); +} + +cmServerResponse cmServerProtocol1_0::ProcessSetGlobalSettings( + const cmServerRequest& request) +{ + const std::vector<std::string> boolValues = { + kDEBUG_OUTPUT_KEY, kTRACE_KEY, kTRACE_EXPAND_KEY, + kWARN_UNINITIALIZED_KEY, kWARN_UNUSED_KEY, kWARN_UNUSED_CLI_KEY, + kCHECK_SYSTEM_VARS_KEY + }; + for (auto i : boolValues) { + if (!request.Data[i].isNull() && !request.Data[i].isBool()) { + return request.ReportError("\"" + i + + "\" must be unset or a bool value."); + } + } + + cmake* cm = this->CMakeInstance(); + + setBool(request, kDEBUG_OUTPUT_KEY, + [cm](bool e) { cm->SetDebugOutputOn(e); }); + setBool(request, kTRACE_KEY, [cm](bool e) { cm->SetTrace(e); }); + setBool(request, kTRACE_EXPAND_KEY, [cm](bool e) { cm->SetTraceExpand(e); }); + setBool(request, kWARN_UNINITIALIZED_KEY, + [cm](bool e) { cm->SetWarnUninitialized(e); }); + setBool(request, kWARN_UNUSED_KEY, [cm](bool e) { cm->SetWarnUnused(e); }); + setBool(request, kWARN_UNUSED_CLI_KEY, + [cm](bool e) { cm->SetWarnUnusedCli(e); }); + setBool(request, kCHECK_SYSTEM_VARS_KEY, + [cm](bool e) { cm->SetCheckSystemVars(e); }); + + return request.Reply(Json::Value()); +} diff --git a/Source/cmServerProtocol.h b/Source/cmServerProtocol.h index bab949b..e772eca 100644 --- a/Source/cmServerProtocol.h +++ b/Source/cmServerProtocol.h @@ -13,6 +13,7 @@ #pragma once #include "cmListFileCache.h" +#include "cmake.h" #if defined(CMAKE_BUILD_WITH_CMAKE) #include "cm_jsoncpp_writer.h" @@ -87,7 +88,10 @@ public: virtual bool IsExperimental() const = 0; virtual const cmServerResponse Process(const cmServerRequest& request) = 0; - bool Activate(const cmServerRequest& request, std::string* errorMessage); + bool Activate(cmServer* server, const cmServerRequest& request, + std::string* errorMessage); + + void SendSignal(const std::string& name, const Json::Value& data) const; protected: cmake* CMakeInstance() const; @@ -97,6 +101,7 @@ protected: private: std::unique_ptr<cmake> m_CMakeInstance; + cmServer* m_Server = nullptr; // not owned! friend class cmServer; }; @@ -112,10 +117,18 @@ private: bool DoActivate(const cmServerRequest& request, std::string* errorMessage) override; + // Handle requests: + cmServerResponse ProcessCompute(const cmServerRequest& request); + cmServerResponse ProcessConfigure(const cmServerRequest& request); + cmServerResponse ProcessGlobalSettings(const cmServerRequest& request); + cmServerResponse ProcessSetGlobalSettings(const cmServerRequest& request); + enum State { STATE_INACTIVE, - STATE_ACTIVE + STATE_ACTIVE, + STATE_CONFIGURED, + STATE_COMPUTED }; State m_State = STATE_INACTIVE; }; diff --git a/Source/cmTarget.cxx b/Source/cmTarget.cxx index 1dad742..eb17561 100644 --- a/Source/cmTarget.cxx +++ b/Source/cmTarget.cxx @@ -1164,7 +1164,8 @@ const char* cmTarget::GetProperty(const std::string& prop, } } // Support "<CONFIG>_LOCATION". - else if (cmHasLiteralSuffix(prop, "_LOCATION")) { + else if (cmHasLiteralSuffix(prop, "_LOCATION") && + !cmHasLiteralPrefix(prop, "XCODE_ATTRIBUTE_")) { std::string configName(prop.c_str(), prop.size() - 9); if (configName != "IMPORTED") { if (!this->HandleLocationPropertyPolicy(context)) { diff --git a/Source/cm_auto_ptr.hxx b/Source/cm_auto_ptr.hxx index f6c4362..f38eda5 100644 --- a/Source/cm_auto_ptr.hxx +++ b/Source/cm_auto_ptr.hxx @@ -14,7 +14,13 @@ #include <cmConfigure.h> -// FIXME: Use std::auto_ptr on compilers that do not warn about it. +#ifdef CMake_HAVE_CXX_AUTO_PTR + +#include <memory> +#define CM_AUTO_PTR std::auto_ptr + +#else + #define CM_AUTO_PTR cm::auto_ptr // The HP compiler cannot handle the conversions necessary to use @@ -219,3 +225,5 @@ public: #endif #endif + +#endif diff --git a/Source/cmcmd.cxx b/Source/cmcmd.cxx index 9daed4b..2b4a830 100644 --- a/Source/cmcmd.cxx +++ b/Source/cmcmd.cxx @@ -52,6 +52,11 @@ #include <stdlib.h> #include <time.h> +int cmcmd_cmake_ninja_depends(std::vector<std::string>::const_iterator argBeg, + std::vector<std::string>::const_iterator argEnd); +int cmcmd_cmake_ninja_dyndep(std::vector<std::string>::const_iterator argBeg, + std::vector<std::string>::const_iterator argEnd); + void CMakeCommandUsage(const char* program) { std::ostringstream errorStream; @@ -782,6 +787,18 @@ int cmcmd::ExecuteCMakeCommand(std::vector<std::string>& args) return cmcmd::ExecuteLinkScript(args); } +#ifdef CMAKE_BUILD_WITH_CMAKE + // Internal CMake ninja dependency scanning support. + else if (args[1] == "cmake_ninja_depends") { + return cmcmd_cmake_ninja_depends(args.begin() + 2, args.end()); + } + + // Internal CMake ninja dyndep support. + else if (args[1] == "cmake_ninja_dyndep") { + return cmcmd_cmake_ninja_dyndep(args.begin() + 2, args.end()); + } +#endif + // Internal CMake unimplemented feature notification. else if (args[1] == "cmake_unimplemented_variable") { std::cerr << "Feature not implemented for this platform."; diff --git a/Source/kwsys/testConsoleBuf.cxx b/Source/kwsys/testConsoleBuf.cxx index 3dc3337..d7775e6 100644 --- a/Source/kwsys/testConsoleBuf.cxx +++ b/Source/kwsys/testConsoleBuf.cxx @@ -103,7 +103,7 @@ static void dumpBuffers(const T *expected, const T *received, size_t size) { } std::cerr << std::endl; } - std::cerr << std::endl << std::flush; + std::cerr << std::endl; } //---------------------------------------------------------------------------- @@ -331,21 +331,23 @@ static int testPipe() didFail = encodedTestString.compare(buffer2) == 0 ? 0 : 1; } if (didFail != 0) { - std::cerr << "Pipe's output didn't match expected output!" << std::endl << std::flush; + std::cerr << "Pipe's output didn't match expected output!" << std::endl; dumpBuffers<char>(encodedTestString.c_str(), buffer, encodedTestString.size()); dumpBuffers<char>(encodedInputTestString.c_str(), buffer + encodedTestString.size() + 1, encodedInputTestString.size()); dumpBuffers<char>(encodedTestString.c_str(), buffer2, encodedTestString.size()); } } catch (const std::runtime_error &ex) { DWORD lastError = GetLastError(); - std::cerr << "In function " << __FUNCTION__ << ":" << ex.what() << std::endl << std::flush; + std::cerr << "In function testPipe, line " << __LINE__ << ": " + << ex.what() << std::endl; displayError(lastError); } finishProcess(didFail == 0); } } catch (const std::runtime_error &ex) { DWORD lastError = GetLastError(); - std::cerr << "In function " << __FUNCTION__ << ":" << ex.what() << std::endl << std::flush; + std::cerr << "In function testPipe, line " << __LINE__ << ": " + << ex.what() << std::endl; displayError(lastError); } finishPipe(inPipeRead, inPipeWrite); @@ -368,11 +370,11 @@ static int testFile() (errFile = createFile(L"stderrFile.txt")) == INVALID_HANDLE_VALUE) { throw std::runtime_error("createFile failed!"); } - int length = 0; DWORD bytesWritten = 0; char buffer[200]; char buffer2[200]; + int length; if ((length = WideCharToMultiByte(TestCodepage, 0, UnicodeInputTestString, -1, buffer, sizeof(buffer), NULL, NULL)) == 0) { @@ -424,21 +426,23 @@ static int testFile() didFail = encodedTestString.compare(buffer2) == 0 ? 0 : 1; } if (didFail != 0) { - std::cerr << "File's output didn't match expected output!" << std::endl << std::flush; + std::cerr << "File's output didn't match expected output!" << std::endl; dumpBuffers<char>(encodedTestString.c_str(), buffer, encodedTestString.size()); dumpBuffers<char>(encodedInputTestString.c_str(), buffer + encodedTestString.size() + 1, encodedInputTestString.size() - 1); dumpBuffers<char>(encodedTestString.c_str(), buffer2, encodedTestString.size()); } } catch (const std::runtime_error &ex) { DWORD lastError = GetLastError(); - std::cerr << "In function " << __FUNCTION__ << ":" << ex.what() << std::endl << std::flush; + std::cerr << "In function testFile, line " << __LINE__ << ": " + << ex.what() << std::endl; displayError(lastError); } finishProcess(didFail == 0); } } catch (const std::runtime_error &ex) { DWORD lastError = GetLastError(); - std::cerr << "In function " << __FUNCTION__ << ":" << ex.what() << std::endl << std::flush; + std::cerr << "In function testFile, line " << __LINE__ << ": " + << ex.what() << std::endl; displayError(lastError); } finishFile(inFile); @@ -508,18 +512,18 @@ static int testConsole() forceNewConsole = true; } } else { - std::cerr << "RegGetValueW(FontFamily) failed!" << std::endl << std::flush; + std::cerr << "RegGetValueW(FontFamily) failed!" << std::endl; } RegCloseKey(hConsoleKey); } else { - std::cerr << "RegOpenKeyExW(HKEY_CURRENT_USER\\Console) failed!" << std::endl << std::flush; + std::cerr << "RegOpenKeyExW(HKEY_CURRENT_USER\\Console) failed!" << std::endl; } } if (forceNewConsole || GetConsoleMode(parentOut, &consoleMode) == 0) { // Not a real console, let's create new one. FreeConsole(); if (!AllocConsole()) { - std::cerr << "AllocConsole failed!" << std::endl << std::flush; + std::cerr << "AllocConsole failed!" << std::endl; return didFail; } SECURITY_ATTRIBUTES securityAttributes; @@ -561,11 +565,11 @@ static int testConsole() consoleFont.FontFamily = TestFontFamily; wcscpy(consoleFont.FaceName, TestFaceName); if (!setConsoleFont(hOut, FALSE, &consoleFont)) { - std::cerr << "SetCurrentConsoleFontEx failed!" << std::endl << std::flush; + std::cerr << "SetCurrentConsoleFontEx failed!" << std::endl; } } } else { - std::cerr << "GetCurrentConsoleFontEx failed!" << std::endl << std::flush; + std::cerr << "GetCurrentConsoleFontEx failed!" << std::endl; } } else { #endif @@ -599,7 +603,7 @@ static int testConsole() INPUT_RECORD inputBuffer[(sizeof(UnicodeInputTestString) / sizeof(UnicodeInputTestString[0])) * 2]; memset(&inputBuffer, 0, sizeof(inputBuffer)); - unsigned int i = 0; + unsigned int i; for (i = 0; i < (sizeof(UnicodeInputTestString) / sizeof(UnicodeInputTestString[0]) - 1); i++) { writeInputKeyEvent(&inputBuffer[i*2], UnicodeInputTestString[i]); @@ -650,7 +654,7 @@ static int testConsole() ) { didFail = 0; } else { - std::cerr << "Console's output didn't match expected output!" << std::endl << std::flush; + std::cerr << "Console's output didn't match expected output!" << std::endl; dumpBuffers<wchar_t>(wideTestString.c_str(), outputBuffer, wideTestString.size()); dumpBuffers<wchar_t>(wideTestString.c_str(), outputBuffer + screenBufferInfo.dwSize.X * 1, wideTestString.size()); dumpBuffers<wchar_t>(UnicodeInputTestString, outputBuffer + screenBufferInfo.dwSize.X * 2, (sizeof(UnicodeInputTestString) - 1) / sizeof(WCHAR)); @@ -659,7 +663,8 @@ static int testConsole() delete[] outputBuffer; } catch (const std::runtime_error &ex) { DWORD lastError = GetLastError(); - std::cerr << "In function " << __FUNCTION__ << ":" << ex.what() << std::endl << std::flush; + std::cerr << "In function testConsole, line " << __LINE__ << ": " + << ex.what() << std::endl; displayError(lastError); } finishProcess(didFail == 0); diff --git a/Tests/CMakeLists.txt b/Tests/CMakeLists.txt index 8cf1faa..3681843 100644 --- a/Tests/CMakeLists.txt +++ b/Tests/CMakeLists.txt @@ -3097,10 +3097,23 @@ ${CMake_BINARY_DIR}/bin/cmake -DDIR=dev -P ${CMake_SOURCE_DIR}/Utilities/Release --build-project testf --build-two-config --build-options ${build_options} - -DCMake_TEST_NESTED_MAKE_PROGRAM:FILEPATH=${CMake_TEST_EXPLICIT_MAKE_PROGRAM} --test-command testf) list(APPEND TEST_BUILD_DIRS "${CMake_BINARY_DIR}/Tests/Fortran") + if(CMAKE_Fortran_COMPILER_SUPPORTS_F90) + add_test(FortranModules ${CMAKE_CTEST_COMMAND} + --build-and-test + "${CMake_SOURCE_DIR}/Tests/FortranModules" + "${CMake_BINARY_DIR}/Tests/FortranModules" + ${build_generator_args} + --build-project FortranModules + --build-options ${build_options} + -DCMake_TEST_NESTED_MAKE_PROGRAM:FILEPATH=${CMake_TEST_EXPLICIT_MAKE_PROGRAM} + -DCMake_TEST_Fortran_SUBMODULES:BOOL=${CMake_TEST_Fortran_SUBMODULES} + ) + list(APPEND TEST_BUILD_DIRS "${CMake_BINARY_DIR}/Tests/FortranModules") + endif() + # FortranCInterface tests. if(UNIX) configure_file(${CMAKE_CURRENT_SOURCE_DIR}/FortranC/Flags.cmake.in diff --git a/Tests/CPackComponentsDEB/MyLibCPackConfig-components-description1.cmake.in b/Tests/CPackComponentsDEB/MyLibCPackConfig-components-description1.cmake.in index 6c5a655..74d816c 100644 --- a/Tests/CPackComponentsDEB/MyLibCPackConfig-components-description1.cmake.in +++ b/Tests/CPackComponentsDEB/MyLibCPackConfig-components-description1.cmake.in @@ -15,8 +15,8 @@ set(CPACK_COMPONENTS_IGNORE_GROUPS 1) #set(CPACK_COMPONENTS_ALL_IN_ONE_PACKAGE 1) # overriding previous descriptions -set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "main description") -set(CPACK_DEBIAN_APPLICATIONS_PACKAGE_DESCRIPTION "applications_description") -set(CPACK_COMPONENT_HEADERS_DESCRIPTION "headers_description") +set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "main description") +set(CPACK_COMPONENT_APPLICATIONS_DESCRIPTION "applications_description") +set(CPACK_COMPONENT_HEADERS_DESCRIPTION "headers_description") # libraries does not have any description and should inherit from CPACK_PACKAGE_DESCRIPTION_SUMMARY unset(CPACK_COMPONENT_LIBRARIES_DESCRIPTION) diff --git a/Tests/CPackComponentsDEB/MyLibCPackConfig-components-description2.cmake.in b/Tests/CPackComponentsDEB/MyLibCPackConfig-components-description2.cmake.in index 92226d8..cda79bc 100644 --- a/Tests/CPackComponentsDEB/MyLibCPackConfig-components-description2.cmake.in +++ b/Tests/CPackComponentsDEB/MyLibCPackConfig-components-description2.cmake.in @@ -17,12 +17,10 @@ set(CPACK_COMPONENTS_IGNORE_GROUPS 1) # overriding previous descriptions set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "main description 2") -# and override CPACK_PACKAGE_DESCRIPTION_SUMMARY because of precedence -set(CPACK_PACKAGE_DESCRIPTION_FILE - "@CPackComponentsDEB_SOURCE_DIR@/pkg_description.txt") - # Components do not have any description unset(CPACK_COMPONENT_APPLICATIONS_DESCRIPTION) unset(CPACK_COMPONENT_HEADERS_DESCRIPTION) +unset(CPACK_COMPONENT_LIBRARIES_DESCRIPTION) + set(CPACK_COMPONENT_LIBRARIES_DESCRIPTION "library description") diff --git a/Tests/CPackComponentsDEB/RunCPackVerifyResult-components-description2.cmake b/Tests/CPackComponentsDEB/RunCPackVerifyResult-components-description2.cmake index 7b94ca0..35ca74c 100644 --- a/Tests/CPackComponentsDEB/RunCPackVerifyResult-components-description2.cmake +++ b/Tests/CPackComponentsDEB/RunCPackVerifyResult-components-description2.cmake @@ -55,12 +55,12 @@ if(DPKGDEB_EXECUTABLE) message(STATUS "package='${dpkg_package_name}', description='${dpkg_description}'") if("${dpkg_package_name}" STREQUAL "mylib-applications") - if(NOT "${dpkg_description}" STREQUAL "Debian package description file") + if(NOT "${dpkg_description}" STREQUAL "main description 2") set(dpkgdeb_output_errors_all ${dpkgdeb_output_errors_all} "dpkg-deb: ${_f}: Incorrect description for package ${dpkg_package_name}: ${dpkg_description} != applications_description") endif() elseif("${dpkg_package_name}" STREQUAL "mylib-headers") - if(NOT "${dpkg_description}" STREQUAL "Debian package description file") + if(NOT "${dpkg_description}" STREQUAL "main description 2") set(dpkgdeb_output_errors_all ${dpkgdeb_output_errors_all} "dpkg-deb: ${_f}: Incorrect description for package ${dpkg_package_name}: ${dpkg_description} != headers_description") endif() diff --git a/Tests/CPackComponentsDEB/pkg_description.txt b/Tests/CPackComponentsDEB/pkg_description.txt deleted file mode 100644 index 99f8f9b..0000000 --- a/Tests/CPackComponentsDEB/pkg_description.txt +++ /dev/null @@ -1 +0,0 @@ -Debian package description file diff --git a/Tests/CheckFortran.cmake b/Tests/CheckFortran.cmake index ebbb426..678bb82 100644 --- a/Tests/CheckFortran.cmake +++ b/Tests/CheckFortran.cmake @@ -22,6 +22,7 @@ project(CheckFortran Fortran) file(WRITE \"\${CMAKE_CURRENT_BINARY_DIR}/result.cmake\" \"set(CMAKE_Fortran_COMPILER \\\"\${CMAKE_Fortran_COMPILER}\\\")\\n\" \"set(CMAKE_Fortran_FLAGS \\\"\${CMAKE_Fortran_FLAGS}\\\")\\n\" + \"set(CMAKE_Fortran_COMPILER_SUPPORTS_F90 \\\"\${CMAKE_Fortran_COMPILER_SUPPORTS_F90}\\\")\\n\" ) ") execute_process( @@ -47,4 +48,6 @@ file(WRITE \"\${CMAKE_CURRENT_BINARY_DIR}/result.cmake\" mark_as_advanced(CMAKE_Fortran_COMPILER) set(CMAKE_Fortran_FLAGS "${CMAKE_Fortran_FLAGS}" CACHE STRING "Fortran flags") mark_as_advanced(CMAKE_Fortran_FLAGS) + set(CMAKE_Fortran_COMPILER_SUPPORTS_F90 "${CMAKE_Fortran_COMPILER_SUPPORTS_F90}" CACHE BOOL "Fortran compiler supports F90") + mark_as_advanced(CMAKE_Fortran_COMPILER_SUPPORTS_F90) endif() diff --git a/Tests/Fortran/CMakeLists.txt b/Tests/Fortran/CMakeLists.txt index 99fcc0a..740e6f5 100644 --- a/Tests/Fortran/CMakeLists.txt +++ b/Tests/Fortran/CMakeLists.txt @@ -1,8 +1,5 @@ cmake_minimum_required (VERSION 3.1) project(testf C CXX Fortran) -if(NOT DEFINED CMake_TEST_NESTED_MAKE_PROGRAM AND NOT CMAKE_GENERATOR MATCHES "Visual Studio") - set(CMake_TEST_NESTED_MAKE_PROGRAM "${CMAKE_MAKE_PROGRAM}") -endif() message("CTEST_FULL_OUTPUT ") set(CMAKE_VERBOSE_MAKEFILE 1) @@ -67,7 +64,7 @@ function(test_fortran_c_interface_module) SYMBOL_NAMESPACE "F_" SYMBOLS ${FORTRAN_FUNCTIONS} ) - include_directories("${testf_BINARY_DIR}") + include_directories("${CMAKE_CURRENT_BINARY_DIR}") # if the name mangling is not found for a F90 compiler # print out some diagnostic stuff for the dashboard @@ -111,7 +108,7 @@ function(test_fortran_c_interface_module) target_link_libraries(maincxx mycxx) # print out some stuff to help debug on machines via cdash - file(READ "${testf_BINARY_DIR}/foo.h" fooh) + file(READ "${CMAKE_CURRENT_BINARY_DIR}/foo.h" fooh) message("foo.h contents:\n${fooh}") endfunction() @@ -144,85 +141,3 @@ else() endif() endif() - - - - -set(TEST_MODULE_DEPENDS 0) -if(CMAKE_Fortran_COMPILER_SUPPORTS_F90) - add_executable(test_module - test_module_main.f90 - test_module_implementation.f90 - test_module_interface.f90) - - add_executable(test_use_in_comment_fixedform - test_use_in_comment_fixedform.f) - set_property(SOURCE test_use_in_comment_fixedform.f PROPERTY Fortran_FORMAT FIXED) - add_executable(test_use_in_comment_freeform - test_use_in_comment_freeform.f90) - set_property(SOURCE test_use_in_comment_freeform.f90 PROPERTY Fortran_FORMAT FREE) - - add_executable(test_in_interface - in_interface/main.f90 - in_interface/module.f90) - - add_definitions(-DFOO -DBAR=1) - include_directories(${testf_SOURCE_DIR}/include) - add_executable(test_preprocess test_preprocess.F90 test_preprocess_module.F90) - - set(TEST_MODULE_DEPENDS 1) -endif() - -if(TEST_MODULE_DEPENDS) - # Build the external project separately using a custom target. - # Make sure it uses the same build configuration as this test. - if(CMAKE_CONFIGURATION_TYPES) - set(External_CONFIG_TYPE -C "${CMAKE_CFG_INTDIR}") - set(External_BUILD_TYPE) - else() - set(External_CONFIG_TYPE) - set(External_BUILD_TYPE -DCMAKE_BUILD_TYPE:STRING=${CMAKE_BUILD_TYPE}) - endif() - set(External_SOURCE_DIR "${testf_SOURCE_DIR}/External") - set(External_BINARY_DIR "${testf_BINARY_DIR}/External") - if("${testf_BINARY_DIR}" MATCHES " ") - # Our build tree has a space, so the build tool supports spaces. - # Test using modules from a path with spaces. - string(APPEND External_BINARY_DIR " Build") - endif() - add_custom_command( - OUTPUT ${testf_BINARY_DIR}/ExternalProject - COMMAND ${CMAKE_CTEST_COMMAND} - ARGS ${External_CONFIG_TYPE} - --build-and-test - ${External_SOURCE_DIR} - ${External_BINARY_DIR} - --build-noclean - --build-two-config - --build-project ExtFort - --build-generator ${CMAKE_GENERATOR} - --build-generator-platform "${CMAKE_GENERATOR_PLATFORM}" - --build-generator-toolset "${CMAKE_GENERATOR_TOOLSET}" - --build-options -DCMAKE_Fortran_COMPILER:STRING=${CMAKE_Fortran_COMPILER} - -DCMAKE_Fortran_FLAGS:STRING=${CMAKE_Fortran_FLAGS} - -DCMAKE_Fortran_FLAGS_DEBUG:STRING=${CMAKE_Fortran_FLAGS_DEBUG} - -DCMAKE_Fortran_FLAGS_RELEASE:STRING=${CMAKE_Fortran_FLAGS_RELEASE} - -DCMAKE_Fortran_FLAGS_MINSIZEREL:STRING=${CMAKE_Fortran_FLAGS_MINSIZEREL} - -DCMAKE_Fortran_FLAGS_RELWITHDEBINFO:STRING=${CMAKE_Fortran_FLAGS_RELWITHDEBINFO} - -DCMAKE_MAKE_PROGRAM:FILEPATH=${CMake_TEST_NESTED_MAKE_PROGRAM} - ${External_BUILD_TYPE} - VERBATIM - ) - add_custom_target(ExternalTarget ALL DEPENDS ${testf_BINARY_DIR}/ExternalProject) - - # Test module output directory if available. - if(CMAKE_Fortran_MODDIR_FLAG) - set(Library_MODDIR "${testf_BINARY_DIR}/Library/modules") - else() - set(Library_MODDIR "${testf_BINARY_DIR}/Library") - endif() - - add_subdirectory(Library) - add_subdirectory(Subdir) - add_subdirectory(Executable) -endif() diff --git a/Tests/FortranModules/CMakeLists.txt b/Tests/FortranModules/CMakeLists.txt new file mode 100644 index 0000000..3996600 --- /dev/null +++ b/Tests/FortranModules/CMakeLists.txt @@ -0,0 +1,111 @@ +cmake_minimum_required (VERSION 3.1) +project(FortranModules Fortran) + +if(NOT DEFINED CMake_TEST_NESTED_MAKE_PROGRAM AND NOT CMAKE_GENERATOR MATCHES "Visual Studio") + set(CMake_TEST_NESTED_MAKE_PROGRAM "${CMAKE_MAKE_PROGRAM}") +endif() + +if("x${CMake_TEST_Fortran_SUBMODULES}" STREQUAL "x" + AND NOT CMAKE_VERSION VERSION_LESS 3.6.20160923 # for CheckFortranSourceCompiles SRC_EXT + ) + include(CheckFortranSourceCompiles) + CHECK_Fortran_SOURCE_COMPILES([[ +module parent + interface + module function id(x) + real, intent(in) :: x + real :: id + end function id + end interface +end module parent +submodule ( parent ) child +contains + module procedure id + f = x + end procedure id +end submodule child +program main +end program +]] HAVE_SUBMODULES SRC_EXT F90) + set(CMake_TEST_Fortran_SUBMODULES ${HAVE_SUBMODULES}) +elseif(CMake_TEST_Fortran_SUBMODULES) + message(STATUS "Enabling Fortran submodule tests by explicit request.") +endif() + +add_executable(test_module + test_module_main.f90 + test_module_implementation.f90 + test_module_interface.f90) + +add_executable(test_use_in_comment_fixedform + test_use_in_comment_fixedform.f) +set_property(SOURCE test_use_in_comment_fixedform.f PROPERTY Fortran_FORMAT FIXED) +add_executable(test_use_in_comment_freeform + test_use_in_comment_freeform.f90) +set_property(SOURCE test_use_in_comment_freeform.f90 PROPERTY Fortran_FORMAT FREE) + +add_executable(test_in_interface + in_interface/main.f90 + in_interface/module.f90) + +add_definitions(-DFOO -DBAR=1) +include_directories(${CMAKE_CURRENT_SOURCE_DIR}/include) +add_executable(test_preprocess test_preprocess.F90 test_preprocess_module.F90) + +add_executable(test_non_pp_include test_non_pp_include_main.f90) + +# Build the external project separately using a custom target. +# Make sure it uses the same build configuration as this test. +if(CMAKE_CONFIGURATION_TYPES) + set(External_CONFIG_TYPE -C "${CMAKE_CFG_INTDIR}") + set(External_BUILD_TYPE) +else() + set(External_CONFIG_TYPE) + set(External_BUILD_TYPE -DCMAKE_BUILD_TYPE:STRING=${CMAKE_BUILD_TYPE}) +endif() +set(External_SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/External") +set(External_BINARY_DIR "${CMAKE_CURRENT_BINARY_DIR}/External") +if("${CMAKE_CURRENT_BINARY_DIR}" MATCHES " ") + # Our build tree has a space, so the build tool supports spaces. + # Test using modules from a path with spaces. + string(APPEND External_BINARY_DIR " Build") +endif() +add_custom_command( + OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/ExternalProject + COMMAND ${CMAKE_CTEST_COMMAND} + ARGS ${External_CONFIG_TYPE} + --build-and-test + ${External_SOURCE_DIR} + ${External_BINARY_DIR} + --build-noclean + --build-two-config + --build-project ExtFort + --build-generator ${CMAKE_GENERATOR} + --build-generator-platform "${CMAKE_GENERATOR_PLATFORM}" + --build-generator-toolset "${CMAKE_GENERATOR_TOOLSET}" + --build-options -DCMAKE_Fortran_COMPILER:STRING=${CMAKE_Fortran_COMPILER} + -DCMAKE_Fortran_FLAGS:STRING=${CMAKE_Fortran_FLAGS} + -DCMAKE_Fortran_FLAGS_DEBUG:STRING=${CMAKE_Fortran_FLAGS_DEBUG} + -DCMAKE_Fortran_FLAGS_RELEASE:STRING=${CMAKE_Fortran_FLAGS_RELEASE} + -DCMAKE_Fortran_FLAGS_MINSIZEREL:STRING=${CMAKE_Fortran_FLAGS_MINSIZEREL} + -DCMAKE_Fortran_FLAGS_RELWITHDEBINFO:STRING=${CMAKE_Fortran_FLAGS_RELWITHDEBINFO} + -DCMAKE_MAKE_PROGRAM:FILEPATH=${CMake_TEST_NESTED_MAKE_PROGRAM} + ${External_BUILD_TYPE} + VERBATIM + ) +add_custom_target(ExternalTarget ALL DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/ExternalProject) + +# Test module output directory if available. +if(CMAKE_Fortran_MODDIR_FLAG) + set(Library_MODDIR "${CMAKE_CURRENT_BINARY_DIR}/Library/modules") +else() + set(Library_MODDIR "${CMAKE_CURRENT_BINARY_DIR}/Library") +endif() + +add_subdirectory(Library) +add_subdirectory(Subdir) +add_subdirectory(Executable) + +if(CMake_TEST_Fortran_SUBMODULES) + add_subdirectory(Submodules) +endif() diff --git a/Tests/Fortran/Executable/CMakeLists.txt b/Tests/FortranModules/Executable/CMakeLists.txt index de08d86..de08d86 100644 --- a/Tests/Fortran/Executable/CMakeLists.txt +++ b/Tests/FortranModules/Executable/CMakeLists.txt diff --git a/Tests/Fortran/Executable/main.f90 b/Tests/FortranModules/Executable/main.f90 index 640259c..640259c 100644 --- a/Tests/Fortran/Executable/main.f90 +++ b/Tests/FortranModules/Executable/main.f90 diff --git a/Tests/Fortran/External/CMakeLists.txt b/Tests/FortranModules/External/CMakeLists.txt index 0eb1cfe..c90a0ffb 100644 --- a/Tests/Fortran/External/CMakeLists.txt +++ b/Tests/FortranModules/External/CMakeLists.txt @@ -1,4 +1,3 @@ project(ExtFort Fortran) add_library(myext a.f90) - diff --git a/Tests/Fortran/External/a.f90 b/Tests/FortranModules/External/a.f90 index 2be73c5..2be73c5 100644 --- a/Tests/Fortran/External/a.f90 +++ b/Tests/FortranModules/External/a.f90 diff --git a/Tests/Fortran/Library/CMakeLists.txt b/Tests/FortranModules/Library/CMakeLists.txt index 17438ca..17438ca 100644 --- a/Tests/Fortran/Library/CMakeLists.txt +++ b/Tests/FortranModules/Library/CMakeLists.txt diff --git a/Tests/Fortran/Library/a.f90 b/Tests/FortranModules/Library/a.f90 index 3031c07..3031c07 100644 --- a/Tests/Fortran/Library/a.f90 +++ b/Tests/FortranModules/Library/a.f90 diff --git a/Tests/Fortran/Library/b.f90 b/Tests/FortranModules/Library/b.f90 index ae1edcb..ae1edcb 100644 --- a/Tests/Fortran/Library/b.f90 +++ b/Tests/FortranModules/Library/b.f90 diff --git a/Tests/Fortran/Library/main.f90 b/Tests/FortranModules/Library/main.f90 index 2385408..2385408 100644 --- a/Tests/Fortran/Library/main.f90 +++ b/Tests/FortranModules/Library/main.f90 diff --git a/Tests/Fortran/Subdir/CMakeLists.txt b/Tests/FortranModules/Subdir/CMakeLists.txt index 52683e5..52683e5 100644 --- a/Tests/Fortran/Subdir/CMakeLists.txt +++ b/Tests/FortranModules/Subdir/CMakeLists.txt diff --git a/Tests/Fortran/Subdir/subdir.f90 b/Tests/FortranModules/Subdir/subdir.f90 index 68955f6..68955f6 100644 --- a/Tests/Fortran/Subdir/subdir.f90 +++ b/Tests/FortranModules/Subdir/subdir.f90 diff --git a/Tests/FortranModules/Submodules/CMakeLists.txt b/Tests/FortranModules/Submodules/CMakeLists.txt new file mode 100644 index 0000000..bf0152f --- /dev/null +++ b/Tests/FortranModules/Submodules/CMakeLists.txt @@ -0,0 +1 @@ +add_executable(submod main.f90 provide.f90) diff --git a/Tests/FortranModules/Submodules/main.f90 b/Tests/FortranModules/Submodules/main.f90 new file mode 100644 index 0000000..3c750ce --- /dev/null +++ b/Tests/FortranModules/Submodules/main.f90 @@ -0,0 +1,5 @@ +program main + use parent, only : child_function,grandchild_subroutine + implicit none + if (child_function()) call grandchild_subroutine +end program diff --git a/Tests/FortranModules/Submodules/provide.f90 b/Tests/FortranModules/Submodules/provide.f90 new file mode 100644 index 0000000..0ad216a --- /dev/null +++ b/Tests/FortranModules/Submodules/provide.f90 @@ -0,0 +1,57 @@ +! The program units in this file consist of a +! module/submodule tree represented by the following +! graph: +! +! parent +! | +! / \ +! / \ +! child sibling +! | +! grandchild +! +! where the parent node is a module and all other +! nodes are submodules. + +module parent + implicit none + + interface + + ! Test Fortran 2008 "module function" syntax + module function child_function() result(child_stuff) + logical :: child_stuff + end function + + ! Test Fortran 2008 "module subroutine" syntax + module subroutine grandchild_subroutine() + end subroutine + + end interface + +end module parent + +! Test the notation for a 1st-generation direct +! descendant of a parent module +submodule ( parent ) child + implicit none +contains + module function child_function() result(child_stuff) + logical :: child_stuff + child_stuff=.true. + end function +end submodule child + +! Empty submodule for checking disambiguation of +! nodes at the same vertical level in the tree +submodule ( parent ) sibling +end submodule sibling + +! Test the notation for an Nth-generation descendant +! for N>1, which necessitates the colon. +submodule ( parent : child ) grandchild +contains + module subroutine grandchild_subroutine() + print *,"Test passed." + end subroutine +end submodule grandchild diff --git a/Tests/Fortran/in_interface/main.f90 b/Tests/FortranModules/in_interface/main.f90 index 28bcf36..28bcf36 100644 --- a/Tests/Fortran/in_interface/main.f90 +++ b/Tests/FortranModules/in_interface/main.f90 diff --git a/Tests/Fortran/in_interface/module.f90 b/Tests/FortranModules/in_interface/module.f90 index 1064d74..1064d74 100644 --- a/Tests/Fortran/in_interface/module.f90 +++ b/Tests/FortranModules/in_interface/module.f90 diff --git a/Tests/Fortran/include/test_preprocess.h b/Tests/FortranModules/include/test_preprocess.h index f4a00a9..f4a00a9 100644 --- a/Tests/Fortran/include/test_preprocess.h +++ b/Tests/FortranModules/include/test_preprocess.h diff --git a/Tests/FortranModules/non_pp_include.f90 b/Tests/FortranModules/non_pp_include.f90 new file mode 100644 index 0000000..7eb1725 --- /dev/null +++ b/Tests/FortranModules/non_pp_include.f90 @@ -0,0 +1,3 @@ +SUBROUTINE NON_PP_INCLUDE_SUBROUTINE + PRINT *, "Hello World!" +END SUBROUTINE NON_PP_INCLUDE_SUBROUTINE diff --git a/Tests/Fortran/test_module_implementation.f90 b/Tests/FortranModules/test_module_implementation.f90 index de3cb57..de3cb57 100644 --- a/Tests/Fortran/test_module_implementation.f90 +++ b/Tests/FortranModules/test_module_implementation.f90 diff --git a/Tests/Fortran/test_module_interface.f90 b/Tests/FortranModules/test_module_interface.f90 index dd0f35c..dd0f35c 100644 --- a/Tests/Fortran/test_module_interface.f90 +++ b/Tests/FortranModules/test_module_interface.f90 diff --git a/Tests/Fortran/test_module_main.f90 b/Tests/FortranModules/test_module_main.f90 index 6ac97fa..6ac97fa 100644 --- a/Tests/Fortran/test_module_main.f90 +++ b/Tests/FortranModules/test_module_main.f90 diff --git a/Tests/FortranModules/test_non_pp_include_main.f90 b/Tests/FortranModules/test_non_pp_include_main.f90 new file mode 100644 index 0000000..8a04fbd --- /dev/null +++ b/Tests/FortranModules/test_non_pp_include_main.f90 @@ -0,0 +1,5 @@ +INCLUDE "non_pp_include.f90" + +PROGRAM MAINF90 + CALL NON_PP_INCLUDE_SUBROUTINE +END PROGRAM MAINF90 diff --git a/Tests/Fortran/test_preprocess.F90 b/Tests/FortranModules/test_preprocess.F90 index 3a09976..3a09976 100644 --- a/Tests/Fortran/test_preprocess.F90 +++ b/Tests/FortranModules/test_preprocess.F90 diff --git a/Tests/Fortran/test_preprocess_module.F90 b/Tests/FortranModules/test_preprocess_module.F90 index 5849b62..5849b62 100644 --- a/Tests/Fortran/test_preprocess_module.F90 +++ b/Tests/FortranModules/test_preprocess_module.F90 diff --git a/Tests/Fortran/test_use_in_comment_fixedform.f b/Tests/FortranModules/test_use_in_comment_fixedform.f index 39f486b..39f486b 100644 --- a/Tests/Fortran/test_use_in_comment_fixedform.f +++ b/Tests/FortranModules/test_use_in_comment_fixedform.f diff --git a/Tests/Fortran/test_use_in_comment_freeform.f90 b/Tests/FortranModules/test_use_in_comment_freeform.f90 index 48bcd5a..48bcd5a 100644 --- a/Tests/Fortran/test_use_in_comment_freeform.f90 +++ b/Tests/FortranModules/test_use_in_comment_freeform.f90 diff --git a/Tests/RunCMake/XcodeProject/RunCMakeTest.cmake b/Tests/RunCMake/XcodeProject/RunCMakeTest.cmake index 0684d57..d43c80d 100644 --- a/Tests/RunCMake/XcodeProject/RunCMakeTest.cmake +++ b/Tests/RunCMake/XcodeProject/RunCMakeTest.cmake @@ -1,6 +1,7 @@ include(RunCMake) run_cmake(XcodeFileType) +run_cmake(XcodeAttributeLocation) run_cmake(XcodeAttributeGenex) run_cmake(XcodeAttributeGenexError) run_cmake(XcodeObjectNeedsEscape) diff --git a/Tests/RunCMake/XcodeProject/XcodeAttributeLocation-check.cmake b/Tests/RunCMake/XcodeProject/XcodeAttributeLocation-check.cmake new file mode 100644 index 0000000..2f55131 --- /dev/null +++ b/Tests/RunCMake/XcodeProject/XcodeAttributeLocation-check.cmake @@ -0,0 +1,7 @@ +set(expect "DEPLOYMENT_LOCATION = YES") +file(STRINGS ${RunCMake_TEST_BINARY_DIR}/XcodeAttributeLocation.xcodeproj/project.pbxproj actual + REGEX "DEPLOYMENT_LOCATION = .*;" LIMIT_COUNT 1) +if(NOT "${actual}" MATCHES "${expect}") + message(SEND_ERROR "The actual project contains the line:\n ${actual}\n" + "which does not match expected regex:\n ${expect}\n") +endif() diff --git a/Tests/RunCMake/XcodeProject/XcodeAttributeLocation.cmake b/Tests/RunCMake/XcodeProject/XcodeAttributeLocation.cmake new file mode 100644 index 0000000..5ab5528 --- /dev/null +++ b/Tests/RunCMake/XcodeProject/XcodeAttributeLocation.cmake @@ -0,0 +1,3 @@ +enable_language(C) +add_executable(some main.c) +set_property(TARGET some PROPERTY XCODE_ATTRIBUTE_DEPLOYMENT_LOCATION YES) diff --git a/Tests/Server/CMakeLists.txt b/Tests/Server/CMakeLists.txt index 8daf12a..03f5042 100644 --- a/Tests/Server/CMakeLists.txt +++ b/Tests/Server/CMakeLists.txt @@ -19,5 +19,6 @@ macro(do_test bsname file) endmacro() do_test("test_handshake" "tc_handshake.json") +do_test("test_globalSettings" "tc_globalSettings.json") add_executable(Server empty.cpp) diff --git a/Tests/Server/cmakelib.py b/Tests/Server/cmakelib.py index 0f98078..8beaeef 100644 --- a/Tests/Server/cmakelib.py +++ b/Tests/Server/cmakelib.py @@ -106,6 +106,7 @@ def waitForReply(cmakeCommand, originalType, cookie): packet = waitForRawMessage(cmakeCommand) if packet['cookie'] != cookie or packet['type'] != 'reply' or packet['inReplyTo'] != originalType: sys.exit(1) + return packet def waitForError(cmakeCommand, originalType, cookie, message): packet = waitForRawMessage(cmakeCommand) @@ -117,10 +118,71 @@ def waitForProgress(cmakeCommand, originalType, cookie, current, message): if packet['cookie'] != cookie or packet['type'] != 'progress' or packet['inReplyTo'] != originalType or packet['progressCurrent'] != current or packet['progressMessage'] != message: sys.exit(1) -def handshake(cmakeCommand, major, minor): +def handshake(cmakeCommand, major, minor, source, build, generator, extraGenerator): version = { 'major': major } if minor >= 0: version['minor'] = minor - writePayload(cmakeCommand, { 'type': 'handshake', 'protocolVersion': version, 'cookie': 'TEST_HANDSHAKE' }) + writePayload(cmakeCommand, { 'type': 'handshake', 'protocolVersion': version, + 'cookie': 'TEST_HANDSHAKE', 'sourceDirectory': source, 'buildDirectory': build, + 'generator': generator, 'extraGenerator': extraGenerator }) waitForReply(cmakeCommand, 'handshake', 'TEST_HANDSHAKE') + +def validateGlobalSettings(cmakeCommand, cmakeCommandPath, data): + packet = waitForReply(cmakeCommand, 'globalSettings', '') + + capabilities = packet['capabilities'] + + # validate version: + cmakeoutput = subprocess.check_output([ cmakeCommandPath, "--version" ], universal_newlines=True) + cmakeVersion = cmakeoutput.splitlines()[0][14:] + + version = capabilities['version'] + versionString = version['string'] + vs = str(version['major']) + '.' + str(version['minor']) + '.' + str(version['patch']) + if (versionString != vs and not versionString.startswith(vs + '-')): + sys.exit(1) + if (versionString != cmakeVersion): + sys.exit(1) + + # validate generators: + generatorObjects = capabilities['generators'] + + cmakeoutput = subprocess.check_output([ cmakeCommandPath, "--help" ], universal_newlines=True) + index = cmakeoutput.index('\nGenerators\n\n') + cmakeGenerators = [] + for line in cmakeoutput[index + 12:].splitlines(): + if not line.startswith(' '): + continue + if line.startswith(' '): + continue + equalPos = line.find('=') + tmp = '' + if (equalPos > 0): + tmp = line[2:equalPos].strip() + else: + tmp = line.strip() + if tmp.endswith(" [arch]"): + tmp = tmp[0:len(tmp) - 7] + if (len(tmp) > 0) and (" - " not in tmp) and (tmp != 'KDevelop3'): + cmakeGenerators.append(tmp) + + generators = [] + for genObj in generatorObjects: + generators.append(genObj['name']) + + generators.sort() + cmakeGenerators.sort() + + for gen in cmakeGenerators: + if (not gen in generators): + sys.exit(1) + + gen = packet['generator'] + if (gen != '' and not (gen in generators)): + sys.exit(1) + + for i in data: + print("Validating", i) + if (packet[i] != data[i]): + sys.exit(1) diff --git a/Tests/Server/server-test.py b/Tests/Server/server-test.py index e0a3b3b..d2bf92e 100644 --- a/Tests/Server/server-test.py +++ b/Tests/Server/server-test.py @@ -68,9 +68,25 @@ for obj in testData: if debug: print("Doing handshake:", json.dumps(data)) major = -1 minor = -1 + generator = 'Ninja' + extraGenerator = 'CodeBlocks' + sourceDirectory = sourceDir + buildDirectory = buildDir if 'major' in data: major = data['major'] if 'minor' in data: minor = data['minor'] - cmakelib.handshake(proc, major, minor) + if 'buildDirectory' in data: buildDirectory = data['buildDirectory'] + if 'sourceDirectory' in data: sourceDirectory = data['sourceDirectory'] + if 'generator' in data: generator = data['generator'] + if 'extraGenerator' in data: extraGenerator = data['extraGenerator'] + cmakelib.handshake(proc, major, minor, sourceDirectory, buildDirectory, + generator, extraGenerator) + elif 'validateGlobalSettings' in obj: + data = obj['validateGlobalSettings'] + if not 'buildDirectory' in data: data['buildDirectory'] = buildDir + if not 'sourceDirectory' in data: data['sourceDirectory'] = sourceDir + if not 'generator' in data: data['generator'] = 'Ninja' + if not 'extraGenerator' in data: data['extraGenerator'] = 'CodeBlocks' + cmakelib.validateGlobalSettings(proc, cmakeCommand, data) elif 'message' in obj: print("MESSAGE:", obj["message"]) else: diff --git a/Tests/Server/tc_globalSettings.json b/Tests/Server/tc_globalSettings.json new file mode 100644 index 0000000..d72fb41 --- /dev/null +++ b/Tests/Server/tc_globalSettings.json @@ -0,0 +1,140 @@ +[ +{ "message": "Testing globalSettings" }, + +{ "handshake": {"major": 1} }, + +{ "send": { "type": "globalSettings"} }, +{ "validateGlobalSettings": { "warnUnused": false, "debugOutput": false, "warnUninitialized": false, "traceExpand": false, "trace": false, "warnUnusedCli": true, "checkSystemVars": false } }, + + + +{ "message": "Change settings:" }, + +{ "send": { "type": "setGlobalSettings", "warnUnused": true } }, +{ "reply": { "type": "setGlobalSettings" } }, + +{ "send": { "type": "globalSettings"} }, +{ "validateGlobalSettings": { "warnUnused": true, "debugOutput": false, "warnUninitialized": false, "traceExpand": false, "trace": false, "warnUnusedCli": true, "checkSystemVars": false } }, + +{ "send": { "type": "setGlobalSettings", "warnUnused": false } }, +{ "reply": { "type": "setGlobalSettings" } }, + +{ "send": { "type": "globalSettings"} }, +{ "validateGlobalSettings": { "warnUnused": false, "debugOutput": false, "warnUninitialized": false, "traceExpand": false, "trace": false, "warnUnusedCli": true, "checkSystemVars": false } }, + +{ "send": { "type": "setGlobalSettings", "debugOutput": true } }, +{ "reply": { "type": "setGlobalSettings" } }, + +{ "send": { "type": "globalSettings"} }, +{ "validateGlobalSettings": { "warnUnused": false, "debugOutput": true, "warnUninitialized": false, "traceExpand": false, "trace": false, "warnUnusedCli": true, "checkSystemVars": false } }, + +{ "send": { "type": "setGlobalSettings", "debugOutput": false } }, +{ "reply": { "type": "setGlobalSettings" } }, + +{ "send": { "type": "globalSettings"} }, +{ "validateGlobalSettings": { "warnUnused": false, "debugOutput": false, "warnUninitialized": false, "traceExpand": false, "trace": false, "warnUnusedCli": true, "checkSystemVars": false } }, + +{ "send": { "type": "setGlobalSettings", "warnUninitialized": true } }, +{ "reply": { "type": "setGlobalSettings" } }, + +{ "send": { "type": "globalSettings"} }, +{ "validateGlobalSettings": { "warnUnused": false, "debugOutput": false, "warnUninitialized": true, "traceExpand": false, "trace": false, "warnUnusedCli": true, "checkSystemVars": false } }, + +{ "send": { "type": "setGlobalSettings", "warnUninitialized": false } }, +{ "reply": { "type": "setGlobalSettings" } }, + +{ "send": { "type": "globalSettings"} }, +{ "validateGlobalSettings": { "warnUnused": false, "debugOutput": false, "warnUninitialized": false, "traceExpand": false, "trace": false, "warnUnusedCli": true, "checkSystemVars": false } }, + +{ "send": { "type": "setGlobalSettings", "traceExpand": true } }, +{ "reply": { "type": "setGlobalSettings" } }, + +{ "send": { "type": "globalSettings"} }, +{ "validateGlobalSettings": { "warnUnused": false, "debugOutput": false, "warnUninitialized": false, "traceExpand": true, "trace": false, "warnUnusedCli": true, "checkSystemVars": false } }, + +{ "send": { "type": "setGlobalSettings", "traceExpand": false } }, +{ "reply": { "type": "setGlobalSettings" } }, + +{ "send": { "type": "globalSettings"} }, +{ "validateGlobalSettings": { "warnUnused": false, "debugOutput": false, "warnUninitialized": false, "traceExpand": false, "trace": false, "warnUnusedCli": true, "checkSystemVars": false } }, + + + +{ "send": { "type": "setGlobalSettings", "trace": true } }, +{ "reply": { "type": "setGlobalSettings" } }, + +{ "send": { "type": "globalSettings"} }, +{ "validateGlobalSettings": { "warnUnused": false, "debugOutput": false, "warnUninitialized": false, "traceExpand": false, "trace": true, "warnUnusedCli": true, "checkSystemVars": false } }, + +{ "send": { "type": "setGlobalSettings", "trace": false } }, +{ "reply": { "type": "setGlobalSettings" } }, + +{ "send": { "type": "globalSettings"} }, +{ "validateGlobalSettings": { "warnUnused": false, "debugOutput": false, "warnUninitialized": false, "traceExpand": false, "trace": false, "warnUnusedCli": true, "checkSystemVars": false } }, + +{ "send": { "type": "setGlobalSettings", "warnUnusedCli": false } }, +{ "reply": { "type": "setGlobalSettings" } }, + +{ "send": { "type": "globalSettings"} }, +{ "validateGlobalSettings": { "warnUnused": false, "debugOutput": false, "warnUninitialized": false, "traceExpand": false, "trace": false, "warnUnusedCli": false, "checkSystemVars": false } }, + +{ "send": { "type": "setGlobalSettings", "warnUnusedCli": true } }, +{ "reply": { "type": "setGlobalSettings" } }, + +{ "send": { "type": "globalSettings"} }, +{ "validateGlobalSettings": { "warnUnused": false, "debugOutput": false, "warnUninitialized": false, "traceExpand": false, "trace": false, "warnUnusedCli": true, "checkSystemVars": false } }, + +{ "send": { "type": "setGlobalSettings", "checkSystemVars": true } }, +{ "reply": { "type": "setGlobalSettings" } }, + +{ "send": { "type": "globalSettings"} }, +{ "validateGlobalSettings": { "warnUnused": false, "debugOutput": false, "warnUninitialized": false, "traceExpand": false, "trace": false, "warnUnusedCli": true, "checkSystemVars": true } }, + +{ "send": { "type": "setGlobalSettings", "checkSystemVars": false } }, +{ "reply": { "type": "setGlobalSettings" } }, + +{ "send": { "type": "globalSettings"} }, +{ "validateGlobalSettings": { "warnUnused": false, "debugOutput": false, "warnUninitialized": false, "traceExpand": false, "trace": false, "warnUnusedCli": true, "checkSystemVars": false } }, + +{ "send": { "type": "setGlobalSettings", "warnUnused": true, "debugOutput": true, "warnUninitialized": true, "traceExpand": true, "trace": true, "warnUnusedCli": false, "checkSystemVars": true } }, +{ "reply": { "type": "setGlobalSettings" } }, + +{ "send": { "type": "globalSettings"} }, +{ "validateGlobalSettings": { "warnUnused": true, "debugOutput": true, "warnUninitialized": true, "traceExpand": true, "trace": true, "warnUnusedCli": false, "checkSystemVars": true } }, + +{ "message": "Ignore unknown/readonly" }, + +{ "send": { "type": "setGlobalSettings", "unknownKey": "unknownValue", "extraGenerator": "XXX", "generator": "YYY", "sourceDirectory": "/tmp/source", "buildDirectory": "/tmp/build" } }, +{ "reply": { "type": "setGlobalSettings" } }, + +{ "send": { "type": "globalSettings"} }, +{ "validateGlobalSettings": { "warnUnused": true, "debugOutput": true, "warnUninitialized": true, "traceExpand": true, "trace": true, "warnUnusedCli": false, "checkSystemVars": true } }, + +{ "message": "Error paths:" }, + +{ "send": { "type": "setGlobalSettings", "debugOutput": true, "warnUnused": 1 } }, +{ "error": { "type": "setGlobalSettings", "message": "\"warnUnused\" must be unset or a bool value." } }, + +{ "send": { "type": "setGlobalSettings", "warnUnused": true, "debugOutput": 1 } }, +{ "error": { "type": "setGlobalSettings", "message": "\"debugOutput\" must be unset or a bool value." } }, + +{ "send": { "type": "setGlobalSettings", "warnUninitialized": 1, "warnUnused": true, "debugOutput": true } }, +{ "error": { "type": "setGlobalSettings", "message": "\"warnUninitialized\" must be unset or a bool value." } }, + +{ "send": { "type": "setGlobalSettings", "warnUnused": true, "debugOutput": true, "traceExpand": 1 } }, +{ "error": { "type": "setGlobalSettings", "message": "\"traceExpand\" must be unset or a bool value." } }, + +{ "send": { "type": "setGlobalSettings", "debugOutput": true, "trace": 1, "warnUnused": true } }, +{ "error": { "type": "setGlobalSettings", "message": "\"trace\" must be unset or a bool value." } }, + +{ "send": { "type": "setGlobalSettings", "warnUnused": true, "debugOutput": true, "warnUnusedCli": 1.0 } }, +{ "error": { "type": "setGlobalSettings", "message": "\"warnUnusedCli\" must be unset or a bool value." } }, + +{ "send": { "type": "setGlobalSettings", "warnUnused": true, "debugOutput": true, "checkSystemVars": "some string" } }, +{ "error": { "type": "setGlobalSettings", "message": "\"checkSystemVars\" must be unset or a bool value." } }, + +{ "send": { "type": "globalSettings"} }, +{ "validateGlobalSettings": { "warnUnused": true, "debugOutput": true, "warnUninitialized": true, "traceExpand": true, "trace": true, "warnUnusedCli": false, "checkSystemVars": true } }, + +{ "message": "Everything ok." } +] diff --git a/Tests/SwiftOnly/CMakeLists.txt b/Tests/SwiftOnly/CMakeLists.txt index 5cb9739..cf4463c 100644 --- a/Tests/SwiftOnly/CMakeLists.txt +++ b/Tests/SwiftOnly/CMakeLists.txt @@ -1,4 +1,8 @@ cmake_minimum_required(VERSION 3.3) project(SwiftOnly Swift) +if(NOT XCODE_VERSION VERSION_LESS 8.0) + set(CMAKE_Swift_LANGUAGE_VERSION 3.0) +endif() + add_executable(SwiftOnly main.swift) |