diff options
68 files changed, 1417 insertions, 499 deletions
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 073630e..680d77b 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -284,6 +284,29 @@ upload:macos-package: variables: RSYNC_DESTINATION: dev +build:macos10.10-package: + extends: + - .macos10.10_package + - .cmake_build_macos_package + - .cmake_release_artifacts + - .macos_builder_tags_package + - .run_only_for_package + dependencies: + - prep:doc-package + needs: + - prep:doc-package + +upload:macos10.10-package: + extends: + - .rsync_upload + - .run_only_for_package + dependencies: + - build:macos10.10-package + needs: + - build:macos10.10-package + variables: + RSYNC_DESTINATION: dev + # Windows builds build:windows-vs2019-x64-ninja: diff --git a/.gitlab/artifacts.yml b/.gitlab/artifacts.yml index 76ffd27..87828e1 100644 --- a/.gitlab/artifacts.yml +++ b/.gitlab/artifacts.yml @@ -75,7 +75,7 @@ # Any packages made. - build/cmake-*-Linux-x86_64.* - build/cmake-*-Linux-aarch64.* - - build/cmake-*-macos-universal.* + - build/cmake-*-macos*-universal.* # Any source packages made. - build/cmake-*.tar.gz - build/cmake-*.zip diff --git a/.gitlab/ci/configure_macos10.10_package.cmake b/.gitlab/ci/configure_macos10.10_package.cmake new file mode 100644 index 0000000..f01e6c8 --- /dev/null +++ b/.gitlab/ci/configure_macos10.10_package.cmake @@ -0,0 +1,4 @@ +set(CPACK_SYSTEM_NAME "macos10.10-universal" CACHE STRING "") +set(CMAKE_OSX_DEPLOYMENT_TARGET "10.10" CACHE STRING "") + +include("${CMAKE_CURRENT_LIST_DIR}/configure_macos_package_common.cmake") diff --git a/.gitlab/ci/configure_macos_package.cmake b/.gitlab/ci/configure_macos_package.cmake index a1dbdb9..380e44c 100644 --- a/.gitlab/ci/configure_macos_package.cmake +++ b/.gitlab/ci/configure_macos_package.cmake @@ -1,28 +1,4 @@ -set(CMake_DOC_ARTIFACT_PREFIX "$ENV{CI_PROJECT_DIR}/build/install-doc" CACHE PATH "") - -# Set up install destinations as expected by the packaging scripts. -set(CMAKE_INSTALL_PREFIX "/" CACHE PATH "") -set(CMAKE_DOC_DIR "doc/cmake" CACHE STRING "") - -# Settings for CMake packages for macOS. -set(CPACK_DMG_FORMAT "UDBZ" CACHE STRING "") -set(CMAKE_CXX_FLAGS "-stdlib=libc++" CACHE STRING "") -set(CMAKE_C_STANDARD "11" CACHE STRING "") -set(CMAKE_CXX_STANDARD "14" CACHE STRING "") -set(CMAKE_OSX_ARCHITECTURES "x86_64;arm64" CACHE STRING "") -set(CMAKE_OSX_DEPLOYMENT_TARGET "10.10" CACHE STRING "") -set(CMAKE_SKIP_BOOTSTRAP_TEST "TRUE" CACHE STRING "") set(CPACK_SYSTEM_NAME "macos-universal" CACHE STRING "") -set(BUILD_CursesDialog "ON" CACHE BOOL "") -set(BUILD_QtDialog "TRUE" CACHE BOOL "") -set(CMake_GUI_DISTRIBUTE_WITH_Qt_LGPL "3" CACHE STRING "") -set(CMake_INSTALL_DEPENDENCIES "ON" CACHE BOOL "") -set(CMAKE_SKIP_RPATH "TRUE" CACHE BOOL "") -set(CMake_TEST_NO_FindPackageModeMakefileTest "TRUE" CACHE BOOL "") - -# XXX(sccache): restore sccache when it works for multiple architectures: -# https://github.com/mozilla/sccache/issues/847 -set(configure_no_sccache 1) +set(CMAKE_OSX_DEPLOYMENT_TARGET "10.13" CACHE STRING "") -include("${CMAKE_CURRENT_LIST_DIR}/configure_macos_common.cmake") -include("${CMAKE_CURRENT_LIST_DIR}/configure_common.cmake") +include("${CMAKE_CURRENT_LIST_DIR}/configure_macos_package_common.cmake") diff --git a/.gitlab/ci/configure_macos_package_common.cmake b/.gitlab/ci/configure_macos_package_common.cmake new file mode 100644 index 0000000..3aa8ae2 --- /dev/null +++ b/.gitlab/ci/configure_macos_package_common.cmake @@ -0,0 +1,26 @@ +set(CMake_DOC_ARTIFACT_PREFIX "$ENV{CI_PROJECT_DIR}/build/install-doc" CACHE PATH "") + +# Set up install destinations as expected by the packaging scripts. +set(CMAKE_INSTALL_PREFIX "/" CACHE PATH "") +set(CMAKE_DOC_DIR "doc/cmake" CACHE STRING "") + +# Settings for CMake packages for macOS. +set(CPACK_DMG_FORMAT "UDBZ" CACHE STRING "") +set(CMAKE_CXX_FLAGS "-stdlib=libc++" CACHE STRING "") +set(CMAKE_C_STANDARD "11" CACHE STRING "") +set(CMAKE_CXX_STANDARD "14" CACHE STRING "") +set(CMAKE_OSX_ARCHITECTURES "x86_64;arm64" CACHE STRING "") +set(CMAKE_SKIP_BOOTSTRAP_TEST "TRUE" CACHE STRING "") +set(BUILD_CursesDialog "ON" CACHE BOOL "") +set(BUILD_QtDialog "TRUE" CACHE BOOL "") +set(CMake_GUI_DISTRIBUTE_WITH_Qt_LGPL "3" CACHE STRING "") +set(CMake_INSTALL_DEPENDENCIES "ON" CACHE BOOL "") +set(CMAKE_SKIP_RPATH "TRUE" CACHE BOOL "") +set(CMake_TEST_NO_FindPackageModeMakefileTest "TRUE" CACHE BOOL "") + +# XXX(sccache): restore sccache when it works for multiple architectures: +# https://github.com/mozilla/sccache/issues/847 +set(configure_no_sccache 1) + +include("${CMAKE_CURRENT_LIST_DIR}/configure_macos_common.cmake") +include("${CMAKE_CURRENT_LIST_DIR}/configure_common.cmake") diff --git a/.gitlab/ci/download_qt.cmake b/.gitlab/ci/download_qt.cmake index 4a33f12..76c693d 100644 --- a/.gitlab/ci/download_qt.cmake +++ b/.gitlab/ci/download_qt.cmake @@ -48,7 +48,11 @@ if (qt_platform STREQUAL "windows_x86") set(qt_subdir "${qt_version}/msvc${msvc_year}_64") elseif (qt_platform STREQUAL "mac_x64") - if ("$ENV{CMAKE_CONFIGURATION}" MATCHES "package") + if ("$ENV{CMAKE_CONFIGURATION}" MATCHES "macos_package") + list(APPEND qt_files + "qt-5.15.2-macosx10.13-x86_64-arm64.tar.xz") + set(qt_subdir "qt-5.15.2-macosx10.13-x86_64-arm64") + elseif ("$ENV{CMAKE_CONFIGURATION}" MATCHES "macos10.10_package") list(APPEND qt_files "qt-5.9.9-macosx10.10-x86_64-arm64.tar.xz") set(qt_subdir "qt-5.9.9-macosx10.10-x86_64-arm64") diff --git a/.gitlab/ci/download_qt_hashes.cmake b/.gitlab/ci/download_qt_hashes.cmake index 832fa98..afbc081 100644 --- a/.gitlab/ci/download_qt_hashes.cmake +++ b/.gitlab/ci/download_qt_hashes.cmake @@ -11,3 +11,4 @@ set("5.15.1-0-202009071110qtwinextras-Windows-Windows_10-MSVC2015-Windows-Window set("5.15.1-0-202009071110qtbase-MacOS-MacOS_10_13-Clang-MacOS-MacOS_10_13-X86_64.7z_hash" df2813ce7c6cb4287abd7956cd1cb9d08312e4ac1208b6cb57af4df11b8ebba1) set("qt-5.9.9-macosx10.10-x86_64-arm64.tar.xz_hash" d4449771afa0bc6a809c14f1e6d939e7732494cf059503ae451e2bfe8fc60cc1) +set("qt-5.15.2-macosx10.13-x86_64-arm64.tar.xz_hash" 7b9463a01c8beeee5bf8d01c70deff2d08561cd20aaf6f7a2f41cf8b68ce8a6b) diff --git a/.gitlab/os-macos.yml b/.gitlab/os-macos.yml index 7819e3f..450bae7 100644 --- a/.gitlab/os-macos.yml +++ b/.gitlab/os-macos.yml @@ -47,6 +47,14 @@ CTEST_NO_WARNINGS_ALLOWED: 1 CMake_SKIP_INSTALL: 1 +.macos10.10_package: + extends: .macos_build + + variables: + CMAKE_CONFIGURATION: macos10.10_package + CTEST_NO_WARNINGS_ALLOWED: 1 + CMake_SKIP_INSTALL: 1 + ### External testing .macos_xcode: diff --git a/.gitlab/upload.yml b/.gitlab/upload.yml index 6bfa763..8b8daa1 100644 --- a/.gitlab/upload.yml +++ b/.gitlab/upload.yml @@ -15,4 +15,4 @@ - dnf install -y --setopt=install_weak_deps=False rsync openssh-clients - chmod 400 $RSYNC_BINARY_KEY - ssh-keygen -y -f $RSYNC_BINARY_KEY > $RSYNC_BINARY_KEY.pub - - rsync -tv --recursive -e "ssh -i $RSYNC_BINARY_KEY -o StrictHostKeyChecking=no -o LogLevel=ERROR" build/ kitware@public.kitware.com:$RSYNC_DESTINATION/ + - rsync -tv --recursive -e "ssh -i $RSYNC_BINARY_KEY -o StrictHostKeyChecking=no -o LogLevel=ERROR" build/ kitware@cmake.org:$RSYNC_DESTINATION/ diff --git a/Help/dev/experimental.rst b/Help/dev/experimental.rst index 4d2b076..d019161 100644 --- a/Help/dev/experimental.rst +++ b/Help/dev/experimental.rst @@ -7,4 +7,64 @@ See documentation on `CMake Development`_ for more information. .. _`CMake Development`: README.rst -No experimental features are under development in this version of CMake. +C++20 Module Dependencies +========================= + +The Ninja generator has experimental infrastructure supporting C++20 module +dependency scanning. This is similar to the Fortran modules support, but +relies on external tools to scan C++20 translation units for module +dependencies. The approach is described by Kitware's `D1483r1`_ paper. + +The ``CMAKE_EXPERIMENTAL_CXX_MODULE_DYNDEP`` variable can be set to ``1`` +in order to activate this undocumented experimental infrastructure. This +is **intended to make the functionality available to compiler writers** so +they can use it to develop and test their dependency scanning tool. +The ``CMAKE_EXPERIMENTAL_CXX_SCANDEP_SOURCE`` variable must also be set +to tell CMake how to invoke the C++20 module dependency scanning tool. + +For example, add code like the following to a test project: + +.. code-block:: cmake + + set(CMAKE_EXPERIMENTAL_CXX_MODULE_DYNDEP 1) + string(CONCAT CMAKE_EXPERIMENTAL_CXX_SCANDEP_SOURCE + "<CMAKE_CXX_COMPILER> <DEFINES> <INCLUDES> <FLAGS> <SOURCE>" + " -MT <DYNDEP_FILE> -MD -MF <DEP_FILE>" + " ${flags_to_scan_deps} -fdep-file=<DYNDEP_FILE> -fdep-output=<OBJECT>" + ) + +The tool specified by ``CMAKE_EXPERIMENTAL_CXX_SCANDEP_SOURCE`` is +expected to process the translation unit, write preprocessor dependencies +to the file specified by the ``<DEP_FILE>`` placeholder, and write module +dependencies to the file specified by the ``<DYNDEP_FILE>`` placeholder. + +The module dependencies should be written in the format described +by the `P1689r3`_ paper. + +Compiler writers may try out their scanning functionality using +the `cxx-modules-sandbox`_ test project, modified to set variables +as above for their compiler. + +For compilers that generate module maps, tell CMake as follows: + +.. code-block:: cmake + + set(CMAKE_EXPERIMENTAL_CXX_MODULE_MAP_FORMAT "gcc") + set(CMAKE_EXPERIMENTAL_CXX_MODULE_MAP_FLAG + "${compiler_flags_for_module_map} -fmodule-mapper=<MODULE_MAP_FILE>") + +Currently, the only supported format is ``gcc``. The format is described in +the GCC documentation, but the relevant section for the purposes of CMake is: + + A mapping file consisting of space-separated module-name, filename + pairs, one per line. Only the mappings for the direct imports and any + module export name need be provided. If other mappings are provided, + they override those stored in any imported CMI files. A repository + root may be specified in the mapping file by using ``$root`` as the + module name in the first active line. + + -- GCC module mapper documentation + +.. _`D1483r1`: https://mathstuf.fedorapeople.org/fortran-modules/fortran-modules.html +.. _`P1689r3`: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2020/p1689r3.html +.. _`cxx-modules-sandbox`: https://github.com/mathstuf/cxx-modules-sandbox diff --git a/Help/guide/importing-exporting/MathFunctions/CMakeLists.txt b/Help/guide/importing-exporting/MathFunctions/CMakeLists.txt index 13c82dd..9a9e40e 100644 --- a/Help/guide/importing-exporting/MathFunctions/CMakeLists.txt +++ b/Help/guide/importing-exporting/MathFunctions/CMakeLists.txt @@ -31,7 +31,7 @@ install(FILES MathFunctions.h DESTINATION include) install(EXPORT MathFunctionsTargets FILE MathFunctionsTargets.cmake NAMESPACE MathFunctions:: - DESTINATION lib/cmake + DESTINATION lib/cmake/MathFunctions ) # include CMakePackageConfigHelpers macro @@ -58,14 +58,14 @@ write_basic_package_version_file( # create config file configure_package_config_file(${CMAKE_CURRENT_SOURCE_DIR}/Config.cmake.in "${CMAKE_CURRENT_BINARY_DIR}/MathFunctionsConfig.cmake" - INSTALL_DESTINATION lib/cmake + INSTALL_DESTINATION lib/cmake/MathFunctions ) # install config files install(FILES "${CMAKE_CURRENT_BINARY_DIR}/MathFunctionsConfig.cmake" "${CMAKE_CURRENT_BINARY_DIR}/MathFunctionsConfigVersion.cmake" - DESTINATION lib/cmake + DESTINATION lib/cmake/MathFunctions ) # generate the export targets for the build tree diff --git a/Help/manual/cmake-properties.7.rst b/Help/manual/cmake-properties.7.rst index 5dbc1a4..af170da 100644 --- a/Help/manual/cmake-properties.7.rst +++ b/Help/manual/cmake-properties.7.rst @@ -196,6 +196,7 @@ Properties on Targets /prop_tgt/EXCLUDE_FROM_ALL /prop_tgt/EXCLUDE_FROM_DEFAULT_BUILD /prop_tgt/EXCLUDE_FROM_DEFAULT_BUILD_CONFIG + /prop_tgt/EXPORT_COMPILE_COMMANDS /prop_tgt/EXPORT_NAME /prop_tgt/EXPORT_PROPERTIES /prop_tgt/FOLDER diff --git a/Help/prop_tgt/EXPORT_COMPILE_COMMANDS.rst b/Help/prop_tgt/EXPORT_COMPILE_COMMANDS.rst new file mode 100644 index 0000000..0b1145c --- /dev/null +++ b/Help/prop_tgt/EXPORT_COMPILE_COMMANDS.rst @@ -0,0 +1,9 @@ +EXPORT_COMPILE_COMMANDS +----------------------- + +.. versionadded:: 3.20 + +Enable/Disable output of compile commands during generation for a target. + +This property is initialized by the value of the variable +:variable:`CMAKE_EXPORT_COMPILE_COMMANDS` if it is set when a target is created. diff --git a/Help/release/3.19.rst b/Help/release/3.19.rst index eb49c6f..7ad27c8 100644 --- a/Help/release/3.19.rst +++ b/Help/release/3.19.rst @@ -390,3 +390,13 @@ Changes made since CMake 3.19.0 include the following. ------ * A precompiled Linux ``aarch64`` binary is now provided on ``cmake.org``. + +* Two precompiled macOS binaries are now provided on ``cmake.org``: + + * The naming pattern ``cmake-$ver-macos-universal`` is a universal + binary with ``x86_64`` and ``arm64`` architectures. It requires + macOS 10.13 or newer. + + * The naming pattern ``cmake-$ver-macos10.10-universal`` is a universal + binary with ``x86_64`` and ``arm64`` architectures. It requires + macOS 10.10 or newer. diff --git a/Help/release/dev/export-compile-commands-per-target.rst b/Help/release/dev/export-compile-commands-per-target.rst new file mode 100644 index 0000000..7063547 --- /dev/null +++ b/Help/release/dev/export-compile-commands-per-target.rst @@ -0,0 +1,6 @@ +export-compile-commands-per-target +---------------------------------- + +* The :prop_tgt:`EXPORT_COMPILE_COMMANDS` target property was added + for the associated :variable:`CMAKE_EXPORT_COMPILE_COMMANDS` variable + to allow for configuration of exporting compile commands per target. diff --git a/Help/variable/CMAKE_EXPORT_COMPILE_COMMANDS.rst b/Help/variable/CMAKE_EXPORT_COMPILE_COMMANDS.rst index 724f309..53a19dc 100644 --- a/Help/variable/CMAKE_EXPORT_COMPILE_COMMANDS.rst +++ b/Help/variable/CMAKE_EXPORT_COMPILE_COMMANDS.rst @@ -28,7 +28,8 @@ form. The format of the JSON file looks like: ] This is initialized by the :envvar:`CMAKE_EXPORT_COMPILE_COMMANDS` environment -variable. +variable, and initializes the :prop_tgt:`EXPORT_COMPILE_COMMANDS` target +property for all targets. .. note:: This option is implemented only by :ref:`Makefile Generators` diff --git a/Modules/CMakeCXXCompiler.cmake.in b/Modules/CMakeCXXCompiler.cmake.in index 92ae2ab..45acfe7 100644 --- a/Modules/CMakeCXXCompiler.cmake.in +++ b/Modules/CMakeCXXCompiler.cmake.in @@ -44,7 +44,7 @@ if(CMAKE_COMPILER_IS_MINGW) set(MINGW 1) endif() set(CMAKE_CXX_COMPILER_ID_RUN 1) -set(CMAKE_CXX_SOURCE_FILE_EXTENSIONS C;M;c++;cc;cpp;cxx;m;mm;CPP) +set(CMAKE_CXX_SOURCE_FILE_EXTENSIONS C;M;c++;cc;cpp;cxx;m;mm;mpp;CPP) set(CMAKE_CXX_IGNORE_EXTENSIONS inl;h;hpp;HPP;H;o;O;obj;OBJ;def;DEF;rc;RC) foreach (lang C OBJC OBJCXX) diff --git a/Source/CMakeVersion.cmake b/Source/CMakeVersion.cmake index 4598fe4..3d136ad 100644 --- a/Source/CMakeVersion.cmake +++ b/Source/CMakeVersion.cmake @@ -1,7 +1,7 @@ # CMake version number components. set(CMake_VERSION_MAJOR 3) set(CMake_VERSION_MINOR 19) -set(CMake_VERSION_PATCH 20210106) +set(CMake_VERSION_PATCH 20210108) #set(CMake_VERSION_RC 0) set(CMake_VERSION_IS_DIRTY 0) diff --git a/Source/CTest/cmCTestRunTest.cxx b/Source/CTest/cmCTestRunTest.cxx index 4ba0ae1..5a6c775 100644 --- a/Source/CTest/cmCTestRunTest.cxx +++ b/Source/CTest/cmCTestRunTest.cxx @@ -139,7 +139,7 @@ bool cmCTestRunTest::EndTest(size_t completed, size_t total, bool started) s << "SKIP_RETURN_CODE=" << this->TestProperties->SkipReturnCode; } this->TestResult.CompletionStatus = s.str(); - cmCTestLog(this->CTest, HANDLER_OUTPUT, "***Skipped "); + outputStream << "***Skipped "; skipped = true; } else if (success != this->TestProperties->WillFail) { this->TestResult.Status = cmCTestTestHandler::COMPLETED; @@ -195,8 +195,9 @@ bool cmCTestRunTest::EndTest(size_t completed, size_t total, bool started) sprintf(buf, "%6.2f sec", this->TestProcess->GetTotalTime().count()); outputStream << buf << "\n"; + bool passedOrSkipped = passed || skipped; if (this->CTest->GetTestProgressOutput()) { - if (!passed) { + if (!passedOrSkipped) { // If the test did not pass, reprint test name and error std::string output = this->GetTestPrefix(completed, total); std::string testName = this->TestProperties->Name; @@ -216,7 +217,7 @@ bool cmCTestRunTest::EndTest(size_t completed, size_t total, bool started) cmCTestLog(this->CTest, HANDLER_TEST_PROGRESS_OUTPUT, testName); } } - if (!this->CTest->GetTestProgressOutput() || !passed) { + if (!this->CTest->GetTestProgressOutput() || !passedOrSkipped) { cmCTestLog(this->CTest, HANDLER_OUTPUT, outputStream.str()); } diff --git a/Source/QtDialog/CMakeLists.txt b/Source/QtDialog/CMakeLists.txt index 452a303..d8f89d6 100644 --- a/Source/QtDialog/CMakeLists.txt +++ b/Source/QtDialog/CMakeLists.txt @@ -3,20 +3,52 @@ project(QtDialog) CMake_OPTIONAL_COMPONENT(cmake-gui) -find_package(Qt5Widgets REQUIRED) +set (QT_COMPONENTS + Core + Widgets + Gui +) + +set(CMake_QT_MAJOR_VERSION "A" CACHE + STRING "Expected Qt major version. Valid values are A (auto-select), 5, 6.") +set(SUPPORTED_QT_VERSIONS "A" 5 6) +set_property(CACHE CMake_QT_MAJOR_VERSION PROPERTY STRINGS ${SUPPORTED_QT_VERSIONS}) +if(NOT CMake_QT_MAJOR_VERSION STREQUAL "A") + if(NOT CMake_QT_MAJOR_VERSION IN_LIST SUPPORTED_QT_VERSIONS) + message(FATAL_ERROR "Supported Qt versions are \"${SUPPORTED_QT_VERSIONS}\"." + " But CMake_QT_MAJOR_VERSION is set to ${CMake_QT_MAJOR_VERSION}.") + endif() + set(INSTALLED_QT_VERSION ${CMake_QT_MAJOR_VERSION}) +else() + find_package(Qt6Widgets QUIET) + set(INSTALLED_QT_VERSION 6) + if(NOT Qt6Widgets_FOUND) + find_package(Qt5Widgets QUIET) + if(NOT Qt5Widgets_FOUND) + message(FATAL_ERROR "Could not find a valid Qt installation.") + endif() + set(INSTALLED_QT_VERSION 5) + endif() +endif() + +find_package(Qt${INSTALLED_QT_VERSION} + COMPONENTS ${QT_COMPONENTS} + REQUIRED QUIET +) set(CMake_QT_EXTRA_LIBRARIES) # Try to find the package WinExtras for the task bar progress if(WIN32) - find_package(Qt5WinExtras QUIET) - if (Qt5WinExtras_FOUND) + find_package(Qt${INSTALLED_QT_VERSION}WinExtras QUIET) + if (Qt${INSTALLED_QT_VERSION}WinExtras_FOUND) add_definitions(-DQT_WINEXTRAS) - list(APPEND CMake_QT_EXTRA_LIBRARIES Qt5::WinExtras) + list(APPEND CMake_QT_EXTRA_LIBRARIES Qt${INSTALLED_QT_VERSION}::WinExtras) + list(APPEND QT_COMPONENTS WinExtras) endif() endif() -set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${Qt5Widgets_EXECUTABLE_COMPILE_FLAGS}") +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${Qt${INSTALLED_QT_VERSION}Widgets_EXECUTABLE_COMPILE_FLAGS}") if(CMake_QT_STATIC_QXcbIntegrationPlugin_LIBRARIES) list(APPEND CMake_QT_EXTRA_LIBRARIES ${CMake_QT_STATIC_QXcbIntegrationPlugin_LIBRARIES}) @@ -31,11 +63,25 @@ if(CMake_QT_STATIC_QWindowsIntegrationPlugin_LIBRARIES) endif() # We need to install platform plugin and add qt.conf for Qt5 on Mac and Windows. -# FIXME: This should be part of Qt5 CMake scripts, but unfortunately -# Qt5 support is missing there. if(CMake_INSTALL_DEPENDENCIES AND (APPLE OR WIN32)) - macro(install_qt5_plugin _qt_plugin_name _qt_plugins_var) - get_target_property(_qt_plugin_path "${_qt_plugin_name}" LOCATION) + function(_qt_get_plugin_name_with_version target out_var) + string(REGEX REPLACE "^Qt::(.+)" "Qt${INSTALLED_QT_VERSION}::\\1" + qt_plugin_with_version "${target}") + if(TARGET "${qt_plugin_with_version}") + set("${out_var}" "${qt_plugin_with_version}" PARENT_SCOPE) + else() + set("${out_var}" "" PARENT_SCOPE) + endif() + endfunction() + macro(install_qt_plugin _qt_plugin_name _qt_plugins_var) + if(TARGET "${_qt_plugin_name}") + get_target_property(_qt_plugin_path "${_qt_plugin_name}" LOCATION) + else() + _qt_get_plugin_name_with_version("Qt::${_qt_plugin_name}" _qt_plugin_with_version_name) + if(TARGET "${_qt_plugin_with_version_name}") + get_target_property(_qt_plugin_path "${_qt_plugin_with_version_name}" LOCATION) + endif() + endif() if(EXISTS "${_qt_plugin_path}") get_filename_component(_qt_plugin_file "${_qt_plugin_path}" NAME) get_filename_component(_qt_plugin_type "${_qt_plugin_path}" PATH) @@ -51,19 +97,47 @@ if(CMake_INSTALL_DEPENDENCIES AND (APPLE OR WIN32)) ${COMPONENT}) set(${_qt_plugins_var} "${${_qt_plugins_var}};\$ENV{DESTDIR}\${CMAKE_INSTALL_PREFIX}/${_qt_plugin_dest}/${_qt_plugin_file}") - else() - message(FATAL_ERROR "QT plugin ${_qt_plugin_name} not found") endif() endmacro() + macro(install_qt_plugins _comps _plugins_var) + foreach(_qt_comp ${${_comps}}) + if (INSTALLED_QT_VERSION VERSION_LESS 6) + set(_qt_module_plugins ${Qt${INSTALLED_QT_VERSION}${_qt_comp}_PLUGINS}) + else() + get_target_property(_qt_module_plugins Qt${INSTALLED_QT_VERSION}::${_qt_comp} QT_PLUGINS) + endif() + foreach(_qt_plugin ${_qt_module_plugins}) + if (INSTALLED_QT_VERSION VERSION_GREATER_EQUAL 6) + # Qt6 provides the plugins as individual packages that need to be found. + find_package(Qt${INSTALLED_QT_VERSION}${_qt_plugin} QUIET + PATHS ${Qt${INSTALLED_QT_VERSION}${_qt_comp}_DIR}) + endif() + install_qt_plugin("${_qt_plugin}" "${_plugins_var}") + endforeach() + endforeach() + endmacro() if(APPLE) - install_qt5_plugin("Qt5::QCocoaIntegrationPlugin" QT_PLUGINS) + if (INSTALLED_QT_VERSION VERSION_EQUAL 5) + install_qt_plugin("Qt5::QCocoaIntegrationPlugin" QT_PLUGINS) + if(TARGET Qt5::QMacStylePlugin) + install_qt_plugin("Qt5::QMacStylePlugin" QT_PLUGINS) + endif() + else() + # FIXME: Minimize plugins for Qt6. + install_qt_plugins(QT_COMPONENTS QT_PLUGINS) + endif() file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/qt.conf" "[Paths]\nPlugins = ${_qt_plugin_dir}\n") install(FILES "${CMAKE_CURRENT_BINARY_DIR}/qt.conf" DESTINATION "${CMAKE_INSTALL_PREFIX}/Resources" ${COMPONENT}) elseif(WIN32 AND NOT CMake_QT_STATIC_QWindowsIntegrationPlugin_LIBRARIES) - install_qt5_plugin("Qt5::QWindowsIntegrationPlugin" QT_PLUGINS) + if (INSTALLED_QT_VERSION VERSION_EQUAL 5) + install_qt_plugin("Qt5::QWindowsIntegrationPlugin" QT_PLUGINS) + else() + # FIXME: Minimize plugins for Qt6. + install_qt_plugins(QT_COMPONENTS QT_PLUGINS) + endif() file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/qt.conf" "[Paths]\nPlugins = ../${_qt_plugin_dir}\n") install(FILES "${CMAKE_CURRENT_BINARY_DIR}/qt.conf" @@ -72,8 +146,8 @@ if(CMake_INSTALL_DEPENDENCIES AND (APPLE OR WIN32)) endif() endif() -get_property(_Qt5_Core_LOCATION TARGET Qt5::Core PROPERTY LOCATION) -get_filename_component(Qt_BIN_DIR "${_Qt5_Core_LOCATION}" PATH) +get_property(_Qt_Core_LOCATION TARGET Qt${INSTALLED_QT_VERSION}::Core PROPERTY LOCATION) +get_filename_component(Qt_BIN_DIR "${_Qt_Core_LOCATION}" PATH) if(APPLE) get_filename_component(Qt_BIN_DIR "${Qt_BIN_DIR}" PATH) endif() @@ -105,7 +179,7 @@ set(SRCS WarningMessagesDialog.cxx WarningMessagesDialog.h ) -qt5_wrap_ui(UI_SRCS +set(UI_SRCS CMakeSetupDialog.ui Compilers.ui CrossCompiler.ui @@ -114,7 +188,7 @@ qt5_wrap_ui(UI_SRCS RegexExplorer.ui WarningMessagesDialog.ui ) -qt5_wrap_cpp(MOC_SRCS +set(MOC_SRCS AddCacheEntry.h Compilers.h CMakeSetupDialog.h @@ -128,7 +202,17 @@ qt5_wrap_cpp(MOC_SRCS RegexExplorer.h WarningMessagesDialog.h ) -qt5_add_resources(RC_SRCS CMakeSetup.qrc) +set(QRC_SRCS CMakeSetup.qrc) + +if (INSTALLED_QT_VERSION VERSION_LESS 6) + qt5_wrap_ui(UI_BUILT_SRCS ${UI_SRCS}) + qt5_wrap_cpp(MOC_BUILT_SRCS ${MOC_SRCS}) + qt5_add_resources(QRC_BUILT_SRCS ${QRC_SRCS}) +else() + qt_wrap_ui(UI_BUILT_SRCS ${UI_SRCS}) + qt_wrap_cpp(MOC_BUILT_SRCS ${MOC_SRCS}) + qt_add_resources(QRC_BUILT_SRCS ${QRC_SRCS}) +endif() if (FALSE) # CMake's bootstrap binary does not support automoc set(CMAKE_AUTOMOC 1) @@ -136,9 +220,9 @@ if (FALSE) # CMake's bootstrap binary does not support automoc set(CMAKE_AUTOUIC 1) else () list(APPEND SRCS - ${UI_SRCS} - ${MOC_SRCS} - ${RC_SRCS}) + ${UI_BUILT_SRCS} + ${MOC_BUILT_SRCS} + ${QRC_BUILT_SRCS}) endif () if(USE_LGPL) @@ -153,13 +237,14 @@ set(CMAKE_INCLUDE_CURRENT_DIR ON) add_library(CMakeGUILib STATIC ${SRCS}) # CMake_QT_EXTRA_LIBRARIES have to come before the main libraries on the link line -target_link_libraries(CMakeGUILib PUBLIC CMakeLib ${CMake_QT_EXTRA_LIBRARIES} Qt5::Core Qt5::Widgets) +target_link_libraries(CMakeGUILib PUBLIC CMakeLib ${CMake_QT_EXTRA_LIBRARIES} + Qt${INSTALLED_QT_VERSION}::Core Qt${INSTALLED_QT_VERSION}::Widgets) add_library(CMakeGUIMainLib STATIC CMakeSetup.cxx) target_link_libraries(CMakeGUIMainLib PUBLIC CMakeGUILib) add_executable(cmake-gui WIN32 MACOSX_BUNDLE CMakeGUIExec.cxx ${MANIFEST_FILE}) -target_link_libraries(cmake-gui CMakeGUIMainLib Qt5::Core) +target_link_libraries(cmake-gui CMakeGUIMainLib Qt${INSTALLED_QT_VERSION}::Core) if(WIN32) target_sources(CMakeGUIMainLib INTERFACE $<TARGET_OBJECTS:CMakeVersion> CMakeSetup.rc) diff --git a/Source/QtDialog/CMakeSetup.cxx b/Source/QtDialog/CMakeSetup.cxx index 861c6e3..5debdb8 100644 --- a/Source/QtDialog/CMakeSetup.cxx +++ b/Source/QtDialog/CMakeSetup.cxx @@ -7,7 +7,6 @@ #include <QDir> #include <QLocale> #include <QString> -#include <QTextCodec> #include <QTranslator> #include <QtPlugin> @@ -122,9 +121,6 @@ int main(int argc, char** argv) setlocale(LC_NUMERIC, "C"); - QTextCodec* utf8_codec = QTextCodec::codecForName("UTF-8"); - QTextCodec::setCodecForLocale(utf8_codec); - // tell the cmake library where cmake is QDir cmExecDir(QApplication::applicationDirPath()); #if defined(Q_OS_MAC) diff --git a/Source/QtDialog/EnvironmentDialog.cxx b/Source/QtDialog/EnvironmentDialog.cxx index 846456c..d4f978c 100644 --- a/Source/QtDialog/EnvironmentDialog.cxx +++ b/Source/QtDialog/EnvironmentDialog.cxx @@ -81,7 +81,11 @@ bool EnvironmentSearchFilter::filterAcceptsRow(int row, auto* model = this->sourceModel(); auto key = model->data(model->index(row, 0, parent), Qt::DisplayRole).toString(); +#if (QT_VERSION >= QT_VERSION_CHECK(5, 12, 0)) + return key.contains(this->filterRegularExpression()); +#else return key.contains(this->filterRegExp()); +#endif } EnvironmentDialog::EnvironmentDialog(const QProcessEnvironment& environment, diff --git a/Source/QtDialog/QCMakeCacheView.cxx b/Source/QtDialog/QCMakeCacheView.cxx index 22f5be1..7c9032e 100644 --- a/Source/QtDialog/QCMakeCacheView.cxx +++ b/Source/QtDialog/QCMakeCacheView.cxx @@ -47,7 +47,11 @@ protected: // check all strings for a match foreach (QString const& str, strs) { +#if (QT_VERSION >= QT_VERSION_CHECK(5, 12, 0)) + if (str.contains(this->filterRegularExpression())) { +#else if (str.contains(this->filterRegExp())) { +#endif return true; } } diff --git a/Source/QtDialog/QCMakeWidgets.cxx b/Source/QtDialog/QCMakeWidgets.cxx index e68faba..ca65d13 100644 --- a/Source/QtDialog/QCMakeWidgets.cxx +++ b/Source/QtDialog/QCMakeWidgets.cxx @@ -1,20 +1,23 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -// FIXME: Port to QFileSystemModel from the deprecated QDirModel. -// Be sure completion works when incrementally editing existing paths. #define QT_DEPRECATED_WARNINGS_SINCE QT_VERSION_CHECK(5, 14, 0) #include "QCMakeWidgets.h" #include <utility> -#include <QDirModel> #include <QFileDialog> #include <QFileInfo> #include <QResizeEvent> #include <QToolButton> +#if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)) +# include <QFileSystemModel> +#else +# include <QDirModel> +#endif + QCMakeFileEditor::QCMakeFileEditor(QWidget* p, QString var) : QLineEdit(p) , Variable(std::move(var)) @@ -93,8 +96,30 @@ void QCMakePathEditor::chooseFile() } } +#if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)) +// use same QFileSystemModel for all completers +static QFileSystemModel* fileDirModel() + +{ + static QFileSystemModel* m = nullptr; + if (!m) { + m = new QFileSystemModel(); + } + return m; +} +static QFileSystemModel* pathDirModel() +{ + static QFileSystemModel* m = nullptr; + if (!m) { + m = new QFileSystemModel(); + m->setFilter(QDir::AllDirs | QDir::Drives | QDir::NoDotAndDotDot); + } + return m; +} +#else // use same QDirModel for all completers static QDirModel* fileDirModel() + { static QDirModel* m = nullptr; if (!m) { @@ -111,12 +136,19 @@ static QDirModel* pathDirModel() } return m; } +#endif QCMakeFileCompleter::QCMakeFileCompleter(QObject* o, bool dirs) : QCompleter(o) { +#if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)) + QFileSystemModel* m = dirs ? pathDirModel() : fileDirModel(); + this->setModel(m); + m->setRootPath(QString()); +#else QDirModel* m = dirs ? pathDirModel() : fileDirModel(); this->setModel(m); +#endif } QString QCMakeFileCompleter::pathFromIndex(const QModelIndex& idx) const diff --git a/Source/cmCTest.cxx b/Source/cmCTest.cxx index 6507f38..6c1071d 100644 --- a/Source/cmCTest.cxx +++ b/Source/cmCTest.cxx @@ -2901,7 +2901,7 @@ bool cmCTest::GetFailover() const bool cmCTest::GetTestProgressOutput() const { - return this->Impl->TestProgressOutput; + return this->Impl->TestProgressOutput && !GetExtraVerbose(); } bool cmCTest::GetVerbose() const diff --git a/Source/cmCommandLineArgument.h b/Source/cmCommandLineArgument.h new file mode 100644 index 0000000..cbedf0a --- /dev/null +++ b/Source/cmCommandLineArgument.h @@ -0,0 +1,159 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing for details. */ +#pragma once + +#include "cmStringAlgorithms.h" +#include "cmSystemTools.h" + +template <typename FunctionSignature> +struct cmCommandLineArgument +{ + enum class Values + { + Zero, + One, + Two, + ZeroOrOne, + OneOrMore + }; + + std::string InvalidSyntaxMessage; + std::string InvalidValueMessage; + std::string Name; + Values Type; + std::function<FunctionSignature> StoreCall; + + template <typename FunctionType> + cmCommandLineArgument(std::string n, Values t, FunctionType&& func) + : InvalidSyntaxMessage(cmStrCat("Invalid syntax used with ", n)) + , InvalidValueMessage(cmStrCat("Invalid value used with ", n)) + , Name(std::move(n)) + , Type(t) + , StoreCall(std::forward<FunctionType>(func)) + { + } + + template <typename FunctionType> + cmCommandLineArgument(std::string n, std::string failedMsg, Values t, + FunctionType&& func) + : InvalidSyntaxMessage(cmStrCat("Invalid syntax used with ", n)) + , InvalidValueMessage(std::move(failedMsg)) + , Name(std::move(n)) + , Type(t) + , StoreCall(std::forward<FunctionType>(func)) + { + } + + bool matches(std::string const& input) const + { + return (this->Type == Values::Zero) ? (input == this->Name) + : cmHasPrefix(input, this->Name); + } + + template <typename T, typename... CallState> + bool parse(std::string const& input, T& index, + std::vector<std::string> const& allArgs, + CallState&&... state) const + { + enum class ParseMode + { + Valid, + Invalid, + SyntaxError, + ValueError + }; + ParseMode parseState = ParseMode::Valid; + + if (this->Type == Values::Zero) { + if (input.size() == this->Name.size()) { + parseState = + this->StoreCall(std::string{}, std::forward<CallState>(state)...) + ? ParseMode::Valid + : ParseMode::Invalid; + } else { + parseState = ParseMode::SyntaxError; + } + + } else if (this->Type == Values::One || this->Type == Values::ZeroOrOne) { + if (input.size() == this->Name.size()) { + ++index; + if (index >= allArgs.size() || allArgs[index][0] == '-') { + if (this->Type == Values::ZeroOrOne) { + parseState = + this->StoreCall(std::string{}, std::forward<CallState>(state)...) + ? ParseMode::Valid + : ParseMode::Invalid; + } else { + parseState = ParseMode::ValueError; + } + } else { + parseState = + this->StoreCall(allArgs[index], std::forward<CallState>(state)...) + ? ParseMode::Valid + : ParseMode::Invalid; + } + } else { + // parse the string to get the value + auto possible_value = cm::string_view(input).substr(this->Name.size()); + if (possible_value.empty()) { + parseState = ParseMode::SyntaxError; + parseState = ParseMode::ValueError; + } else if (possible_value[0] == '=') { + possible_value.remove_prefix(1); + if (possible_value.empty()) { + parseState = ParseMode::ValueError; + } else { + parseState = this->StoreCall(std::string(possible_value), + std::forward<CallState>(state)...) + ? ParseMode::Valid + : ParseMode::Invalid; + } + } + if (parseState == ParseMode::Valid) { + parseState = this->StoreCall(std::string(possible_value), + std::forward<CallState>(state)...) + ? ParseMode::Valid + : ParseMode::Invalid; + } + } + } else if (this->Type == Values::Two) { + if (input.size() == this->Name.size()) { + if (index + 2 >= allArgs.size() || allArgs[index + 1][0] == '-' || + allArgs[index + 2][0] == '-') { + parseState = ParseMode::ValueError; + } else { + index += 2; + parseState = + this->StoreCall(cmStrCat(allArgs[index - 1], ";", allArgs[index]), + std::forward<CallState>(state)...) + ? ParseMode::Valid + : ParseMode::Invalid; + } + } + } else if (this->Type == Values::OneOrMore) { + if (input.size() == this->Name.size()) { + auto nextValueIndex = index + 1; + if (nextValueIndex >= allArgs.size() || allArgs[index + 1][0] == '-') { + parseState = ParseMode::ValueError; + } else { + std::string buffer = allArgs[nextValueIndex++]; + while (nextValueIndex < allArgs.size() && + allArgs[nextValueIndex][0] != '-') { + buffer = cmStrCat(buffer, ";", allArgs[nextValueIndex++]); + } + parseState = + this->StoreCall(buffer, std::forward<CallState>(state)...) + ? ParseMode::Valid + : ParseMode::Invalid; + } + } + } + + if (parseState == ParseMode::SyntaxError) { + cmSystemTools::Error(this->InvalidSyntaxMessage); + } else if (parseState == ParseMode::ValueError) { + cmSystemTools::Error(this->InvalidValueMessage); + } + return (parseState == ParseMode::Valid); + } +}; diff --git a/Source/cmExportFileGenerator.cxx b/Source/cmExportFileGenerator.cxx index a853bb1..113751a 100644 --- a/Source/cmExportFileGenerator.cxx +++ b/Source/cmExportFileGenerator.cxx @@ -707,7 +707,7 @@ void cmExportFileGenerator::ResolveTargetsInGeneratorExpression( break; } input.replace(pos, endPos - pos + 1, targetName); - lastPos = endPos; + lastPos = pos + targetName.size(); } pos = 0; diff --git a/Source/cmGlobalNinjaGenerator.cxx b/Source/cmGlobalNinjaGenerator.cxx index a95ab25..4f17408 100644 --- a/Source/cmGlobalNinjaGenerator.cxx +++ b/Source/cmGlobalNinjaGenerator.cxx @@ -555,6 +555,7 @@ void cmGlobalNinjaGenerator::Generate() this->TargetAll = this->NinjaOutputPath("all"); this->CMakeCacheFile = this->NinjaOutputPath("CMakeCache.txt"); this->DisableCleandead = false; + this->DiagnosedCxxModuleSupport = false; this->PolicyCMP0058 = this->LocalGenerators[0]->GetMakefile()->GetPolicyStatus( @@ -755,6 +756,37 @@ bool cmGlobalNinjaGenerator::CheckLanguages( return true; } +bool cmGlobalNinjaGenerator::CheckCxxModuleSupport() +{ + bool const diagnose = !this->DiagnosedCxxModuleSupport && + !this->CMakeInstance->GetIsInTryCompile(); + if (diagnose) { + this->DiagnosedCxxModuleSupport = true; + this->GetCMakeInstance()->IssueMessage( + MessageType::AUTHOR_WARNING, + "C++20 modules support via CMAKE_EXPERIMENTAL_CXX_MODULE_DYNDEP " + "is experimental. It is meant only for compiler developers to try."); + } + if (this->NinjaSupportsDyndeps) { + return true; + } + if (diagnose) { + std::ostringstream e; + /* clang-format off */ + e << + "The Ninja generator does not support C++20 modules " + "using Ninja version \n" + " " << this->NinjaVersion << "\n" + "due to lack of required features. " + "Ninja " << RequiredNinjaVersionForDyndeps() << " or higher is required." + ; + /* clang-format on */ + this->GetCMakeInstance()->IssueMessage(MessageType::FATAL_ERROR, e.str()); + cmSystemTools::SetFatalErrorOccured(); + } + return false; +} + bool cmGlobalNinjaGenerator::CheckFortran(cmMakefile* mf) const { if (this->NinjaSupportsDyndeps) { @@ -766,7 +798,8 @@ bool cmGlobalNinjaGenerator::CheckFortran(cmMakefile* mf) const e << "The Ninja generator does not support Fortran using Ninja version\n" " " << this->NinjaVersion << "\n" - "due to lack of required features. Ninja 1.10 or higher is required." + "due to lack of required features. " + "Ninja " << RequiredNinjaVersionForDyndeps() << " or higher is required." ; /* clang-format on */ mf->IssueMessage(MessageType::FATAL_ERROR, e.str()); @@ -785,7 +818,9 @@ bool cmGlobalNinjaGenerator::CheckISPC(cmMakefile* mf) const e << "The Ninja generator does not support ISPC using Ninja version\n" " " << this->NinjaVersion << "\n" - "due to lack of required features. Ninja 1.10 or higher is required." + "due to lack of required features. " + "Ninja " << RequiredNinjaVersionForMultipleOutputs() << + " or higher is required." ; /* clang-format on */ mf->IssueMessage(MessageType::FATAL_ERROR, e.str()); @@ -2336,7 +2371,7 @@ bool cmGlobalNinjaGenerator::WriteDyndepFile( 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, - std::string const& arg_lang) + std::string const& arg_lang, std::string const& arg_modmapfmt) { // Setup path conversions. { @@ -2423,6 +2458,48 @@ bool cmGlobalNinjaGenerator::WriteDyndepFile( build.Variables.emplace("restat", "1"); } + if (arg_modmapfmt.empty()) { + // nothing to do. + } else { + std::stringstream mm; + if (arg_modmapfmt == "gcc") { + // Documented in GCC's documentation. The format is a series of lines + // with a module name and the associated filename separated by + // spaces. The first line may use `$root` as the module name to + // specify a "repository root". That is used to anchor any relative + // paths present in the file (CMake should never generate any). + + // Write the root directory to use for module paths. + mm << "$root .\n"; + + for (auto const& l : object.Provides) { + auto m = mod_files.find(l.LogicalName); + if (m != mod_files.end()) { + mm << l.LogicalName << " " << this->ConvertToNinjaPath(m->second) + << "\n"; + } + } + for (auto const& r : object.Requires) { + auto m = mod_files.find(r.LogicalName); + if (m != mod_files.end()) { + mm << r.LogicalName << " " << this->ConvertToNinjaPath(m->second) + << "\n"; + } + } + } else { + cmSystemTools::Error( + cmStrCat("-E cmake_ninja_dyndep does not understand the ", + arg_modmapfmt, " module map format")); + return false; + } + + // XXX(modmap): If changing this path construction, change + // `cmNinjaTargetGenerator::WriteObjectBuildStatements` to generate the + // corresponding file path. + cmGeneratedFileStream mmf(cmStrCat(object.PrimaryOutput, ".modmap")); + mmf << mm.str(); + } + this->WriteBuild(ddf, build); } } @@ -2446,6 +2523,7 @@ int cmcmd_cmake_ninja_dyndep(std::vector<std::string>::const_iterator argBeg, std::string arg_dd; std::string arg_lang; std::string arg_tdi; + std::string arg_modmapfmt; std::vector<std::string> arg_ddis; for (std::string const& arg : arg_full) { if (cmHasLiteralPrefix(arg, "--tdi=")) { @@ -2454,6 +2532,8 @@ int cmcmd_cmake_ninja_dyndep(std::vector<std::string>::const_iterator argBeg, arg_lang = arg.substr(7); } else if (cmHasLiteralPrefix(arg, "--dd=")) { arg_dd = arg.substr(5); + } else if (cmHasLiteralPrefix(arg, "--modmapfmt=")) { + arg_modmapfmt = arg.substr(12); } else if (!cmHasLiteralPrefix(arg, "--") && cmHasLiteralSuffix(arg, ".ddi")) { arg_ddis.push_back(arg); @@ -2512,7 +2592,7 @@ int cmcmd_cmake_ninja_dyndep(std::vector<std::string>::const_iterator argBeg, if (!ggd || !cm::static_reference_cast<cmGlobalNinjaGenerator>(ggd).WriteDyndepFile( dir_top_src, dir_top_bld, dir_cur_src, dir_cur_bld, arg_dd, arg_ddis, - module_dir, linked_target_dirs, arg_lang)) { + module_dir, linked_target_dirs, arg_lang, arg_modmapfmt)) { return 1; } return 0; diff --git a/Source/cmGlobalNinjaGenerator.h b/Source/cmGlobalNinjaGenerator.h index 0e94678..fd8542f 100644 --- a/Source/cmGlobalNinjaGenerator.h +++ b/Source/cmGlobalNinjaGenerator.h @@ -396,15 +396,13 @@ 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, - std::string const& arg_lang); + 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, + std::string const& arg_lang, std::string const& arg_modmapfmt); virtual std::string BuildAlias(const std::string& alias, const std::string& /*config*/) const @@ -448,6 +446,8 @@ public: bool IsSingleConfigUtility(cmGeneratorTarget const* target) const; + bool CheckCxxModuleSupport(); + protected: void Generate() override; @@ -568,6 +568,8 @@ private: bool NinjaSupportsMultipleOutputs = false; bool NinjaSupportsMetadataOnRegeneration = false; + bool DiagnosedCxxModuleSupport = false; + private: void InitOutputPathPrefix(); diff --git a/Source/cmGlobalXCodeGenerator.cxx b/Source/cmGlobalXCodeGenerator.cxx index d59d382..8a1ccd1 100644 --- a/Source/cmGlobalXCodeGenerator.cxx +++ b/Source/cmGlobalXCodeGenerator.cxx @@ -18,6 +18,7 @@ #include "cmsys/RegularExpression.hxx" #include "cmComputeLinkInformation.h" +#include "cmCryptoHash.h" #include "cmCustomCommand.h" #include "cmCustomCommandGenerator.h" #include "cmCustomCommandLines.h" @@ -798,9 +799,10 @@ void cmGlobalXCodeGenerator::addObject(std::unique_ptr<cmXCodeObject> obj) } cmXCodeObject* cmGlobalXCodeGenerator::CreateObject( - cmXCodeObject::PBXType ptype) + cmXCodeObject::PBXType ptype, cm::string_view key) { - auto obj = cm::make_unique<cmXCode21Object>(ptype, cmXCodeObject::OBJECT); + auto obj = cm::make_unique<cmXCode21Object>(ptype, cmXCodeObject::OBJECT, + this->GetObjectId(ptype, key)); auto ptr = obj.get(); this->addObject(std::move(obj)); return ptr; @@ -808,7 +810,9 @@ cmXCodeObject* cmGlobalXCodeGenerator::CreateObject( cmXCodeObject* cmGlobalXCodeGenerator::CreateObject(cmXCodeObject::Type type) { - auto obj = cm::make_unique<cmXCodeObject>(cmXCodeObject::None, type); + auto obj = cm::make_unique<cmXCodeObject>( + cmXCodeObject::None, type, + "Temporary cmake object, should not be referred to in Xcode file"); auto ptr = obj.get(); this->addObject(std::move(obj)); return ptr; @@ -1739,13 +1743,13 @@ void cmGlobalXCodeGenerator::CreateCustomCommands( if (this->XcodeBuildSystem >= BuildSystem::Twelve) { // create prebuild phase preBuildPhase = - this->CreateRunScriptBuildPhase("CMake PreBuild Rules", prebuild); + this->CreateRunScriptBuildPhase("CMake PreBuild Rules", gtgt, prebuild); // create prelink phase preLinkPhase = - this->CreateRunScriptBuildPhase("CMake PreLink Rules", prelink); + this->CreateRunScriptBuildPhase("CMake PreLink Rules", gtgt, prelink); // create postbuild phase - postBuildPhase = - this->CreateRunScriptBuildPhase("CMake PostBuild Rules", postbuild); + postBuildPhase = this->CreateRunScriptBuildPhase("CMake PostBuild Rules", + gtgt, postbuild); } else { std::vector<cmSourceFile*> classes; if (!gtgt->GetConfigCommonSourceFilesForXcode(classes)) { @@ -1877,7 +1881,8 @@ cmXCodeObject* cmGlobalXCodeGenerator::CreateRunScriptBuildPhase( } cmXCodeObject* buildPhase = - this->CreateObject(cmXCodeObject::PBXShellScriptBuildPhase); + this->CreateObject(cmXCodeObject::PBXShellScriptBuildPhase, + cmStrCat(gt->GetName(), ':', sf->GetFullPath())); buildPhase->AddAttribute("buildActionMask", this->CreateString("2147483647")); cmXCodeObject* buildFiles = this->CreateObject(cmXCodeObject::OBJECT_LIST); @@ -1936,7 +1941,8 @@ cmXCodeObject* cmGlobalXCodeGenerator::CreateRunScriptBuildPhase( } cmXCodeObject* cmGlobalXCodeGenerator::CreateRunScriptBuildPhase( - std::string const& name, std::vector<cmCustomCommand> const& commands) + std::string const& name, cmGeneratorTarget const* gt, + std::vector<cmCustomCommand> const& commands) { if (commands.empty()) { return nullptr; @@ -1959,7 +1965,8 @@ cmXCodeObject* cmGlobalXCodeGenerator::CreateRunScriptBuildPhase( } cmXCodeObject* buildPhase = - this->CreateObject(cmXCodeObject::PBXShellScriptBuildPhase); + this->CreateObject(cmXCodeObject::PBXShellScriptBuildPhase, + cmStrCat(gt->GetName(), ':', name)); buildPhase->AddAttribute("buildActionMask", this->CreateString("2147483647")); cmXCodeObject* buildFiles = this->CreateObject(cmXCodeObject::OBJECT_LIST); @@ -2927,8 +2934,8 @@ void cmGlobalXCodeGenerator::CreateBuildSettings(cmGeneratorTarget* gtgt, cmXCodeObject* cmGlobalXCodeGenerator::CreateUtilityTarget( cmGeneratorTarget* gtgt) { - cmXCodeObject* shellBuildPhase = - this->CreateObject(cmXCodeObject::PBXShellScriptBuildPhase); + cmXCodeObject* shellBuildPhase = this->CreateObject( + cmXCodeObject::PBXShellScriptBuildPhase, gtgt->GetName()); shellBuildPhase->AddAttribute("buildActionMask", this->CreateString("2147483647")); cmXCodeObject* buildFiles = this->CreateObject(cmXCodeObject::OBJECT_LIST); @@ -3156,6 +3163,32 @@ cmXCodeObject* cmGlobalXCodeGenerator::FindXCodeTarget( return i->second; } +std::string cmGlobalXCodeGenerator::GetObjectId(cmXCodeObject::PBXType ptype, + cm::string_view key) +{ + std::string objectId; + if (!key.empty()) { + cmCryptoHash hash(cmCryptoHash::AlgoSHA256); + hash.Initialize(); + hash.Append(&ptype, sizeof(ptype)); + hash.Append(key); + objectId = cmSystemTools::UpperCase(hash.FinalizeHex().substr(0, 24)); + } else { + char cUuid[40] = { 0 }; + CFUUIDRef uuid = CFUUIDCreate(kCFAllocatorDefault); + CFStringRef s = CFUUIDCreateString(kCFAllocatorDefault, uuid); + CFStringGetCString(s, cUuid, sizeof(cUuid), kCFStringEncodingUTF8); + objectId = cUuid; + CFRelease(s); + CFRelease(uuid); + cmSystemTools::ReplaceString(objectId, "-", ""); + if (objectId.size() > 24) { + objectId = objectId.substr(0, 24); + } + } + return objectId; +} + std::string cmGlobalXCodeGenerator::GetOrCreateId(const std::string& name, const std::string& id) { diff --git a/Source/cmGlobalXCodeGenerator.h b/Source/cmGlobalXCodeGenerator.h index 14db1dc..1ab56e2 100644 --- a/Source/cmGlobalXCodeGenerator.h +++ b/Source/cmGlobalXCodeGenerator.h @@ -11,6 +11,8 @@ #include <string> #include <vector> +#include <cm/string_view> + #include "cmGlobalGenerator.h" #include "cmXCodeObject.h" @@ -162,11 +164,13 @@ private: const std::string& configName); cmXCodeObject* FindXCodeTarget(const cmGeneratorTarget*); + std::string GetObjectId(cmXCodeObject::PBXType ptype, cm::string_view key); std::string GetOrCreateId(const std::string& name, const std::string& id); // create cmXCodeObject from these functions so that memory can be managed // correctly. All objects created are stored in this->XCodeObjects. - cmXCodeObject* CreateObject(cmXCodeObject::PBXType ptype); + cmXCodeObject* CreateObject(cmXCodeObject::PBXType ptype, + cm::string_view key = {}); cmXCodeObject* CreateObject(cmXCodeObject::Type type); cmXCodeObject* CreateString(const std::string& s); cmXCodeObject* CreateObjectReference(cmXCodeObject*); @@ -256,7 +260,8 @@ private: cmGeneratorTarget const* gt, cmCustomCommand const& cc); cmXCodeObject* CreateRunScriptBuildPhase( - std::string const& name, std::vector<cmCustomCommand> const& commands); + std::string const& name, cmGeneratorTarget const* gt, + std::vector<cmCustomCommand> const& commands); std::string ConstructScript(cmCustomCommandGenerator const& ccg); void CreateReRunCMakeFile(cmLocalGenerator* root, std::vector<cmLocalGenerator*> const& gens); diff --git a/Source/cmMakefile.cxx b/Source/cmMakefile.cxx index 0faef15..e4c8e4b 100644 --- a/Source/cmMakefile.cxx +++ b/Source/cmMakefile.cxx @@ -1216,9 +1216,10 @@ void cmMakefile::AddCustomCommandOldStyle( }; // Each output must get its own copy of this rule. - cmsys::RegularExpression sourceFiles("\\.(C|M|c|c\\+\\+|cc|cpp|cxx|cu|m|mm|" - "rc|def|r|odl|idl|hpj|bat|h|h\\+\\+|" - "hm|hpp|hxx|in|txx|inl)$"); + cmsys::RegularExpression sourceFiles( + "\\.(C|M|c|c\\+\\+|cc|cpp|cxx|mpp|cu|m|mm|" + "rc|def|r|odl|idl|hpj|bat|h|h\\+\\+|" + "hm|hpp|hxx|in|txx|inl)$"); // Choose whether to use a main dependency. if (sourceFiles.find(source)) { diff --git a/Source/cmMakefileTargetGenerator.cxx b/Source/cmMakefileTargetGenerator.cxx index 0f91d6d..c33fabd 100644 --- a/Source/cmMakefileTargetGenerator.cxx +++ b/Source/cmMakefileTargetGenerator.cxx @@ -898,7 +898,7 @@ void cmMakefileTargetGenerator::WriteObjectRuleFiles( cmExpandList(compileRule, compileCommands); } - if (this->Makefile->IsOn("CMAKE_EXPORT_COMPILE_COMMANDS") && + if (this->GeneratorTarget->GetPropertyAsBool("EXPORT_COMPILE_COMMANDS") && lang_can_export_cmds && compileCommands.size() == 1) { std::string compileCommand = compileCommands[0]; diff --git a/Source/cmNinjaTargetGenerator.cxx b/Source/cmNinjaTargetGenerator.cxx index 18c9d82..672b579 100644 --- a/Source/cmNinjaTargetGenerator.cxx +++ b/Source/cmNinjaTargetGenerator.cxx @@ -35,6 +35,7 @@ #include "cmRange.h" #include "cmRulePlaceholderExpander.h" #include "cmSourceFile.h" +#include "cmStandardLevelResolver.h" #include "cmState.h" #include "cmStateTypes.h" #include "cmStringAlgorithms.h" @@ -146,9 +147,26 @@ std::string cmNinjaTargetGenerator::LanguageDyndepRule( '_', config); } -bool cmNinjaTargetGenerator::NeedDyndep(std::string const& lang) const +bool cmNinjaTargetGenerator::NeedCxxModuleSupport( + std::string const& lang, std::string const& config) const { - return lang == "Fortran"; + if (lang != "CXX") { + return false; + } + if (!this->Makefile->IsOn("CMAKE_EXPERIMENTAL_CXX_MODULE_DYNDEP")) { + return false; + } + cmGeneratorTarget const* tgt = this->GetGeneratorTarget(); + cmStandardLevelResolver standardResolver(this->Makefile); + bool const uses_cxx20 = + standardResolver.HaveStandardAvailable(tgt, "CXX", config, "cxx_std_20"); + return uses_cxx20 && this->GetGlobalGenerator()->CheckCxxModuleSupport(); +} + +bool cmNinjaTargetGenerator::NeedDyndep(std::string const& lang, + std::string const& config) const +{ + return lang == "Fortran" || this->NeedCxxModuleSupport(lang, config); } std::string cmNinjaTargetGenerator::OrderDependsTargetForTarget( @@ -530,8 +548,9 @@ cmNinjaRule GetScanRule( scanVars.CMTargetName = vars.CMTargetName; scanVars.CMTargetType = vars.CMTargetType; scanVars.Language = vars.Language; - scanVars.Object = "$out"; // for RULE_LAUNCH_COMPILE + scanVars.Object = "$OBJ_FILE"; scanVars.PreprocessedSource = "$out"; + scanVars.DynDepFile = "$DYNDEP_INTERMEDIATE_FILE"; scanVars.DependencyFile = rule.DepFile.c_str(); scanVars.DependencyTarget = "$out"; @@ -586,7 +605,7 @@ void cmNinjaTargetGenerator::WriteCompileRule(const std::string& lang, cmMakefile* mf = this->GetMakefile(); // For some cases we scan to dynamically discover dependencies. - bool const needDyndep = this->NeedDyndep(lang); + bool const needDyndep = this->NeedDyndep(lang, config); bool const compilationPreprocesses = !this->NeedExplicitPreprocessing(lang); std::string flags = "$FLAGS"; @@ -601,6 +620,10 @@ void cmNinjaTargetGenerator::WriteCompileRule(const std::string& lang, responseFlag = "@"; } } + std::string const modmapFormatVar = + cmStrCat("CMAKE_EXPERIMENTAL_", lang, "_MODULE_MAP_FORMAT"); + std::string const modmapFormat = + this->Makefile->GetSafeDefinition(modmapFormatVar); std::unique_ptr<cmRulePlaceholderExpander> rulePlaceholderExpander( this->GetLocalGenerator()->CreateRulePlaceholderExpander()); @@ -624,16 +647,26 @@ void cmNinjaTargetGenerator::WriteCompileRule(const std::string& lang, // Rule to scan dependencies of sources that need preprocessing. { std::vector<std::string> scanCommands; - std::string const& scanRuleName = - this->LanguagePreprocessAndScanRule(lang, config); - std::string const& ppCommmand = mf->GetRequiredDefinition( - cmStrCat("CMAKE_", lang, "_PREPROCESS_SOURCE")); - cmExpandList(ppCommmand, scanCommands); - for (std::string& i : scanCommands) { - i = cmStrCat(launcher, i); + std::string scanRuleName; + if (compilationPreprocesses) { + scanRuleName = this->LanguageScanRule(lang, config); + std::string const& scanCommand = mf->GetRequiredDefinition( + cmStrCat("CMAKE_EXPERIMENTAL_", lang, "_SCANDEP_SOURCE")); + cmExpandList(scanCommand, scanCommands); + for (std::string& i : scanCommands) { + i = cmStrCat(launcher, i); + } + } else { + scanRuleName = this->LanguagePreprocessAndScanRule(lang, config); + std::string const& ppCommmand = mf->GetRequiredDefinition( + cmStrCat("CMAKE_", lang, "_PREPROCESS_SOURCE")); + cmExpandList(ppCommmand, scanCommands); + for (std::string& i : scanCommands) { + i = cmStrCat(launcher, i); + } + scanCommands.emplace_back(GetScanCommand(cmakeCmd, tdi, lang, "$out", + "$DYNDEP_INTERMEDIATE_FILE")); } - scanCommands.emplace_back(GetScanCommand(cmakeCmd, tdi, lang, "$out", - "$DYNDEP_INTERMEDIATE_FILE")); auto scanRule = GetScanRule( scanRuleName, vars, responseFlag, flags, rulePlaceholderExpander.get(), @@ -641,12 +674,18 @@ void cmNinjaTargetGenerator::WriteCompileRule(const std::string& lang, scanRule.Comment = cmStrCat("Rule for generating ", lang, " dependencies."); - scanRule.Description = cmStrCat("Building ", lang, " preprocessed $out"); + if (compilationPreprocesses) { + scanRule.Description = + cmStrCat("Scanning $in for ", lang, " dependencies"); + } else { + scanRule.Description = + cmStrCat("Building ", lang, " preprocessed $out"); + } this->GetGlobalGenerator()->AddRule(scanRule); } - { + if (!compilationPreprocesses) { // Compilation will not preprocess, so it does not need the defines // unless the compiler wants them for some other purpose. if (!this->CompileWithDefines(lang)) { @@ -681,12 +720,16 @@ void cmNinjaTargetGenerator::WriteCompileRule(const std::string& lang, // Run CMake dependency scanner on the source file (using the preprocessed // source if that was performed). + std::string ddModmapArg; + if (!modmapFormat.empty()) { + ddModmapArg += cmStrCat(" --modmapfmt=", modmapFormat); + } { std::vector<std::string> ddCmds; { - std::string ccmd = - cmStrCat(cmakeCmd, " -E cmake_ninja_dyndep --tdi=", tdi, - " --lang=", lang, " --dd=$out @", rule.RspFile); + std::string ccmd = cmStrCat( + cmakeCmd, " -E cmake_ninja_dyndep --tdi=", tdi, " --lang=", lang, + ddModmapArg, " --dd=$out @", rule.RspFile); ddCmds.emplace_back(std::move(ccmd)); } rule.Command = this->GetLocalGenerator()->BuildCommandLine(ddCmds); @@ -748,6 +791,14 @@ void cmNinjaTargetGenerator::WriteCompileRule(const std::string& lang, } } + if (needDyndep && !modmapFormat.empty()) { + std::string modmapFlags = mf->GetRequiredDefinition( + cmStrCat("CMAKE_EXPERIMENTAL_", lang, "_MODULE_MAP_FLAG")); + cmSystemTools::ReplaceString(modmapFlags, "<MODULE_MAP_FILE>", + "$DYNDEP_MODULE_MAP_FILE"); + flags += cmStrCat(' ', modmapFlags); + } + vars.Flags = flags.c_str(); vars.DependencyFile = rule.DepFile.c_str(); @@ -1053,6 +1104,7 @@ cmNinjaBuild GetScanBuildStatement(const std::string& ruleName, const std::string& ppFileName, bool compilePP, bool compilePPWithDefines, cmNinjaBuild& objBuild, cmNinjaVars& vars, + std::string const& modmapFormat, const std::string& objectFileName, cmLocalGenerator* lg) { @@ -1123,6 +1175,15 @@ cmNinjaBuild GetScanBuildStatement(const std::string& ruleName, vars.erase("DEP_FILE"); } + if (!modmapFormat.empty()) { + // XXX(modmap): If changing this path construction, change + // `cmGlobalNinjaGenerator::WriteDyndep` to expect the corresponding + // file path. + std::string const ddModmapFile = cmStrCat(objectFileName, ".modmap"); + scanBuild.Variables["DYNDEP_MODULE_MAP_FILE"] = ddModmapFile; + scanBuild.ImplicitOuts.push_back(ddModmapFile); + } + return scanBuild; } } @@ -1262,10 +1323,17 @@ void cmNinjaTargetGenerator::WriteObjectBuildStatement( } // For some cases we scan to dynamically discover dependencies. - bool const needDyndep = this->NeedDyndep(language); + bool const needDyndep = this->NeedDyndep(language, config); bool const compilationPreprocesses = !this->NeedExplicitPreprocessing(language); + std::string modmapFormat; + if (needDyndep) { + std::string const modmapFormatVar = + cmStrCat("CMAKE_EXPERIMENTAL_", language, "_MODULE_MAP_FORMAT"); + modmapFormat = this->Makefile->GetSafeDefinition(modmapFormatVar); + } + if (needDyndep) { // If source/target has preprocessing turned off, we still need to // generate an explicit dependency step @@ -1295,7 +1363,7 @@ void cmNinjaTargetGenerator::WriteObjectBuildStatement( cmNinjaBuild ppBuild = GetScanBuildStatement( scanRuleName, ppFileName, compilePP, compilePPWithDefines, objBuild, - vars, objectFileName, this->LocalGenerator); + vars, modmapFormat, objectFileName, this->LocalGenerator); if (compilePP) { // In case compilation requires flags that are incompatible with @@ -1331,6 +1399,12 @@ void cmNinjaTargetGenerator::WriteObjectBuildStatement( std::string const dyndep = this->GetDyndepFilePath(language, config); objBuild.OrderOnlyDeps.push_back(dyndep); vars["dyndep"] = dyndep; + + if (!modmapFormat.empty()) { + std::string const ddModmapFile = cmStrCat(objectFileName, ".modmap"); + vars["DYNDEP_MODULE_MAP_FILE"] = ddModmapFile; + objBuild.OrderOnlyDeps.push_back(ddModmapFile); + } } this->EnsureParentDirectoryExists(objectFileName); @@ -1444,17 +1518,26 @@ void cmNinjaTargetGenerator::WriteTargetDependInfo(std::string const& lang, tdi["compiler-id"] = this->Makefile->GetSafeDefinition( cmStrCat("CMAKE_", lang, "_COMPILER_ID")); + std::string mod_dir; if (lang == "Fortran") { - std::string mod_dir = this->GeneratorTarget->GetFortranModuleDirectory( + mod_dir = this->GeneratorTarget->GetFortranModuleDirectory( this->Makefile->GetHomeOutputDirectory()); - if (mod_dir.empty()) { - mod_dir = this->Makefile->GetCurrentBinaryDirectory(); - } - tdi["module-dir"] = mod_dir; + } else if (lang == "CXX") { + mod_dir = + cmSystemTools::CollapseFullPath(this->GeneratorTarget->ObjectDirectory); + } + if (mod_dir.empty()) { + mod_dir = this->Makefile->GetCurrentBinaryDirectory(); + } + tdi["module-dir"] = mod_dir; + + if (lang == "Fortran") { tdi["submodule-sep"] = this->Makefile->GetSafeDefinition("CMAKE_Fortran_SUBMODULE_SEP"); tdi["submodule-ext"] = this->Makefile->GetSafeDefinition("CMAKE_Fortran_SUBMODULE_EXT"); + } else if (lang == "CXX") { + // No extra information necessary. } tdi["dir-cur-bld"] = this->Makefile->GetCurrentBinaryDirectory(); @@ -1536,7 +1619,7 @@ void cmNinjaTargetGenerator::ExportObjectCompileCommand( std::string const& objectFileDir, std::string const& flags, std::string const& defines, std::string const& includes) { - if (!this->Makefile->IsOn("CMAKE_EXPORT_COMPILE_COMMANDS")) { + if (!this->GeneratorTarget->GetPropertyAsBool("EXPORT_COMPILE_COMMANDS")) { return; } diff --git a/Source/cmNinjaTargetGenerator.h b/Source/cmNinjaTargetGenerator.h index 83a4342..79dc622 100644 --- a/Source/cmNinjaTargetGenerator.h +++ b/Source/cmNinjaTargetGenerator.h @@ -71,9 +71,11 @@ protected: const std::string& config) const; std::string LanguageDyndepRule(std::string const& lang, const std::string& config) const; - bool NeedDyndep(std::string const& lang) const; + bool NeedDyndep(std::string const& lang, std::string const& config) const; bool NeedExplicitPreprocessing(std::string const& lang) const; bool CompileWithDefines(std::string const& lang) const; + bool NeedCxxModuleSupport(std::string const& lang, + std::string const& config) const; std::string OrderDependsTargetForTarget(const std::string& config); diff --git a/Source/cmQtAutoMocUic.cxx b/Source/cmQtAutoMocUic.cxx index 7365fdb..3556051 100644 --- a/Source/cmQtAutoMocUic.cxx +++ b/Source/cmQtAutoMocUic.cxx @@ -522,6 +522,7 @@ public: class JobDepFilesMergeT : public JobFenceT { private: + std::vector<std::string> initialDependencies() const; void Process() override; }; @@ -2198,6 +2199,29 @@ std::string escapeDependencyPath(cm::string_view path) return escapedPath; } +/* + * Return the initial dependencies of the merged depfile. + * Those are dependencies from the project files, not from moc runs. + */ +std::vector<std::string> +cmQtAutoMocUicT::JobDepFilesMergeT::initialDependencies() const +{ + std::vector<std::string> dependencies; + dependencies.reserve(this->BaseConst().ListFiles.size() + + this->BaseEval().Headers.size() + + this->BaseEval().Sources.size()); + cm::append(dependencies, this->BaseConst().ListFiles); + auto append_file_path = + [&dependencies](const SourceFileMapT::value_type& p) { + dependencies.push_back(p.first); + }; + std::for_each(this->BaseEval().Headers.begin(), + this->BaseEval().Headers.end(), append_file_path); + std::for_each(this->BaseEval().Sources.begin(), + this->BaseEval().Sources.end(), append_file_path); + return dependencies; +} + void cmQtAutoMocUicT::JobDepFilesMergeT::Process() { if (this->Log().Verbose()) { @@ -2215,7 +2239,7 @@ void cmQtAutoMocUicT::JobDepFilesMergeT::Process() return dependenciesFromDepFile(f.c_str()); }; - std::vector<std::string> dependencies = this->BaseConst().ListFiles; + std::vector<std::string> dependencies = this->initialDependencies(); ParseCacheT& parseCache = this->BaseEval().ParseCache; auto processMappingEntry = [&](const MappingMapT::value_type& m) { auto cacheEntry = parseCache.GetOrInsert(m.first); diff --git a/Source/cmRulePlaceholderExpander.cxx b/Source/cmRulePlaceholderExpander.cxx index 5363fef..d00bbdf 100644 --- a/Source/cmRulePlaceholderExpander.cxx +++ b/Source/cmRulePlaceholderExpander.cxx @@ -44,6 +44,11 @@ std::string cmRulePlaceholderExpander::ExpandRuleVariable( return replaceValues.Source; } } + if (replaceValues.DynDepFile) { + if (variable == "DYNDEP_FILE") { + return replaceValues.DynDepFile; + } + } if (replaceValues.PreprocessedSource) { if (variable == "PREPROCESSED_SOURCE") { return replaceValues.PreprocessedSource; diff --git a/Source/cmRulePlaceholderExpander.h b/Source/cmRulePlaceholderExpander.h index 710f8a6..f8dc368 100644 --- a/Source/cmRulePlaceholderExpander.h +++ b/Source/cmRulePlaceholderExpander.h @@ -41,6 +41,7 @@ public: const char* Source = nullptr; const char* AssemblySource = nullptr; const char* PreprocessedSource = nullptr; + const char* DynDepFile = nullptr; const char* Output = nullptr; const char* Object = nullptr; const char* ObjectDir = nullptr; diff --git a/Source/cmScanDepFormat.cxx b/Source/cmScanDepFormat.cxx index 40bf4c9..e046069 100644 --- a/Source/cmScanDepFormat.cxx +++ b/Source/cmScanDepFormat.cxx @@ -55,9 +55,8 @@ static Json::Value EncodeFilename(std::string const& path) #define PARSE_BLOB(val, res) \ do { \ if (!ParseFilename(val, res)) { \ - cmSystemTools::Error( \ - cmStrCat("-E cmake_ninja_depends failed to parse ", arg_pp, \ - ": invalid blob")); \ + cmSystemTools::Error(cmStrCat("-E cmake_ninja_dyndep failed to parse ", \ + arg_pp, ": invalid blob")); \ return false; \ } \ } while (0) @@ -65,9 +64,8 @@ static Json::Value EncodeFilename(std::string const& path) #define PARSE_FILENAME(val, res) \ do { \ if (!ParseFilename(val, res)) { \ - cmSystemTools::Error( \ - cmStrCat("-E cmake_ninja_depends failed to parse ", arg_pp, \ - ": invalid filename")); \ + cmSystemTools::Error(cmStrCat("-E cmake_ninja_dyndep failed to parse ", \ + arg_pp, ": invalid filename")); \ return false; \ } \ \ @@ -84,7 +82,7 @@ bool cmScanDepFormat_P1689_Parse(std::string const& arg_pp, cmSourceInfo* info) { Json::Reader reader; if (!reader.parse(ppf, ppio, false)) { - cmSystemTools::Error(cmStrCat("-E cmake_ninja_depends failed to parse ", + cmSystemTools::Error(cmStrCat("-E cmake_ninja_dyndep failed to parse ", arg_pp, reader.getFormattedErrorMessages())); return false; @@ -93,7 +91,7 @@ bool cmScanDepFormat_P1689_Parse(std::string const& arg_pp, cmSourceInfo* info) Json::Value const& version = ppi["version"]; if (version.asUInt() != 0) { - cmSystemTools::Error(cmStrCat("-E cmake_ninja_depends failed to parse ", + cmSystemTools::Error(cmStrCat("-E cmake_ninja_dyndep failed to parse ", arg_pp, ": version ", version.asString())); return false; } @@ -101,7 +99,7 @@ bool cmScanDepFormat_P1689_Parse(std::string const& arg_pp, cmSourceInfo* info) Json::Value const& rules = ppi["rules"]; if (rules.isArray()) { if (rules.size() != 1) { - cmSystemTools::Error(cmStrCat("-E cmake_ninja_depends failed to parse ", + cmSystemTools::Error(cmStrCat("-E cmake_ninja_dyndep failed to parse ", arg_pp, ": expected 1 source entry")); return false; } @@ -109,9 +107,9 @@ bool cmScanDepFormat_P1689_Parse(std::string const& arg_pp, cmSourceInfo* info) for (auto const& rule : rules) { Json::Value const& workdir = rule["work-directory"]; if (!workdir.isString()) { - cmSystemTools::Error( - cmStrCat("-E cmake_ninja_depends failed to parse ", arg_pp, - ": work-directory is not a string")); + cmSystemTools::Error(cmStrCat("-E cmake_ninja_dyndep failed to parse ", + arg_pp, + ": work-directory is not a string")); return false; } std::string work_directory; @@ -134,7 +132,7 @@ bool cmScanDepFormat_P1689_Parse(std::string const& arg_pp, cmSourceInfo* info) if (outputs.isArray()) { if (outputs.empty()) { cmSystemTools::Error( - cmStrCat("-E cmake_ninja_depends failed to parse ", arg_pp, + cmStrCat("-E cmake_ninja_dyndep failed to parse ", arg_pp, ": expected at least one 1 output")); return false; } diff --git a/Source/cmSourceFile.h b/Source/cmSourceFile.h index 94b5cc8..76a5ded 100644 --- a/Source/cmSourceFile.h +++ b/Source/cmSourceFile.h @@ -175,7 +175,7 @@ private: #define CM_HEADER_REGEX "\\.(h|hh|h\\+\\+|hm|hpp|hxx|in|txx|inl)$" #define CM_SOURCE_REGEX \ - "\\.(C|F|M|c|c\\+\\+|cc|cpp|cxx|cu|f|f90|for|fpp|ftn|m|mm|" \ + "\\.(C|F|M|c|c\\+\\+|cc|cpp|mpp|cxx|cu|f|f90|for|fpp|ftn|m|mm|" \ "rc|def|r|odl|idl|hpj|bat)$" #define CM_PCH_REGEX "cmake_pch(_[^.]+)?\\.(h|hxx)$" diff --git a/Source/cmStandardLevelResolver.cxx b/Source/cmStandardLevelResolver.cxx index 5d8ccf1..bf6925e 100644 --- a/Source/cmStandardLevelResolver.cxx +++ b/Source/cmStandardLevelResolver.cxx @@ -44,6 +44,16 @@ struct StandardNeeded int value; }; +int ParseStd(std::string const& level) +{ + try { + return std::stoi(level); + } catch (std::invalid_argument&) { + // Fall through to use an invalid value. + } + return -1; +} + struct StanardLevelComputer { explicit StanardLevelComputer(std::string lang, std::vector<int> levels, @@ -113,17 +123,8 @@ struct StanardLevelComputer standardStr = "03"; } - int standardValue = -1; - int defaultValue = -1; - try { - standardValue = std::stoi(standardStr); - defaultValue = std::stoi(*defaultStd); - } catch (std::invalid_argument&) { - // fall through as we want an error - // when we can't find the bad value in the `stds` vector - } - - auto stdIt = std::find(cm::cbegin(stds), cm::cend(stds), standardValue); + auto stdIt = + std::find(cm::cbegin(stds), cm::cend(stds), ParseStd(standardStr)); if (stdIt == cm::cend(stds)) { std::string e = cmStrCat(this->Language, "_STANDARD is set to invalid value '", @@ -134,7 +135,7 @@ struct StanardLevelComputer } auto defaultStdIt = - std::find(cm::cbegin(stds), cm::cend(stds), defaultValue); + std::find(cm::cbegin(stds), cm::cend(stds), ParseStd(*defaultStd)); if (defaultStdIt == cm::cend(stds)) { std::string e = cmStrCat("CMAKE_", this->Language, "_STANDARD_DEFAULT is set to invalid value '", @@ -195,7 +196,7 @@ struct StanardLevelComputer if (existingStandard) { existingLevelIter = std::find(cm::cbegin(this->Levels), cm::cend(this->Levels), - std::stoi(*existingStandard)); + ParseStd(*existingStandard)); if (existingLevelIter == cm::cend(this->Levels)) { const std::string e = cmStrCat("The ", this->Language, "_STANDARD property on target \"", @@ -240,7 +241,7 @@ struct StanardLevelComputer } // convert defaultStandard to an integer if (std::find(cm::cbegin(this->Levels), cm::cend(this->Levels), - std::stoi(*defaultStandard)) == cm::cend(this->Levels)) { + ParseStd(*defaultStandard)) == cm::cend(this->Levels)) { const std::string e = cmStrCat("The CMAKE_", this->Language, "_STANDARD_DEFAULT variable contains an " "invalid value: \"", @@ -257,7 +258,7 @@ struct StanardLevelComputer auto existingLevelIter = std::find(cm::cbegin(this->Levels), cm::cend(this->Levels), - std::stoi(*existingStandard)); + ParseStd(*existingStandard)); if (existingLevelIter == cm::cend(this->Levels)) { const std::string e = cmStrCat("The ", this->Language, "_STANDARD property on target \"", diff --git a/Source/cmTarget.cxx b/Source/cmTarget.cxx index 2fd6063..1fd2355 100644 --- a/Source/cmTarget.cxx +++ b/Source/cmTarget.cxx @@ -383,6 +383,7 @@ cmTarget::cmTarget(std::string const& name, cmStateEnums::TargetType type, initProp("UNITY_BUILD"); initProp("UNITY_BUILD_UNIQUE_ID"); initProp("OPTIMIZE_DEPENDENCIES"); + initProp("EXPORT_COMPILE_COMMANDS"); initPropValue("UNITY_BUILD_BATCH_SIZE", "8"); initPropValue("UNITY_BUILD_MODE", "BATCH"); initPropValue("PCH_WARN_INVALID", "ON"); diff --git a/Source/cmVisualStudio10TargetGenerator.cxx b/Source/cmVisualStudio10TargetGenerator.cxx index 9c41504..a93a78a 100644 --- a/Source/cmVisualStudio10TargetGenerator.cxx +++ b/Source/cmVisualStudio10TargetGenerator.cxx @@ -1897,8 +1897,15 @@ void cmVisualStudio10TargetGenerator::WriteExtraSource(Elem& e1, } // Figure out if there's any additional flags to use if (cmProp saf = sf->GetProperty("VS_SHADER_FLAGS")) { + cmGeneratorExpression ge; + std::unique_ptr<cmCompiledGeneratorExpression> cge = ge.Parse(*saf); + for (const std::string& config : this->Configurations) { - toolSettings[config]["AdditionalOptions"] = *saf; + std::string evaluated = cge->Evaluate(this->LocalGenerator, config); + + if (!evaluated.empty()) { + toolSettings[config]["AdditionalOptions"] = evaluated; + } } } // Figure out if debug information should be generated diff --git a/Source/cmXCode21Object.cxx b/Source/cmXCode21Object.cxx index 1cf9a95..9b0dc58 100644 --- a/Source/cmXCode21Object.cxx +++ b/Source/cmXCode21Object.cxx @@ -4,11 +4,12 @@ #include <ostream> #include <string> +#include <utility> #include "cmSystemTools.h" -cmXCode21Object::cmXCode21Object(PBXType ptype, Type type) - : cmXCodeObject(ptype, type) +cmXCode21Object::cmXCode21Object(PBXType ptype, Type type, std::string id) + : cmXCodeObject(ptype, type, std::move(id)) { this->Version = 21; } diff --git a/Source/cmXCode21Object.h b/Source/cmXCode21Object.h index eb017447..f3fc438 100644 --- a/Source/cmXCode21Object.h +++ b/Source/cmXCode21Object.h @@ -13,7 +13,7 @@ class cmXCode21Object : public cmXCodeObject { public: - cmXCode21Object(PBXType ptype, Type type); + cmXCode21Object(PBXType ptype, Type type, std::string id); void PrintComment(std::ostream&) override; static void PrintList(std::vector<std::unique_ptr<cmXCodeObject>> const&, std::ostream& out, PBXType t); diff --git a/Source/cmXCodeObject.cxx b/Source/cmXCodeObject.cxx index b301ab1..d5c5275 100644 --- a/Source/cmXCodeObject.cxx +++ b/Source/cmXCodeObject.cxx @@ -40,7 +40,7 @@ cmXCodeObject::~cmXCodeObject() this->Version = 15; } -cmXCodeObject::cmXCodeObject(PBXType ptype, Type type) +cmXCodeObject::cmXCodeObject(PBXType ptype, Type type, std::string id) { this->Version = 15; this->Target = nullptr; @@ -48,27 +48,7 @@ cmXCodeObject::cmXCodeObject(PBXType ptype, Type type) this->IsA = ptype; - if (type == OBJECT) { - // Set the Id of an Xcode object to a unique string for each instance. - // However the Xcode user file references certain Ids: for those cases, - // override the generated Id using SetId(). - // - char cUuid[40] = { 0 }; - CFUUIDRef uuid = CFUUIDCreate(kCFAllocatorDefault); - CFStringRef s = CFUUIDCreateString(kCFAllocatorDefault, uuid); - CFStringGetCString(s, cUuid, sizeof(cUuid), kCFStringEncodingUTF8); - this->Id = cUuid; - CFRelease(s); - CFRelease(uuid); - } else { - this->Id = - "Temporary cmake object, should not be referred to in Xcode file"; - } - - cmSystemTools::ReplaceString(this->Id, "-", ""); - if (this->Id.size() > 24) { - this->Id = this->Id.substr(0, 24); - } + this->Id = std::move(id); this->TypeValue = type; if (this->TypeValue == OBJECT) { diff --git a/Source/cmXCodeObject.h b/Source/cmXCodeObject.h index ab7f99e..dd5e86e 100644 --- a/Source/cmXCodeObject.h +++ b/Source/cmXCodeObject.h @@ -57,7 +57,7 @@ public: }; static const char* PBXTypeNames[]; virtual ~cmXCodeObject(); - cmXCodeObject(PBXType ptype, Type type); + cmXCodeObject(PBXType ptype, Type type, std::string id); Type GetType() const { return this->TypeValue; } PBXType GetIsA() const { return this->IsA; } diff --git a/Source/cmake.cxx b/Source/cmake.cxx index 1691037..48848a7 100644 --- a/Source/cmake.cxx +++ b/Source/cmake.cxx @@ -29,6 +29,7 @@ #include "cm_sys_stat.h" #include "cmCMakePresetsFile.h" +#include "cmCommandLineArgument.h" #include "cmCommands.h" #include "cmDocumentation.h" #include "cmDocumentationEntry.h" @@ -132,131 +133,13 @@ namespace { using JsonValueMapType = std::unordered_map<std::string, Json::Value>; #endif -struct CommandArgument -{ - enum struct Values - { - Zero, - One, - Two, - }; - - std::string InvalidSyntaxMessage; - std::string InvalidValueMessage; - std::string Name; - CommandArgument::Values Type; - std::function<bool(std::string const& value, cmake* state)> StoreCall; - - template <typename FunctionType> - CommandArgument(std::string n, CommandArgument::Values t, - FunctionType&& func) - : InvalidSyntaxMessage(cmStrCat("Invalid syntax used with ", n)) - , InvalidValueMessage(cmStrCat("Invalid value used with ", n)) - , Name(std::move(n)) - , Type(t) - , StoreCall(std::forward<FunctionType>(func)) - { - } - - template <typename FunctionType> - CommandArgument(std::string n, std::string failedMsg, - CommandArgument::Values t, FunctionType&& func) - : InvalidSyntaxMessage(cmStrCat("Invalid syntax used with ", n)) - , InvalidValueMessage(std::move(failedMsg)) - , Name(std::move(n)) - , Type(t) - , StoreCall(std::forward<FunctionType>(func)) - { - } - - bool matches(std::string const& input) const - { - return cmHasPrefix(input, this->Name); - } - - template <typename T> - bool parse(std::string const& input, T& index, - std::vector<std::string> const& allArgs, cmake* state) const - { - enum struct ParseMode - { - Valid, - Invalid, - SyntaxError, - ValueError - }; - ParseMode parseState = ParseMode::Valid; - - // argument is the next parameter - if (this->Type == CommandArgument::Values::Zero) { - if (input.size() == this->Name.size()) { - parseState = this->StoreCall(input, state) ? ParseMode::Valid - : ParseMode::Invalid; - } else { - parseState = ParseMode::SyntaxError; - } - - } else if (this->Type == CommandArgument::Values::One) { - if (input.size() == this->Name.size()) { - ++index; - if (index >= allArgs.size() || allArgs[index][0] == '-') { - parseState = ParseMode::ValueError; - } else { - parseState = this->StoreCall(allArgs[index], state) - ? ParseMode::Valid - : ParseMode::Invalid; - } - } else { - // parse the string to get the value - auto possible_value = cm::string_view(input).substr(this->Name.size()); - if (possible_value.empty()) { - parseState = ParseMode::SyntaxError; - parseState = ParseMode::ValueError; - } else if (possible_value[0] == '=') { - possible_value.remove_prefix(1); - if (possible_value.empty()) { - parseState = ParseMode::ValueError; - } else { - parseState = this->StoreCall(std::string(possible_value), state) - ? ParseMode::Valid - : ParseMode::Invalid; - } - } - if (parseState == ParseMode::Valid) { - parseState = this->StoreCall(std::string(possible_value), state) - ? ParseMode::Valid - : ParseMode::Invalid; - } - } - } else if (this->Type == CommandArgument::Values::Two) { - if (input.size() == this->Name.size()) { - if (index + 2 >= allArgs.size() || allArgs[index + 1][0] == '-' || - allArgs[index + 2][0] == '-') { - parseState = ParseMode::ValueError; - } else { - index += 2; - parseState = - this->StoreCall(cmStrCat(allArgs[index - 1], ";", allArgs[index]), - state) - ? ParseMode::Valid - : ParseMode::Invalid; - } - } - } - - if (parseState == ParseMode::SyntaxError) { - cmSystemTools::Error(this->InvalidSyntaxMessage); - } else if (parseState == ParseMode::ValueError) { - cmSystemTools::Error(this->InvalidValueMessage); - } - return (parseState == ParseMode::Valid); - } -}; - auto IgnoreAndTrueLambda = [](std::string const&, cmake*) -> bool { return true; }; +using CommandArgument = + cmCommandLineArgument<bool(std::string const& value, cmake* state)>; + } // namespace static bool cmakeCheckStampFile(const std::string& stampName); @@ -325,8 +208,9 @@ cmake::cmake(Role role, cmState::Mode mode) }; // The "c" extension MUST precede the "C" extension. - setupExts(this->CLikeSourceFileExtensions, - { "c", "C", "c++", "cc", "cpp", "cxx", "cu", "m", "M", "mm" }); + setupExts( + this->CLikeSourceFileExtensions, + { "c", "C", "c++", "cc", "cpp", "cxx", "cu", "mpp", "m", "M", "mm" }); setupExts(this->HeaderFileExtensions, { "h", "hh", "h++", "hm", "hpp", "hxx", "in", "txx" }); setupExts(this->CudaFileExtensions, { "cu" }); diff --git a/Source/cmakemain.cxx b/Source/cmakemain.cxx index f7734a6..ba471b7 100644 --- a/Source/cmakemain.cxx +++ b/Source/cmakemain.cxx @@ -5,7 +5,6 @@ #include <algorithm> #include <cassert> -#include <cctype> #include <climits> #include <cstring> #include <iostream> @@ -19,6 +18,7 @@ #include <cm3p/uv.h> +#include "cmCommandLineArgument.h" #include "cmConsoleBuf.h" #include "cmDocumentationEntry.h" // IWYU pragma: keep #include "cmGlobalGenerator.h" @@ -213,61 +213,114 @@ int do_cmake(int ac, char const* const* av) } #endif + bool wizard_mode = false; bool sysinfo = false; bool list_cached = false; bool list_all_cached = false; bool list_help = false; bool view_only = false; cmake::WorkingMode workingMode = cmake::NORMAL_MODE; - std::vector<std::string> args; - for (int i = 0; i < ac; ++i) { - if (strcmp(av[i], "-i") == 0) { - /* clang-format off */ - std::cerr << - "The \"cmake -i\" wizard mode is no longer supported.\n" - "Use the -D option to set cache values on the command line.\n" - "Use cmake-gui or ccmake for an interactive dialog.\n"; - /* clang-format on */ - return 1; - } - if (strcmp(av[i], "--system-information") == 0) { - sysinfo = true; - } else if (strcmp(av[i], "-N") == 0) { - view_only = true; - } else if (strcmp(av[i], "-L") == 0) { - list_cached = true; - } else if (strcmp(av[i], "-LA") == 0) { - list_all_cached = true; - } else if (strcmp(av[i], "-LH") == 0) { - list_cached = true; - list_help = true; - } else if (strcmp(av[i], "-LAH") == 0) { - list_all_cached = true; - list_help = true; - } else if (cmHasLiteralPrefix(av[i], "-P")) { - if (i == ac - 1) { - cmSystemTools::Error("No script specified for argument -P"); - return 1; + std::vector<std::string> parsedArgs; + + using CommandArgument = + cmCommandLineArgument<bool(std::string const& value)>; + std::vector<CommandArgument> arguments = { + CommandArgument{ + "-i", CommandArgument::Values::Zero, + [&wizard_mode](std::string const&) -> bool { + /* clang-format off */ + std::cerr << + "The \"cmake -i\" wizard mode is no longer supported.\n" + "Use the -D option to set cache values on the command line.\n" + "Use cmake-gui or ccmake for an interactive dialog.\n"; + /* clang-format on */ + wizard_mode = true; + return true; + } }, + CommandArgument{ "--system-information", CommandArgument::Values::Zero, + [&](std::string const&) -> bool { + sysinfo = true; + return true; + } }, + CommandArgument{ "-N", CommandArgument::Values::Zero, + [&](std::string const&) -> bool { + view_only = true; + return true; + } }, + CommandArgument{ "-LAH", CommandArgument::Values::Zero, + [&](std::string const&) -> bool { + list_all_cached = true; + list_help = true; + return true; + } }, + CommandArgument{ "-LA", CommandArgument::Values::Zero, + [&](std::string const&) -> bool { + list_all_cached = true; + return true; + } }, + CommandArgument{ "-LH", CommandArgument::Values::Zero, + [&](std::string const&) -> bool { + list_cached = true; + list_help = true; + return true; + } }, + CommandArgument{ "-L", CommandArgument::Values::Zero, + [&](std::string const&) -> bool { + list_cached = true; + return true; + } }, + CommandArgument{ "-P", "No script specified for argument -P", + CommandArgument::Values::One, + [&](std::string const& value) -> bool { + workingMode = cmake::SCRIPT_MODE; + parsedArgs.emplace_back("-P"); + parsedArgs.push_back(value); + return true; + } }, + CommandArgument{ "--find-package", CommandArgument::Values::Zero, + [&](std::string const&) -> bool { + workingMode = cmake::FIND_PACKAGE_MODE; + parsedArgs.emplace_back("--find-package"); + return true; + } }, + CommandArgument{ "--list-presets", CommandArgument::Values::Zero, + [&](std::string const&) -> bool { + workingMode = cmake::HELP_MODE; + parsedArgs.emplace_back("--list-presets"); + return true; + } }, + }; + + std::vector<std::string> inputArgs; + inputArgs.reserve(ac); + cm::append(inputArgs, av, av + ac); + + for (decltype(inputArgs.size()) i = 0; i < inputArgs.size(); ++i) { + std::string const& arg = inputArgs[i]; + bool matched = false; + for (auto const& m : arguments) { + if (m.matches(arg)) { + matched = true; + if (m.parse(arg, i, inputArgs)) { + break; + } + return 1; // failed to parse } - workingMode = cmake::SCRIPT_MODE; - args.emplace_back(av[i]); - i++; - args.emplace_back(av[i]); - } else if (cmHasLiteralPrefix(av[i], "--find-package")) { - workingMode = cmake::FIND_PACKAGE_MODE; - args.emplace_back(av[i]); - } else if (strcmp(av[i], "--list-presets") == 0) { - workingMode = cmake::HELP_MODE; - args.emplace_back(av[i]); - } else { - args.emplace_back(av[i]); } + if (!matched) { + parsedArgs.emplace_back(av[i]); + } + } + + if (wizard_mode) { + return 1; } + if (sysinfo) { cmake cm(cmake::RoleProject, cmState::Project); cm.SetHomeDirectory(""); cm.SetHomeOutputDirectory(""); - int ret = cm.GetSystemInformation(args); + int ret = cm.GetSystemInformation(parsedArgs); return ret; } cmake::Role const role = @@ -297,7 +350,7 @@ int do_cmake(int ac, char const* const* av) }); cm.SetWorkingMode(workingMode); - int res = cm.Run(args, view_only); + int res = cm.Run(parsedArgs, view_only); if (list_cached || list_all_cached) { std::cout << "-- Cache values" << std::endl; std::vector<std::string> keys = cm.GetState()->GetCacheEntryKeys(); @@ -332,16 +385,9 @@ int do_cmake(int ac, char const* const* av) } #ifndef CMAKE_BOOTSTRAP -int extract_job_number(int& index, char const* current, char const* next, - int len_of_flag) +int extract_job_number(std::string const& command, + std::string const& jobString) { - std::string command(current); - std::string jobString = command.substr(len_of_flag); - if (jobString.empty() && next && isdigit(next[0])) { - ++index; // skip parsing the job number - jobString = std::string(next); - } - int jobs = -1; unsigned long numJobs = 0; if (jobString.empty()) { @@ -356,8 +402,8 @@ int extract_job_number(int& index, char const* current, char const* next, jobs = int(numJobs); } } else { - std::cerr << "'" << command.substr(0, len_of_flag) << "' invalid number '" - << jobString << "' given.\n\n"; + std::cerr << "'" << command << "' invalid number '" << jobString + << "' given.\n\n"; } return jobs; } @@ -374,88 +420,107 @@ int do_build(int ac, char const* const* av) std::string config; std::string dir; std::vector<std::string> nativeOptions; + bool nativeOptionsPassed = false; bool cleanFirst = false; bool foundClean = false; bool foundNonClean = false; bool verbose = cmSystemTools::HasEnv("VERBOSE"); - enum Doing - { - DoingNone, - DoingDir, - DoingTarget, - DoingConfig, - DoingNative + auto jLambda = [&](std::string const& value) -> bool { + jobs = extract_job_number("-j", value); + if (jobs < 0) { + dir.clear(); + } + return true; }; - Doing doing = DoingDir; - for (int i = 2; i < ac; ++i) { - if (doing == DoingNative) { - nativeOptions.emplace_back(av[i]); - } else if (cmHasLiteralPrefix(av[i], "-j")) { - const char* nextArg = ((i + 1 < ac) ? av[i + 1] : nullptr); - jobs = extract_job_number(i, av[i], nextArg, sizeof("-j") - 1); - if (jobs < 0) { - dir.clear(); - } - doing = DoingNone; - } else if (cmHasLiteralPrefix(av[i], "--parallel")) { - const char* nextArg = ((i + 1 < ac) ? av[i + 1] : nullptr); - jobs = extract_job_number(i, av[i], nextArg, sizeof("--parallel") - 1); - if (jobs < 0) { - dir.clear(); + auto parallelLambda = [&](std::string const& value) -> bool { + jobs = extract_job_number("--parallel", value); + if (jobs < 0) { + dir.clear(); + } + return true; + }; + auto targetLambda = [&](std::string const& value) -> bool { + if (!value.empty()) { + std::vector<std::string> values = cmExpandedList(value); + for (auto const& v : values) { + targets.emplace_back(v); + if (v == "clean") { + foundClean = true; + } else { + foundNonClean = true; + } } - doing = DoingNone; - } else if ((strcmp(av[i], "--target") == 0) || - (strcmp(av[i], "-t") == 0)) { - doing = DoingTarget; - } else if (strcmp(av[i], "--config") == 0) { - doing = DoingConfig; - } else if (strcmp(av[i], "--clean-first") == 0) { - cleanFirst = true; - doing = DoingNone; - } else if ((strcmp(av[i], "--verbose") == 0) || - (strcmp(av[i], "-v") == 0)) { - verbose = true; - doing = DoingNone; - } else if (strcmp(av[i], "--use-stderr") == 0) { - /* tolerate legacy option */ - } else if (strcmp(av[i], "--") == 0) { - doing = DoingNative; - } else { - switch (doing) { - case DoingDir: - dir = cmSystemTools::CollapseFullPath(av[i]); - doing = DoingNone; - break; - case DoingTarget: - if (strlen(av[i]) == 0) { - std::cerr << "Warning: Argument number " << i - << " after --target option is empty." << std::endl; - } else { - targets.emplace_back(av[i]); - if (strcmp(av[i], "clean") == 0) { - foundClean = true; - } else { - foundNonClean = true; - } - } - if (foundClean && foundNonClean) { - std::cerr << "Error: Building 'clean' and other targets together " - "is not supported." - << std::endl; - dir.clear(); - } - break; - case DoingConfig: - config = av[i]; - doing = DoingNone; - break; - default: - std::cerr << "Unknown argument " << av[i] << std::endl; - dir.clear(); + return true; + } + return false; + }; + auto verboseLambda = [&](std::string const&) -> bool { + verbose = true; + return true; + }; + + using CommandArgument = + cmCommandLineArgument<bool(std::string const& value)>; + + std::vector<CommandArgument> arguments = { + CommandArgument{ "-j", CommandArgument::Values::ZeroOrOne, jLambda }, + CommandArgument{ "--parallel", CommandArgument::Values::ZeroOrOne, + parallelLambda }, + CommandArgument{ "-t", CommandArgument::Values::OneOrMore, targetLambda }, + CommandArgument{ "--target", CommandArgument::Values::OneOrMore, + targetLambda }, + CommandArgument{ "--config", CommandArgument::Values::One, + [&](std::string const& value) -> bool { + config = value; + return true; + } }, + CommandArgument{ "--clean-first", CommandArgument::Values::Zero, + [&](std::string const&) -> bool { + cleanFirst = true; + return true; + } }, + CommandArgument{ "-v", CommandArgument::Values::Zero, verboseLambda }, + CommandArgument{ "--verbose", CommandArgument::Values::Zero, + verboseLambda }, + /* legacy option no-op*/ + CommandArgument{ "--use-stderr", CommandArgument::Values::Zero, + [](std::string const&) -> bool { return true; } }, + CommandArgument{ "--", CommandArgument::Values::Zero, + [&](std::string const&) -> bool { + nativeOptionsPassed = true; + return true; + } }, + }; + + if (ac >= 3) { + dir = cmSystemTools::CollapseFullPath(av[2]); + + std::vector<std::string> inputArgs; + inputArgs.reserve(ac - 3); + cm::append(inputArgs, av + 3, av + ac); + + decltype(inputArgs.size()) i = 0; + for (; i < inputArgs.size() && !nativeOptionsPassed; ++i) { + + std::string const& arg = inputArgs[i]; + for (auto const& m : arguments) { + if (m.matches(arg) && m.parse(arg, i, inputArgs)) { break; + } } } + + if (nativeOptionsPassed) { + cm::append(nativeOptions, inputArgs.begin() + i, inputArgs.end()); + } + } + + if (foundClean && foundNonClean) { + std::cerr << "Error: Building 'clean' and other targets together " + "is not supported." + << std::endl; + dir.clear(); } if (jobs == cmake::NO_BUILD_PARALLEL_LEVEL) { @@ -658,60 +723,59 @@ int do_install(int ac, char const* const* av) bool strip = false; bool verbose = cmSystemTools::HasEnv("VERBOSE"); - enum Doing - { - DoingNone, - DoingDir, - DoingConfig, - DoingComponent, - DoingPrefix, - DoingDefaultDirectoryPermissions, + auto verboseLambda = [&](std::string const&) -> bool { + verbose = true; + return true; }; - Doing doing = DoingDir; + using CommandArgument = + cmCommandLineArgument<bool(std::string const& value)>; + + std::vector<CommandArgument> arguments = { + CommandArgument{ "--config", CommandArgument::Values::One, + [&](std::string const& value) -> bool { + config = value; + return true; + } }, + CommandArgument{ "--component", CommandArgument::Values::One, + [&](std::string const& value) -> bool { + component = value; + return true; + } }, + CommandArgument{ "--default-directory-permissions", + CommandArgument::Values::One, + [&](std::string const& value) -> bool { + defaultDirectoryPermissions = value; + return true; + } }, + CommandArgument{ "--prefix", CommandArgument::Values::One, + [&](std::string const& value) -> bool { + prefix = value; + return true; + } }, + CommandArgument{ "--strip", CommandArgument::Values::Zero, + [&](std::string const&) -> bool { + strip = true; + return true; + } }, + CommandArgument{ "-v", CommandArgument::Values::Zero, verboseLambda }, + CommandArgument{ "--verbose", CommandArgument::Values::Zero, + verboseLambda } + }; - for (int i = 2; i < ac; ++i) { - if (strcmp(av[i], "--config") == 0) { - doing = DoingConfig; - } else if (strcmp(av[i], "--component") == 0) { - doing = DoingComponent; - } else if (strcmp(av[i], "--prefix") == 0) { - doing = DoingPrefix; - } else if (strcmp(av[i], "--strip") == 0) { - strip = true; - doing = DoingNone; - } else if ((strcmp(av[i], "--verbose") == 0) || - (strcmp(av[i], "-v") == 0)) { - verbose = true; - doing = DoingNone; - } else if (strcmp(av[i], "--default-directory-permissions") == 0) { - doing = DoingDefaultDirectoryPermissions; - } else { - switch (doing) { - case DoingDir: - dir = cmSystemTools::CollapseFullPath(av[i]); - doing = DoingNone; - break; - case DoingConfig: - config = av[i]; - doing = DoingNone; - break; - case DoingComponent: - component = av[i]; - doing = DoingNone; - break; - case DoingPrefix: - prefix = av[i]; - doing = DoingNone; - break; - case DoingDefaultDirectoryPermissions: - defaultDirectoryPermissions = av[i]; - doing = DoingNone; - break; - default: - std::cerr << "Unknown argument " << av[i] << std::endl; - dir.clear(); + if (ac >= 3) { + dir = cmSystemTools::CollapseFullPath(av[2]); + + std::vector<std::string> inputArgs; + inputArgs.reserve(ac - 3); + cm::append(inputArgs, av + 3, av + ac); + for (decltype(inputArgs.size()) i = 0; i < inputArgs.size(); ++i) { + + std::string const& arg = inputArgs[i]; + for (auto const& m : arguments) { + if (m.matches(arg) && m.parse(arg, i, inputArgs)) { break; + } } } } diff --git a/Tests/ConfigSources/CMakeLists.txt b/Tests/ConfigSources/CMakeLists.txt index ab0b5d8..a3d98f6 100644 --- a/Tests/ConfigSources/CMakeLists.txt +++ b/Tests/ConfigSources/CMakeLists.txt @@ -5,6 +5,11 @@ if(NOT _isMultiConfig AND NOT CMAKE_BUILD_TYPE) endif() project(ConfigSources CXX) +if("${CMAKE_CXX_COMPILER_ID};${CMAKE_CXX_SIMULATE_ID}" STREQUAL "Intel;MSVC") + string(APPEND CMAKE_CXX_FLAGS_DEBUG " -Z7") + string(APPEND CMAKE_CXX_FLAGS_RELWITHDEBINFO " -Z7") +endif() + # Source file(s) named with the configuration(s). file(GENERATE OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/config_$<CONFIG>.cpp" diff --git a/Tests/QtAutogen/RerunMocBasic/CMakeLists.txt b/Tests/QtAutogen/RerunMocBasic/CMakeLists.txt index 9b32e59..c53e857 100644 --- a/Tests/QtAutogen/RerunMocBasic/CMakeLists.txt +++ b/Tests/QtAutogen/RerunMocBasic/CMakeLists.txt @@ -47,19 +47,38 @@ macro(require_change_not) endmacro() -# Initial build +# Configure the test project configure_file("${mocBasicSrcDir}/test1a.h.in" "${mocBasicBinDir}/test1.h" COPYONLY) -try_compile(MOC_RERUN - "${mocBasicBinDir}" - "${mocBasicSrcDir}" - MocBasic - CMAKE_FLAGS "-DQT_TEST_VERSION=${QT_TEST_VERSION}" - "-DCMAKE_AUTOGEN_VERBOSE=${CMAKE_AUTOGEN_VERBOSE}" - "-DQT_QMAKE_EXECUTABLE:FILEPATH=${QT_QMAKE_EXECUTABLE}" +configure_file("${mocBasicSrcDir}/myobject3a.h.in" "${mocBasicBinDir}/myobject3.h" @ONLY) +if(CMAKE_GENERATOR_INSTANCE) + set(_D_CMAKE_GENERATOR_INSTANCE "-DCMAKE_GENERATOR_INSTANCE=${CMAKE_GENERATOR_INSTANCE}") +else() + set(_D_CMAKE_GENERATOR_INSTANCE "") +endif() +execute_process( + COMMAND "${CMAKE_COMMAND}" -B "${mocBasicBinDir}" -S "${mocBasicSrcDir}" + -G "${CMAKE_GENERATOR}" + -A "${CMAKE_GENERATOR_PLATFORM}" + -T "${CMAKE_GENERATOR_TOOLSET}" + ${_D_CMAKE_GENERATOR_INSTANCE} + "-DQT_TEST_VERSION=${QT_TEST_VERSION}" + "-DCMAKE_AUTOGEN_VERBOSE=${CMAKE_AUTOGEN_VERBOSE}" + "-DQT_QMAKE_EXECUTABLE:FILEPATH=${QT_QMAKE_EXECUTABLE}" + RESULT_VARIABLE exit_code OUTPUT_VARIABLE output ) -if (NOT MOC_RERUN) - message(FATAL_ERROR "Initial build of mocBasic failed. Output: ${output}") +if(NOT exit_code EQUAL 0) + message(FATAL_ERROR "Initial configuration of mocBasic failed. Output: ${output}") +endif() + +# Initial build +execute_process( + COMMAND "${CMAKE_COMMAND}" --build "${mocBasicBinDir}" + RESULT_VARIABLE exit_code + OUTPUT_VARIABLE output +) +if(NOT exit_code EQUAL 0) + message(FATAL_ERROR "Initial build of mocBasic failed. Output: ${output}") endif() # Get name of the output binary @@ -100,3 +119,43 @@ message(STATUS "Changing nothing for no MOC re-run") rebuild(3) acquire_timestamp(After) require_change_not() + + +# - Ensure that the timestamp will change +# - Remove Q_OBJECT from header +# - Rebuild +acquire_timestamp(Before) +sleep() +message(STATUS "Remove Q_OBJECT from header file for a MOC re-run") +configure_file("${mocBasicSrcDir}/test1c.h.in" "${mocBasicBinDir}/test1.h" COPYONLY) +sleep() +rebuild(4) +acquire_timestamp(After) +require_change() + + +# - Ensure that the timestamp will change +# - Add Q_OBJECT to header again +# - Rebuild +acquire_timestamp(Before) +sleep() +message(STATUS "Add Q_OBJECT to test1.h for a MOC re-run") +configure_file("${mocBasicSrcDir}/test1a.h.in" "${mocBasicBinDir}/test1.h" COPYONLY) +sleep() +rebuild(5) +acquire_timestamp(After) +require_change() + + +# - Ensure that the timestamp will change +# - Add Q_OBJECT to MyObject3 +# - Rebuild +acquire_timestamp(Before) +sleep() +message(STATUS "Add Q_OBJECT to myobject3.h file for a MOC re-run") +set(CLASS_CONTENT "Q_OBJECT") +configure_file("${mocBasicSrcDir}/myobject3a.h.in" "${mocBasicBinDir}/myobject3.h" @ONLY) +sleep() +rebuild(6) +acquire_timestamp(After) +require_change() diff --git a/Tests/QtAutogen/RerunMocBasic/MocBasic/CMakeLists.txt b/Tests/QtAutogen/RerunMocBasic/MocBasic/CMakeLists.txt index 6a9f550..42f2f57 100644 --- a/Tests/QtAutogen/RerunMocBasic/MocBasic/CMakeLists.txt +++ b/Tests/QtAutogen/RerunMocBasic/MocBasic/CMakeLists.txt @@ -13,10 +13,15 @@ add_custom_command(OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/main.cpp add_executable(mocBasic ${CMAKE_CURRENT_BINARY_DIR}/test1.h + ${CMAKE_CURRENT_BINARY_DIR}/myobject3.h ${CMAKE_CURRENT_BINARY_DIR}/main.cpp + plainobject.cpp res1.qrc ) -target_include_directories(mocBasic PRIVATE ${CMAKE_CURRENT_BINARY_DIR}) +target_include_directories(mocBasic PRIVATE + ${CMAKE_CURRENT_BINARY_DIR} + ${CMAKE_CURRENT_SOURCE_DIR} +) target_link_libraries(mocBasic ${QT_QTCORE_TARGET}) # Write target name to text file add_custom_command(TARGET mocBasic POST_BUILD COMMAND diff --git a/Tests/QtAutogen/RerunMocBasic/MocBasic/main.cpp.in b/Tests/QtAutogen/RerunMocBasic/MocBasic/main.cpp.in index 9d7ea37..5accfd6 100644 --- a/Tests/QtAutogen/RerunMocBasic/MocBasic/main.cpp.in +++ b/Tests/QtAutogen/RerunMocBasic/MocBasic/main.cpp.in @@ -1,4 +1,5 @@ #include "test1.h" +#include "plainobject.h" extern int qInitResources_res1(); @@ -16,6 +17,7 @@ int main() Test1 test1; Test2 test2; + PlainObject plainObject; return 0; } diff --git a/Tests/QtAutogen/RerunMocBasic/MocBasic/myobject3a.h.in b/Tests/QtAutogen/RerunMocBasic/MocBasic/myobject3a.h.in new file mode 100644 index 0000000..d62c314 --- /dev/null +++ b/Tests/QtAutogen/RerunMocBasic/MocBasic/myobject3a.h.in @@ -0,0 +1,13 @@ +#ifndef MYOBJECT3_H +#define MYOBJECT3_H + +#include <qobject.h> + +class MyObject3 : public QObject +{ + @CLASS_CONTENT@ +public: + MyObject3() {} +}; + +#endif diff --git a/Tests/QtAutogen/RerunMocBasic/MocBasic/plainobject.cpp b/Tests/QtAutogen/RerunMocBasic/MocBasic/plainobject.cpp new file mode 100644 index 0000000..0ca785e --- /dev/null +++ b/Tests/QtAutogen/RerunMocBasic/MocBasic/plainobject.cpp @@ -0,0 +1,12 @@ +#include "plainobject.h" + +#include "myobject3.h" + +PlainObject::PlainObject() +{ +} + +void PlainObject::doSomething() +{ + MyObject3 obj3; +} diff --git a/Tests/QtAutogen/RerunMocBasic/MocBasic/plainobject.h b/Tests/QtAutogen/RerunMocBasic/MocBasic/plainobject.h new file mode 100644 index 0000000..8037858 --- /dev/null +++ b/Tests/QtAutogen/RerunMocBasic/MocBasic/plainobject.h @@ -0,0 +1,12 @@ +#ifndef PLAINOBJECT_H +#define PLAINOBJECT_H + +// Class that is plain C++, no Qt involved. +class PlainObject +{ +public: + PlainObject(); + void doSomething(); +}; + +#endif diff --git a/Tests/QtAutogen/RerunMocBasic/MocBasic/test1c.h.in b/Tests/QtAutogen/RerunMocBasic/MocBasic/test1c.h.in new file mode 100644 index 0000000..d0b9868 --- /dev/null +++ b/Tests/QtAutogen/RerunMocBasic/MocBasic/test1c.h.in @@ -0,0 +1,6 @@ +#include <QObject> +class Test1 +{ +public: + void onTst1() {} +}; diff --git a/Tests/RunCMake/BuildDepends/RepeatCMake-Custom-Script.cmake b/Tests/RunCMake/BuildDepends/RepeatCMake-Custom-Script.cmake new file mode 100644 index 0000000..3e953b3 --- /dev/null +++ b/Tests/RunCMake/BuildDepends/RepeatCMake-Custom-Script.cmake @@ -0,0 +1,4 @@ +if(EXISTS "${CMAKE_CURRENT_BINARY_DIR}/exists-for-build2") + message(FATAL_ERROR "Custom command incorrectly re-ran after CMake re-ran!") +endif() +file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/out.txt") diff --git a/Tests/RunCMake/BuildDepends/RepeatCMake-Custom.cmake b/Tests/RunCMake/BuildDepends/RepeatCMake-Custom.cmake new file mode 100644 index 0000000..697e485 --- /dev/null +++ b/Tests/RunCMake/BuildDepends/RepeatCMake-Custom.cmake @@ -0,0 +1,5 @@ +add_custom_command(OUTPUT out.txt + COMMAND ${CMAKE_COMMAND} -P ${CMAKE_CURRENT_LIST_DIR}/RepeatCMake-Custom-Script.cmake + DEPENDS ${CMAKE_CURRENT_LIST_DIR}/RepeatCMake-Custom-Script.cmake + ) +add_custom_target(drive ALL DEPENDS out.txt) diff --git a/Tests/RunCMake/BuildDepends/RunCMakeTest.cmake b/Tests/RunCMake/BuildDepends/RunCMakeTest.cmake index 67da6ae..6232634 100644 --- a/Tests/RunCMake/BuildDepends/RunCMakeTest.cmake +++ b/Tests/RunCMake/BuildDepends/RunCMakeTest.cmake @@ -79,6 +79,23 @@ if(RunCMake_GENERATOR MATCHES "Make") endif() endif() +function(run_RepeatCMake CASE) + set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/${CASE}-build) + if(RunCMake_GENERATOR_IS_MULTI_CONFIG) + set(RunCMake_TEST_OPTIONS -DCMAKE_CONFIGURATION_TYPES=Debug) + else() + set(RunCMake_TEST_OPTIONS -DCMAKE_BUILD_TYPE=Debug) + endif() + run_cmake(${CASE}) + set(RunCMake_TEST_NO_CLEAN 1) + run_cmake_command(${CASE}-build1 ${CMAKE_COMMAND} --build . --config Debug) + run_cmake_command(${CASE}-rerun1 ${CMAKE_COMMAND} .) + file(WRITE ${RunCMake_TEST_BINARY_DIR}/exists-for-build2 "") + run_cmake_command(${CASE}-build2 ${CMAKE_COMMAND} --build . --config Debug) +endfunction() + +run_RepeatCMake(RepeatCMake-Custom) + function(run_ReGeneration) # test re-generation of project even if CMakeLists.txt files disappeared diff --git a/Tests/RunCMake/ExportCompileCommands/Properties.cmake b/Tests/RunCMake/ExportCompileCommands/Properties.cmake new file mode 100644 index 0000000..c7a38b7 --- /dev/null +++ b/Tests/RunCMake/ExportCompileCommands/Properties.cmake @@ -0,0 +1,22 @@ +enable_language(C) + +add_library(Unset STATIC empty.c) +set(CMAKE_EXPORT_COMPILE_COMMANDS ON) +add_library(SetOn STATIC empty.c) +set(CMAKE_EXPORT_COMPILE_COMMANDS OFF) +add_library(SetOff STATIC empty.c) + +get_property(_set TARGET Unset PROPERTY EXPORT_COMPILE_COMMANDS) +if(_set) + message(SEND_ERROR "EXPORT_COMPILE_COMMANDS property should be unset for Unset target (got \"${_set}\")") +endif() + +get_property(_on TARGET SetOn PROPERTY EXPORT_COMPILE_COMMANDS) +if(NOT _on STREQUAL "ON") + message(SEND_ERROR "EXPORT_COMPILE_COMMANDS property should be \"ON\" for SetOn target (got \"${_on}\")") +endif() + +get_property(_off TARGET SetOff PROPERTY EXPORT_COMPILE_COMMANDS) +if(NOT _off STREQUAL "OFF") + message(SEND_ERROR "EXPORT_COMPILE_COMMANDS property should be \"OFF\" for SetOff target (got \"${_off}\")") +endif() diff --git a/Tests/RunCMake/ExportCompileCommands/PropertiesGenerateCommand-check.cmake b/Tests/RunCMake/ExportCompileCommands/PropertiesGenerateCommand-check.cmake new file mode 100644 index 0000000..d698742 --- /dev/null +++ b/Tests/RunCMake/ExportCompileCommands/PropertiesGenerateCommand-check.cmake @@ -0,0 +1,32 @@ +if(NOT EXISTS "${RunCMake_TEST_BINARY_DIR}/compile_commands.json") + set(RunCMake_TEST_FAILED "compile_commands.json not generated") + return() +endif() + +file(READ "${RunCMake_TEST_BINARY_DIR}/compile_commands.json" compile_commands) + +macro(check_error) + if(error) + message(SEND_ERROR "Unexpected error \"${error}\"\nFor: ${compile_commands}") + endif() +endmacro() + +string(JSON num_commands ERROR_VARIABLE error LENGTH "${compile_commands}") +check_error() + +# Only one of the targets has the EXPORT_COMPILE_COMMANDS property enabled. +if(NOT num_commands STREQUAL 1) + message(SEND_ERROR "Expected 1 compile command, got ${num_commands} for ${compile_commands}") +endif() + +# Get the compile command generated. +string(JSON result ERROR_VARIABLE error GET "${compile_commands}" 0) +check_error() +string(JSON result ERROR_VARIABLE error GET "${result}" file) +check_error() + +# And ensure the correct target is in that compile command. +cmake_path(COMPARE "${result}" EQUAL "${RunCMake_TEST_SOURCE_DIR}/expected_file.c" good) +if(NOT good) + message(SEND_ERROR "Expected \"${result}\" in \"${RunCMake_TEST_SOURCE_DIR}/expected_file.c\"") +endif() diff --git a/Tests/RunCMake/ExportCompileCommands/PropertiesGenerateCommand.cmake b/Tests/RunCMake/ExportCompileCommands/PropertiesGenerateCommand.cmake new file mode 100644 index 0000000..46c8845 --- /dev/null +++ b/Tests/RunCMake/ExportCompileCommands/PropertiesGenerateCommand.cmake @@ -0,0 +1,7 @@ +enable_language(C) + +add_library(Unset STATIC empty.c) +add_library(ToBeSet STATIC expected_file.c) + +# Only one target with EXPORT_COMPILE_COMMANDS property. +set_property(TARGET ToBeSet PROPERTY EXPORT_COMPILE_COMMANDS TRUE) diff --git a/Tests/RunCMake/ExportCompileCommands/RunCMakeTest.cmake b/Tests/RunCMake/ExportCompileCommands/RunCMakeTest.cmake index 9e7e732..b691637 100644 --- a/Tests/RunCMake/ExportCompileCommands/RunCMakeTest.cmake +++ b/Tests/RunCMake/ExportCompileCommands/RunCMakeTest.cmake @@ -1,4 +1,12 @@ include(RunCMake) +if(RunCMake_GENERATOR_IS_MULTI_CONFIG) + set(RunCMake_TEST_OPTIONS -DCMAKE_CONFIGURATION_TYPES=Debug) +else() + set(RunCMake_TEST_OPTIONS -DCMAKE_BUILD_TYPE=Debug) +endif() + run_cmake_with_options(BeforeProject -DCMAKE_PROJECT_INCLUDE_BEFORE=BeforeProjectBEFORE.cmake) run_cmake(CustomCompileRule) +run_cmake(Properties) +run_cmake(PropertiesGenerateCommand) diff --git a/Tests/RunCMake/ExportCompileCommands/expected_file.c b/Tests/RunCMake/ExportCompileCommands/expected_file.c new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/Tests/RunCMake/ExportCompileCommands/expected_file.c diff --git a/Tests/VSWinStorePhone/CMakeLists.txt b/Tests/VSWinStorePhone/CMakeLists.txt index 56e4c1d..edd4330 100644 --- a/Tests/VSWinStorePhone/CMakeLists.txt +++ b/Tests/VSWinStorePhone/CMakeLists.txt @@ -119,13 +119,13 @@ set_property(SOURCE ${RELEASE_CONTENT_FILES} PROPERTY set_property(SOURCE ${PIXELSHADER_FILES} PROPERTY VS_SHADER_TYPE Pixel) set_property(SOURCE ${PIXELSHADER_FILES} PROPERTY VS_SHADER_ENTRYPOINT mainPS) set_property(SOURCE ${PIXELSHADER_FILES} PROPERTY VS_SHADER_MODEL 4.0_level_9_3) -set_property(SOURCE ${PIXELSHADER_FILES} PROPERTY VS_SHADER_FLAGS "/DFLAGS_ADDED") +set_property(SOURCE ${PIXELSHADER_FILES} PROPERTY VS_SHADER_FLAGS $<1:/DFLAGS_ADDED>) set_property(SOURCE ${PIXELSHADER_FILES} PROPERTY VS_SHADER_OUTPUT_HEADER_FILE "$(OutDir)%(Filename).h") set_property(SOURCE ${VERTEXSHADER_FILES} PROPERTY VS_SHADER_TYPE Vertex) set_property(SOURCE ${VERTEXSHADER_FILES} PROPERTY VS_SHADER_ENTRYPOINT mainVS) set_property(SOURCE ${VERTEXSHADER_FILES} PROPERTY VS_SHADER_MODEL 4.0_level_9_3) -set_property(SOURCE ${VERTEXSHADER_FILES} PROPERTY VS_SHADER_FLAGS "/DFLAGS_ADDED") +set_property(SOURCE ${VERTEXSHADER_FILES} PROPERTY VS_SHADER_FLAGS $<1:/DFLAGS_ADDED>) set_property(SOURCE ${VERTEXSHADER_FILES} PROPERTY VS_SHADER_OUTPUT_HEADER_FILE "$(OutDir)%(Filename).h") set_property(SOURCE ${VERTEXSHADER_FILES} PROPERTY VS_SETTINGS "$<$<CONFIG:DEBUG>:SourceProperty1=SourceProperty1Value>") diff --git a/Utilities/Release/macos/qt-5.15.2-macosx10.13-x86_64-arm64.bash b/Utilities/Release/macos/qt-5.15.2-macosx10.13-x86_64-arm64.bash new file mode 100755 index 0000000..bf92e62 --- /dev/null +++ b/Utilities/Release/macos/qt-5.15.2-macosx10.13-x86_64-arm64.bash @@ -0,0 +1,125 @@ +#!/usr/bin/env bash + +# Run this script on a macOS x86_64 host to generate Qt universal binaries. +# +# This script requires the 'makeuniversal' tool from: +# +# https://github.com/fizzyade/makeuniversal +# +# Build it with an existing local Qt installation first. +# +# Set the PATH environment variable to contain the location of 'makeuniversal'. + +set -e +set -x + +umask 022 + +# Verify that 'makeuniversal' is available in the PATH. +type -p makeuniversal >/dev/null + +# Download, verify, and extract sources. +curl -OL https://download.qt.io/archive/qt/5.15/5.15.2/single/qt-everywhere-src-5.15.2.tar.xz +shasum -a 256 qt-everywhere-src-5.15.2.tar.xz | grep -q 3a530d1b243b5dec00bc54937455471aaa3e56849d2593edb8ded07228202240 +tar xjf qt-everywhere-src-5.15.2.tar.xz + +# Build the x86_64 variant. +mkdir qt-5.15.2-x86_64 +cd qt-5.15.2-x86_64 +../qt-everywhere-src-5.15.2/configure \ + --prefix=/ \ + -platform macx-clang \ + -device-option QMAKE_APPLE_DEVICE_ARCHS=x86_64 \ + -device-option QMAKE_MACOSX_DEPLOYMENT_TARGET=10.13 \ + -release \ + -opensource -confirm-license \ + -gui \ + -widgets \ + -no-gif \ + -no-icu \ + -no-pch \ + -no-angle \ + -no-opengl \ + -no-dbus \ + -no-harfbuzz \ + -skip declarative \ + -skip multimedia \ + -skip qtcanvas3d \ + -skip qtcharts \ + -skip qtconnectivity \ + -skip qtdeclarative \ + -skip qtgamepad \ + -skip qtlocation \ + -skip qtmultimedia \ + -skip qtnetworkauth \ + -skip qtpurchasing \ + -skip qtremoteobjects \ + -skip qtscript \ + -skip qtsensors \ + -skip qtserialbus \ + -skip qtserialport \ + -skip qtsvg \ + -skip qtwebchannel \ + -skip qtwebengine \ + -skip qtwebsockets \ + -skip qtxmlpatterns \ + -nomake examples \ + -nomake tests \ + -nomake tools +make -j 8 +cd .. + +# Build the arm64 variant. +mkdir qt-5.15.2-arm64 +cd qt-5.15.2-arm64 +../qt-everywhere-src-5.15.2/configure \ + --prefix=/ \ + -platform macx-clang \ + -device-option QMAKE_APPLE_DEVICE_ARCHS=arm64 \ + -device-option QMAKE_MACOSX_DEPLOYMENT_TARGET=10.13 \ + -release \ + -opensource -confirm-license \ + -gui \ + -widgets \ + -no-gif \ + -no-icu \ + -no-pch \ + -no-angle \ + -no-opengl \ + -no-dbus \ + -no-harfbuzz \ + -skip declarative \ + -skip multimedia \ + -skip qtcanvas3d \ + -skip qtcharts \ + -skip qtconnectivity \ + -skip qtdeclarative \ + -skip qtgamepad \ + -skip qtlocation \ + -skip qtmultimedia \ + -skip qtnetworkauth \ + -skip qtpurchasing \ + -skip qtremoteobjects \ + -skip qtscript \ + -skip qtsensors \ + -skip qtserialbus \ + -skip qtserialport \ + -skip qtsvg \ + -skip qtwebchannel \ + -skip qtwebengine \ + -skip qtwebsockets \ + -skip qtxmlpatterns \ + -nomake examples \ + -nomake tests \ + -nomake tools +make -j 8 -k +cd .. + +# Combine the two builds into universal binaries. +makeuniversal qt-5.15.2-univ qt-5.15.2-x86_64 qt-5.15.2-arm64 +cd qt-5.15.2-univ +make install -j 8 INSTALL_ROOT=/tmp/qt-5.15.2-macosx10.13-x86_64-arm64 +cd .. + +# Create the final tarball containing universal binaries. +tar cjf qt-5.15.2-macosx10.13-x86_64-arm64.tar.xz -C /tmp qt-5.15.2-macosx10.13-x86_64-arm64 diff --git a/Utilities/Release/macos/qt-5.9.9-macosx10.10-x86_64-arm64.bash b/Utilities/Release/macos/qt-5.9.9-macosx10.10-x86_64-arm64.bash index a61e114..79931ec 100755 --- a/Utilities/Release/macos/qt-5.9.9-macosx10.10-x86_64-arm64.bash +++ b/Utilities/Release/macos/qt-5.9.9-macosx10.10-x86_64-arm64.bash @@ -13,6 +13,8 @@ set -e set -x +umask 022 + # Verify that 'makeuniversal' is available in the PATH. type -p makeuniversal >/dev/null |