diff options
291 files changed, 8326 insertions, 4929 deletions
diff --git a/Help/command/enable_language.rst b/Help/command/enable_language.rst index fb49b44..fdc44f2 100644 --- a/Help/command/enable_language.rst +++ b/Help/command/enable_language.rst @@ -1,7 +1,6 @@ enable_language --------------- - -Enable a language (CXX/C/Fortran/etc) +Enable a language (CXX/C/OBJC/OBJCXX/Fortran/etc) .. code-block:: cmake @@ -10,7 +9,7 @@ Enable a language (CXX/C/Fortran/etc) Enables support for the named language in CMake. This is the same as the :command:`project` command but does not create any of the extra variables that are created by the project command. Example languages -are ``CXX``, ``C``, ``CUDA``, ``Fortran``, and ``ASM``. +are ``CXX``, ``C``, ``CUDA``, ``OBJC``, ``OBJCXX``, ``Fortran``, and ``ASM``. If enabling ``ASM``, enable it last so that CMake can check whether compilers for other languages like ``C`` work for assembly too. diff --git a/Help/command/project.rst b/Help/command/project.rst index baf18be..3951456 100644 --- a/Help/command/project.rst +++ b/Help/command/project.rst @@ -87,7 +87,8 @@ The options are: Can also be specified without ``LANGUAGES`` keyword per the first, short signature. Selects which programming languages are needed to build the project. - Supported languages include ``C``, ``CXX`` (i.e. C++), ``CUDA``, ``Fortran``, and ``ASM``. + Supported languages include ``C``, ``CXX`` (i.e. C++), ``CUDA``, + ``OBJC`` (i.e. Objective-C), ``OBJCXX``, ``Fortran``, and ``ASM``. By default ``C`` and ``CXX`` are enabled if no language options are given. Specify language ``NONE``, or use the ``LANGUAGES`` keyword and list no languages, to skip enabling any languages. diff --git a/Help/command/target_precompile_headers.rst b/Help/command/target_precompile_headers.rst index 7d36b11..3a32f41 100644 --- a/Help/command/target_precompile_headers.rst +++ b/Help/command/target_precompile_headers.rst @@ -48,17 +48,32 @@ See the :manual:`cmake-generator-expressions(7)` manual for available expressions. See the :manual:`cmake-compile-features(7)` manual for information on compile features and a list of supported compilers. +Usage +^^^^^ + .. code-block:: cmake target_precompile_headers(<target> PUBLIC - "project_header.h" + project_header.h PRIVATE + [["other_header.h"]] <unordered_map> ) -Header files will be double quoted if they are not specified with double -quotes or angle brackets. +The list of header files is used to generate a header file named +``cmake_pch.h|xx`` which is used to generate the precompiled header file +(``.pch``, ``.gch``, ``.pchi``) artifact. The ``cmake_pch.h|xx`` header +file will be force included (``-include`` for GCC, ``/FI`` for MSVC) to +all source files, so sources do not need to have ``#include "pch.h"``. + +Header file names specified with angle brackets (e.g. ``<unordered_map>``) or +explicit double quotes (escaped for the :manual:`cmake-language(7)`, +e.g. ``[["other_header.h"]]``) will be treated as is, and include directories +must be available for the compiler to find them. Other header file names +(e.g. ``project_header.h``) are interpreted as being relative to the current +source directory (e.g. :variable:`CMAKE_CURRENT_SOURCE_DIR`) and will be +included by absolute path. See Also ^^^^^^^^ diff --git a/Help/cpack_gen/deb.rst b/Help/cpack_gen/deb.rst index 23f0515..db71c87 100644 --- a/Help/cpack_gen/deb.rst +++ b/Help/cpack_gen/deb.rst @@ -179,16 +179,24 @@ List of CPack DEB generator specific variables: * Default : ``CPACK_PACKAGE_CONTACT`` .. variable:: CPACK_DEBIAN_PACKAGE_DESCRIPTION - CPACK_COMPONENT_<COMPONENT>_DESCRIPTION + CPACK_DEBIAN_<COMPONENT>_DESCRIPTION The Debian package description * Mandatory : YES * Default : - - :variable:`CPACK_DEBIAN_PACKAGE_DESCRIPTION` if set or - - :variable:`CPACK_PACKAGE_DESCRIPTION_SUMMARY` + - :variable:`CPACK_DEBIAN_<COMPONENT>_DESCRIPTION` (component + based installers only) if set, or :variable:`CPACK_DEBIAN_PACKAGE_DESCRIPTION` if set, or + - :variable:`CPACK_COMPONENT_<compName>_DESCRIPTION` (component + based installers only) if set, or :variable:`CPACK_PACKAGE_DESCRIPTION` if set, or + - content of the file specified in :variable:`CPACK_PACKAGE_DESCRIPTION_FILE` if set + If after that description is not set, :variable:`CPACK_PACKAGE_DESCRIPTION_SUMMARY` going to be + used if set. Otherwise, :variable:`CPACK_PACKAGE_DESCRIPTION_SUMMARY` will be added as the first + line of description as defined in `Debian Policy Manual`_. + +.. _Debian Policy Manual: https://www.debian.org/doc/debian-policy/ch-controlfields.html#description .. variable:: CPACK_DEBIAN_PACKAGE_SECTION CPACK_DEBIAN_<COMPONENT>_PACKAGE_SECTION diff --git a/Help/manual/cmake-generator-expressions.7.rst b/Help/manual/cmake-generator-expressions.7.rst index c0449fb..6e7f9b5 100644 --- a/Help/manual/cmake-generator-expressions.7.rst +++ b/Help/manual/cmake-generator-expressions.7.rst @@ -130,6 +130,16 @@ Variable Queries ``1`` if the CMake's compiler id of the CUDA compiler matches any one of the entries in ``compiler_ids``, otherwise ``0``. See also the :variable:`CMAKE_<LANG>_COMPILER_ID` variable. +``$<OBJC_COMPILER_ID:compiler_ids>`` + where ``compiler_ids`` is a comma-separated list. + ``1`` if the CMake's compiler id of the Objective-C compiler matches any one + of the entries in ``compiler_ids``, otherwise ``0``. + See also the :variable:`CMAKE_<LANG>_COMPILER_ID` variable. +``$<OBJCXX_COMPILER_ID:compiler_ids>`` + where ``compiler_ids`` is a comma-separated list. + ``1`` if the CMake's compiler id of the Objective-C++ compiler matches any one + of the entries in ``compiler_ids``, otherwise ``0``. + See also the :variable:`CMAKE_<LANG>_COMPILER_ID` variable. ``$<Fortran_COMPILER_ID:compiler_ids>`` where ``compiler_ids`` is a comma-separated list. ``1`` if the CMake's compiler id of the Fortran compiler matches any one @@ -144,6 +154,12 @@ Variable Queries ``$<CUDA_COMPILER_VERSION:version>`` ``1`` if the version of the CXX compiler matches ``version``, otherwise ``0``. See also the :variable:`CMAKE_<LANG>_COMPILER_VERSION` variable. +``$<OBJC_COMPILER_VERSION:version>`` + ``1`` if the version of the OBJC compiler matches ``version``, otherwise ``0``. + See also the :variable:`CMAKE_<LANG>_COMPILER_VERSION` variable. +``$<OBJCXX_COMPILER_VERSION:version>`` + ``1`` if the version of the OBJCXX compiler matches ``version``, otherwise ``0``. + See also the :variable:`CMAKE_<LANG>_COMPILER_VERSION` variable. ``$<Fortran_COMPILER_VERSION:version>`` ``1`` if the version of the Fortran compiler matches ``version``, otherwise ``0``. See also the :variable:`CMAKE_<LANG>_COMPILER_VERSION` variable. @@ -401,6 +417,12 @@ Variable Queries ``$<CUDA_COMPILER_ID>`` The CMake's compiler id of the CUDA compiler used. See also the :variable:`CMAKE_<LANG>_COMPILER_ID` variable. +``$<OBJC_COMPILER_ID>`` + The CMake's compiler id of the OBJC compiler used. + See also the :variable:`CMAKE_<LANG>_COMPILER_ID` variable. +``$<OBJCXX_COMPILER_ID>`` + The CMake's compiler id of the OBJCXX compiler used. + See also the :variable:`CMAKE_<LANG>_COMPILER_ID` variable. ``$<Fortran_COMPILER_ID>`` The CMake's compiler id of the Fortran compiler used. See also the :variable:`CMAKE_<LANG>_COMPILER_ID` variable. @@ -413,6 +435,12 @@ Variable Queries ``$<CUDA_COMPILER_VERSION>`` The version of the CUDA compiler used. See also the :variable:`CMAKE_<LANG>_COMPILER_VERSION` variable. +``$<OBJC_COMPILER_VERSION>`` + The version of the OBJC compiler used. + See also the :variable:`CMAKE_<LANG>_COMPILER_VERSION` variable. +``$<OBJCXX_COMPILER_VERSION>`` + The version of the OBJCXX compiler used. + See also the :variable:`CMAKE_<LANG>_COMPILER_VERSION` variable. ``$<Fortran_COMPILER_VERSION>`` The version of the Fortran compiler used. See also the :variable:`CMAKE_<LANG>_COMPILER_VERSION` variable. diff --git a/Help/manual/cmake-modules.7.rst b/Help/manual/cmake-modules.7.rst index fc4bfdc..c60dc40 100644 --- a/Help/manual/cmake-modules.7.rst +++ b/Help/manual/cmake-modules.7.rst @@ -36,6 +36,12 @@ These modules are loaded using the :command:`include` command. /module/CheckIncludeFiles /module/CheckLanguage /module/CheckLibraryExists + /module/CheckOBJCCompilerFlag + /module/CheckOBJCSourceCompiles + /module/CheckOBJCSourceRuns + /module/CheckOBJCXXCompilerFlag + /module/CheckOBJCXXSourceCompiles + /module/CheckOBJCXXSourceRuns /module/CheckPIESupported /module/CheckPrototypeDefinition /module/CheckStructHasMember diff --git a/Help/module/CheckOBJCCompilerFlag.rst b/Help/module/CheckOBJCCompilerFlag.rst new file mode 100644 index 0000000..e4bd6fd --- /dev/null +++ b/Help/module/CheckOBJCCompilerFlag.rst @@ -0,0 +1 @@ +.. cmake-module:: ../../Modules/CheckOBJCCompilerFlag.cmake diff --git a/Help/module/CheckOBJCSourceCompiles.rst b/Help/module/CheckOBJCSourceCompiles.rst new file mode 100644 index 0000000..d4a1484 --- /dev/null +++ b/Help/module/CheckOBJCSourceCompiles.rst @@ -0,0 +1 @@ +.. cmake-module:: ../../Modules/CheckOBJCSourceCompiles.cmake diff --git a/Help/module/CheckOBJCSourceRuns.rst b/Help/module/CheckOBJCSourceRuns.rst new file mode 100644 index 0000000..c72f0db --- /dev/null +++ b/Help/module/CheckOBJCSourceRuns.rst @@ -0,0 +1 @@ +.. cmake-module:: ../../Modules/CheckOBJCSourceRuns.cmake diff --git a/Help/module/CheckOBJCXXCompilerFlag.rst b/Help/module/CheckOBJCXXCompilerFlag.rst new file mode 100644 index 0000000..1518a48 --- /dev/null +++ b/Help/module/CheckOBJCXXCompilerFlag.rst @@ -0,0 +1 @@ +.. cmake-module:: ../../Modules/CheckOBJCXXCompilerFlag.cmake diff --git a/Help/module/CheckOBJCXXSourceCompiles.rst b/Help/module/CheckOBJCXXSourceCompiles.rst new file mode 100644 index 0000000..a1c8ae9 --- /dev/null +++ b/Help/module/CheckOBJCXXSourceCompiles.rst @@ -0,0 +1 @@ +.. cmake-module:: ../../Modules/CheckOBJCXXSourceCompiles.cmake diff --git a/Help/module/CheckOBJCXXSourceRuns.rst b/Help/module/CheckOBJCXXSourceRuns.rst new file mode 100644 index 0000000..5198e1b --- /dev/null +++ b/Help/module/CheckOBJCXXSourceRuns.rst @@ -0,0 +1 @@ +.. cmake-module:: ../../Modules/CheckOBJCXXSourceRuns.cmake diff --git a/Help/prop_dir/EXCLUDE_FROM_ALL.rst b/Help/prop_dir/EXCLUDE_FROM_ALL.rst index 9d3192c..8e3cca0 100644 --- a/Help/prop_dir/EXCLUDE_FROM_ALL.rst +++ b/Help/prop_dir/EXCLUDE_FROM_ALL.rst @@ -1,15 +1,13 @@ EXCLUDE_FROM_ALL ---------------- -Exclude the directory from the all target of its parent. +Set this directory property to a true value on a subdirectory to exclude +its targets from the "all" target of its ancestors. If excluded, running +e.g. ``make`` in the parent directory will not build targets the +subdirectory by default. This does not affect the "all" target of the +subdirectory itself. Running e.g. ``make`` inside the subdirectory will +still build its targets. -A property on a directory that indicates if its targets are excluded -from the default build target. If it is not, then with a Makefile for -example typing make will cause the targets to be built. The same -concept applies to the default build of other generators. - -Targets inherit the :prop_tgt:`EXCLUDE_FROM_ALL` property from the directory -that they are created in. When a directory is excluded, all of its targets will -have :prop_tgt:`EXCLUDE_FROM_ALL` set to ``TRUE``. After creating such a target -you can change its :prop_tgt:`EXCLUDE_FROM_ALL` property to ``FALSE``. This -will cause the target to be included in the default build target. +If the :prop_tgt:`EXCLUDE_FROM_ALL` target property is set on a target +then its value determines whether the target is included in the "all" +target of this directory and its ancestors. diff --git a/Help/prop_tgt/EXCLUDE_FROM_ALL.rst b/Help/prop_tgt/EXCLUDE_FROM_ALL.rst index 0eee297..3aa296d 100644 --- a/Help/prop_tgt/EXCLUDE_FROM_ALL.rst +++ b/Help/prop_tgt/EXCLUDE_FROM_ALL.rst @@ -1,12 +1,15 @@ EXCLUDE_FROM_ALL ---------------- -Exclude the target from the all target. +Set this target property to a true (or false) value to exclude (or include) +the target from the "all" target of the containing directory and its +ancestors. If excluded, running e.g. ``make`` in the containing directory +or its ancestors will not build the target by default. -A property on a target that indicates if the target is excluded from -the default build target. If it is not, then with a Makefile for -example typing make will cause this target to be built. The same -concept applies to the default build of other generators. +If this target property is not set then the target will be included in +the "all" target of the containing directory. Furthermore, it will be +included in the "all" target of its ancestor directories unless the +:prop_dir:`EXCLUDE_FROM_ALL` directory property is set. With ``EXCLUDE_FROM_ALL`` set to false or not set at all, the target will be brought up to date as part of doing a ``make install`` or its @@ -16,6 +19,3 @@ target has undefined behavior. Note that such a target can still safely be listed in an :command:`install(TARGETS)` command as long as the install components the target belongs to are not part of the set of components that anything tries to install. - -This property is enabled by default for targets that are created in -directories that have :prop_dir:`EXCLUDE_FROM_ALL` set to ``TRUE``. diff --git a/Help/release/3.14.rst b/Help/release/3.14.rst index 229d8dc..8a9738c 100644 --- a/Help/release/3.14.rst +++ b/Help/release/3.14.rst @@ -428,3 +428,11 @@ Changes made since CMake 3.14.0 include the following. policy :policy:`CMP0088` ``NEW`` behavior accidentally interpreted a relative path to the ``.y`` input as relative to the build tree directory instead of the source tree directory. This has been fixed. + +3.14.7 +------ + +* In CMake 3.14.0 through 3.14.6, the :prop_dir:`EXCLUDE_FROM_ALL` + directory property was regressed from pre-3.14 behavior and caused + targets within the directory to be excluded even from its own "all". + This has been fixed. diff --git a/Help/release/3.15.rst b/Help/release/3.15.rst index 48f3aa4..957e6e9 100644 --- a/Help/release/3.15.rst +++ b/Help/release/3.15.rst @@ -376,3 +376,12 @@ Changes made since CMake 3.15.0 include the following. * ``CrayPrgEnv`` compiler wrapper support has been updated for the 19.06 release of the Cray Programming Environment for which the default linking mode on XC Cray systems is now dynamic instead of static. + +3.15.4 +------ + +* In CMake 3.15.0 through 3.15.3, the :prop_dir:`EXCLUDE_FROM_ALL` + directory property was regressed from pre-3.14 behavior and caused + targets within the directory to be excluded even from its own "all". + This has been fixed. + The bug also existed in 3.14.0 through 3.14.6 and is fixed in 3.14.7. diff --git a/Help/release/dev/FPHSA-reason-failure-message.rst b/Help/release/dev/FPHSA-reason-failure-message.rst new file mode 100644 index 0000000..419c3ba --- /dev/null +++ b/Help/release/dev/FPHSA-reason-failure-message.rst @@ -0,0 +1,5 @@ +FPHSA-reason-failure-message +---------------------------- + +* Modules :module:`FindPackageHandleStandardArgs` gains the capability to + specify a message giving the reason for the failure. diff --git a/Help/release/dev/objective-c-cxx.rst b/Help/release/dev/objective-c-cxx.rst new file mode 100644 index 0000000..218af4e --- /dev/null +++ b/Help/release/dev/objective-c-cxx.rst @@ -0,0 +1,9 @@ +Objective C/C++ +--------------- + +* CMake learned to support the Objective C (``OBJC``) and Objective C++ + (``OBJCXX``) languages. They may be enabled via the :command:`project` + and :command:`enable_language` commands. When ``OBJC`` or ``OBJCXX`` + is enabled, source files with the ``.m`` or ``.mm``, respectively, + will be compiled as Objective C or C++. Otherwise they will be treated + as plain C++ sources as they were before. diff --git a/Modules/CMakeCXXCompiler.cmake.in b/Modules/CMakeCXXCompiler.cmake.in index e7f0e70..ef65021 100644 --- a/Modules/CMakeCXXCompiler.cmake.in +++ b/Modules/CMakeCXXCompiler.cmake.in @@ -42,8 +42,17 @@ 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_IGNORE_EXTENSIONS inl;h;hpp;HPP;H;o;O;obj;OBJ;def;DEF;rc;RC) -set(CMAKE_CXX_SOURCE_FILE_EXTENSIONS C;M;c++;cc;cpp;cxx;mm;CPP) + +foreach (lang OBJC OBJCXX) + if (CMAKE_${lang}_COMPILER_ID_RUN) + foreach(extension IN LISTS CMAKE_${lang}_SOURCE_FILE_EXTENSIONS) + list(REMOVE_ITEM CMAKE_CXX_SOURCE_FILE_EXTENSIONS ${extension}) + endforeach() + endif() +endforeach() + set(CMAKE_CXX_LINKER_PREFERENCE 30) set(CMAKE_CXX_LINKER_PREFERENCE_PROPAGATES 1) diff --git a/Modules/CMakeDetermineOBJCCompiler.cmake b/Modules/CMakeDetermineOBJCCompiler.cmake new file mode 100644 index 0000000..ad13eab --- /dev/null +++ b/Modules/CMakeDetermineOBJCCompiler.cmake @@ -0,0 +1,189 @@ +# Distributed under the OSI-approved BSD 3-Clause License. See accompanying +# file Copyright.txt or https://cmake.org/licensing for details. + + +# determine the compiler to use for Objective-C programs +# NOTE, a generator may set CMAKE_OBJC_COMPILER before +# loading this file to force a compiler. +# use environment variable OBJC first if defined by user, next use +# the cmake variable CMAKE_GENERATOR_OBJC which can be defined by a generator +# as a default compiler +# +# Sets the following variables: +# CMAKE_OBJC_COMPILER +# CMAKE_AR +# CMAKE_RANLIB +# CMAKE_COMPILER_IS_GNUOBJC +# CMAKE_COMPILER_IS_CLANGOBJC +# +# If not already set before, it also sets +# _CMAKE_TOOLCHAIN_PREFIX + +include(${CMAKE_ROOT}/Modules/CMakeDetermineCompiler.cmake) + +# Load system-specific compiler preferences for this language. +include(Platform/${CMAKE_SYSTEM_NAME}-Determine-OBJC OPTIONAL) +include(Platform/${CMAKE_SYSTEM_NAME}-OBJC OPTIONAL) +if(NOT CMAKE_OBJC_COMPILER_NAMES) + set(CMAKE_OBJC_COMPILER_NAMES clang) +endif() + +if("${CMAKE_GENERATOR}" MATCHES "Xcode") + set(CMAKE_OBJC_COMPILER_XCODE_TYPE sourcecode.c.objc) +else() + if(NOT CMAKE_OBJC_COMPILER) + set(CMAKE_OBJC_COMPILER_INIT NOTFOUND) + + # prefer the environment variable OBJC + if($ENV{OBJC} MATCHES ".+") + get_filename_component(CMAKE_OBJC_COMPILER_INIT $ENV{OBJC} PROGRAM PROGRAM_ARGS CMAKE_OBJC_FLAGS_ENV_INIT) + if(CMAKE_OBJC_FLAGS_ENV_INIT) + set(CMAKE_OBJC_COMPILER_ARG1 "${CMAKE_OBJC_FLAGS_ENV_INIT}" CACHE STRING "First argument to Objective-C compiler") + endif() + if(NOT EXISTS ${CMAKE_OBJC_COMPILER_INIT}) + message(FATAL_ERROR "Could not find compiler set in environment variable OBJC:\n$ENV{OBJC}.") + endif() + endif() + + # next try prefer the compiler specified by the generator + if(CMAKE_GENERATOR_OBJC) + if(NOT CMAKE_OBJC_COMPILER_INIT) + set(CMAKE_OBJC_COMPILER_INIT ${CMAKE_GENERATOR_OBJC}) + endif() + endif() + + # finally list compilers to try + if(NOT CMAKE_OBJC_COMPILER_INIT) + set(CMAKE_OBJC_COMPILER_LIST ${_CMAKE_TOOLCHAIN_PREFIX}cc ${_CMAKE_TOOLCHAIN_PREFIX}gcc clang) + endif() + + _cmake_find_compiler(OBJC) + + else() + # we only get here if CMAKE_OBJC_COMPILER was specified using -D or a pre-made CMakeCache.txt + # (e.g. via ctest) or set in CMAKE_TOOLCHAIN_FILE + # if CMAKE_OBJC_COMPILER is a list of length 2, use the first item as + # CMAKE_OBJC_COMPILER and the 2nd one as CMAKE_OBJC_COMPILER_ARG1 + + list(LENGTH CMAKE_OBJC_COMPILER _CMAKE_OBJC_COMPILER_LIST_LENGTH) + if("${_CMAKE_OBJC_COMPILER_LIST_LENGTH}" EQUAL 2) + list(GET CMAKE_OBJC_COMPILER 1 CMAKE_OBJC_COMPILER_ARG1) + list(GET CMAKE_OBJC_COMPILER 0 CMAKE_OBJC_COMPILER) + endif() + + # if a compiler was specified by the user but without path, + # now try to find it with the full path + # if it is found, force it into the cache, + # if not, don't overwrite the setting (which was given by the user) with "NOTFOUND" + # if the C compiler already had a path, reuse it for searching the CXX compiler + get_filename_component(_CMAKE_USER_OBJC_COMPILER_PATH "${CMAKE_OBJC_COMPILER}" PATH) + if(NOT _CMAKE_USER_OBJC_COMPILER_PATH) + find_program(CMAKE_OBJC_COMPILER_WITH_PATH NAMES ${CMAKE_OBJC_COMPILER}) + if(CMAKE_OBJC_COMPILER_WITH_PATH) + set(CMAKE_OBJC_COMPILER ${CMAKE_OBJC_COMPILER_WITH_PATH} CACHE STRING "Objective-C compiler" FORCE) + endif() + unset(CMAKE_OBJC_COMPILER_WITH_PATH CACHE) + endif() + endif() + mark_as_advanced(CMAKE_OBJC_COMPILER) + + # Each entry in this list is a set of extra flags to try + # adding to the compile line to see if it helps produce + # a valid identification file. + set(CMAKE_OBJC_COMPILER_ID_TEST_FLAGS_FIRST) + set(CMAKE_OBJC_COMPILER_ID_TEST_FLAGS + # Try compiling to an object file only. + "-c" + + ) +endif() + +# Build a small source file to identify the compiler. +if(NOT CMAKE_OBJC_COMPILER_ID_RUN) + set(CMAKE_OBJC_COMPILER_ID_RUN 1) + + # Try to identify the compiler. + set(CMAKE_OBJC_COMPILER_ID) + file(READ ${CMAKE_ROOT}/Modules/CMakePlatformId.h.in + CMAKE_OBJC_COMPILER_ID_PLATFORM_CONTENT) + + # Match the link line from xcodebuild output of the form + # Ld ... + # ... + # /path/to/cc ...CompilerIdOBJC/... + # to extract the compiler front-end for the language. + set(CMAKE_OBJC_COMPILER_ID_TOOL_MATCH_REGEX "\nLd[^\n]*(\n[ \t]+[^\n]*)*\n[ \t]+([^ \t\r\n]+)[^\r\n]*-o[^\r\n]*CompilerIdOBJC/(\\./)?(CompilerIdOBJC.(framework|xctest)/)?CompilerIdOBJC[ \t\n\\\"]") + set(CMAKE_OBJC_COMPILER_ID_TOOL_MATCH_INDEX 2) + + include(${CMAKE_ROOT}/Modules/CMakeDetermineCompilerId.cmake) + CMAKE_DETERMINE_COMPILER_ID(OBJC OBJCCFLAGS CMakeOBJCCompilerId.m) + + # Set old compiler and platform id variables. + if(CMAKE_OBJC_COMPILER_ID STREQUAL "GNU") + set(CMAKE_COMPILER_IS_GNUOBJC 1) + endif() + if(CMAKE_OBJC_COMPILER_ID STREQUAL "Clang") + set(CMAKE_COMPILER_IS_CLANGOBJC 1) + endif() +endif() + +if (NOT _CMAKE_TOOLCHAIN_LOCATION) + get_filename_component(_CMAKE_TOOLCHAIN_LOCATION "${CMAKE_OBJC_COMPILER}" PATH) +endif () + +# If we have a gcc cross compiler, they have usually some prefix, like +# e.g. powerpc-linux-gcc, arm-elf-gcc or i586-mingw32msvc-gcc, optionally +# with a 3-component version number at the end (e.g. arm-eabi-gcc-4.5.2). +# The other tools of the toolchain usually have the same prefix +# NAME_WE cannot be used since then this test will fail for names like +# "arm-unknown-nto-qnx6.3.0-gcc.exe", where BASENAME would be +# "arm-unknown-nto-qnx6" instead of the correct "arm-unknown-nto-qnx6.3.0-" +if (CMAKE_CROSSCOMPILING AND NOT _CMAKE_TOOLCHAIN_PREFIX) + + if(CMAKE_OBJC_COMPILER_ID MATCHES "GNU|Clang|QCC") + get_filename_component(COMPILER_BASENAME "${CMAKE_OBJC_COMPILER}" NAME) + if (COMPILER_BASENAME MATCHES "^(.+-)(clang|g?cc)(-[0-9]+(\\.[0-9]+)*)?(-[^.]+)?(\\.exe)?$") + set(_CMAKE_TOOLCHAIN_PREFIX ${CMAKE_MATCH_1}) + set(_CMAKE_COMPILER_SUFFIX ${CMAKE_MATCH_5}) + elseif(CMAKE_OBJC_COMPILER_ID MATCHES "Clang") + if(CMAKE_OBJC_COMPILER_TARGET) + set(_CMAKE_TOOLCHAIN_PREFIX ${CMAKE_OBJC_COMPILER_TARGET}-) + endif() + elseif(COMPILER_BASENAME MATCHES "qcc(\\.exe)?$") + if(CMAKE_OBJC_COMPILER_TARGET MATCHES "gcc_nto([a-z0-9]+_[0-9]+|[^_le]+)(le)?") + set(_CMAKE_TOOLCHAIN_PREFIX nto${CMAKE_MATCH_1}-) + endif() + endif () + + # if "llvm-" is part of the prefix, remove it, since llvm doesn't have its own binutils + # but uses the regular ar, objcopy, etc. (instead of llvm-objcopy etc.) + if ("${_CMAKE_TOOLCHAIN_PREFIX}" MATCHES "(.+-)?llvm-$") + set(_CMAKE_TOOLCHAIN_PREFIX ${CMAKE_MATCH_1}) + endif () + endif() + +endif () + +set(_CMAKE_PROCESSING_LANGUAGE "OBJC") +include(CMakeFindBinUtils) +include(Compiler/${CMAKE_OBJC_COMPILER_ID}-FindBinUtils OPTIONAL) +unset(_CMAKE_PROCESSING_LANGUAGE) + +if(CMAKE_OBJC_COMPILER_ARCHITECTURE_ID) + set(_SET_CMAKE_OBJC_COMPILER_ARCHITECTURE_ID + "set(CMAKE_OBJC_COMPILER_ARCHITECTURE_ID ${CMAKE_OBJC_COMPILER_ARCHITECTURE_ID})") +else() + set(_SET_CMAKE_OBJC_COMPILER_ARCHITECTURE_ID "") +endif() + +if(CMAKE_OBJC_XCODE_ARCHS) + set(SET_CMAKE_XCODE_ARCHS + "set(CMAKE_XCODE_ARCHS \"${CMAKE_OBJC_XCODE_ARCHS}\")") +endif() + +# configure variables set in this file for fast reload later on +configure_file(${CMAKE_ROOT}/Modules/CMakeOBJCCompiler.cmake.in + ${CMAKE_PLATFORM_INFO_DIR}/CMakeOBJCCompiler.cmake + @ONLY + ) +set(CMAKE_OBJC_COMPILER_ENV_VAR "OBJC") diff --git a/Modules/CMakeDetermineOBJCXXCompiler.cmake b/Modules/CMakeDetermineOBJCXXCompiler.cmake new file mode 100644 index 0000000..60fcbb3 --- /dev/null +++ b/Modules/CMakeDetermineOBJCXXCompiler.cmake @@ -0,0 +1,197 @@ +# Distributed under the OSI-approved BSD 3-Clause License. See accompanying +# file Copyright.txt or https://cmake.org/licensing for details. + + +# determine the compiler to use for Objective-C++ programs +# NOTE, a generator may set CMAKE_OBJCXX_COMPILER before +# loading this file to force a compiler. +# use environment variable OBJCXX first if defined by user, next use +# the cmake variable CMAKE_GENERATOR_OBJCXX which can be defined by a generator +# as a default compiler +# If the internal cmake variable _CMAKE_TOOLCHAIN_PREFIX is set, this is used +# as prefix for the tools (e.g. arm-elf-g++, arm-elf-ar etc.) +# +# Sets the following variables: +# CMAKE_OBJCXX_COMPILER +# CMAKE_COMPILER_IS_GNUOBJCXX +# CMAKE_COMPILER_IS_CLANGOBJCXX +# CMAKE_AR +# CMAKE_RANLIB +# +# If not already set before, it also sets +# _CMAKE_TOOLCHAIN_PREFIX + +include(${CMAKE_ROOT}/Modules/CMakeDetermineCompiler.cmake) + +# Load system-specific compiler preferences for this language. +include(Platform/${CMAKE_SYSTEM_NAME}-Determine-OBJCXX OPTIONAL) +include(Platform/${CMAKE_SYSTEM_NAME}-OBJCXX OPTIONAL) +if(NOT CMAKE_OBJCXX_COMPILER_NAMES) + set(CMAKE_OBJCXX_COMPILER_NAMES clang++) +endif() + +if("${CMAKE_GENERATOR}" MATCHES "Xcode") + set(CMAKE_OBJCXX_COMPILER_XCODE_TYPE sourcecode.cpp.objcpp) +else() + if(NOT CMAKE_OBJCXX_COMPILER) + set(CMAKE_OBJCXX_COMPILER_INIT NOTFOUND) + + # prefer the environment variable OBJCXX + if($ENV{OBJCXX} MATCHES ".+") + get_filename_component(CMAKE_OBJCXX_COMPILER_INIT $ENV{OBJCXX} PROGRAM PROGRAM_ARGS CMAKE_OBJCXX_FLAGS_ENV_INIT) + if(CMAKE_OBJCXX_FLAGS_ENV_INIT) + set(CMAKE_OBJCXX_COMPILER_ARG1 "${CMAKE_OBJCXX_FLAGS_ENV_INIT}" CACHE STRING "First argument to Objective-C++ compiler") + endif() + if(NOT EXISTS ${CMAKE_OBJCXX_COMPILER_INIT}) + message(FATAL_ERROR "Could not find compiler set in environment variable OBJCXX:\n$ENV{OBJCXX}.\n${CMAKE_OBJCXX_COMPILER_INIT}") + endif() + endif() + + # next prefer the generator specified compiler + if(CMAKE_GENERATOR_OBJCXX) + if(NOT CMAKE_OBJCXX_COMPILER_INIT) + set(CMAKE_OBJCXX_COMPILER_INIT ${CMAKE_GENERATOR_OBJCXX}) + endif() + endif() + + # finally list compilers to try + if(NOT CMAKE_OBJCXX_COMPILER_INIT) + set(CMAKE_OBJCXX_COMPILER_LIST ${_CMAKE_TOOLCHAIN_PREFIX}c++ ${_CMAKE_TOOLCHAIN_PREFIX}g++ clang++) + endif() + + _cmake_find_compiler(OBJCXX) + + else() + # we only get here if CMAKE_OBJCXX_COMPILER was specified using -D or a pre-made CMakeCache.txt + # (e.g. via ctest) or set in CMAKE_TOOLCHAIN_FILE + # if CMAKE_OBJCXX_COMPILER is a list of length 2, use the first item as + # CMAKE_OBJCXX_COMPILER and the 2nd one as CMAKE_OBJCXX_COMPILER_ARG1 + + list(LENGTH CMAKE_OBJCXX_COMPILER _CMAKE_OBJCXX_COMPILER_LIST_LENGTH) + if("${_CMAKE_OBJCXX_COMPILER_LIST_LENGTH}" EQUAL 2) + list(GET CMAKE_OBJCXX_COMPILER 1 CMAKE_OBJCXX_COMPILER_ARG1) + list(GET CMAKE_OBJCXX_COMPILER 0 CMAKE_OBJCXX_COMPILER) + endif() + + # if a compiler was specified by the user but without path, + # now try to find it with the full path + # if it is found, force it into the cache, + # if not, don't overwrite the setting (which was given by the user) with "NOTFOUND" + # if the C compiler already had a path, reuse it for searching the CXX compiler + get_filename_component(_CMAKE_USER_OBJCXX_COMPILER_PATH "${CMAKE_OBJCXX_COMPILER}" PATH) + if(NOT _CMAKE_USER_OBJCXX_COMPILER_PATH) + find_program(CMAKE_OBJCXX_COMPILER_WITH_PATH NAMES ${CMAKE_OBJCXX_COMPILER}) + if(CMAKE_OBJCXX_COMPILER_WITH_PATH) + set(CMAKE_OBJCXX_COMPILER ${CMAKE_OBJCXX_COMPILER_WITH_PATH} CACHE STRING "Objective-C++ compiler" FORCE) + endif() + unset(CMAKE_OBJCXX_COMPILER_WITH_PATH CACHE) + endif() + + endif() + mark_as_advanced(CMAKE_OBJCXX_COMPILER) + + # Each entry in this list is a set of extra flags to try + # adding to the compile line to see if it helps produce + # a valid identification file. + set(CMAKE_OBJCXX_COMPILER_ID_TEST_FLAGS_FIRST) + set(CMAKE_OBJCXX_COMPILER_ID_TEST_FLAGS + # Try compiling to an object file only. + "-c" + + # ARMClang need target options + "--target=arm-arm-none-eabi -mcpu=cortex-m3" + ) +endif() + +# Build a small source file to identify the compiler. +if(NOT CMAKE_OBJCXX_COMPILER_ID_RUN) + set(CMAKE_OBJCXX_COMPILER_ID_RUN 1) + + # Try to identify the compiler. + set(CMAKE_OBJCXX_COMPILER_ID) + file(READ ${CMAKE_ROOT}/Modules/CMakePlatformId.h.in + CMAKE_OBJCXX_COMPILER_ID_PLATFORM_CONTENT) + + # Match the link line from xcodebuild output of the form + # Ld ... + # ... + # /path/to/cc ...CompilerIdOBJCXX/... + # to extract the compiler front-end for the language. + set(CMAKE_OBJCXX_COMPILER_ID_TOOL_MATCH_REGEX "\nLd[^\n]*(\n[ \t]+[^\n]*)*\n[ \t]+([^ \t\r\n]+)[^\r\n]*-o[^\r\n]*CompilerIdOBJCXX/(\\./)?(CompilerIdOBJCXX.(framework|xctest)/)?CompilerIdOBJCXX[ \t\n\\\"]") + set(CMAKE_OBJCXX_COMPILER_ID_TOOL_MATCH_INDEX 2) + + include(${CMAKE_ROOT}/Modules/CMakeDetermineCompilerId.cmake) + CMAKE_DETERMINE_COMPILER_ID(OBJCXX OBJCXXFLAGS CMakeOBJCXXCompilerId.mm) + + # Set old compiler and platform id variables. + if(CMAKE_OBJCXX_COMPILER_ID MATCHES "GNU") + set(CMAKE_COMPILER_IS_GNUOBJCXX 1) + endif() + if(CMAKE_OBJCXX_COMPILER_ID MATCHES "Clang") + set(CMAKE_COMPILER_IS_CLANGOBJCXX 1) + endif() +endif() + +if (NOT _CMAKE_TOOLCHAIN_LOCATION) + get_filename_component(_CMAKE_TOOLCHAIN_LOCATION "${CMAKE_OBJCXX_COMPILER}" PATH) +endif () + +# if we have a g++ cross compiler, they have usually some prefix, like +# e.g. powerpc-linux-g++, arm-elf-g++ or i586-mingw32msvc-g++ , optionally +# with a 3-component version number at the end (e.g. arm-eabi-gcc-4.5.2). +# The other tools of the toolchain usually have the same prefix +# NAME_WE cannot be used since then this test will fail for names like +# "arm-unknown-nto-qnx6.3.0-gcc.exe", where BASENAME would be +# "arm-unknown-nto-qnx6" instead of the correct "arm-unknown-nto-qnx6.3.0-" + + +if (CMAKE_CROSSCOMPILING AND NOT _CMAKE_TOOLCHAIN_PREFIX) + + if("${CMAKE_OBJCXX_COMPILER_ID}" MATCHES "GNU|Clang|QCC") + get_filename_component(COMPILER_BASENAME "${CMAKE_OBJCXX_COMPILER}" NAME) + if (COMPILER_BASENAME MATCHES "^(.+-)(clan)?[gc]\\+\\+(-[0-9]+(\\.[0-9]+)*)?(-[^.]+)?(\\.exe)?$") + set(_CMAKE_TOOLCHAIN_PREFIX ${CMAKE_MATCH_1}) + set(_CMAKE_COMPILER_SUFFIX ${CMAKE_MATCH_5}) + elseif("${CMAKE_OBJCXX_COMPILER_ID}" MATCHES "Clang") + if(CMAKE_OBJCXX_COMPILER_TARGET) + set(_CMAKE_TOOLCHAIN_PREFIX ${CMAKE_OBJCXX_COMPILER_TARGET}-) + endif() + elseif(COMPILER_BASENAME MATCHES "QCC(\\.exe)?$") + if(CMAKE_OBJCXX_COMPILER_TARGET MATCHES "gcc_nto([a-z0-9]+_[0-9]+|[^_le]+)(le)") + set(_CMAKE_TOOLCHAIN_PREFIX nto${CMAKE_MATCH_1}-) + endif() + endif () + + # if "llvm-" is part of the prefix, remove it, since llvm doesn't have its own binutils + # but uses the regular ar, objcopy, etc. (instead of llvm-objcopy etc.) + if ("${_CMAKE_TOOLCHAIN_PREFIX}" MATCHES "(.+-)?llvm-$") + set(_CMAKE_TOOLCHAIN_PREFIX ${CMAKE_MATCH_1}) + endif () + endif() + +endif () + +set(_CMAKE_PROCESSING_LANGUAGE "OBJCXX") +include(CMakeFindBinUtils) +include(Compiler/${CMAKE_OBJCXX_COMPILER_ID}-FindBinUtils OPTIONAL) +unset(_CMAKE_PROCESSING_LANGUAGE) + +if(CMAKE_OBJCXX_COMPILER_ARCHITECTURE_ID) + set(_SET_CMAKE_OBJCXX_COMPILER_ARCHITECTURE_ID + "set(CMAKE_OBJCXX_COMPILER_ARCHITECTURE_ID ${CMAKE_OBJCXX_COMPILER_ARCHITECTURE_ID})") +else() + set(_SET_CMAKE_OBJCXX_COMPILER_ARCHITECTURE_ID "") +endif() + +if(CMAKE_OBJCXX_XCODE_ARCHS) + set(SET_CMAKE_XCODE_ARCHS + "set(CMAKE_XCODE_ARCHS \"${CMAKE_OBJCXX_XCODE_ARCHS}\")") +endif() + +# configure all variables set in this file +configure_file(${CMAKE_ROOT}/Modules/CMakeOBJCXXCompiler.cmake.in + ${CMAKE_PLATFORM_INFO_DIR}/CMakeOBJCXXCompiler.cmake + @ONLY + ) + +set(CMAKE_OBJCXX_COMPILER_ENV_VAR "OBJCXX") diff --git a/Modules/CMakeFindBinUtils.cmake b/Modules/CMakeFindBinUtils.cmake index 781b48c..3887b2d 100644 --- a/Modules/CMakeFindBinUtils.cmake +++ b/Modules/CMakeFindBinUtils.cmake @@ -57,6 +57,44 @@ endfunction() __resolve_tool_path(CMAKE_LINKER "${_CMAKE_TOOLCHAIN_LOCATION}" "Default Linker") __resolve_tool_path(CMAKE_MT "${_CMAKE_TOOLCHAIN_LOCATION}" "Default Manifest Tool") +function(__get_compiler_component CMAKE_TOOL NAME) + get_property(_CMAKE_TOOL_CACHED CACHE ${CMAKE_TOOL} PROPERTY TYPE) + # If CMAKE_TOOL is present in the CMake Cache, return + if(_CMAKE_TOOL_CACHED) + return() + endif() + + cmake_parse_arguments(_COMPILER_COMP_ARGS "" "DOC" "HINTS;NAMES" ${ARGN}) + + set(_LOCATION_FROM_COMPILER ) + set(_NAME_FROM_COMPILER ) + + if (NOT DEFINED ${CMAKE_TOOL}) + if("x${CMAKE_${_CMAKE_PROCESSING_LANGUAGE}_COMPILER_ID}" STREQUAL "xGNU" OR + "x${CMAKE_${_CMAKE_PROCESSING_LANGUAGE}_COMPILER_ID}" STREQUAL "xClang") + execute_process( + COMMAND ${CMAKE_${_CMAKE_PROCESSING_LANGUAGE}_COMPILER} -print-prog-name=${NAME} + RESULT_VARIABLE _CMAKE_TOOL_PROG_NAME_RESULT + OUTPUT_VARIABLE _CMAKE_TOOL_PROG_NAME_OUTPUT + OUTPUT_STRIP_TRAILING_WHITESPACE + ) + if (_CMAKE_TOOL_PROG_NAME_RESULT STREQUAL "0" AND IS_ABSOLUTE "${_CMAKE_TOOL_PROG_NAME_OUTPUT}") + get_filename_component(_LOCATION_FROM_COMPILER "${_CMAKE_TOOL_PROG_NAME_OUTPUT}" DIRECTORY) + get_filename_component(_NAME_FROM_COMPILER "${_CMAKE_TOOL_PROG_NAME_OUTPUT}" NAME) + endif() + endif() + endif() + + if (NOT _COMPILER_COMP_ARGS_DOC) + set(_COMPILER_COMP_ARGS_DOC "Path to ${NAME} program") + endif() + find_program(${CMAKE_TOOL} + NAMES ${_NAME_FROM_COMPILER} ${_COMPILER_COMP_ARGS_NAMES} + HINTS ${_LOCATION_FROM_COMPILER} ${_COMPILER_COMP_ARGS_HINTS} + DOC "${_COMPILER_COMP_ARGS_DOC}" + ) +endfunction() + set(_CMAKE_TOOL_VARS "") # if it's the MS C/CXX compiler, search for link @@ -101,22 +139,32 @@ else() set(_CMAKE_ADDITIONAL_ADDR2LINE_NAMES "llvm-addr2line") endif() - find_program(CMAKE_AR NAMES ${_CMAKE_TOOLCHAIN_PREFIX}ar${_CMAKE_TOOLCHAIN_SUFFIX} ${_CMAKE_ADDITIONAL_AR_NAMES} HINTS ${_CMAKE_TOOLCHAIN_LOCATION}) + __get_compiler_component(CMAKE_AR ar NAMES ${_CMAKE_TOOLCHAIN_PREFIX}ar${_CMAKE_TOOLCHAIN_SUFFIX} ${_CMAKE_ADDITIONAL_AR_NAMES} + HINTS ${_CMAKE_TOOLCHAIN_LOCATION}) - find_program(CMAKE_RANLIB NAMES ${_CMAKE_TOOLCHAIN_PREFIX}ranlib ${_CMAKE_ADDITIONAL_RANLIB_NAMES} HINTS ${_CMAKE_TOOLCHAIN_LOCATION}) + __get_compiler_component(CMAKE_RANLIB ranlib NAMES ${_CMAKE_TOOLCHAIN_PREFIX}ranlib ${_CMAKE_ADDITIONAL_RANLIB_NAMES} + HINTS ${_CMAKE_TOOLCHAIN_LOCATION}) if(NOT CMAKE_RANLIB) set(CMAKE_RANLIB : CACHE INTERNAL "noop for ranlib") endif() - find_program(CMAKE_STRIP NAMES ${_CMAKE_TOOLCHAIN_PREFIX}strip${_CMAKE_TOOLCHAIN_SUFFIX} ${_CMAKE_ADDITIONAL_STRIP_NAMES} HINTS ${_CMAKE_TOOLCHAIN_LOCATION}) - find_program(CMAKE_LINKER NAMES ${_CMAKE_TOOLCHAIN_PREFIX}ld ${_CMAKE_ADDITIONAL_LINKER_NAMES} HINTS ${_CMAKE_TOOLCHAIN_LOCATION}) - find_program(CMAKE_NM NAMES ${_CMAKE_TOOLCHAIN_PREFIX}nm ${_CMAKE_ADDITIONAL_NM_NAMES} HINTS ${_CMAKE_TOOLCHAIN_LOCATION}) - find_program(CMAKE_OBJDUMP NAMES ${_CMAKE_TOOLCHAIN_PREFIX}objdump ${_CMAKE_ADDITIONAL_OBJDUMP_NAMES} HINTS ${_CMAKE_TOOLCHAIN_LOCATION}) - find_program(CMAKE_OBJCOPY NAMES ${_CMAKE_TOOLCHAIN_PREFIX}objcopy ${_CMAKE_ADDITIONAL_OBJCOPY_NAMES} HINTS ${_CMAKE_TOOLCHAIN_LOCATION}) - find_program(CMAKE_READELF NAMES ${_CMAKE_TOOLCHAIN_PREFIX}readelf ${_CMAKE_ADDITIONAL_READELF_NAMES} HINTS ${_CMAKE_TOOLCHAIN_LOCATION}) - find_program(CMAKE_DLLTOOL NAMES ${_CMAKE_TOOLCHAIN_PREFIX}dlltool ${_CMAKE_ADDITIONAL_DLLTOOL_NAMES} HINTS ${_CMAKE_TOOLCHAIN_LOCATION}) - find_program(CMAKE_ADDR2LINE NAMES ${_CMAKE_TOOLCHAIN_PREFIX}addr2line ${_CMAKE_ADDITIONAL_ADDR2LINE_NAMES} HINTS ${_CMAKE_TOOLCHAIN_LOCATION}) + __get_compiler_component(CMAKE_STRIP strip NAMES ${_CMAKE_TOOLCHAIN_PREFIX}strip${_CMAKE_TOOLCHAIN_SUFFIX} ${_CMAKE_ADDITIONAL_STRIP_NAMES} + HINTS ${_CMAKE_TOOLCHAIN_LOCATION}) + __get_compiler_component(CMAKE_LINKER ld NAMES ${_CMAKE_TOOLCHAIN_PREFIX}ld ${_CMAKE_ADDITIONAL_LINKER_NAMES} + HINTS ${_CMAKE_TOOLCHAIN_LOCATION}) + __get_compiler_component(CMAKE_NM nm NAMES ${_CMAKE_TOOLCHAIN_PREFIX}nm ${_CMAKE_ADDITIONAL_NM_NAMES} + HINTS ${_CMAKE_TOOLCHAIN_LOCATION}) + __get_compiler_component(CMAKE_OBJDUMP objdump NAMES ${_CMAKE_TOOLCHAIN_PREFIX}objdump ${_CMAKE_ADDITIONAL_OBJDUMP_NAMES} + HINTS ${_CMAKE_TOOLCHAIN_LOCATION}) + __get_compiler_component(CMAKE_OBJCOPY objcopy NAMES ${_CMAKE_TOOLCHAIN_PREFIX}objcopy ${_CMAKE_ADDITIONAL_OBJCOPY_NAMES} + HINTS ${_CMAKE_TOOLCHAIN_LOCATION}) + __get_compiler_component(CMAKE_READELF readelf NAMES ${_CMAKE_TOOLCHAIN_PREFIX}readelf ${_CMAKE_ADDITIONAL_READELF_NAMES} + HINTS ${_CMAKE_TOOLCHAIN_LOCATION}) + __get_compiler_component(CMAKE_DLLTOOL dlltool NAMES ${_CMAKE_TOOLCHAIN_PREFIX}dlltool ${_CMAKE_ADDITIONAL_DLLTOOL_NAMES} + HINTS ${_CMAKE_TOOLCHAIN_LOCATION}) + __get_compiler_component(CMAKE_ADDR2LINE addr2line NAMES ${_CMAKE_TOOLCHAIN_PREFIX}addr2line ${_CMAKE_ADDITIONAL_ADDR2LINE_NAMES} + HINTS ${_CMAKE_TOOLCHAIN_LOCATION}) list(APPEND _CMAKE_TOOL_VARS AR RANLIB STRIP LINKER NM OBJDUMP OBJCOPY READELF DLLTOOL ADDR2LINE) @@ -134,7 +182,7 @@ else() endif() if(CMAKE_PLATFORM_HAS_INSTALLNAME) - find_program(CMAKE_INSTALL_NAME_TOOL NAMES ${_CMAKE_TOOLCHAIN_PREFIX}install_name_tool HINTS ${_CMAKE_TOOLCHAIN_LOCATION}) + __get_compiler_component(CMAKE_INSTALL_NAME_TOOL install_name_tool NAMES ${_CMAKE_TOOLCHAIN_PREFIX}install_name_tool HINTS ${_CMAKE_TOOLCHAIN_LOCATION}) if(NOT CMAKE_INSTALL_NAME_TOOL) message(FATAL_ERROR "Could not find install_name_tool, please check your installation.") diff --git a/Modules/CMakeOBJCCompiler.cmake.in b/Modules/CMakeOBJCCompiler.cmake.in new file mode 100644 index 0000000..1555517 --- /dev/null +++ b/Modules/CMakeOBJCCompiler.cmake.in @@ -0,0 +1,69 @@ +set(CMAKE_OBJC_COMPILER "@CMAKE_OBJC_COMPILER@") +set(CMAKE_OBJC_COMPILER_ARG1 "@CMAKE_OBJC_COMPILER_ARG1@") +set(CMAKE_OBJC_COMPILER_ID "@CMAKE_OBJC_COMPILER_ID@") +set(CMAKE_OBJC_COMPILER_VERSION "@CMAKE_OBJC_COMPILER_VERSION@") +set(CMAKE_OBJC_COMPILER_VERSION_INTERNAL "@CMAKE_OBJC_COMPILER_VERSION_INTERNAL@") +set(CMAKE_OBJC_COMPILER_WRAPPER "@CMAKE_OBJC_COMPILER_WRAPPER@") +set(CMAKE_OBJC_STANDARD_COMPUTED_DEFAULT "@CMAKE_OBJC_STANDARD_COMPUTED_DEFAULT@") +set(CMAKE_OBJC_COMPILE_FEATURES "@CMAKE_OBJC_COMPILE_FEATURES@") +set(CMAKE_OBJC90_COMPILE_FEATURES "@CMAKE_OBJC90_COMPILE_FEATURES@") +set(CMAKE_OBJC99_COMPILE_FEATURES "@CMAKE_OBJC99_COMPILE_FEATURES@") +set(CMAKE_OBJC11_COMPILE_FEATURES "@CMAKE_OBJC11_COMPILE_FEATURES@") + +set(CMAKE_OBJC_PLATFORM_ID "@CMAKE_OBJC_PLATFORM_ID@") +set(CMAKE_OBJC_SIMULATE_ID "@CMAKE_OBJC_SIMULATE_ID@") +set(CMAKE_OBJC_COMPILER_FRONTEND_VARIANT "@CMAKE_OBJC_COMPILER_FRONTEND_VARIANT@") +set(CMAKE_OBJC_SIMULATE_VERSION "@CMAKE_OBJC_SIMULATE_VERSION@") +@_SET_CMAKE_OBJC_COMPILER_ARCHITECTURE_ID@ +@SET_CMAKE_XCODE_ARCHS@ +set(CMAKE_AR "@CMAKE_AR@") +set(CMAKE_OBJC_COMPILER_AR "@CMAKE_OBJC_COMPILER_AR@") +set(CMAKE_RANLIB "@CMAKE_RANLIB@") +set(CMAKE_OBJC_COMPILER_RANLIB "@CMAKE_OBJC_COMPILER_RANLIB@") +set(CMAKE_LINKER "@CMAKE_LINKER@") +set(CMAKE_MT "@CMAKE_MT@") +set(CMAKE_COMPILER_IS_GNUOBJC @CMAKE_COMPILER_IS_GNUOBJC@) +set(CMAKE_OBJC_COMPILER_LOADED 1) +set(CMAKE_OBJC_COMPILER_WORKS @CMAKE_OBJC_COMPILER_WORKS@) +set(CMAKE_OBJC_ABI_COMPILED @CMAKE_OBJC_ABI_COMPILED@) + +set(CMAKE_OBJC_COMPILER_ENV_VAR "OBJC") + +set(CMAKE_OBJC_COMPILER_ID_RUN 1) +set(CMAKE_OBJC_SOURCE_FILE_EXTENSIONS m) +set(CMAKE_OBJC_IGNORE_EXTENSIONS h;H;o;O) +set(CMAKE_OBJC_LINKER_PREFERENCE 5) + +foreach (lang C CXX OBJCXX) + foreach(extension IN LISTS CMAKE_OBJC_SOURCE_FILE_EXTENSIONS) + if (CMAKE_${lang}_COMPILER_ID_RUN) + list(REMOVE_ITEM CMAKE_${lang}_SOURCE_FILE_EXTENSIONS ${extension}) + endif() + endforeach() +endforeach() + +# Save compiler ABI information. +set(CMAKE_OBJC_SIZEOF_DATA_PTR "@CMAKE_OBJC_SIZEOF_DATA_PTR@") +set(CMAKE_OBJC_COMPILER_ABI "@CMAKE_OBJC_COMPILER_ABI@") +set(CMAKE_OBJC_LIBRARY_ARCHITECTURE "@CMAKE_OBJC_LIBRARY_ARCHITECTURE@") + +if(CMAKE_OBJC_SIZEOF_DATA_PTR) + set(CMAKE_SIZEOF_VOID_P "${CMAKE_OBJC_SIZEOF_DATA_PTR}") +endif() + +if(CMAKE_OBJC_COMPILER_ABI) + set(CMAKE_INTERNAL_PLATFORM_ABI "${CMAKE_OBJC_COMPILER_ABI}") +endif() + +if(CMAKE_OBJC_LIBRARY_ARCHITECTURE) + set(CMAKE_LIBRARY_ARCHITECTURE "@CMAKE_OBJC_LIBRARY_ARCHITECTURE@") +endif() + +@CMAKE_OBJC_COMPILER_CUSTOM_CODE@ +@CMAKE_OBJC_SYSROOT_FLAG_CODE@ +@CMAKE_OBJC_OSX_DEPLOYMENT_TARGET_FLAG_CODE@ + +set(CMAKE_OBJC_IMPLICIT_INCLUDE_DIRECTORIES "@CMAKE_OBJC_IMPLICIT_INCLUDE_DIRECTORIES@") +set(CMAKE_OBJC_IMPLICIT_LINK_LIBRARIES "@CMAKE_OBJC_IMPLICIT_LINK_LIBRARIES@") +set(CMAKE_OBJC_IMPLICIT_LINK_DIRECTORIES "@CMAKE_OBJC_IMPLICIT_LINK_DIRECTORIES@") +set(CMAKE_OBJC_IMPLICIT_LINK_FRAMEWORK_DIRECTORIES "@CMAKE_OBJC_IMPLICIT_LINK_FRAMEWORK_DIRECTORIES@") diff --git a/Modules/CMakeOBJCCompilerABI.m b/Modules/CMakeOBJCCompilerABI.m new file mode 100644 index 0000000..8fa8511 --- /dev/null +++ b/Modules/CMakeOBJCCompilerABI.m @@ -0,0 +1,20 @@ +#ifdef __cplusplus +# error "A C++ compiler has been selected for Objective-C." +#endif + +/*--------------------------------------------------------------------------*/ + +#include "CMakeCompilerABI.h" + +/*--------------------------------------------------------------------------*/ + +int main(int argc, char *argv[]) +{ + int require = 0; + require += info_sizeof_dptr[argc]; +#if defined(ABI_ID) + require += info_abi[argc]; +#endif + (void)argv; + return require; +} diff --git a/Modules/CMakeOBJCCompilerId.m.in b/Modules/CMakeOBJCCompilerId.m.in new file mode 100644 index 0000000..2b8aa30 --- /dev/null +++ b/Modules/CMakeOBJCCompilerId.m.in @@ -0,0 +1,63 @@ +#ifdef __cplusplus +# error "An Objective-C++ compiler has been selected for Objective-C." +#endif + +@CMAKE_OBJC_COMPILER_ID_CONTENT@ + +/* Construct the string literal in pieces to prevent the source from + getting matched. Store it in a pointer rather than an array + because some compilers will just produce instructions to fill the + array rather than assigning a pointer to a static array. */ +char const* info_compiler = "INFO" ":" "compiler[" COMPILER_ID "]"; +#ifdef SIMULATE_ID +char const* info_simulate = "INFO" ":" "simulate[" SIMULATE_ID "]"; +#endif + +#ifdef __QNXNTO__ +char const* qnxnto = "INFO" ":" "qnxnto[]"; +#endif + +@CMAKE_OBJC_COMPILER_ID_PLATFORM_CONTENT@ +@CMAKE_OBJC_COMPILER_ID_ERROR_FOR_TEST@ + +#if !defined(__STDC__) +# if (defined(_MSC_VER) && !defined(__clang__)) \ + || (defined(__ibmxl__) || defined(__IBMC__)) +# define C_DIALECT "90" +# else +# define C_DIALECT +# endif +#elif __STDC_VERSION__ >= 201000L +# define C_DIALECT "11" +#elif __STDC_VERSION__ >= 199901L +# define C_DIALECT "99" +#else +# define C_DIALECT "90" +#endif +const char* info_language_dialect_default = + "INFO" ":" "dialect_default[" C_DIALECT "]"; + +/*--------------------------------------------------------------------------*/ + +int main(int argc, char* argv[]) +{ + int require = 0; + require += info_compiler[argc]; + require += info_platform[argc]; + require += info_arch[argc]; +#ifdef COMPILER_VERSION_MAJOR + require += info_version[argc]; +#endif +#ifdef COMPILER_VERSION_INTERNAL + require += info_version_internal[argc]; +#endif +#ifdef SIMULATE_ID + require += info_simulate[argc]; +#endif +#ifdef SIMULATE_VERSION_MAJOR + require += info_simulate_version[argc]; +#endif + require += info_language_dialect_default[argc]; + (void)argv; + return require; +} diff --git a/Modules/CMakeOBJCInformation.cmake b/Modules/CMakeOBJCInformation.cmake new file mode 100644 index 0000000..2baad4a --- /dev/null +++ b/Modules/CMakeOBJCInformation.cmake @@ -0,0 +1,188 @@ +# Distributed under the OSI-approved BSD 3-Clause License. See accompanying +# file Copyright.txt or https://cmake.org/licensing for details. + + +# This file sets the basic flags for the Objective-C language in CMake. +# It also loads the available platform file for the system-compiler +# if it exists. +# It also loads a system - compiler - processor (or target hardware) +# specific file, which is mainly useful for crosscompiling and embedded systems. + +include(CMakeLanguageInformation) + +# some compilers use different extensions (e.g. sdcc uses .rel) +# so set the extension here first so it can be overridden by the compiler specific file +set(CMAKE_OBJC_OUTPUT_EXTENSION .o) + +if(NOT CMAKE_INCLUDE_FLAG_OBJC) + set(CMAKE_INCLUDE_FLAG_OBJC ${CMAKE_INCLUDE_FLAG_C}) +endif() + +set(_INCLUDED_FILE 0) + +# Load compiler-specific information. +if(CMAKE_OBJC_COMPILER_ID) + include(Compiler/${CMAKE_OBJC_COMPILER_ID}-OBJC OPTIONAL) +endif() + +set(CMAKE_BASE_NAME) +get_filename_component(CMAKE_BASE_NAME "${CMAKE_OBJC_COMPILER}" NAME_WE) +if(CMAKE_COMPILER_IS_GNUOBJC) + set(CMAKE_BASE_NAME gcc) +endif() + + +# load a hardware specific file, mostly useful for embedded compilers +if(CMAKE_SYSTEM_PROCESSOR) + if(CMAKE_OBJC_COMPILER_ID) + include(Platform/${CMAKE_EFFECTIVE_SYSTEM_NAME}-${CMAKE_OBJC_COMPILER_ID}-OBJC-${CMAKE_SYSTEM_PROCESSOR} OPTIONAL RESULT_VARIABLE _INCLUDED_FILE) + endif() + if (NOT _INCLUDED_FILE) + include(Platform/${CMAKE_EFFECTIVE_SYSTEM_NAME}-${CMAKE_BASE_NAME}-${CMAKE_SYSTEM_PROCESSOR} OPTIONAL) + endif () +endif() + + +# load the system- and compiler specific files +if(CMAKE_OBJC_COMPILER_ID) + include(Platform/${CMAKE_EFFECTIVE_SYSTEM_NAME}-${CMAKE_OBJC_COMPILER_ID}-OBJC + OPTIONAL RESULT_VARIABLE _INCLUDED_FILE) +endif() +if (NOT _INCLUDED_FILE) + include(Platform/${CMAKE_EFFECTIVE_SYSTEM_NAME}-${CMAKE_BASE_NAME} + OPTIONAL RESULT_VARIABLE _INCLUDED_FILE) +endif () + +# load any compiler-wrapper specific information +if (CMAKE_OBJC_COMPILER_WRAPPER) + __cmake_include_compiler_wrapper(OBJC) +endif () + +# We specify the compiler information in the system file for some +# platforms, but this language may not have been enabled when the file +# was first included. Include it again to get the language info. +# Remove this when all compiler info is removed from system files. +if (NOT _INCLUDED_FILE) + include(Platform/${CMAKE_SYSTEM_NAME} OPTIONAL) +endif () + +if(CMAKE_OBJC_SIZEOF_DATA_PTR) + foreach(f ${CMAKE_OBJC_ABI_FILES}) + include(${f}) + endforeach() + unset(CMAKE_OBJC_ABI_FILES) +endif() + +# This should be included before the _INIT variables are +# used to initialize the cache. Since the rule variables +# have if blocks on them, users can still define them here. +# But, it should still be after the platform file so changes can +# be made to those values. + +if(CMAKE_USER_MAKE_RULES_OVERRIDE) + # Save the full path of the file so try_compile can use it. + include(${CMAKE_USER_MAKE_RULES_OVERRIDE} RESULT_VARIABLE _override) + set(CMAKE_USER_MAKE_RULES_OVERRIDE "${_override}") +endif() + +if(CMAKE_USER_MAKE_RULES_OVERRIDE_OBJC) + # Save the full path of the file so try_compile can use it. + include(${CMAKE_USER_MAKE_RULES_OVERRIDE_OBJC} RESULT_VARIABLE _override) + set(CMAKE_USER_MAKE_RULES_OVERRIDE_OBJC "${_override}") +endif() + + +# for most systems a module is the same as a shared library +# so unless the variable CMAKE_MODULE_EXISTS is set just +# copy the values from the LIBRARY variables +if(NOT CMAKE_MODULE_EXISTS) + set(CMAKE_SHARED_MODULE_OBJC_FLAGS ${CMAKE_SHARED_LIBRARY_OBJC_FLAGS}) + set(CMAKE_SHARED_MODULE_CREATE_OBJC_FLAGS ${CMAKE_SHARED_LIBRARY_CREATE_OBJC_FLAGS}) +endif() + +set(CMAKE_OBJC_FLAGS_INIT "$ENV{OBJCFLAGS} ${CMAKE_OBJC_FLAGS_INIT}") + +cmake_initialize_per_config_variable(CMAKE_OBJC_FLAGS "Flags used by the Objective-C compiler") + +if(CMAKE_OBJC_STANDARD_LIBRARIES_INIT) + set(CMAKE_OBJC_STANDARD_LIBRARIES "${CMAKE_OBJC_STANDARD_LIBRARIES_INIT}" + CACHE STRING "Libraries linked by default with all Objective-C applications.") + mark_as_advanced(CMAKE_OBJC_STANDARD_LIBRARIES) +endif() + +include(CMakeCommonLanguageInclude) + +# now define the following rule variables + +# CMAKE_OBJC_CREATE_SHARED_LIBRARY +# CMAKE_OBJC_CREATE_SHARED_MODULE +# CMAKE_OBJC_COMPILE_OBJECT +# CMAKE_OBJC_LINK_EXECUTABLE + +# variables supplied by the generator at use time +# <TARGET> +# <TARGET_BASE> the target without the suffix +# <OBJECTS> +# <OBJECT> +# <LINK_LIBRARIES> +# <FLAGS> +# <LINK_FLAGS> + +# Objective-C compiler information +# <CMAKE_OBJC_COMPILER> +# <CMAKE_SHARED_LIBRARY_CREATE_OBJC_FLAGS> +# <CMAKE_SHARED_MODULE_CREATE_OBJC_FLAGS> +# <CMAKE_OBJC_LINK_FLAGS> + +# Static library tools +# <CMAKE_AR> +# <CMAKE_RANLIB> + + +# create an Objective-C shared library +if(NOT CMAKE_OBJC_CREATE_SHARED_LIBRARY) + set(CMAKE_OBJC_CREATE_SHARED_LIBRARY + "<CMAKE_OBJC_COMPILER> <CMAKE_SHARED_LIBRARY_OBJC_FLAGS> <LANGUAGE_COMPILE_FLAGS> <LINK_FLAGS> <CMAKE_SHARED_LIBRARY_CREATE_OBJC_FLAGS> <SONAME_FLAG><TARGET_SONAME> -o <TARGET> <OBJECTS> <LINK_LIBRARIES>") +endif() + +# create an Objective-C shared module just copy the shared library rule +if(NOT CMAKE_OBJC_CREATE_SHARED_MODULE) + set(CMAKE_OBJC_CREATE_SHARED_MODULE ${CMAKE_OBJC_CREATE_SHARED_LIBRARY}) +endif() + +# Create an static archive incrementally for large object file counts. +# If CMAKE_OBJC_CREATE_STATIC_LIBRARY is set it will override these. +if(NOT DEFINED CMAKE_OBJC_ARCHIVE_CREATE) + set(CMAKE_OBJC_ARCHIVE_CREATE "<CMAKE_AR> qc <TARGET> <LINK_FLAGS> <OBJECTS>") +endif() +if(NOT DEFINED CMAKE_OBJC_ARCHIVE_APPEND) + set(CMAKE_OBJC_ARCHIVE_APPEND "<CMAKE_AR> q <TARGET> <LINK_FLAGS> <OBJECTS>") +endif() +if(NOT DEFINED CMAKE_OBJC_ARCHIVE_FINISH) + set(CMAKE_OBJC_ARCHIVE_FINISH "<CMAKE_RANLIB> <TARGET>") +endif() + +# compile an Objective-C file into an object file +if(NOT CMAKE_OBJC_COMPILE_OBJECT) + set(CMAKE_OBJC_COMPILE_OBJECT + "<CMAKE_OBJC_COMPILER> <DEFINES> <INCLUDES> <FLAGS> -o <OBJECT> -c <SOURCE>") +endif() + +if(NOT CMAKE_OBJC_LINK_EXECUTABLE) + set(CMAKE_OBJC_LINK_EXECUTABLE + "<CMAKE_OBJC_COMPILER> <FLAGS> <CMAKE_OBJC_LINK_FLAGS> <LINK_FLAGS> <OBJECTS> -o <TARGET> <LINK_LIBRARIES>") +endif() + +if(NOT CMAKE_EXECUTABLE_RUNTIME_OBJC_FLAG) + set(CMAKE_EXECUTABLE_RUNTIME_OBJC_FLAG ${CMAKE_SHARED_LIBRARY_RUNTIME_OBJC_FLAG}) +endif() + +if(NOT CMAKE_EXECUTABLE_RUNTIME_OBJC_FLAG_SEP) + set(CMAKE_EXECUTABLE_RUNTIME_OBJC_FLAG_SEP ${CMAKE_SHARED_LIBRARY_RUNTIME_OBJC_FLAG_SEP}) +endif() + +if(NOT CMAKE_EXECUTABLE_RPATH_LINK_OBJC_FLAG) + set(CMAKE_EXECUTABLE_RPATH_LINK_OBJC_FLAG ${CMAKE_SHARED_LIBRARY_RPATH_LINK_OBJC_FLAG}) +endif() + +set(CMAKE_OBJC_INFORMATION_LOADED 1) diff --git a/Modules/CMakeOBJCXXCompiler.cmake.in b/Modules/CMakeOBJCXXCompiler.cmake.in new file mode 100644 index 0000000..b6452c4 --- /dev/null +++ b/Modules/CMakeOBJCXXCompiler.cmake.in @@ -0,0 +1,79 @@ +set(CMAKE_OBJCXX_COMPILER "@CMAKE_OBJCXX_COMPILER@") +set(CMAKE_OBJCXX_COMPILER_ARG1 "@CMAKE_OBJCXX_COMPILER_ARG1@") +set(CMAKE_OBJCXX_COMPILER_ID "@CMAKE_OBJCXX_COMPILER_ID@") +set(CMAKE_OBJCXX_COMPILER_VERSION "@CMAKE_OBJCXX_COMPILER_VERSION@") +set(CMAKE_OBJCXX_COMPILER_VERSION_INTERNAL "@CMAKE_OBJCXX_COMPILER_VERSION_INTERNAL@") +set(CMAKE_OBJCXX_COMPILER_WRAPPER "@CMAKE_OBJCXX_COMPILER_WRAPPER@") +set(CMAKE_OBJCXX_STANDARD_COMPUTED_DEFAULT "@CMAKE_OBJCXX_STANDARD_COMPUTED_DEFAULT@") +set(CMAKE_OBJCXX_COMPILE_FEATURES "@CMAKE_OBJCXX_COMPILE_FEATURES@") +set(CMAKE_OBJCXX98_COMPILE_FEATURES "@CMAKE_OBJCXX98_COMPILE_FEATURES@") +set(CMAKE_OBJCXX11_COMPILE_FEATURES "@CMAKE_OBJCXX11_COMPILE_FEATURES@") +set(CMAKE_OBJCXX14_COMPILE_FEATURES "@CMAKE_OBJCXX14_COMPILE_FEATURES@") +set(CMAKE_OBJCXX17_COMPILE_FEATURES "@CMAKE_OBJCXX17_COMPILE_FEATURES@") +set(CMAKE_OBJCXX20_COMPILE_FEATURES "@CMAKE_OBJCXX20_COMPILE_FEATURES@") + +set(CMAKE_OBJCXX_PLATFORM_ID "@CMAKE_OBJCXX_PLATFORM_ID@") +set(CMAKE_OBJCXX_SIMULATE_ID "@CMAKE_OBJCXX_SIMULATE_ID@") +set(CMAKE_OBJCXX_COMPILER_FRONTEND_VARIANT "@CMAKE_OBJCXX_COMPILER_FRONTEND_VARIANT@") +set(CMAKE_OBJCXX_SIMULATE_VERSION "@CMAKE_OBJCXX_SIMULATE_VERSION@") +@_SET_CMAKE_OBJCXX_COMPILER_ARCHITECTURE_ID@ +@SET_CMAKE_XCODE_ARCHS@ +set(CMAKE_AR "@CMAKE_AR@") +set(CMAKE_OBJCXX_COMPILER_AR "@CMAKE_OBJCXX_COMPILER_AR@") +set(CMAKE_RANLIB "@CMAKE_RANLIB@") +set(CMAKE_OBJCXX_COMPILER_RANLIB "@CMAKE_OBJCXX_COMPILER_RANLIB@") +set(CMAKE_LINKER "@CMAKE_LINKER@") +set(CMAKE_MT "@CMAKE_MT@") +set(CMAKE_COMPILER_IS_GNUOBJCXX @CMAKE_COMPILER_IS_GNUOBJCXX@) +set(CMAKE_OBJCXX_COMPILER_LOADED 1) +set(CMAKE_OBJCXX_COMPILER_WORKS @CMAKE_OBJCXX_COMPILER_WORKS@) +set(CMAKE_OBJCXX_ABI_COMPILED @CMAKE_OBJCXX_ABI_COMPILED@) + +set(CMAKE_OBJCXX_COMPILER_ENV_VAR "OBJCXX") + +set(CMAKE_OBJCXX_COMPILER_ID_RUN 1) +set(CMAKE_OBJCXX_SOURCE_FILE_EXTENSIONS M;m;mm) +set(CMAKE_OBJCXX_IGNORE_EXTENSIONS inl;h;hpp;HPP;H;o;O) + +if (CMAKE_OBJC_COMPILER_ID_RUN) + foreach(extension IN LISTS CMAKE_OBJC_SOURCE_FILE_EXTENSIONS) + list(REMOVE_ITEM CMAKE_OBJCXX_SOURCE_FILE_EXTENSIONS ${extension}) + endforeach() +endif() + +foreach (lang C CXX OBJC) + foreach(extension IN LISTS CMAKE_OBJCXX_SOURCE_FILE_EXTENSIONS) + if (CMAKE_${lang}_COMPILER_ID_RUN) + list(REMOVE_ITEM CMAKE_${lang}_SOURCE_FILE_EXTENSIONS ${extension}) + endif() + endforeach() +endforeach() + +set(CMAKE_OBJCXX_LINKER_PREFERENCE 25) +set(CMAKE_OBJCXX_LINKER_PREFERENCE_PROPAGATES 1) + +# Save compiler ABI information. +set(CMAKE_OBJCXX_SIZEOF_DATA_PTR "@CMAKE_OBJCXX_SIZEOF_DATA_PTR@") +set(CMAKE_OBJCXX_COMPILER_ABI "@CMAKE_OBJCXX_COMPILER_ABI@") +set(CMAKE_OBJCXX_LIBRARY_ARCHITECTURE "@CMAKE_OBJCXX_LIBRARY_ARCHITECTURE@") + +if(CMAKE_OBJCXX_SIZEOF_DATA_PTR) + set(CMAKE_SIZEOF_VOID_P "${CMAKE_OBJCXX_SIZEOF_DATA_PTR}") +endif() + +if(CMAKE_OBJCXX_COMPILER_ABI) + set(CMAKE_INTERNAL_PLATFORM_ABI "${CMAKE_OBJCXX_COMPILER_ABI}") +endif() + +if(CMAKE_OBJCXX_LIBRARY_ARCHITECTURE) + set(CMAKE_LIBRARY_ARCHITECTURE "@CMAKE_OBJCXX_LIBRARY_ARCHITECTURE@") +endif() + +@CMAKE_OBJCXX_COMPILER_CUSTOM_CODE@ +@CMAKE_OBJCXX_SYSROOT_FLAG_CODE@ +@CMAKE_OBJCXX_OSX_DEPLOYMENT_TARGET_FLAG_CODE@ + +set(CMAKE_OBJCXX_IMPLICIT_INCLUDE_DIRECTORIES "@CMAKE_OBJCXX_IMPLICIT_INCLUDE_DIRECTORIES@") +set(CMAKE_OBJCXX_IMPLICIT_LINK_LIBRARIES "@CMAKE_OBJCXX_IMPLICIT_LINK_LIBRARIES@") +set(CMAKE_OBJCXX_IMPLICIT_LINK_DIRECTORIES "@CMAKE_OBJCXX_IMPLICIT_LINK_DIRECTORIES@") +set(CMAKE_OBJCXX_IMPLICIT_LINK_FRAMEWORK_DIRECTORIES "@CMAKE_OBJCXX_IMPLICIT_LINK_FRAMEWORK_DIRECTORIES@") diff --git a/Modules/CMakeOBJCXXCompilerABI.mm b/Modules/CMakeOBJCXXCompilerABI.mm new file mode 100644 index 0000000..288a58c --- /dev/null +++ b/Modules/CMakeOBJCXXCompilerABI.mm @@ -0,0 +1,20 @@ +#ifndef __cplusplus +# error "A C compiler has been selected for Objective-C++." +#endif + +/*--------------------------------------------------------------------------*/ + +#include "CMakeCompilerABI.h" + +/*--------------------------------------------------------------------------*/ + +int main(int argc, char *argv[]) +{ + int require = 0; + require += info_sizeof_dptr[argc]; +#if defined(ABI_ID) + require += info_abi[argc]; +#endif + (void)argv; + return require; +} diff --git a/Modules/CMakeOBJCXXCompilerId.mm.in b/Modules/CMakeOBJCXXCompilerId.mm.in new file mode 100644 index 0000000..fe04de1 --- /dev/null +++ b/Modules/CMakeOBJCXXCompilerId.mm.in @@ -0,0 +1,68 @@ +/* This source file must have a .cpp extension so that all C++ compilers + recognize the extension without flags. Borland does not know .cxx for + example. */ +#ifndef __cplusplus +# error "An Objective-C compiler has been selected for Objective-C++." +#endif + +@CMAKE_OBJCXX_COMPILER_ID_CONTENT@ + +/* Construct the string literal in pieces to prevent the source from + getting matched. Store it in a pointer rather than an array + because some compilers will just produce instructions to fill the + array rather than assigning a pointer to a static array. */ +char const* info_compiler = "INFO" ":" "compiler[" COMPILER_ID "]"; +#ifdef SIMULATE_ID +char const* info_simulate = "INFO" ":" "simulate[" SIMULATE_ID "]"; +#endif + +#ifdef __QNXNTO__ +char const* qnxnto = "INFO" ":" "qnxnto[]"; +#endif + +@CMAKE_OBJCXX_COMPILER_ID_PLATFORM_CONTENT@ +@CMAKE_OBJCXX_COMPILER_ID_ERROR_FOR_TEST@ + +#if defined(_MSC_VER) && defined(_MSVC_LANG) +#define CXX_STD _MSVC_LANG +#else +#define CXX_STD __cplusplus +#endif + +const char* info_language_dialect_default = "INFO" ":" "dialect_default[" +#if CXX_STD > 201703L + "20" +#elif CXX_STD >= 201703L + "17" +#elif CXX_STD >= 201402L + "14" +#elif CXX_STD >= 201103L + "11" +#else + "98" +#endif +"]"; + +/*--------------------------------------------------------------------------*/ + +int main(int argc, char* argv[]) +{ + int require = 0; + require += info_compiler[argc]; + require += info_platform[argc]; +#ifdef COMPILER_VERSION_MAJOR + require += info_version[argc]; +#endif +#ifdef COMPILER_VERSION_INTERNAL + require += info_version_internal[argc]; +#endif +#ifdef SIMULATE_ID + require += info_simulate[argc]; +#endif +#ifdef SIMULATE_VERSION_MAJOR + require += info_simulate_version[argc]; +#endif + require += info_language_dialect_default[argc]; + (void)argv; + return require; +} diff --git a/Modules/CMakeOBJCXXInformation.cmake b/Modules/CMakeOBJCXXInformation.cmake new file mode 100644 index 0000000..3f55b01 --- /dev/null +++ b/Modules/CMakeOBJCXXInformation.cmake @@ -0,0 +1,273 @@ +# Distributed under the OSI-approved BSD 3-Clause License. See accompanying +# file Copyright.txt or https://cmake.org/licensing for details. + + +# This file sets the basic flags for the Objective-C++ language in CMake. +# It also loads the available platform file for the system-compiler +# if it exists. +# It also loads a system - compiler - processor (or target hardware) +# specific file, which is mainly useful for crosscompiling and embedded systems. + +include(CMakeLanguageInformation) + +# some compilers use different extensions (e.g. sdcc uses .rel) +# so set the extension here first so it can be overridden by the compiler specific file +set(CMAKE_OBJCXX_OUTPUT_EXTENSION .o) + +set(_INCLUDED_FILE 0) + +# Load compiler-specific information. +if(CMAKE_OBJCXX_COMPILER_ID) + include(Compiler/${CMAKE_OBJCXX_COMPILER_ID}-OBJCXX OPTIONAL) +endif() + +set(CMAKE_BASE_NAME) +get_filename_component(CMAKE_BASE_NAME "${CMAKE_OBJCXX_COMPILER}" NAME_WE) +# since the gnu compiler has several names force g++ +if(CMAKE_COMPILER_IS_GNUOBJCXX) + set(CMAKE_BASE_NAME g++) +endif() + + +# load a hardware specific file, mostly useful for embedded compilers +if(CMAKE_SYSTEM_PROCESSOR) + if(CMAKE_OBJCXX_COMPILER_ID) + include(Platform/${CMAKE_EFFECTIVE_SYSTEM_NAME}-${CMAKE_OBJCXX_COMPILER_ID}-OBJCXX-${CMAKE_SYSTEM_PROCESSOR} OPTIONAL RESULT_VARIABLE _INCLUDED_FILE) + endif() + if (NOT _INCLUDED_FILE) + include(Platform/${CMAKE_EFFECTIVE_SYSTEM_NAME}-${CMAKE_BASE_NAME}-${CMAKE_SYSTEM_PROCESSOR} OPTIONAL) + endif () +endif() + +# load the system- and compiler specific files +if(CMAKE_OBJCXX_COMPILER_ID) + include(Platform/${CMAKE_EFFECTIVE_SYSTEM_NAME}-${CMAKE_OBJCXX_COMPILER_ID}-OBJCXX OPTIONAL RESULT_VARIABLE _INCLUDED_FILE) +endif() +if (NOT _INCLUDED_FILE) + include(Platform/${CMAKE_EFFECTIVE_SYSTEM_NAME}-${CMAKE_BASE_NAME} OPTIONAL + RESULT_VARIABLE _INCLUDED_FILE) +endif () + +# load any compiler-wrapper specific information +if (CMAKE_OBJCXX_COMPILER_WRAPPER) + __cmake_include_compiler_wrapper(OBJCXX) +endif () + +# We specify the compiler information in the system file for some +# platforms, but this language may not have been enabled when the file +# was first included. Include it again to get the language info. +# Remove this when all compiler info is removed from system files. +if (NOT _INCLUDED_FILE) + include(Platform/${CMAKE_SYSTEM_NAME} OPTIONAL) +endif () + +if(CMAKE_OBJCXX_SIZEOF_DATA_PTR) + foreach(f ${CMAKE_OBJCXX_ABI_FILES}) + include(${f}) + endforeach() + unset(CMAKE_OBJCXX_ABI_FILES) +endif() + +# This should be included before the _INIT variables are +# used to initialize the cache. Since the rule variables +# have if blocks on them, users can still define them here. +# But, it should still be after the platform file so changes can +# be made to those values. + +if(CMAKE_USER_MAKE_RULES_OVERRIDE) + # Save the full path of the file so try_compile can use it. + include(${CMAKE_USER_MAKE_RULES_OVERRIDE} RESULT_VARIABLE _override) + set(CMAKE_USER_MAKE_RULES_OVERRIDE "${_override}") +endif() + +if(CMAKE_USER_MAKE_RULES_OVERRIDE_OBJCXX) + # Save the full path of the file so try_compile can use it. + include(${CMAKE_USER_MAKE_RULES_OVERRIDE_OBJCXX} RESULT_VARIABLE _override) + set(CMAKE_USER_MAKE_RULES_OVERRIDE_OBJCXX "${_override}") +endif() + + +# Create a set of shared library variable specific to Objective-C++ +# For 90% of the systems, these are the same flags as the Objective-C versions +# so if these are not set just copy the flags from the Objective-C version +if(NOT CMAKE_SHARED_LIBRARY_CREATE_OBJCXX_FLAGS) + set(CMAKE_SHARED_LIBRARY_CREATE_OBJCXX_FLAGS ${CMAKE_SHARED_LIBRARY_CREATE_OBJC_FLAGS}) +endif() + +if(NOT CMAKE_OBJCXX_COMPILE_OPTIONS_PIC) + set(CMAKE_OBJCXX_COMPILE_OPTIONS_PIC ${CMAKE_OBJC_COMPILE_OPTIONS_PIC}) +endif() + +if(NOT CMAKE_OBJCXX_COMPILE_OPTIONS_PIE) + set(CMAKE_OBJCXX_COMPILE_OPTIONS_PIE ${CMAKE_OBJC_COMPILE_OPTIONS_PIE}) +endif() +if(NOT CMAKE_OBJCXX_LINK_OPTIONS_PIE) + set(CMAKE_OBJCXX_LINK_OPTIONS_PIE ${CMAKE_OBJC_LINK_OPTIONS_PIE}) +endif() +if(NOT CMAKE_OBJCXX_LINK_OPTIONS_NO_PIE) + set(CMAKE_OBJCXX_LINK_OPTIONS_NO_PIE ${CMAKE_OBJC_LINK_OPTIONS_NO_PIE}) +endif() + +if(NOT CMAKE_OBJCXX_COMPILE_OPTIONS_DLL) + set(CMAKE_OBJCXX_COMPILE_OPTIONS_DLL ${CMAKE_OBJC_COMPILE_OPTIONS_DLL}) +endif() + +if(NOT CMAKE_SHARED_LIBRARY_OBJCXX_FLAGS) + set(CMAKE_SHARED_LIBRARY_OBJCXX_FLAGS ${CMAKE_SHARED_LIBRARY_OBJC_FLAGS}) +endif() + +if(NOT DEFINED CMAKE_SHARED_LIBRARY_LINK_OBJCXX_FLAGS) + set(CMAKE_SHARED_LIBRARY_LINK_OBJCXX_FLAGS ${CMAKE_SHARED_LIBRARY_LINK_OBJC_FLAGS}) +endif() + +if(NOT CMAKE_SHARED_LIBRARY_RUNTIME_OBJCXX_FLAG) + set(CMAKE_SHARED_LIBRARY_RUNTIME_OBJCXX_FLAG ${CMAKE_SHARED_LIBRARY_RUNTIME_OBJC_FLAG}) +endif() + +if(NOT CMAKE_SHARED_LIBRARY_RUNTIME_OBJCXX_FLAG_SEP) + set(CMAKE_SHARED_LIBRARY_RUNTIME_OBJCXX_FLAG_SEP ${CMAKE_SHARED_LIBRARY_RUNTIME_OBJC_FLAG_SEP}) +endif() + +if(NOT CMAKE_SHARED_LIBRARY_RPATH_LINK_OBJCXX_FLAG) + set(CMAKE_SHARED_LIBRARY_RPATH_LINK_OBJCXX_FLAG ${CMAKE_SHARED_LIBRARY_RPATH_LINK_OBJC_FLAG}) +endif() + +if(NOT DEFINED CMAKE_EXE_EXPORTS_OBJCXX_FLAG) + set(CMAKE_EXE_EXPORTS_OBJCXX_FLAG ${CMAKE_EXE_EXPORTS_OBJC_FLAG}) +endif() + +if(NOT DEFINED CMAKE_SHARED_LIBRARY_SONAME_OBJCXX_FLAG) + set(CMAKE_SHARED_LIBRARY_SONAME_OBJCXX_FLAG ${CMAKE_SHARED_LIBRARY_SONAME_OBJC_FLAG}) +endif() + +if(NOT CMAKE_EXECUTABLE_RUNTIME_OBJCXX_FLAG) + set(CMAKE_EXECUTABLE_RUNTIME_OBJCXX_FLAG ${CMAKE_SHARED_LIBRARY_RUNTIME_OBJCXX_FLAG}) +endif() + +if(NOT CMAKE_EXECUTABLE_RUNTIME_OBJCXX_FLAG_SEP) + set(CMAKE_EXECUTABLE_RUNTIME_OBJCXX_FLAG_SEP ${CMAKE_SHARED_LIBRARY_RUNTIME_OBJCXX_FLAG_SEP}) +endif() + +if(NOT CMAKE_EXECUTABLE_RPATH_LINK_OBJCXX_FLAG) + set(CMAKE_EXECUTABLE_RPATH_LINK_OBJCXX_FLAG ${CMAKE_SHARED_LIBRARY_RPATH_LINK_OBJCXX_FLAG}) +endif() + +if(NOT DEFINED CMAKE_SHARED_LIBRARY_LINK_OBJCXX_WITH_RUNTIME_PATH) + set(CMAKE_SHARED_LIBRARY_LINK_OBJCXX_WITH_RUNTIME_PATH ${CMAKE_SHARED_LIBRARY_LINK_OBJC_WITH_RUNTIME_PATH}) +endif() + +if(NOT CMAKE_INCLUDE_FLAG_OBJCXX) + set(CMAKE_INCLUDE_FLAG_OBJCXX ${CMAKE_INCLUDE_FLAG_C}) +endif() + +# for most systems a module is the same as a shared library +# so unless the variable CMAKE_MODULE_EXISTS is set just +# copy the values from the LIBRARY variables +if(NOT CMAKE_MODULE_EXISTS) + set(CMAKE_SHARED_MODULE_OBJCXX_FLAGS ${CMAKE_SHARED_LIBRARY_OBJCXX_FLAGS}) + set(CMAKE_SHARED_MODULE_CREATE_OBJCXX_FLAGS ${CMAKE_SHARED_LIBRARY_CREATE_OBJCXX_FLAGS}) +endif() + +# repeat for modules +if(NOT CMAKE_SHARED_MODULE_CREATE_OBJCXX_FLAGS) + set(CMAKE_SHARED_MODULE_CREATE_OBJCXX_FLAGS ${CMAKE_SHARED_MODULE_CREATE_OBJC_FLAGS}) +endif() + +if(NOT CMAKE_SHARED_MODULE_OBJCXX_FLAGS) + set(CMAKE_SHARED_MODULE_OBJCXX_FLAGS ${CMAKE_SHARED_MODULE_OBJC_FLAGS}) +endif() + +# Initialize OBJCXX link type selection flags from OBJC versions. +foreach(type SHARED_LIBRARY SHARED_MODULE EXE) + if(NOT CMAKE_${type}_LINK_STATIC_OBJCXX_FLAGS) + set(CMAKE_${type}_LINK_STATIC_OBJCXX_FLAGS + ${CMAKE_${type}_LINK_STATIC_OBJC_FLAGS}) + endif() + if(NOT CMAKE_${type}_LINK_DYNAMIC_OBJCXX_FLAGS) + set(CMAKE_${type}_LINK_DYNAMIC_OBJCXX_FLAGS + ${CMAKE_${type}_LINK_DYNAMIC_OBJC_FLAGS}) + endif() +endforeach() + +# add the flags to the cache based +# on the initial values computed in the platform/*.cmake files +# use _INIT variables so that this only happens the first time +# and you can set these flags in the cmake cache +set(CMAKE_OBJCXX_FLAGS_INIT "$ENV{OBJCXXFLAGS} ${CMAKE_OBJCXX_FLAGS_INIT}") + +cmake_initialize_per_config_variable(CMAKE_OBJCXX_FLAGS "Flags used by the Objective-C++ compiler") + +if(CMAKE_OBJCXX_STANDARD_LIBRARIES_INIT) + set(CMAKE_OBJCXX_STANDARD_LIBRARIES "${CMAKE_OBJCXX_STANDARD_LIBRARIES_INIT}" + CACHE STRING "Libraries linked by default with all Objective-C++ applications.") + mark_as_advanced(CMAKE_OBJCXX_STANDARD_LIBRARIES) +endif() + +include(CMakeCommonLanguageInclude) + +# now define the following rules: +# CMAKE_OBJCXX_CREATE_SHARED_LIBRARY +# CMAKE_OBJCXX_CREATE_SHARED_MODULE +# CMAKE_OBJCXX_COMPILE_OBJECT +# CMAKE_OBJCXX_LINK_EXECUTABLE + +# variables supplied by the generator at use time +# <TARGET> +# <TARGET_BASE> the target without the suffix +# <OBJECTS> +# <OBJECT> +# <LINK_LIBRARIES> +# <FLAGS> +# <LINK_FLAGS> + +# Objective-C++ compiler information +# <CMAKE_OBJCXX_COMPILER> +# <CMAKE_SHARED_LIBRARY_CREATE_OBJCXX_FLAGS> +# <CMAKE_OBJCXX_SHARED_MODULE_CREATE_FLAGS> +# <CMAKE_OBJCXX_LINK_FLAGS> + +# Static library tools +# <CMAKE_AR> +# <CMAKE_RANLIB> + + +# create a shared Objective-C++ library +if(NOT CMAKE_OBJCXX_CREATE_SHARED_LIBRARY) + set(CMAKE_OBJCXX_CREATE_SHARED_LIBRARY + "<CMAKE_OBJCXX_COMPILER> <CMAKE_SHARED_LIBRARY_OBJCXX_FLAGS> <LANGUAGE_COMPILE_FLAGS> <LINK_FLAGS> <CMAKE_SHARED_LIBRARY_CREATE_OBJCXX_FLAGS> <SONAME_FLAG><TARGET_SONAME> -o <TARGET> <OBJECTS> <LINK_LIBRARIES>") +endif() + +# create an Objective-C++ shared module copy the shared library rule by default +if(NOT CMAKE_OBJCXX_CREATE_SHARED_MODULE) + set(CMAKE_OBJCXX_CREATE_SHARED_MODULE ${CMAKE_OBJCXX_CREATE_SHARED_LIBRARY}) +endif() + + +# Create a static archive incrementally for large object file counts. +# If CMAKE_OBJCXX_CREATE_STATIC_LIBRARY is set it will override these. +if(NOT DEFINED CMAKE_OBJCXX_ARCHIVE_CREATE) + set(CMAKE_OBJCXX_ARCHIVE_CREATE "<CMAKE_AR> qc <TARGET> <LINK_FLAGS> <OBJECTS>") +endif() +if(NOT DEFINED CMAKE_OBJCXX_ARCHIVE_APPEND) + set(CMAKE_OBJCXX_ARCHIVE_APPEND "<CMAKE_AR> q <TARGET> <LINK_FLAGS> <OBJECTS>") +endif() +if(NOT DEFINED CMAKE_OBJCXX_ARCHIVE_FINISH) + set(CMAKE_OBJCXX_ARCHIVE_FINISH "<CMAKE_RANLIB> <TARGET>") +endif() + +# compile an Objective-C++ file into an object file +if(NOT CMAKE_OBJCXX_COMPILE_OBJECT) + set(CMAKE_OBJCXX_COMPILE_OBJECT + "<CMAKE_OBJCXX_COMPILER> <DEFINES> <INCLUDES> <FLAGS> -o <OBJECT> -c <SOURCE>") +endif() + +if(NOT CMAKE_OBJCXX_LINK_EXECUTABLE) + set(CMAKE_OBJCXX_LINK_EXECUTABLE + "<CMAKE_OBJCXX_COMPILER> <FLAGS> <CMAKE_OBJCXX_LINK_FLAGS> <LINK_FLAGS> <OBJECTS> -o <TARGET> <LINK_LIBRARIES>") +endif() + +mark_as_advanced( +CMAKE_VERBOSE_MAKEFILE +) + +set(CMAKE_OBJCXX_INFORMATION_LOADED 1) diff --git a/Modules/CMakeTestOBJCCompiler.cmake b/Modules/CMakeTestOBJCCompiler.cmake new file mode 100644 index 0000000..0030683 --- /dev/null +++ b/Modules/CMakeTestOBJCCompiler.cmake @@ -0,0 +1,94 @@ +# Distributed under the OSI-approved BSD 3-Clause License. See accompanying +# file Copyright.txt or https://cmake.org/licensing for details. + + +if(CMAKE_OBJC_COMPILER_FORCED) + # The compiler configuration was forced by the user. + # Assume the user has configured all compiler information. + set(CMAKE_OBJC_COMPILER_WORKS TRUE) + return() +endif() + +include(CMakeTestCompilerCommon) + +# work around enforced code signing and / or missing exectuable target type +set(__CMAKE_SAVED_TRY_COMPILE_TARGET_TYPE ${CMAKE_TRY_COMPILE_TARGET_TYPE}) +if(_CMAKE_FEATURE_DETECTION_TARGET_TYPE) + set(CMAKE_TRY_COMPILE_TARGET_TYPE ${_CMAKE_FEATURE_DETECTION_TARGET_TYPE}) +endif() + +# Remove any cached result from an older CMake version. +# We now store this in CMakeCCompiler.cmake. +unset(CMAKE_OBJC_COMPILER_WORKS CACHE) + +# This file is used by EnableLanguage in cmGlobalGenerator to +# determine that that selected Objective-C compiler can actually compile +# and link the most basic of programs. If not, a fatal error +# is set and cmake stops processing commands and will not generate +# any makefiles or projects. +if(NOT CMAKE_OBJC_COMPILER_WORKS) + PrintTestCompilerStatus("OBJC" "") + __TestCompiler_setTryCompileTargetType() + file(WRITE ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeTmp/testOBJCCompiler.m + "#ifdef __cplusplus\n" + "# error \"The CMAKE_OBJC_COMPILER is set to a C++ compiler\"\n" + "#endif\n" + "#ifndef __OBJC__\n" + "# error \"The CMAKE_OBJC_COMPILER is not an Objective-C compiler\"\n" + "#endif\n" + "int main(int argc, char* argv[])\n" + "{ (void)argv; return argc-1;}\n") + try_compile(CMAKE_OBJC_COMPILER_WORKS ${CMAKE_BINARY_DIR} + ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeTmp/testOBJCCompiler.m + OUTPUT_VARIABLE __CMAKE_OBJC_COMPILER_OUTPUT) + # Move result from cache to normal variable. + set(CMAKE_OBJC_COMPILER_WORKS ${CMAKE_OBJC_COMPILER_WORKS}) + unset(CMAKE_OBJC_COMPILER_WORKS CACHE) + set(OBJC_TEST_WAS_RUN 1) + __TestCompiler_restoreTryCompileTargetType() +endif() + +if(NOT CMAKE_OBJC_COMPILER_WORKS) + PrintTestCompilerStatus("OBJC" " -- broken") + file(APPEND ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeError.log + "Determining if the Objective-C compiler works failed with " + "the following output:\n${__CMAKE_OBJC_COMPILER_OUTPUT}\n\n") + string(REPLACE "\n" "\n " _output "${__CMAKE_OBJC_COMPILER_OUTPUT}") + message(FATAL_ERROR "The Objective-C compiler\n \"${CMAKE_OBJC_COMPILER}\"\n" + "is not able to compile a simple test program.\nIt fails " + "with the following output:\n ${_output}\n\n" + "CMake will not be able to correctly generate this project.") +else() + if(OBJC_TEST_WAS_RUN) + PrintTestCompilerStatus("OBJC" " -- works") + file(APPEND ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeOutput.log + "Determining if the Objective-C compiler works passed with " + "the following output:\n${__CMAKE_OBJC_COMPILER_OUTPUT}\n\n") + endif() + + # Try to identify the ABI and configure it into CMakeOBJCCompiler.cmake + include(${CMAKE_ROOT}/Modules/CMakeDetermineCompilerABI.cmake) + CMAKE_DETERMINE_COMPILER_ABI(OBJC ${CMAKE_ROOT}/Modules/CMakeOBJCCompilerABI.m) + # Try to identify the compiler features + include(${CMAKE_ROOT}/Modules/CMakeDetermineCompileFeatures.cmake) + CMAKE_DETERMINE_COMPILE_FEATURES(OBJC) + + # Re-configure to save learned information. + configure_file( + ${CMAKE_ROOT}/Modules/CMakeOBJCCompiler.cmake.in + ${CMAKE_PLATFORM_INFO_DIR}/CMakeOBJCCompiler.cmake + @ONLY + ) + include(${CMAKE_PLATFORM_INFO_DIR}/CMakeOBJCCompiler.cmake) + + if(CMAKE_OBJC_SIZEOF_DATA_PTR) + foreach(f ${CMAKE_OBJC_ABI_FILES}) + include(${f}) + endforeach() + unset(CMAKE_OBJC_ABI_FILES) + endif() +endif() + +set(CMAKE_TRY_COMPILE_TARGET_TYPE ${__CMAKE_SAVED_TRY_COMPILE_TARGET_TYPE}) +unset(__CMAKE_SAVED_TRY_COMPILE_TARGET_TYPE) +unset(__CMAKE_OBJC_COMPILER_OUTPUT) diff --git a/Modules/CMakeTestOBJCXXCompiler.cmake b/Modules/CMakeTestOBJCXXCompiler.cmake new file mode 100644 index 0000000..bcce2f1 --- /dev/null +++ b/Modules/CMakeTestOBJCXXCompiler.cmake @@ -0,0 +1,93 @@ +# Distributed under the OSI-approved BSD 3-Clause License. See accompanying +# file Copyright.txt or https://cmake.org/licensing for details. + + +if(CMAKE_OBJCXX_COMPILER_FORCED) + # The compiler configuration was forced by the user. + # Assume the user has configured all compiler information. + set(CMAKE_OBJCXX_COMPILER_WORKS TRUE) + return() +endif() + +include(CMakeTestCompilerCommon) + +# work around enforced code signing and / or missing exectuable target type +set(__CMAKE_SAVED_TRY_COMPILE_TARGET_TYPE ${CMAKE_TRY_COMPILE_TARGET_TYPE}) +if(_CMAKE_FEATURE_DETECTION_TARGET_TYPE) + set(CMAKE_TRY_COMPILE_TARGET_TYPE ${_CMAKE_FEATURE_DETECTION_TARGET_TYPE}) +endif() + +# Remove any cached result from an older CMake version. +# We now store this in CMakeOBJCXXCompiler.cmake. +unset(CMAKE_OBJCXX_COMPILER_WORKS CACHE) + +# This file is used by EnableLanguage in cmGlobalGenerator to +# determine that the selected Objective-C++ compiler can actually compile +# and link the most basic of programs. If not, a fatal error +# is set and cmake stops processing commands and will not generate +# any makefiles or projects. +if(NOT CMAKE_OBJCXX_COMPILER_WORKS) + PrintTestCompilerStatus("OBJCXX" "") + __TestCompiler_setTryCompileTargetType() + file(WRITE ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeTmp/testOBJCXXCompiler.mm + "#ifndef __cplusplus\n" + "# error \"The CMAKE_OBJCXX_COMPILER is set to a C compiler\"\n" + "#endif\n" + "#ifndef __OBJC__\n" + "# error \"The CMAKE_OBJCXX_COMPILER is not an Objective-C++ compiler\"\n" + "#endif\n" + "int main(){return 0;}\n") + try_compile(CMAKE_OBJCXX_COMPILER_WORKS ${CMAKE_BINARY_DIR} + ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeTmp/testOBJCXXCompiler.mm + OUTPUT_VARIABLE __CMAKE_OBJCXX_COMPILER_OUTPUT) + # Move result from cache to normal variable. + set(CMAKE_OBJCXX_COMPILER_WORKS ${CMAKE_OBJCXX_COMPILER_WORKS}) + unset(CMAKE_OBJCXX_COMPILER_WORKS CACHE) + set(OBJCXX_TEST_WAS_RUN 1) + __TestCompiler_restoreTryCompileTargetType() +endif() + +if(NOT CMAKE_OBJCXX_COMPILER_WORKS) + PrintTestCompilerStatus("OBJCXX" " -- broken") + file(APPEND ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeError.log + "Determining if the Objective-C++ compiler works failed with " + "the following output:\n${__CMAKE_OBJCXX_COMPILER_OUTPUT}\n\n") + string(REPLACE "\n" "\n " _output "${__CMAKE_OBJCXX_COMPILER_OUTPUT}") + message(FATAL_ERROR "The Objective-C++ compiler\n \"${CMAKE_OBJCXX_COMPILER}\"\n" + "is not able to compile a simple test program.\nIt fails " + "with the following output:\n ${_output}\n\n" + "CMake will not be able to correctly generate this project.") +else() + if(OBJCXX_TEST_WAS_RUN) + PrintTestCompilerStatus("OBJCXX" " -- works") + file(APPEND ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeOutput.log + "Determining if the Objective-C++ compiler works passed with " + "the following output:\n${__CMAKE_OBJCXX_COMPILER_OUTPUT}\n\n") + endif() + + # Try to identify the ABI and configure it into CMakeOBJCXXCompiler.cmake + include(${CMAKE_ROOT}/Modules/CMakeDetermineCompilerABI.cmake) + CMAKE_DETERMINE_COMPILER_ABI(OBJCXX ${CMAKE_ROOT}/Modules/CMakeOBJCXXCompilerABI.mm) + # Try to identify the compiler features + include(${CMAKE_ROOT}/Modules/CMakeDetermineCompileFeatures.cmake) + CMAKE_DETERMINE_COMPILE_FEATURES(OBJCXX) + + # Re-configure to save learned information. + configure_file( + ${CMAKE_ROOT}/Modules/CMakeOBJCXXCompiler.cmake.in + ${CMAKE_PLATFORM_INFO_DIR}/CMakeOBJCXXCompiler.cmake + @ONLY + ) + include(${CMAKE_PLATFORM_INFO_DIR}/CMakeOBJCXXCompiler.cmake) + + if(CMAKE_OBJCXX_SIZEOF_DATA_PTR) + foreach(f ${CMAKE_OBJCXX_ABI_FILES}) + include(${f}) + endforeach() + unset(CMAKE_OBJCXX_ABI_FILES) + endif() +endif() + +set(CMAKE_TRY_COMPILE_TARGET_TYPE ${__CMAKE_SAVED_TRY_COMPILE_TARGET_TYPE}) +unset(__CMAKE_SAVED_TRY_COMPILE_TARGET_TYPE) +unset(__CMAKE_OBJCXX_COMPILER_OUTPUT) diff --git a/Modules/CheckOBJCCompilerFlag.cmake b/Modules/CheckOBJCCompilerFlag.cmake new file mode 100644 index 0000000..1d975da --- /dev/null +++ b/Modules/CheckOBJCCompilerFlag.cmake @@ -0,0 +1,64 @@ +# Distributed under the OSI-approved BSD 3-Clause License. See accompanying +# file Copyright.txt or https://cmake.org/licensing for details. + +#[=======================================================================[.rst: +CheckOBJCCompilerFlag +--------------------- + +Check whether the Objective-C compiler supports a given flag. + +.. command:: check_objc_compiler_flag + + .. code-block:: cmake + + check_objc_compiler_flag(<flag> <var>) + + Check that the ``<flag>`` is accepted by the compiler without + a diagnostic. Stores the result in an internal cache entry + named ``<var>``. + +This command temporarily sets the ``CMAKE_REQUIRED_DEFINITIONS`` variable +and calls the ``check_objc_source_compiles`` macro from the +:module:`CheckOBJCSourceCompiles` module. See documentation of that +module for a listing of variables that can otherwise modify the build. + +A positive result from this check indicates only that the compiler did not +issue a diagnostic message when given the flag. Whether the flag has any +effect or even a specific one is beyond the scope of this module. + +.. note:: + Since the :command:`try_compile` command forwards flags from variables + like :variable:`CMAKE_OBJC_FLAGS <CMAKE_<LANG>_FLAGS>`, unknown flags + in such variables may cause a false negative for this check. +#]=======================================================================] + +include_guard(GLOBAL) +include(CheckOBJCSourceCompiles) +include(CMakeCheckCompilerFlagCommonPatterns) + +macro (CHECK_OBJC_COMPILER_FLAG _FLAG _RESULT) + set(SAFE_CMAKE_REQUIRED_DEFINITIONS "${CMAKE_REQUIRED_DEFINITIONS}") + set(CMAKE_REQUIRED_DEFINITIONS "${_FLAG}") + + # Normalize locale during test compilation. + set(_CheckOBJCCompilerFlag_LOCALE_VARS LC_ALL LC_MESSAGES LANG) + foreach(v ${_CheckOBJCCompilerFlag_LOCALE_VARS}) + set(_CheckOBJCCompilerFlag_SAVED_${v} "$ENV{${v}}") + set(ENV{${v}} OBJC) + endforeach() + CHECK_COMPILER_FLAG_COMMON_PATTERNS(_CheckOBJCCompilerFlag_COMMON_PATTERNS) + CHECK_OBJC_SOURCE_COMPILES("#ifndef __OBJC__\n# error \"Not an Objective-C compiler\"\n#endif\nint main(void) { return 0; }" ${_RESULT} + # Some compilers do not fail with a bad flag + FAIL_REGEX "command line option .* is valid for .* but not for Objective-C" # GNU + FAIL_REGEX "argument unused during compilation: .*" # Clang + ${_CheckOBJCCompilerFlag_COMMON_PATTERNS} + ) + foreach(v ${_CheckOBJCCompilerFlag_LOCALE_VARS}) + set(ENV{${v}} ${_CheckOBJCCompilerFlag_SAVED_${v}}) + unset(_CheckOBJCCompilerFlag_SAVED_${v}) + endforeach() + unset(_CheckOBJCCompilerFlag_LOCALE_VARS) + unset(_CheckOBJCCompilerFlag_COMMON_PATTERNS) + + set (CMAKE_REQUIRED_DEFINITIONS "${SAFE_CMAKE_REQUIRED_DEFINITIONS}") +endmacro () diff --git a/Modules/CheckOBJCSourceCompiles.cmake b/Modules/CheckOBJCSourceCompiles.cmake new file mode 100644 index 0000000..a4676ad --- /dev/null +++ b/Modules/CheckOBJCSourceCompiles.cmake @@ -0,0 +1,145 @@ +# Distributed under the OSI-approved BSD 3-Clause License. See accompanying +# file Copyright.txt or https://cmake.org/licensing for details. + +#[=======================================================================[.rst: +CheckOBJCSourceCompiles +----------------------- + +Check if given Objective-C source compiles and links into an executable. + +.. command:: check_objc_source_compiles + + .. code-block:: cmake + + check_objc_source_compiles(<code> <resultVar> + [FAIL_REGEX <regex1> [<regex2>...]]) + + Check that the source supplied in ``<code>`` can be compiled as a Objectie-C source + file and linked as an executable (so it must contain at least a ``main()`` + function). The result will be stored in the internal cache variable specified + by ``<resultVar>``, with a boolean true value for success and boolean false + for failure. If ``FAIL_REGEX`` is provided, then failure is determined by + checking if anything in the output matches any of the specified regular + expressions. + + The underlying check is performed by the :command:`try_compile` command. The + compile and link commands can be influenced by setting any of the following + variables prior to calling ``check_objc_source_compiles()``: + + ``CMAKE_REQUIRED_FLAGS`` + Additional flags to pass to the compiler. Note that the contents of + :variable:`CMAKE_OBJC_FLAGS <CMAKE_<LANG>_FLAGS>` and its associated + configuration-specific variable are automatically added to the compiler + command before the contents of ``CMAKE_REQUIRED_FLAGS``. + + ``CMAKE_REQUIRED_DEFINITIONS`` + A :ref:`;-list <CMake Language Lists>` of compiler definitions of the form + ``-DFOO`` or ``-DFOO=bar``. A definition for the name specified by + ``<resultVar>`` will also be added automatically. + + ``CMAKE_REQUIRED_INCLUDES`` + A :ref:`;-list <CMake Language Lists>` of header search paths to pass to + the compiler. These will be the only header search paths used by + ``try_compile()``, i.e. the contents of the :prop_dir:`INCLUDE_DIRECTORIES` + directory property will be ignored. + + ``CMAKE_REQUIRED_LINK_OPTIONS`` + A :ref:`;-list <CMake Language Lists>` of options to add to the link + command (see :command:`try_compile` for further details). + + ``CMAKE_REQUIRED_LIBRARIES`` + A :ref:`;-list <CMake Language Lists>` of libraries to add to the link + command. These can be the name of system libraries or they can be + :ref:`Imported Targets <Imported Targets>` (see :command:`try_compile` for + further details). + + ``CMAKE_REQUIRED_QUIET`` + If this variable evaluates to a boolean true value, all status messages + associated with the check will be suppressed. + + The check is only performed once, with the result cached in the variable + named by ``<resultVar>``. Every subsequent CMake run will re-use this cached + value rather than performing the check again, even if the ``<code>`` changes. + In order to force the check to be re-evaluated, the variable named by + ``<resultVar>`` must be manually removed from the cache. + +#]=======================================================================] + +include_guard(GLOBAL) + +macro(CHECK_OBJC_SOURCE_COMPILES SOURCE VAR) + if(NOT DEFINED "${VAR}") + set(_FAIL_REGEX) + set(_key) + foreach(arg ${ARGN}) + if("${arg}" MATCHES "^(FAIL_REGEX)$") + set(_key "${arg}") + elseif(_key) + list(APPEND _${_key} "${arg}") + else() + message(FATAL_ERROR "Unknown argument:\n ${arg}\n") + endif() + endforeach() + set(MACRO_CHECK_FUNCTION_DEFINITIONS + "-D${VAR} ${CMAKE_REQUIRED_FLAGS}") + if(CMAKE_REQUIRED_LINK_OPTIONS) + set(CHECK_OBJC_SOURCE_COMPILES_ADD_LINK_OPTIONS + LINK_OPTIONS ${CMAKE_REQUIRED_LINK_OPTIONS}) + else() + set(CHECK_OBJC_SOURCE_COMPILES_ADD_LINK_OPTIONS) + endif() + if(CMAKE_REQUIRED_LIBRARIES) + set(CHECK_OBJC_SOURCE_COMPILES_ADD_LIBRARIES + LINK_LIBRARIES ${CMAKE_REQUIRED_LIBRARIES}) + else() + set(CHECK_OBJC_SOURCE_COMPILES_ADD_LIBRARIES) + endif() + if(CMAKE_REQUIRED_INCLUDES) + set(CHECK_OBJC_SOURCE_COMPILES_ADD_INCLUDES + "-DINCLUDE_DIRECTORIES:STRING=${CMAKE_REQUIRED_INCLUDES}") + else() + set(CHECK_OBJC_SOURCE_COMPILES_ADD_INCLUDES) + endif() + file(WRITE "${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeTmp/src.m" + "${SOURCE}\n") + + if(NOT CMAKE_REQUIRED_QUIET) + message(STATUS "Performing Test ${VAR}") + endif() + try_compile(${VAR} + ${CMAKE_BINARY_DIR} + ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeTmp/src.m + COMPILE_DEFINITIONS ${CMAKE_REQUIRED_DEFINITIONS} + ${CHECK_OBJC_SOURCE_COMPILES_ADD_LINK_OPTIONS} + ${CHECK_OBJC_SOURCE_COMPILES_ADD_LIBRARIES} + CMAKE_FLAGS -DCOMPILE_DEFINITIONS:STRING=${MACRO_CHECK_FUNCTION_DEFINITIONS} + "${CHECK_OBJC_SOURCE_COMPILES_ADD_INCLUDES}" + OUTPUT_VARIABLE OUTPUT) + + foreach(_regex ${_FAIL_REGEX}) + if("${OUTPUT}" MATCHES "${_regex}") + set(${VAR} 0) + endif() + endforeach() + + if(${VAR}) + set(${VAR} 1 CACHE INTERNAL "Test ${VAR}") + if(NOT CMAKE_REQUIRED_QUIET) + message(STATUS "Performing Test ${VAR} - Success") + endif() + file(APPEND ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeOutput.log + "Performing Objective-C SOURCE FILE Test ${VAR} succeeded with the following output:\n" + "${OUTPUT}\n" + "Source file was:\n${SOURCE}\n") + else() + if(NOT CMAKE_REQUIRED_QUIET) + message(STATUS "Performing Test ${VAR} - Failed") + endif() + set(${VAR} "" CACHE INTERNAL "Test ${VAR}") + file(APPEND ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeError.log + "Performing Objective-C SOURCE FILE Test ${VAR} failed with the following output:\n" + "${OUTPUT}\n" + "Source file was:\n${SOURCE}\n") + endif() + endif() +endmacro() diff --git a/Modules/CheckOBJCSourceRuns.cmake b/Modules/CheckOBJCSourceRuns.cmake new file mode 100644 index 0000000..00a1ebd --- /dev/null +++ b/Modules/CheckOBJCSourceRuns.cmake @@ -0,0 +1,145 @@ +# Distributed under the OSI-approved BSD 3-Clause License. See accompanying +# file Copyright.txt or https://cmake.org/licensing for details. + +#[=======================================================================[.rst: +CheckOBJCSourceRuns +------------------- + +Check if given Objective-C source compiles and links into an executable and can +subsequently be run. + +.. command:: check_objc_source_runs + + .. code-block:: cmake + + check_objc_source_runs(<code> <resultVar>) + + Check that the source supplied in ``<code>`` can be compiled as a Objective-C source + file, linked as an executable and then run. The ``<code>`` must contain at + least a ``main()`` function. If the ``<code>`` could be built and run + successfully, the internal cache variable specified by ``<resultVar>`` will + be set to 1, otherwise it will be set to an value that evaluates to boolean + false (e.g. an empty string or an error message). + + The underlying check is performed by the :command:`try_run` command. The + compile and link commands can be influenced by setting any of the following + variables prior to calling ``check_objc_source_runs()``: + + ``CMAKE_REQUIRED_FLAGS`` + Additional flags to pass to the compiler. Note that the contents of + :variable:`CMAKE_OBJC_FLAGS <CMAKE_<LANG>_FLAGS>` and its associated + configuration-specific variable are automatically added to the compiler + command before the contents of ``CMAKE_REQUIRED_FLAGS``. + + ``CMAKE_REQUIRED_DEFINITIONS`` + A :ref:`;-list <CMake Language Lists>` of compiler definitions of the form + ``-DFOO`` or ``-DFOO=bar``. A definition for the name specified by + ``<resultVar>`` will also be added automatically. + + ``CMAKE_REQUIRED_INCLUDES`` + A :ref:`;-list <CMake Language Lists>` of header search paths to pass to + the compiler. These will be the only header search paths used by + ``try_run()``, i.e. the contents of the :prop_dir:`INCLUDE_DIRECTORIES` + directory property will be ignored. + + ``CMAKE_REQUIRED_LINK_OPTIONS`` + A :ref:`;-list <CMake Language Lists>` of options to add to the link + command (see :command:`try_run` for further details). + + ``CMAKE_REQUIRED_LIBRARIES`` + A :ref:`;-list <CMake Language Lists>` of libraries to add to the link + command. These can be the name of system libraries or they can be + :ref:`Imported Targets <Imported Targets>` (see :command:`try_run` for + further details). + + ``CMAKE_REQUIRED_QUIET`` + If this variable evaluates to a boolean true value, all status messages + associated with the check will be suppressed. + + The check is only performed once, with the result cached in the variable + named by ``<resultVar>``. Every subsequent CMake run will re-use this cached + value rather than performing the check again, even if the ``<code>`` changes. + In order to force the check to be re-evaluated, the variable named by + ``<resultVar>`` must be manually removed from the cache. + +#]=======================================================================] + +include_guard(GLOBAL) + +macro(CHECK_OBJC_SOURCE_RUNS SOURCE VAR) + if(NOT DEFINED "${VAR}") + set(MACRO_CHECK_FUNCTION_DEFINITIONS + "-D${VAR} ${CMAKE_REQUIRED_FLAGS}") + if(CMAKE_REQUIRED_LINK_OPTIONS) + set(CHECK_OBJC_SOURCE_COMPILES_ADD_LINK_OPTIONS + LINK_OPTIONS ${CMAKE_REQUIRED_LINK_OPTIONS}) + else() + set(CHECK_OBJC_SOURCE_COMPILES_ADD_LINK_OPTIONS) + endif() + if(CMAKE_REQUIRED_LIBRARIES) + set(CHECK_OBJC_SOURCE_COMPILES_ADD_LIBRARIES + LINK_LIBRARIES ${CMAKE_REQUIRED_LIBRARIES}) + else() + set(CHECK_OBJC_SOURCE_COMPILES_ADD_LIBRARIES) + endif() + if(CMAKE_REQUIRED_INCLUDES) + set(CHECK_OBJC_SOURCE_COMPILES_ADD_INCLUDES + "-DINCLUDE_DIRECTORIES:STRING=${CMAKE_REQUIRED_INCLUDES}") + else() + set(CHECK_OBJC_SOURCE_COMPILES_ADD_INCLUDES) + endif() + file(WRITE "${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeTmp/src.m" + "${SOURCE}\n") + + if(NOT CMAKE_REQUIRED_QUIET) + message(STATUS "Performing Test ${VAR}") + endif() + try_run(${VAR}_EXITCODE ${VAR}_COMPILED + ${CMAKE_BINARY_DIR} + ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeTmp/src.m + COMPILE_DEFINITIONS ${CMAKE_REQUIRED_DEFINITIONS} + ${CHECK_OBJC_SOURCE_COMPILES_ADD_LINK_OPTIONS} + ${CHECK_OBJC_SOURCE_COMPILES_ADD_LIBRARIES} + CMAKE_FLAGS -DCOMPILE_DEFINITIONS:STRING=${MACRO_CHECK_FUNCTION_DEFINITIONS} + -DCMAKE_SKIP_RPATH:BOOL=${CMAKE_SKIP_RPATH} + "${CHECK_OBJC_SOURCE_COMPILES_ADD_INCLUDES}" + COMPILE_OUTPUT_VARIABLE OUTPUT + RUN_OUTPUT_VARIABLE RUN_OUTPUT) + # if it did not compile make the return value fail code of 1 + if(NOT ${VAR}_COMPILED) + set(${VAR}_EXITCODE 1) + endif() + # if the return value was 0 then it worked + if("${${VAR}_EXITCODE}" EQUAL 0) + set(${VAR} 1 CACHE INTERNAL "Test ${VAR}") + if(NOT CMAKE_REQUIRED_QUIET) + message(STATUS "Performing Test ${VAR} - Success") + endif() + file(APPEND ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeOutput.log + "Performing Objective-C SOURCE FILE Test ${VAR} succeeded with the following compile output:\n" + "${OUTPUT}\n" + "...and run output:\n" + "${RUN_OUTPUT}\n" + "Return value: ${${VAR}}\n" + "Source file was:\n${SOURCE}\n") + else() + if(CMAKE_CROSSCOMPILING AND "${${VAR}_EXITCODE}" MATCHES "FAILED_TO_RUN") + set(${VAR} "${${VAR}_EXITCODE}") + else() + set(${VAR} "" CACHE INTERNAL "Test ${VAR}") + endif() + + if(NOT CMAKE_REQUIRED_QUIET) + message(STATUS "Performing Test ${VAR} - Failed") + endif() + file(APPEND ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeError.log + "Performing Objective-C SOURCE FILE Test ${VAR} failed with the following compile output:\n" + "${OUTPUT}\n" + "...and run output:\n" + "${RUN_OUTPUT}\n" + "Return value: ${${VAR}_EXITCODE}\n" + "Source file was:\n${SOURCE}\n") + + endif() + endif() +endmacro() diff --git a/Modules/CheckOBJCXXCompilerFlag.cmake b/Modules/CheckOBJCXXCompilerFlag.cmake new file mode 100644 index 0000000..c32741b --- /dev/null +++ b/Modules/CheckOBJCXXCompilerFlag.cmake @@ -0,0 +1,64 @@ +# Distributed under the OSI-approved BSD 3-Clause License. See accompanying +# file Copyright.txt or https://cmake.org/licensing for details. + +#[=======================================================================[.rst: +CheckOBJCXXCompilerFlag +----------------------- + +Check whether the Objective-C++ compiler supports a given flag. + +.. command:: check_objcxx_compiler_flag + + .. code-block:: cmake + + check_objcxx_compiler_flag(<flag> <var>) + + Check that the ``<flag>`` is accepted by the compiler without + a diagnostic. Stores the result in an internal cache entry + named ``<var>``. + +This command temporarily sets the ``CMAKE_REQUIRED_DEFINITIONS`` variable +and calls the ``check_objcxx_source_compiles`` macro from the +:module:`CheckOBJCXXSourceCompiles` module. See documentation of that +module for a listing of variables that can otherwise modify the build. + +A positive result from this check indicates only that the compiler did not +issue a diagnostic message when given the flag. Whether the flag has any +effect or even a specific one is beyond the scope of this module. + +.. note:: + Since the :command:`try_compile` command forwards flags from variables + like :variable:`CMAKE_OBJCXX_FLAGS <CMAKE_<LANG>_FLAGS>`, unknown flags + in such variables may cause a false negative for this check. +#]=======================================================================] + +include_guard(GLOBAL) +include(CheckOBJCXXSourceCompiles) +include(CMakeCheckCompilerFlagCommonPatterns) + +macro (CHECK_OBJCXX_COMPILER_FLAG _FLAG _RESULT) + set(SAFE_CMAKE_REQUIRED_DEFINITIONS "${CMAKE_REQUIRED_DEFINITIONS}") + set(CMAKE_REQUIRED_DEFINITIONS "${_FLAG}") + + # Normalize locale during test compilation. + set(_CheckOBJCXXCompilerFlag_LOCALE_VARS LC_ALL LC_MESSAGES LANG) + foreach(v ${_CheckOBJCXXCompilerFlag_LOCALE_VARS}) + set(_CheckOBJCXXCompilerFlag_SAVED_${v} "$ENV{${v}}") + set(ENV{${v}} OBJCXX) + endforeach() + CHECK_COMPILER_FLAG_COMMON_PATTERNS(_CheckOBJCXXCompilerFlag_COMMON_PATTERNS) + CHECK_OBJCXX_SOURCE_COMPILES("#ifndef __OBJC__\n# error \"Not an Objective-C++ compiler\"\n#endif\nint main(void) { return 0; }" ${_RESULT} + # Some compilers do not fail with a bad flag + FAIL_REGEX "command line option .* is valid for .* but not for Objective-C\\\\+\\\\+" # GNU + FAIL_REGEX "argument unused during compilation: .*" # Clang + ${_CheckOBJCXXCompilerFlag_COMMON_PATTERNS} + ) + foreach(v ${_CheckOBJCXXCompilerFlag_LOCALE_VARS}) + set(ENV{${v}} ${_CheckOBJCXXCompilerFlag_SAVED_${v}}) + unset(_CheckOBJCXXCompilerFlag_SAVED_${v}) + endforeach() + unset(_CheckOBJCXXCompilerFlag_LOCALE_VARS) + unset(_CheckOBJCXXCompilerFlag_COMMON_PATTERNS) + + set (CMAKE_REQUIRED_DEFINITIONS "${SAFE_CMAKE_REQUIRED_DEFINITIONS}") +endmacro () diff --git a/Modules/CheckOBJCXXSourceCompiles.cmake b/Modules/CheckOBJCXXSourceCompiles.cmake new file mode 100644 index 0000000..4c0fdd0 --- /dev/null +++ b/Modules/CheckOBJCXXSourceCompiles.cmake @@ -0,0 +1,146 @@ +# Distributed under the OSI-approved BSD 3-Clause License. See accompanying +# file Copyright.txt or https://cmake.org/licensing for details. + +#[=======================================================================[.rst: +CheckOBJCXXSourceCompiles +------------------------- + +Check if given Objective-C++ source compiles and links into an executable. + +.. command:: check_objcxx_source_compiles + + .. code-block:: cmake + + check_objcxx_source_compiles(<code> <resultVar> + [FAIL_REGEX <regex1> [<regex2>...]]) + + Check that the source supplied in ``<code>`` can be compiled as a Objective-C++ source + file and linked as an executable (so it must contain at least a ``main()`` + function). The result will be stored in the internal cache variable specified + by ``<resultVar>``, with a boolean true value for success and boolean false + for failure. If ``FAIL_REGEX`` is provided, then failure is determined by + checking if anything in the output matches any of the specified regular + expressions. + + The underlying check is performed by the :command:`try_compile` command. The + compile and link commands can be influenced by setting any of the following + variables prior to calling ``check_objcxx_source_compiles()``: + + ``CMAKE_REQUIRED_FLAGS`` + Additional flags to pass to the compiler. Note that the contents of + :variable:`CMAKE_OBJCXX_FLAGS <CMAKE_<LANG>_FLAGS>` and its associated + configuration-specific variable are automatically added to the compiler + command before the contents of ``CMAKE_REQUIRED_FLAGS``. + + ``CMAKE_REQUIRED_DEFINITIONS`` + A :ref:`;-list <CMake Language Lists>` of compiler definitions of the form + ``-DFOO`` or ``-DFOO=bar``. A definition for the name specified by + ``<resultVar>`` will also be added automatically. + + ``CMAKE_REQUIRED_INCLUDES`` + A :ref:`;-list <CMake Language Lists>` of header search paths to pass to + the compiler. These will be the only header search paths used by + ``try_compile()``, i.e. the contents of the :prop_dir:`INCLUDE_DIRECTORIES` + directory property will be ignored. + + ``CMAKE_REQUIRED_LINK_OPTIONS`` + A :ref:`;-list <CMake Language Lists>` of options to add to the link + command (see :command:`try_compile` for further details). + + ``CMAKE_REQUIRED_LIBRARIES`` + A :ref:`;-list <CMake Language Lists>` of libraries to add to the link + command. These can be the name of system libraries or they can be + :ref:`Imported Targets <Imported Targets>` (see :command:`try_compile` for + further details). + + ``CMAKE_REQUIRED_QUIET`` + If this variable evaluates to a boolean true value, all status messages + associated with the check will be suppressed. + + The check is only performed once, with the result cached in the variable + named by ``<resultVar>``. Every subsequent CMake run will re-use this cached + value rather than performing the check again, even if the ``<code>`` changes. + In order to force the check to be re-evaluated, the variable named by + ``<resultVar>`` must be manually removed from the cache. + +#]=======================================================================] + +include_guard(GLOBAL) + +macro(CHECK_OBJCXX_SOURCE_COMPILES SOURCE VAR) + if(NOT DEFINED "${VAR}") + set(_FAIL_REGEX) + set(_key) + foreach(arg ${ARGN}) + if("${arg}" MATCHES "^(FAIL_REGEX)$") + set(_key "${arg}") + elseif(_key) + list(APPEND _${_key} "${arg}") + else() + message(FATAL_ERROR "Unknown argument:\n ${arg}\n") + endif() + endforeach() + + set(MACRO_CHECK_FUNCTION_DEFINITIONS + "-D${VAR} ${CMAKE_REQUIRED_FLAGS}") + if(CMAKE_REQUIRED_LINK_OPTIONS) + set(CHECK_OBJCXX_SOURCE_COMPILES_ADD_LINK_OPTIONS + LINK_OPTIONS ${CMAKE_REQUIRED_LINK_OPTIONS}) + else() + set(CHECK_OBJCXX_SOURCE_COMPILES_ADD_LINK_OPTIONS) + endif() + if(CMAKE_REQUIRED_LIBRARIES) + set(CHECK_OBJCXX_SOURCE_COMPILES_ADD_LIBRARIES + LINK_LIBRARIES ${CMAKE_REQUIRED_LIBRARIES}) + else() + set(CHECK_OBJCXX_SOURCE_COMPILES_ADD_LIBRARIES) + endif() + if(CMAKE_REQUIRED_INCLUDES) + set(CHECK_OBJCXX_SOURCE_COMPILES_ADD_INCLUDES + "-DINCLUDE_DIRECTORIES:STRING=${CMAKE_REQUIRED_INCLUDES}") + else() + set(CHECK_OBJCXX_SOURCE_COMPILES_ADD_INCLUDES) + endif() + file(WRITE "${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeTmp/src.mm" + "${SOURCE}\n") + + if(NOT CMAKE_REQUIRED_QUIET) + message(STATUS "Performing Test ${VAR}") + endif() + try_compile(${VAR} + ${CMAKE_BINARY_DIR} + ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeTmp/src.mm + COMPILE_DEFINITIONS ${CMAKE_REQUIRED_DEFINITIONS} + ${CHECK_OBJCXX_SOURCE_COMPILES_ADD_LINK_OPTIONS} + ${CHECK_OBJCXX_SOURCE_COMPILES_ADD_LIBRARIES} + CMAKE_FLAGS -DCOMPILE_DEFINITIONS:STRING=${MACRO_CHECK_FUNCTION_DEFINITIONS} + "${CHECK_OBJCXX_SOURCE_COMPILES_ADD_INCLUDES}" + OUTPUT_VARIABLE OUTPUT) + + foreach(_regex ${_FAIL_REGEX}) + if("${OUTPUT}" MATCHES "${_regex}") + set(${VAR} 0) + endif() + endforeach() + + if(${VAR}) + set(${VAR} 1 CACHE INTERNAL "Test ${VAR}") + if(NOT CMAKE_REQUIRED_QUIET) + message(STATUS "Performing Test ${VAR} - Success") + endif() + file(APPEND ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeOutput.log + "Performing Objective-C++ SOURCE FILE Test ${VAR} succeeded with the following output:\n" + "${OUTPUT}\n" + "Source file was:\n${SOURCE}\n") + else() + if(NOT CMAKE_REQUIRED_QUIET) + message(STATUS "Performing Test ${VAR} - Failed") + endif() + set(${VAR} "" CACHE INTERNAL "Test ${VAR}") + file(APPEND ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeError.log + "Performing Objective-C++ SOURCE FILE Test ${VAR} failed with the following output:\n" + "${OUTPUT}\n" + "Source file was:\n${SOURCE}\n") + endif() + endif() +endmacro() diff --git a/Modules/CheckOBJCXXSourceRuns.cmake b/Modules/CheckOBJCXXSourceRuns.cmake new file mode 100644 index 0000000..a3d5923 --- /dev/null +++ b/Modules/CheckOBJCXXSourceRuns.cmake @@ -0,0 +1,145 @@ +# Distributed under the OSI-approved BSD 3-Clause License. See accompanying +# file Copyright.txt or https://cmake.org/licensing for details. + +#[=======================================================================[.rst: +CheckOBJCXXSourceRuns +--------------------- + +Check if given Objective-C++ source compiles and links into an executable and can +subsequently be run. + +.. command:: check_objcxx_source_runs + + .. code-block:: cmake + + check_objcxx_source_runs(<code> <resultVar>) + + Check that the source supplied in ``<code>`` can be compiled as a Objective-C++ source + file, linked as an executable and then run. The ``<code>`` must contain at + least a ``main()`` function. If the ``<code>`` could be built and run + successfully, the internal cache variable specified by ``<resultVar>`` will + be set to 1, otherwise it will be set to an value that evaluates to boolean + false (e.g. an empty string or an error message). + + The underlying check is performed by the :command:`try_run` command. The + compile and link commands can be influenced by setting any of the following + variables prior to calling ``check_objcxx_source_runs()``: + + ``CMAKE_REQUIRED_FLAGS`` + Additional flags to pass to the compiler. Note that the contents of + :variable:`CMAKE_OBJCXX_FLAGS <CMAKE_<LANG>_FLAGS>` and its associated + configuration-specific variable are automatically added to the compiler + command before the contents of ``CMAKE_REQUIRED_FLAGS``. + + ``CMAKE_REQUIRED_DEFINITIONS`` + A :ref:`;-list <CMake Language Lists>` of compiler definitions of the form + ``-DFOO`` or ``-DFOO=bar``. A definition for the name specified by + ``<resultVar>`` will also be added automatically. + + ``CMAKE_REQUIRED_INCLUDES`` + A :ref:`;-list <CMake Language Lists>` of header search paths to pass to + the compiler. These will be the only header search paths used by + ``try_run()``, i.e. the contents of the :prop_dir:`INCLUDE_DIRECTORIES` + directory property will be ignored. + + ``CMAKE_REQUIRED_LINK_OPTIONS`` + A :ref:`;-list <CMake Language Lists>` of options to add to the link + command (see :command:`try_run` for further details). + + ``CMAKE_REQUIRED_LIBRARIES`` + A :ref:`;-list <CMake Language Lists>` of libraries to add to the link + command. These can be the name of system libraries or they can be + :ref:`Imported Targets <Imported Targets>` (see :command:`try_run` for + further details). + + ``CMAKE_REQUIRED_QUIET`` + If this variable evaluates to a boolean true value, all status messages + associated with the check will be suppressed. + + The check is only performed once, with the result cached in the variable + named by ``<resultVar>``. Every subsequent CMake run will re-use this cached + value rather than performing the check again, even if the ``<code>`` changes. + In order to force the check to be re-evaluated, the variable named by + ``<resultVar>`` must be manually removed from the cache. + +#]=======================================================================] + +include_guard(GLOBAL) + +macro(CHECK_OBJCXX_SOURCE_RUNS SOURCE VAR) + if(NOT DEFINED "${VAR}") + set(MACRO_CHECK_FUNCTION_DEFINITIONS + "-D${VAR} ${CMAKE_REQUIRED_FLAGS}") + if(CMAKE_REQUIRED_LINK_OPTIONS) + set(CHECK_OBJCXX_SOURCE_COMPILES_ADD_LINK_OPTIONS + LINK_OPTIONS ${CMAKE_REQUIRED_LINK_OPTIONS}) + else() + set(CHECK_OBJCXX_SOURCE_COMPILES_ADD_LINK_OPTIONS) + endif() + if(CMAKE_REQUIRED_LIBRARIES) + set(CHECK_OBJCXX_SOURCE_COMPILES_ADD_LIBRARIES + LINK_LIBRARIES ${CMAKE_REQUIRED_LIBRARIES}) + else() + set(CHECK_OBJCXX_SOURCE_COMPILES_ADD_LIBRARIES) + endif() + if(CMAKE_REQUIRED_INCLUDES) + set(CHECK_OBJCXX_SOURCE_COMPILES_ADD_INCLUDES + "-DINCLUDE_DIRECTORIES:STRING=${CMAKE_REQUIRED_INCLUDES}") + else() + set(CHECK_OBJCXX_SOURCE_COMPILES_ADD_INCLUDES) + endif() + file(WRITE "${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeTmp/src.mm" + "${SOURCE}\n") + + if(NOT CMAKE_REQUIRED_QUIET) + message(STATUS "Performing Test ${VAR}") + endif() + try_run(${VAR}_EXITCODE ${VAR}_COMPILED + ${CMAKE_BINARY_DIR} + ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeTmp/src.mm + COMPILE_DEFINITIONS ${CMAKE_REQUIRED_DEFINITIONS} + ${CHECK_OBJCXX_SOURCE_COMPILES_ADD_LINK_OPTIONS} + ${CHECK_OBJCXX_SOURCE_COMPILES_ADD_LIBRARIES} + CMAKE_FLAGS -DCOMPILE_DEFINITIONS:STRING=${MACRO_CHECK_FUNCTION_DEFINITIONS} + -DCMAKE_SKIP_RPATH:BOOL=${CMAKE_SKIP_RPATH} + "${CHECK_OBJCXX_SOURCE_COMPILES_ADD_INCLUDES}" + COMPILE_OUTPUT_VARIABLE OUTPUT + RUN_OUTPUT_VARIABLE RUN_OUTPUT) + + # if it did not compile make the return value fail code of 1 + if(NOT ${VAR}_COMPILED) + set(${VAR}_EXITCODE 1) + endif() + # if the return value was 0 then it worked + if("${${VAR}_EXITCODE}" EQUAL 0) + set(${VAR} 1 CACHE INTERNAL "Test ${VAR}") + if(NOT CMAKE_REQUIRED_QUIET) + message(STATUS "Performing Test ${VAR} - Success") + endif() + file(APPEND ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeOutput.log + "Performing Objective-C++ SOURCE FILE Test ${VAR} succeeded with the following output:\n" + "${OUTPUT}\n" + "...and run output:\n" + "${RUN_OUTPUT}\n" + "Return value: ${${VAR}}\n" + "Source file was:\n${SOURCE}\n") + else() + if(CMAKE_CROSSCOMPILING AND "${${VAR}_EXITCODE}" MATCHES "FAILED_TO_RUN") + set(${VAR} "${${VAR}_EXITCODE}") + else() + set(${VAR} "" CACHE INTERNAL "Test ${VAR}") + endif() + + if(NOT CMAKE_REQUIRED_QUIET) + message(STATUS "Performing Test ${VAR} - Failed") + endif() + file(APPEND ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeError.log + "Performing Objective-C++ SOURCE FILE Test ${VAR} failed with the following output:\n" + "${OUTPUT}\n" + "...and run output:\n" + "${RUN_OUTPUT}\n" + "Return value: ${${VAR}_EXITCODE}\n" + "Source file was:\n${SOURCE}\n") + endif() + endif() +endmacro() diff --git a/Modules/Compiler/AppleClang-OBJC.cmake b/Modules/Compiler/AppleClang-OBJC.cmake new file mode 100644 index 0000000..d1f3706 --- /dev/null +++ b/Modules/Compiler/AppleClang-OBJC.cmake @@ -0,0 +1,17 @@ +include(Compiler/Clang-OBJC) + +if(NOT CMAKE_OBJC_COMPILER_VERSION VERSION_LESS 4.0) + set(CMAKE_OBJC90_STANDARD_COMPILE_OPTION "-std=c90") + set(CMAKE_OBJC90_EXTENSION_COMPILE_OPTION "-std=gnu90") + set(CMAKE_OBJC90_STANDARD__HAS_FULL_SUPPORT ON) + + set(CMAKE_OBJC99_STANDARD_COMPILE_OPTION "-std=c99") + set(CMAKE_OBJC99_EXTENSION_COMPILE_OPTION "-std=gnu99") + set(CMAKE_OBJC99_STANDARD__HAS_FULL_SUPPORT ON) + + set(CMAKE_OBJC11_STANDARD_COMPILE_OPTION "-std=c11") + set(CMAKE_OBJC11_EXTENSION_COMPILE_OPTION "-std=gnu11") + set(CMAKE_OBJC11_STANDARD__HAS_FULL_SUPPORT ON) +endif() + +__compiler_check_default_language_standard(OBJC 4.0 99) diff --git a/Modules/Compiler/AppleClang-OBJCXX.cmake b/Modules/Compiler/AppleClang-OBJCXX.cmake new file mode 100644 index 0000000..7c6f763 --- /dev/null +++ b/Modules/Compiler/AppleClang-OBJCXX.cmake @@ -0,0 +1,37 @@ +include(Compiler/Clang-OBJCXX) + +if(NOT CMAKE_OBJCXX_COMPILER_VERSION VERSION_LESS 4.0) + set(CMAKE_OBJCXX98_STANDARD_COMPILE_OPTION "-std=c++98") + set(CMAKE_OBJCXX98_EXTENSION_COMPILE_OPTION "-std=gnu++98") + set(CMAKE_OBJCXX98_STANDARD__HAS_FULL_SUPPORT ON) + + set(CMAKE_OBJCXX11_STANDARD_COMPILE_OPTION "-std=c++11") + set(CMAKE_OBJCXX11_EXTENSION_COMPILE_OPTION "-std=gnu++11") +endif() + +if(NOT CMAKE_OBJCXX_COMPILER_VERSION VERSION_LESS 6.1) + set(CMAKE_OBJCXX14_STANDARD_COMPILE_OPTION "-std=c++14") + set(CMAKE_OBJCXX14_EXTENSION_COMPILE_OPTION "-std=gnu++14") + set(CMAKE_OBJCXX14_STANDARD__HAS_FULL_SUPPORT ON) +elseif(NOT CMAKE_OBJCXX_COMPILER_VERSION VERSION_LESS 5.1) + # AppleClang 5.0 knows this flag, but does not set a __cplusplus macro greater than 201103L + set(CMAKE_OBJCXX14_STANDARD_COMPILE_OPTION "-std=c++1y") + set(CMAKE_OBJCXX14_EXTENSION_COMPILE_OPTION "-std=gnu++1y") + set(CMAKE_OBJCXX14_STANDARD__HAS_FULL_SUPPORT ON) +endif() + +if (NOT CMAKE_OBJCXX_COMPILER_VERSION VERSION_LESS 6.1) + set(CMAKE_OBJCXX17_STANDARD_COMPILE_OPTION "-std=c++1z") + set(CMAKE_OBJCXX17_EXTENSION_COMPILE_OPTION "-std=gnu++1z") +endif() + +if (NOT CMAKE_OBJCXX_COMPILER_VERSION VERSION_LESS 8.0) + set(CMAKE_OBJCXX11_STANDARD__HAS_FULL_SUPPORT ON) +endif() + +if (NOT CMAKE_OBJCXX_COMPILER_VERSION VERSION_LESS 10.0) + set(CMAKE_OBJCXX20_STANDARD_COMPILE_OPTION "-std=c++2a") + set(CMAKE_OBJCXX20_EXTENSION_COMPILE_OPTION "-std=gnu++2a") +endif() + +__compiler_check_default_language_standard(OBJCXX 4.0 98) diff --git a/Modules/Compiler/Clang-OBJC.cmake b/Modules/Compiler/Clang-OBJC.cmake new file mode 100644 index 0000000..c61c497 --- /dev/null +++ b/Modules/Compiler/Clang-OBJC.cmake @@ -0,0 +1,18 @@ +include(Compiler/Clang) +__compiler_clang(OBJC) + +if(NOT CMAKE_OBJC_COMPILER_VERSION VERSION_LESS 3.4) + set(CMAKE_OBJC90_STANDARD_COMPILE_OPTION "-std=c90") + set(CMAKE_OBJC90_EXTENSION_COMPILE_OPTION "-std=gnu90") + set(CMAKE_OBJC90_STANDARD__HAS_FULL_SUPPORT ON) + + set(CMAKE_OBJC99_STANDARD_COMPILE_OPTION "-std=c99") + set(CMAKE_OBJC99_EXTENSION_COMPILE_OPTION "-std=gnu99") + set(CMAKE_OBJC99_STANDARD__HAS_FULL_SUPPORT ON) + + set(CMAKE_OBJC11_STANDARD_COMPILE_OPTION "-std=c11") + set(CMAKE_OBJC11_EXTENSION_COMPILE_OPTION "-std=gnu11") + set(CMAKE_OBJC11_STANDARD__HAS_FULL_SUPPORT ON) +endif() + +__compiler_check_default_language_standard(OBJC 3.4 99 3.6 11) diff --git a/Modules/Compiler/Clang-OBJCXX.cmake b/Modules/Compiler/Clang-OBJCXX.cmake new file mode 100644 index 0000000..b01ce64 --- /dev/null +++ b/Modules/Compiler/Clang-OBJCXX.cmake @@ -0,0 +1,70 @@ +include(Compiler/Clang) +__compiler_clang(OBJCXX) + +if("x${CMAKE_OBJCXX_COMPILER_FRONTEND_VARIANT}" STREQUAL "xGNU") + if(NOT CMAKE_OBJCXX_COMPILER_VERSION VERSION_LESS 2.1) + set(CMAKE_OBJCXX98_STANDARD_COMPILE_OPTION "-std=c++98") + set(CMAKE_OBJCXX98_EXTENSION_COMPILE_OPTION "-std=gnu++98") + endif() + + if(NOT CMAKE_OBJCXX_COMPILER_VERSION VERSION_LESS 3.1) + set(CMAKE_OBJCXX98_STANDARD__HAS_FULL_SUPPORT ON) + set(CMAKE_OBJCXX11_STANDARD_COMPILE_OPTION "-std=c++11") + set(CMAKE_OBJCXX11_EXTENSION_COMPILE_OPTION "-std=gnu++11") + set(CMAKE_OBJCXX11_STANDARD__HAS_FULL_SUPPORT ON) + elseif(NOT CMAKE_OBJCXX_COMPILER_VERSION VERSION_LESS 2.1) + set(CMAKE_OBJCXX11_STANDARD_COMPILE_OPTION "-std=c++0x") + set(CMAKE_OBJCXX11_EXTENSION_COMPILE_OPTION "-std=gnu++0x") + endif() + + if(NOT CMAKE_OBJCXX_COMPILER_VERSION VERSION_LESS 3.5) + set(CMAKE_OBJCXX14_STANDARD_COMPILE_OPTION "-std=c++14") + set(CMAKE_OBJCXX14_EXTENSION_COMPILE_OPTION "-std=gnu++14") + set(CMAKE_OBJCXX14_STANDARD__HAS_FULL_SUPPORT ON) + elseif(NOT CMAKE_OBJCXX_COMPILER_VERSION VERSION_LESS 3.4) + set(CMAKE_OBJCXX14_STANDARD_COMPILE_OPTION "-std=c++1y") + set(CMAKE_OBJCXX14_EXTENSION_COMPILE_OPTION "-std=gnu++1y") + set(CMAKE_OBJCXX14_STANDARD__HAS_FULL_SUPPORT ON) + endif() + + set(_clang_version_std17 5.0) + + if (NOT CMAKE_OBJCXX_COMPILER_VERSION VERSION_LESS "${_clang_version_std17}") + set(CMAKE_OBJCXX17_STANDARD_COMPILE_OPTION "-std=c++17") + set(CMAKE_OBJCXX17_EXTENSION_COMPILE_OPTION "-std=gnu++17") + elseif (NOT CMAKE_OBJCXX_COMPILER_VERSION VERSION_LESS 3.5) + set(CMAKE_OBJCXX17_STANDARD_COMPILE_OPTION "-std=c++1z") + set(CMAKE_OBJCXX17_EXTENSION_COMPILE_OPTION "-std=gnu++1z") + endif() + + if (NOT CMAKE_OBJCXX_COMPILER_VERSION VERSION_LESS "${_clang_version_std17}") + set(CMAKE_OBJCXX20_STANDARD_COMPILE_OPTION "-std=c++2a") + set(CMAKE_OBJCXX20_EXTENSION_COMPILE_OPTION "-std=gnu++2a") + endif() + + unset(_clang_version_std17) + + __compiler_check_default_language_standard(OBJCXX 2.1 98) +elseif(CMAKE_OBJCXX_COMPILER_VERSION VERSION_GREATER_EQUAL 3.9 + AND CMAKE_OBJCXX_SIMULATE_VERSION VERSION_GREATER_EQUAL 19.0) + # This version of clang-cl and the MSVC version it simulates have + # support for -std: flags. + set(CMAKE_OBJCXX98_STANDARD_COMPILE_OPTION "") + set(CMAKE_OBJCXX98_EXTENSION_COMPILE_OPTION "") + set(CMAKE_OBJCXX98_STANDARD__HAS_FULL_SUPPORT ON) + set(CMAKE_OBJCXX11_STANDARD_COMPILE_OPTION "") + set(CMAKE_OBJCXX11_EXTENSION_COMPILE_OPTION "") + set(CMAKE_OBJCXX14_STANDARD_COMPILE_OPTION "-std:c++14") + set(CMAKE_OBJCXX14_EXTENSION_COMPILE_OPTION "-std:c++14") + if (CMAKE_OBJCXX_COMPILER_VERSION VERSION_GREATER_EQUAL 6.0) + set(CMAKE_OBJCXX17_STANDARD_COMPILE_OPTION "-std:c++17") + set(CMAKE_OBJCXX17_EXTENSION_COMPILE_OPTION "-std:c++17") + set(CMAKE_OBJCXX20_STANDARD_COMPILE_OPTION "-std:c++latest") + set(CMAKE_OBJCXX20_EXTENSION_COMPILE_OPTION "-std:c++latest") + else() + set(CMAKE_OBJCXX17_STANDARD_COMPILE_OPTION "-std:c++latest") + set(CMAKE_OBJCXX17_EXTENSION_COMPILE_OPTION "-std:c++latest") + endif() + + __compiler_check_default_language_standard(OBJCXX 3.9 14) +endif() diff --git a/Modules/Compiler/GNU-FindBinUtils.cmake b/Modules/Compiler/GNU-FindBinUtils.cmake index 097fbf3..a47b7ad 100644 --- a/Modules/Compiler/GNU-FindBinUtils.cmake +++ b/Modules/Compiler/GNU-FindBinUtils.cmake @@ -15,21 +15,27 @@ string(REGEX MATCH "^([0-9]+\\.[0-9]+)" __version_x_y get_filename_component(__gcc_hints "${CMAKE_${_CMAKE_PROCESSING_LANGUAGE}_COMPILER}" DIRECTORY) # http://manpages.ubuntu.com/manpages/wily/en/man1/gcc-ar.1.html -find_program(CMAKE_${_CMAKE_PROCESSING_LANGUAGE}_COMPILER_AR NAMES - "${_CMAKE_TOOLCHAIN_PREFIX}gcc-ar-${__version_x_y}" - "${_CMAKE_TOOLCHAIN_PREFIX}gcc-ar-${__version_x}" - "${_CMAKE_TOOLCHAIN_PREFIX}gcc-ar${_CMAKE_COMPILER_SUFFIX}" - HINTS ${__gcc_hints} +__get_compiler_component( + CMAKE_${_CMAKE_PROCESSING_LANGUAGE}_COMPILER_AR gcc-ar + HINTS + ${__gcc_hints} + NAMES + "${_CMAKE_TOOLCHAIN_PREFIX}gcc-ar-${__version_x_y}" + "${_CMAKE_TOOLCHAIN_PREFIX}gcc-ar-${__version_x}" + "${_CMAKE_TOOLCHAIN_PREFIX}gcc-ar" DOC "A wrapper around 'ar' adding the appropriate '--plugin' option for the GCC compiler" ) mark_as_advanced(CMAKE_${_CMAKE_PROCESSING_LANGUAGE}_COMPILER_AR) # http://manpages.ubuntu.com/manpages/wily/en/man1/gcc-ranlib.1.html -find_program(CMAKE_${_CMAKE_PROCESSING_LANGUAGE}_COMPILER_RANLIB NAMES - "${_CMAKE_TOOLCHAIN_PREFIX}gcc-ranlib-${__version_x_y}" - "${_CMAKE_TOOLCHAIN_PREFIX}gcc-ranlib-${__version_x}" - "${_CMAKE_TOOLCHAIN_PREFIX}gcc-ranlib${_CMAKE_COMPILER_SUFFIX}" - HINTS ${__gcc_hints} +__get_compiler_component( + CMAKE_${_CMAKE_PROCESSING_LANGUAGE}_COMPILER_RANLIB gcc-ranlib + HINTS + ${__gcc_hints} + NAMES + "${_CMAKE_TOOLCHAIN_PREFIX}gcc-ranlib-${__version_x_y}" + "${_CMAKE_TOOLCHAIN_PREFIX}gcc-ranlib-${__version_x}" + "${_CMAKE_TOOLCHAIN_PREFIX}gcc-ranlib" DOC "A wrapper around 'ranlib' adding the appropriate '--plugin' option for the GCC compiler" ) mark_as_advanced(CMAKE_${_CMAKE_PROCESSING_LANGUAGE}_COMPILER_RANLIB) diff --git a/Modules/Compiler/GNU-OBJC.cmake b/Modules/Compiler/GNU-OBJC.cmake new file mode 100644 index 0000000..5fba801 --- /dev/null +++ b/Modules/Compiler/GNU-OBJC.cmake @@ -0,0 +1,6 @@ +include(Compiler/GNU) +__compiler_gnu(OBJC) + +if(NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 4.2) + set(CMAKE_CXX_COMPILE_OPTIONS_VISIBILITY_INLINES_HIDDEN "-fvisibility-inlines-hidden") +endif() diff --git a/Modules/Compiler/GNU-OBJCXX.cmake b/Modules/Compiler/GNU-OBJCXX.cmake new file mode 100644 index 0000000..66a547e --- /dev/null +++ b/Modules/Compiler/GNU-OBJCXX.cmake @@ -0,0 +1,10 @@ +include(Compiler/GNU) +__compiler_gnu(OBJC) + +if(NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 4.2) + set(CMAKE_CXX_COMPILE_OPTIONS_VISIBILITY_INLINES_HIDDEN "-fvisibility-inlines-hidden") +endif() + +if(NOT CMAKE_OBJCXX_LINK_FLAGS) + set(CMAKE_OBCXX_LINK_FLAGS "-lstdc++") +endif() diff --git a/Modules/Compiler/IAR-ASM.cmake b/Modules/Compiler/IAR-ASM.cmake index 437678e..413d9e2 100644 --- a/Modules/Compiler/IAR-ASM.cmake +++ b/Modules/Compiler/IAR-ASM.cmake @@ -37,6 +37,11 @@ elseif("${CMAKE_ASM${ASM_DIALECT}_COMPILER_ARCHITECTURE_ID}" STREQUAL "MSP430") __compiler_iar_xlink(ASM) set(CMAKE_ASM_SOURCE_FILE_EXTENSIONS s43;asm;msa) +elseif("${CMAKE_ASM${ASM_DIALECT}_COMPILER_ARCHITECTURE_ID}" STREQUAL "V850") + set(CMAKE_ASM_COMPILE_OBJECT "<CMAKE_ASM_COMPILER> -S <SOURCE> <DEFINES> <INCLUDES> <FLAGS> -o <OBJECT>") + __compiler_iar_xlink(ASM) + set(CMAKE_ASM_SOURCE_FILE_EXTENSIONS r85;asm;msa) + else() message(FATAL_ERROR "CMAKE_ASM${ASM_DIALECT}_COMPILER_ARCHITECTURE_ID not detected. This should be automatic.") endif() diff --git a/Modules/FindOpenSSL.cmake b/Modules/FindOpenSSL.cmake index 5f947fe..33ceab7 100644 --- a/Modules/FindOpenSSL.cmake +++ b/Modules/FindOpenSSL.cmake @@ -35,10 +35,14 @@ This module will set the following variables in your project: The OpenSSL include directory. ``OPENSSL_CRYPTO_LIBRARY`` The OpenSSL crypto library. +``OPENSSL_CRYPTO_LIBRARIES`` + The OpenSSL crypto library and its dependencies. ``OPENSSL_SSL_LIBRARY`` The OpenSSL SSL library. +``OPENSSL_SSL_LIBRARIES`` + The OpenSSL SSL library and its dependencies. ``OPENSSL_LIBRARIES`` - All OpenSSL libraries. + All OpenSSL libraries and their dependencies. ``OPENSSL_VERSION`` This is set to ``$major.$minor.$revision$patch`` (e.g. ``0.9.8s``). @@ -50,6 +54,32 @@ Set ``OPENSSL_USE_STATIC_LIBS`` to ``TRUE`` to look for static libraries. Set ``OPENSSL_MSVC_STATIC_RT`` set ``TRUE`` to choose the MT version of the lib. #]=======================================================================] +macro(_OpenSSL_test_and_find_dependencies ssl_library crypto_library) + if((CMAKE_SYSTEM_NAME STREQUAL "Linux") AND + (("${ssl_library}" MATCHES "\\${CMAKE_STATIC_LIBRARY_SUFFIX}$") OR + ("${crypto_library}" MATCHES "\\${CMAKE_STATIC_LIBRARY_SUFFIX}$"))) + set(_OpenSSL_has_dependencies TRUE) + find_package(Threads) + else() + set(_OpenSSL_has_dependencies FALSE) + endif() +endmacro() + +function(_OpenSSL_add_dependencies libraries_var library) + if(CMAKE_THREAD_LIBS_INIT) + list(APPEND ${libraries_var} ${CMAKE_THREAD_LIBS_INIT}) + endif() + list(APPEND ${libraries_var} ${CMAKE_DL_LIBS}) + set(${libraries_var} ${${libraries_var}} PARENT_SCOPE) +endfunction() + +function(_OpenSSL_target_add_dependencies target) + if(_OpenSSL_has_dependencies) + set_property( TARGET ${target} APPEND PROPERTY INTERFACE_LINK_LIBRARIES Threads::Threads ) + set_property( TARGET ${target} APPEND PROPERTY INTERFACE_LINK_LIBRARIES ${CMAKE_DL_LIBS} ) + endif() +endfunction() + if (UNIX) find_package(PkgConfig QUIET) pkg_check_modules(_OPENSSL QUIET openssl) @@ -306,10 +336,15 @@ else() mark_as_advanced(OPENSSL_CRYPTO_LIBRARY OPENSSL_SSL_LIBRARY) - # compat defines - set(OPENSSL_SSL_LIBRARIES ${OPENSSL_SSL_LIBRARY}) - set(OPENSSL_CRYPTO_LIBRARIES ${OPENSSL_CRYPTO_LIBRARY}) +endif() +# compat defines +set(OPENSSL_SSL_LIBRARIES ${OPENSSL_SSL_LIBRARY}) +set(OPENSSL_CRYPTO_LIBRARIES ${OPENSSL_CRYPTO_LIBRARY}) +_OpenSSL_test_and_find_dependencies("${OPENSSL_SSL_LIBRARY}" "${OPENSSL_CRYPTO_LIBRARY}") +if(_OpenSSL_has_dependencies) + _OpenSSL_add_dependencies( OPENSSL_SSL_LIBRARIES "${OPENSSL_SSL_LIBRARY}" ) + _OpenSSL_add_dependencies( OPENSSL_CRYPTO_LIBRARIES "${OPENSSL_CRYPTO_LIBRARY}" ) endif() function(from_hex HEX DEC) @@ -379,7 +414,8 @@ if(OPENSSL_INCLUDE_DIR AND EXISTS "${OPENSSL_INCLUDE_DIR}/openssl/opensslv.h") endif () endif () -set(OPENSSL_LIBRARIES ${OPENSSL_SSL_LIBRARY} ${OPENSSL_CRYPTO_LIBRARY} ) +set(OPENSSL_LIBRARIES ${OPENSSL_SSL_LIBRARIES} ${OPENSSL_CRYPTO_LIBRARIES} ) +list(REMOVE_DUPLICATES OPENSSL_LIBRARIES) foreach(_comp IN LISTS OpenSSL_FIND_COMPONENTS) if(_comp STREQUAL "Crypto") @@ -451,6 +487,7 @@ if(OPENSSL_FOUND) IMPORTED_LINK_INTERFACE_LANGUAGES_DEBUG "C" IMPORTED_LOCATION_DEBUG "${LIB_EAY_LIBRARY_DEBUG}") endif() + _OpenSSL_target_add_dependencies(OpenSSL::Crypto) endif() if(NOT TARGET OpenSSL::SSL AND @@ -484,6 +521,7 @@ if(OPENSSL_FOUND) set_target_properties(OpenSSL::SSL PROPERTIES INTERFACE_LINK_LIBRARIES OpenSSL::Crypto) endif() + _OpenSSL_target_add_dependencies(OpenSSL::SSL) endif() endif() diff --git a/Modules/FindPackageHandleStandardArgs.cmake b/Modules/FindPackageHandleStandardArgs.cmake index a2999fc..d824ee8 100644 --- a/Modules/FindPackageHandleStandardArgs.cmake +++ b/Modules/FindPackageHandleStandardArgs.cmake @@ -27,6 +27,7 @@ valid filepaths. [VERSION_VAR <version-var>] [HANDLE_COMPONENTS] [CONFIG_MODE] + [REASON_FAILURE_MESSAGE <reason-failure-message>] [FAIL_MESSAGE <custom-failure-message>] ) @@ -81,6 +82,10 @@ valid filepaths. will automatically check whether the package configuration file was found. + ``REASON_FAILURE_MESSAGE <reason-failure-message>`` + Specify a custom message of the reason for the failure which will be + appended to the default generated message. + ``FAIL_MESSAGE <custom-failure-message>`` Specify a custom failure message instead of using the default generated message. Not recommended. @@ -133,11 +138,15 @@ include(${CMAKE_CURRENT_LIST_DIR}/FindPackageMessage.cmake) # internal helper macro macro(_FPHSA_FAILURE_MESSAGE _msg) + set (__msg "${_msg}") + if (FPHSA_REASON_FAILURE_MESSAGE) + string(APPEND __msg "\n Reason given by package: ${FPHSA_REASON_FAILURE_MESSAGE}\n") + endif() if (${_NAME}_FIND_REQUIRED) - message(FATAL_ERROR "${_msg}") + message(FATAL_ERROR "${__msg}") else () if (NOT ${_NAME}_FIND_QUIETLY) - message(STATUS "${_msg}") + message(STATUS "${__msg}") endif () endif () endmacro() @@ -158,12 +167,18 @@ macro(_FPHSA_HANDLE_FAILURE_CONFIG_MODE) foreach(currentConfigIndex RANGE ${configsCount}) list(GET ${_NAME}_CONSIDERED_CONFIGS ${currentConfigIndex} filename) list(GET ${_NAME}_CONSIDERED_VERSIONS ${currentConfigIndex} version) - string(APPEND configsText " ${filename} (version ${version})\n") + string(APPEND configsText "\n ${filename} (version ${version})") endforeach() if (${_NAME}_NOT_FOUND_MESSAGE) - string(APPEND configsText " Reason given by package: ${${_NAME}_NOT_FOUND_MESSAGE}\n") + if (FPHSA_REASON_FAILURE_MESSAGE) + string(PREPEND FPHSA_REASON_FAILURE_MESSAGE "${${_NAME}_NOT_FOUND_MESSAGE}\n ") + else() + set(FPHSA_REASON_FAILURE_MESSAGE "${${_NAME}_NOT_FOUND_MESSAGE}") + endif() + else() + string(APPEND configsText "\n") endif() - _FPHSA_FAILURE_MESSAGE("${FPHSA_FAIL_MESSAGE} ${VERSION_MSG}, checked the following files:\n${configsText}") + _FPHSA_FAILURE_MESSAGE("${FPHSA_FAIL_MESSAGE} ${VERSION_MSG}, checked the following files:${configsText}") else() # Simple case: No Config-file was found at all: @@ -177,7 +192,7 @@ function(FIND_PACKAGE_HANDLE_STANDARD_ARGS _NAME _FIRST_ARG) # Set up the arguments for `cmake_parse_arguments`. set(options CONFIG_MODE HANDLE_COMPONENTS) - set(oneValueArgs FAIL_MESSAGE VERSION_VAR FOUND_VAR) + set(oneValueArgs FAIL_MESSAGE REASON_FAILURE_MESSAGE VERSION_VAR FOUND_VAR) set(multiValueArgs REQUIRED_VARS) # Check whether we are in 'simple' or 'extended' mode: diff --git a/Modules/FindThreads.cmake b/Modules/FindThreads.cmake index b0c91b2..d39fe33 100644 --- a/Modules/FindThreads.cmake +++ b/Modules/FindThreads.cmake @@ -77,7 +77,7 @@ macro(_check_threads_lib LIBNAME FUNCNAME VARNAME) if(NOT Threads_FOUND) CHECK_LIBRARY_EXISTS(${LIBNAME} ${FUNCNAME} "" ${VARNAME}) if(${VARNAME}) - set(CMAKE_THREAD_LIBS_INIT "-l${LIBNAME}") + set(CMAKE_THREAD_LIBS_INIT "${LIBNAME}") set(CMAKE_HAVE_THREADS_LIBRARY 1) set(Threads_FOUND TRUE) endif() @@ -88,7 +88,7 @@ endmacro() # Do NOT even think about using it outside of this file! macro(_check_pthreads_flag) if(NOT Threads_FOUND) - # If we did not found -lpthread, -lpthread, or -lthread, look for -pthread + # If we did not find a thread library look for -pthread compiler option. if(NOT DEFINED THREADS_HAVE_PTHREAD_ARG) message(STATUS "Check if compiler accepts -pthread") if(CMAKE_C_COMPILER_LOADED) @@ -164,7 +164,7 @@ if(CMAKE_HAVE_PTHREAD_H) _check_threads_lib(pthreads pthread_create CMAKE_HAVE_PTHREADS_CREATE) _check_threads_lib(pthread pthread_create CMAKE_HAVE_PTHREAD_CREATE) if(CMAKE_SYSTEM_NAME MATCHES "SunOS") - # On sun also check for -lthread + # On sun also check for thread library with thr_create _check_threads_lib(thread thr_create CMAKE_HAVE_THR_CREATE) endif() endif() @@ -195,7 +195,7 @@ if(CMAKE_USE_PTHREADS_INIT) # are available. CHECK_LIBRARY_EXISTS(cma pthread_attr_create "" CMAKE_HAVE_HP_CMA) if(CMAKE_HAVE_HP_CMA) - set(CMAKE_THREAD_LIBS_INIT "-lcma") + set(CMAKE_THREAD_LIBS_INIT "cma") set(CMAKE_HP_PTHREADS_INIT 1) set(Threads_FOUND TRUE) endif() diff --git a/Modules/Internal/CPack/CPackDeb.cmake b/Modules/Internal/CPack/CPackDeb.cmake index 7791822..ad8e078 100644 --- a/Modules/Internal/CPack/CPackDeb.cmake +++ b/Modules/Internal/CPack/CPackDeb.cmake @@ -56,6 +56,67 @@ function(extract_so_info shared_object libname version) endif() endfunction() +function(cpack_deb_check_description SUMMARY LINES RESULT_VARIABLE) + set(_result TRUE) + + # Get the summary line + if(NOT SUMMARY MATCHES "^[^\\s].*$") + set(_result FALSE) + set(${RESULT_VARIABLE} ${_result} PARENT_SCOPE) + return() + endif() + + foreach(_line IN LISTS LINES) + if(NOT _line MATCHES "^ +[^ ]+.*$") + set(_result FALSE) + break() + endif() + endforeach() + + set(${RESULT_VARIABLE} ${_result} PARENT_SCOPE) +endfunction() + +function(cpack_deb_format_package_description TEXT OUTPUT_VAR) + # Turn the possible multi-line string into a list + string(UUID uuid NAMESPACE 00000000-0000-0000-0000-000000000000 TYPE SHA1) + string(REPLACE ";" "${uuid}" _text "${TEXT}") + string(REPLACE "\n" ";" _lines "${_text}") + list(POP_FRONT _lines _summary) + + # Check if reformatting required + cpack_deb_check_description("${_summary}" "${_lines}" _result) + if(_result) + # Ok, no formatting required + set(${OUTPUT_VAR} "${TEXT}" PARENT_SCOPE) + return() + endif() + + # Format the summary line + string(STRIP "${_summary}" _summary) + + # Make sure the rest formatted properly + set(_result) + foreach(_line IN LISTS _lines) + string(STRIP "${_line}" _line_strip) + if(NOT _line_strip) + # Replace empty lines w/ a _single full stop character_ + set(_line " .") + else() + # Prepend the normal lines w/ a single space. + # If the line already starts w/ at least one space, + # it'll become _verbatim_ (assuming it supposed to be + # verbatim in the original text). + string(PREPEND _line " ") + endif() + list(APPEND _result "${_line}") + endforeach() + + list(PREPEND _result "${_summary}") + list(JOIN _result "\n" _result) + string(REPLACE "${uuid}" ";" _result "${_result}") + set(${OUTPUT_VAR} "${_result}" PARENT_SCOPE) +endfunction() + function(cpack_deb_prepare_package_vars) # CPACK_DEBIAN_PACKAGE_SHLIBDEPS # If specify OFF, only user depends are used @@ -102,7 +163,7 @@ function(cpack_deb_prepare_package_vars) RESULT_VARIABLE FILE_RESULT_ OUTPUT_VARIABLE INSTALL_FILE_) if(NOT FILE_RESULT_ EQUAL 0) - message (FATAL_ERROR "CPackDeb: execution of command: '${FILE_EXECUTABLE} ./${FILE_}' failed with exit code: ${FILE_RESULT_}") + message(FATAL_ERROR "CPackDeb: execution of command: '${FILE_EXECUTABLE} ./${FILE_}' failed with exit code: ${FILE_RESULT_}") endif() list(APPEND CPACK_DEB_INSTALL_FILES "${INSTALL_FILE_}") endforeach() @@ -210,7 +271,7 @@ function(cpack_deb_prepare_package_vars) if(_TMP_VERSION MATCHES "dpkg-shlibdeps version ([0-9]+\\.[0-9]+\\.[0-9]+)") set(SHLIBDEPS_EXECUTABLE_VERSION "${CMAKE_MATCH_1}") else() - set(SHLIBDEPS_EXECUTABLE_VERSION "") + unset(SHLIBDEPS_EXECUTABLE_VERSION) endif() if(CPACK_DEBIAN_PACKAGE_DEBUG) @@ -253,7 +314,7 @@ function(cpack_deb_prepare_package_vars) message( "CPackDeb Debug: dpkg-shlibdeps warnings \n${SHLIBDEPS_ERROR}") endif() if(NOT SHLIBDEPS_RESULT EQUAL 0) - message (FATAL_ERROR "CPackDeb: dpkg-shlibdeps: '${SHLIBDEPS_ERROR}';\n" + message(FATAL_ERROR "CPackDeb: dpkg-shlibdeps: '${SHLIBDEPS_ERROR}';\n" "executed command: '${SHLIBDEPS_EXECUTABLE} ${IGNORE_MISSING_INFO_FLAG} -O ${CPACK_DEB_BINARY_FILES}';\n" "found files: '${INSTALL_FILE_}';\n" "files info: '${CPACK_DEB_INSTALL_FILES}';\n" @@ -388,7 +449,7 @@ function(cpack_deb_prepare_package_vars) # if per-component variable, overrides the global CPACK_DEBIAN_PACKAGE_${variable_type_} # automatic dependency discovery will be performed afterwards. if(CPACK_DEB_PACKAGE_COMPONENT) - foreach(value_type_ DEPENDS RECOMMENDS SUGGESTS PREDEPENDS ENHANCES BREAKS CONFLICTS PROVIDES REPLACES SOURCE SECTION PRIORITY NAME) + foreach(value_type_ IN ITEMS DEPENDS RECOMMENDS SUGGESTS PREDEPENDS ENHANCES BREAKS CONFLICTS PROVIDES REPLACES SOURCE SECTION PRIORITY NAME) set(_component_var "CPACK_DEBIAN_${_local_component_name}_PACKAGE_${value_type_}") # if set, overrides the global variable @@ -402,21 +463,15 @@ function(cpack_deb_prepare_package_vars) endforeach() if(CPACK_DEBIAN_ENABLE_COMPONENT_DEPENDS) - set(COMPONENT_DEPENDS "") - foreach (_PACK ${CPACK_COMPONENT_${_local_component_name}_DEPENDS}) + unset(COMPONENT_DEPENDS) + foreach(_PACK IN LISTS CPACK_COMPONENT_${_local_component_name}_DEPENDS) get_component_package_name(_PACK_NAME "${_PACK}") - if(COMPONENT_DEPENDS) - set(COMPONENT_DEPENDS "${_PACK_NAME} (= ${CPACK_DEBIAN_PACKAGE_VERSION}), ${COMPONENT_DEPENDS}") - else() - set(COMPONENT_DEPENDS "${_PACK_NAME} (= ${CPACK_DEBIAN_PACKAGE_VERSION})") - endif() + list(PREPEND COMPONENT_DEPENDS "${_PACK_NAME} (= ${CPACK_DEBIAN_PACKAGE_VERSION})") endforeach() + list(JOIN COMPONENT_DEPENDS ", " COMPONENT_DEPENDS) if(COMPONENT_DEPENDS) - if(CPACK_DEBIAN_PACKAGE_DEPENDS) - set(CPACK_DEBIAN_PACKAGE_DEPENDS "${COMPONENT_DEPENDS}, ${CPACK_DEBIAN_PACKAGE_DEPENDS}") - else() - set(CPACK_DEBIAN_PACKAGE_DEPENDS "${COMPONENT_DEPENDS}") - endif() + list(PREPEND CPACK_DEBIAN_PACKAGE_DEPENDS ${COMPONENT_DEPENDS}) + list(JOIN CPACK_DEBIAN_PACKAGE_DEPENDS ", " CPACK_DEBIAN_PACKAGE_DEPENDS) endif() endif() endif() @@ -424,12 +479,9 @@ function(cpack_deb_prepare_package_vars) # at this point, the CPACK_DEBIAN_PACKAGE_DEPENDS is properly set # to the minimal dependency of the package # Append automatically discovered dependencies . - if(NOT "${CPACK_DEBIAN_PACKAGE_AUTO_DEPENDS}" STREQUAL "") - if (CPACK_DEBIAN_PACKAGE_DEPENDS) - set (CPACK_DEBIAN_PACKAGE_DEPENDS "${CPACK_DEBIAN_PACKAGE_DEPENDS}, ${CPACK_DEBIAN_PACKAGE_AUTO_DEPENDS}") - else () - set (CPACK_DEBIAN_PACKAGE_DEPENDS "${CPACK_DEBIAN_PACKAGE_AUTO_DEPENDS}") - endif () + if(CPACK_DEBIAN_PACKAGE_AUTO_DEPENDS) + list(APPEND CPACK_DEBIAN_PACKAGE_DEPENDS ${CPACK_DEBIAN_PACKAGE_AUTO_DEPENDS}) + list(JOIN CPACK_DEBIAN_PACKAGE_DEPENDS ", " CPACK_DEBIAN_PACKAGE_DEPENDS) endif() if(NOT CPACK_DEBIAN_PACKAGE_DEPENDS) @@ -445,26 +497,53 @@ function(cpack_deb_prepare_package_vars) endif() # Description: (mandatory) - if(NOT CPACK_DEB_PACKAGE_COMPONENT) - if(NOT CPACK_DEBIAN_PACKAGE_DESCRIPTION) - if(NOT CPACK_PACKAGE_DESCRIPTION_SUMMARY) - message(FATAL_ERROR "CPackDeb: Debian package requires a summary for a package, set CPACK_PACKAGE_DESCRIPTION_SUMMARY or CPACK_DEBIAN_PACKAGE_DESCRIPTION") - endif() - set(CPACK_DEBIAN_PACKAGE_DESCRIPTION ${CPACK_PACKAGE_DESCRIPTION_SUMMARY}) - endif() + # Try package description first + if(CPACK_DEB_PACKAGE_COMPONENT) + cpack_deb_variable_fallback("CPACK_DEBIAN_PACKAGE_DESCRIPTION" + "CPACK_DEBIAN_${_local_component_name}_DESCRIPTION" + "CPACK_COMPONENT_${_local_component_name}_DESCRIPTION") else() - set(component_description_var CPACK_COMPONENT_${_local_component_name}_DESCRIPTION) - - # component description overrides package description - if(${component_description_var}) - set(CPACK_DEBIAN_PACKAGE_DESCRIPTION ${${component_description_var}}) - elseif(NOT CPACK_DEBIAN_PACKAGE_DESCRIPTION) - if(NOT CPACK_PACKAGE_DESCRIPTION_SUMMARY) - message(FATAL_ERROR "CPackDeb: Debian package requires a summary for a package, set CPACK_PACKAGE_DESCRIPTION_SUMMARY or CPACK_DEBIAN_PACKAGE_DESCRIPTION or ${component_description_var}") - endif() + cpack_deb_variable_fallback("CPACK_DEBIAN_PACKAGE_DESCRIPTION" + "CPACK_DEBIAN_PACKAGE_DESCRIPTION" + "CPACK_PACKAGE_DESCRIPTION") + endif() + + # Still no description? ... and description file has set ... + if(NOT CPACK_DEBIAN_PACKAGE_DESCRIPTION AND CPACK_PACKAGE_DESCRIPTION_FILE) + # Read `CPACK_PACKAGE_DESCRIPTION_FILE` then... + file(READ ${CPACK_PACKAGE_DESCRIPTION_FILE} CPACK_DEBIAN_PACKAGE_DESCRIPTION) + endif() + + # Still no description? #2 + if(NOT CPACK_DEBIAN_PACKAGE_DESCRIPTION) + # Try to get `CPACK_PACKAGE_DESCRIPTION_SUMMARY` as the last hope + if(CPACK_PACKAGE_DESCRIPTION_SUMMARY) set(CPACK_DEBIAN_PACKAGE_DESCRIPTION ${CPACK_PACKAGE_DESCRIPTION_SUMMARY}) + else() + # Giving up! Report an error... + set(_description_failure_message + "CPackDeb: Debian package requires a summary for a package, set CPACK_PACKAGE_DESCRIPTION_SUMMARY or CPACK_DEBIAN_PACKAGE_DESCRIPTION") + if(CPACK_DEB_PACKAGE_COMPONENT) + string(APPEND _description_failure_message + " or CPACK_DEBIAN_${_local_component_name}_DESCRIPTION") + endif() + message(FATAL_ERROR _description_failure_message) endif() + + # Ok, description has set. According to the `Debian Policy Manual`_ the frist + # line is a pacakge summary. Try to get it as well... + # See also: https://www.debian.org/doc/debian-policy/ch-controlfields.html#description + elseif(CPACK_PACKAGE_DESCRIPTION_SUMMARY) + # Merge summary w/ the detailed description + string(PREPEND CPACK_DEBIAN_PACKAGE_DESCRIPTION "${CPACK_PACKAGE_DESCRIPTION_SUMMARY}\n") endif() + # assert(CPACK_DEBIAN_PACKAGE_DESCRIPTION) + + # Make sure description is properly formatted + cpack_deb_format_package_description( + "${CPACK_DEBIAN_PACKAGE_DESCRIPTION}" + CPACK_DEBIAN_PACKAGE_DESCRIPTION + ) # Homepage: (optional) if(NOT CPACK_DEBIAN_PACKAGE_HOMEPAGE AND CMAKE_PROJECT_HOMEPAGE_URL) @@ -519,7 +598,7 @@ function(cpack_deb_prepare_package_vars) # Are we packaging components ? if(CPACK_DEB_PACKAGE_COMPONENT) # override values with per component version if set - foreach(VAR_NAME_ "PACKAGE_CONTROL_EXTRA" "PACKAGE_CONTROL_STRICT_PERMISSION") + foreach(VAR_NAME_ IN ITEMS PACKAGE_CONTROL_EXTRA PACKAGE_CONTROL_STRICT_PERMISSION) if(CPACK_DEBIAN_${_local_component_name}_${VAR_NAME_}) set(CPACK_DEBIAN_${VAR_NAME_} "${CPACK_DEBIAN_${_local_component_name}_${VAR_NAME_}}") endif() @@ -527,12 +606,12 @@ function(cpack_deb_prepare_package_vars) get_component_package_name(CPACK_DEBIAN_PACKAGE_NAME ${_local_component_name}) endif() - set(CPACK_DEBIAN_PACKAGE_SHLIBS_LIST "") - - if (NOT CPACK_DEBIAN_PACKAGE_GENERATE_SHLIBS_POLICY) + if(NOT CPACK_DEBIAN_PACKAGE_GENERATE_SHLIBS_POLICY) set(CPACK_DEBIAN_PACKAGE_GENERATE_SHLIBS_POLICY "=") endif() + unset(CPACK_DEBIAN_PACKAGE_SHLIBS_LIST) + if(CPACK_DEBIAN_PACKAGE_GENERATE_SHLIBS) if(READELF_EXECUTABLE) foreach(_FILE IN LISTS CPACK_DEB_SHARED_OBJECT_FILES) @@ -544,9 +623,7 @@ function(cpack_deb_prepare_package_vars) message(AUTHOR_WARNING "Shared library '${_FILE}' is missing soname or soversion. Library will not be added to DEBIAN/shlibs control file.") endif() endforeach() - if (CPACK_DEBIAN_PACKAGE_SHLIBS_LIST) - string(REPLACE ";" "\n" CPACK_DEBIAN_PACKAGE_SHLIBS_LIST "${CPACK_DEBIAN_PACKAGE_SHLIBS_LIST}") - endif() + list(JOIN CPACK_DEBIAN_PACKAGE_SHLIBS_LIST "\n" CPACK_DEBIAN_PACKAGE_SHLIBS_LIST) else() message(FATAL_ERROR "Readelf utility is not available. CPACK_DEBIAN_PACKAGE_GENERATE_SHLIBS option is not available.") endif() @@ -554,7 +631,7 @@ function(cpack_deb_prepare_package_vars) # add ldconfig call in default postrm and postint set(CPACK_ADD_LDCONFIG_CALL 0) - foreach(_FILE ${CPACK_DEB_SHARED_OBJECT_FILES}) + foreach(_FILE IN LISTS CPACK_DEB_SHARED_OBJECT_FILES) get_filename_component(_DIR ${_FILE} DIRECTORY) # all files in CPACK_DEB_SHARED_OBJECT_FILES have dot at the beginning if(_DIR STREQUAL "./lib" OR _DIR STREQUAL "./usr/lib") @@ -565,12 +642,12 @@ function(cpack_deb_prepare_package_vars) if(CPACK_ADD_LDCONFIG_CALL) set(CPACK_DEBIAN_GENERATE_POSTINST 1) set(CPACK_DEBIAN_GENERATE_POSTRM 1) - foreach(f ${PACKAGE_CONTROL_EXTRA}) + foreach(f IN LISTS PACKAGE_CONTROL_EXTRA) get_filename_component(n "${f}" NAME) - if("${n}" STREQUAL "postinst") + if(n STREQUAL "postinst") set(CPACK_DEBIAN_GENERATE_POSTINST 0) endif() - if("${n}" STREQUAL "postrm") + if(n STREQUAL "postrm") set(CPACK_DEBIAN_GENERATE_POSTRM 0) endif() endforeach() @@ -671,7 +748,7 @@ function(cpack_deb_prepare_package_vars) if(BUILD_IDS) set(GEN_DBGSYMDIR "${DBGSYMDIR}" PARENT_SCOPE) set(GEN_CPACK_DBGSYM_OUTPUT_FILE_NAME "${CPACK_DBGSYM_OUTPUT_FILE_NAME}" PARENT_SCOPE) - string(REPLACE ";" " " BUILD_IDS "${BUILD_IDS}") + list(JOIN BUILD_IDS " " BUILD_IDS) set(GEN_BUILD_IDS "${BUILD_IDS}" PARENT_SCOPE) endif() endfunction() diff --git a/Modules/Platform/Apple-AppleClang-OBJC.cmake b/Modules/Platform/Apple-AppleClang-OBJC.cmake new file mode 100644 index 0000000..b78edb1 --- /dev/null +++ b/Modules/Platform/Apple-AppleClang-OBJC.cmake @@ -0,0 +1,6 @@ +include(Platform/Apple-Clang-OBJC) +if(NOT CMAKE_OBJC_COMPILER_VERSION VERSION_LESS 4.2) + set(CMAKE_OBJC_SYSTEM_FRAMEWORK_SEARCH_FLAG "-iframework ") +else() + unset(CMAKE_OBJC_SYSTEM_FRAMEWORK_SEARCH_FLAG) +endif() diff --git a/Modules/Platform/Apple-AppleClang-OBJCXX.cmake b/Modules/Platform/Apple-AppleClang-OBJCXX.cmake new file mode 100644 index 0000000..ed172f1 --- /dev/null +++ b/Modules/Platform/Apple-AppleClang-OBJCXX.cmake @@ -0,0 +1,6 @@ +include(Platform/Apple-Clang-OBJCXX) +if(NOT CMAKE_OBJCXX_COMPILER_VERSION VERSION_LESS 4.2) + set(CMAKE_OBJCXX_SYSTEM_FRAMEWORK_SEARCH_FLAG "-iframework ") +else() + unset(CMAKE_OBJCXX_SYSTEM_FRAMEWORK_SEARCH_FLAG) +endif() diff --git a/Modules/Platform/Apple-Clang-OBJC.cmake b/Modules/Platform/Apple-Clang-OBJC.cmake new file mode 100644 index 0000000..63cd846 --- /dev/null +++ b/Modules/Platform/Apple-Clang-OBJC.cmake @@ -0,0 +1,2 @@ +include(Platform/Apple-Clang) +__apple_compiler_clang(OBJC) diff --git a/Modules/Platform/Apple-Clang-OBJCXX.cmake b/Modules/Platform/Apple-Clang-OBJCXX.cmake new file mode 100644 index 0000000..28fc352 --- /dev/null +++ b/Modules/Platform/Apple-Clang-OBJCXX.cmake @@ -0,0 +1,2 @@ +include(Platform/Apple-Clang) +__apple_compiler_clang(OBJCXX) diff --git a/Modules/Platform/Apple-GNU-OBJC.cmake b/Modules/Platform/Apple-GNU-OBJC.cmake new file mode 100644 index 0000000..aa8b33f --- /dev/null +++ b/Modules/Platform/Apple-GNU-OBJC.cmake @@ -0,0 +1,4 @@ +include(Platform/Apple-GNU) +__apple_compiler_gnu(OBJC) +cmake_gnu_set_sysroot_flag(OBJC) +cmake_gnu_set_osx_deployment_target_flag(OBJC) diff --git a/Modules/Platform/Apple-GNU-OBJCXX.cmake b/Modules/Platform/Apple-GNU-OBJCXX.cmake new file mode 100644 index 0000000..919e11d --- /dev/null +++ b/Modules/Platform/Apple-GNU-OBJCXX.cmake @@ -0,0 +1,4 @@ +include(Platform/Apple-GNU) +__apple_compiler_gnu(OBJCXX) +cmake_gnu_set_sysroot_flag(OBJCXX) +cmake_gnu_set_osx_deployment_target_flag(OBJCXX) diff --git a/Modules/Platform/Darwin.cmake b/Modules/Platform/Darwin.cmake index 7e02814..d0bb359 100644 --- a/Modules/Platform/Darwin.cmake +++ b/Modules/Platform/Darwin.cmake @@ -119,10 +119,18 @@ set(CMAKE_C_CREATE_MACOSX_FRAMEWORK set(CMAKE_CXX_CREATE_MACOSX_FRAMEWORK "<CMAKE_CXX_COMPILER> <LANGUAGE_COMPILE_FLAGS> <CMAKE_SHARED_LIBRARY_CREATE_CXX_FLAGS> <LINK_FLAGS> -o <TARGET> <SONAME_FLAG> <TARGET_INSTALLNAME_DIR><TARGET_SONAME> <OBJECTS> <LINK_LIBRARIES>") +set(CMAKE_OBJC_CREATE_MACOSX_FRAMEWORK + "<CMAKE_OBJC_COMPILER> <LANGUAGE_COMPILE_FLAGS> <CMAKE_SHARED_LIBRARY_CREATE_OBJC_FLAGS> <LINK_FLAGS> -o <TARGET> <SONAME_FLAG> <TARGET_INSTALLNAME_DIR><TARGET_SONAME> <OBJECTS> <LINK_LIBRARIES>") + +set(CMAKE_OBJCXX_CREATE_MACOSX_FRAMEWORK + "<CMAKE_OBJCXX_COMPILER> <LANGUAGE_COMPILE_FLAGS> <CMAKE_SHARED_LIBRARY_CREATE_OBJCXX_FLAGS> <LINK_FLAGS> -o <TARGET> <SONAME_FLAG> <TARGET_INSTALLNAME_DIR><TARGET_SONAME> <OBJECTS> <LINK_LIBRARIES>") + # Set default framework search path flag for languages known to use a # preprocessor that may find headers in frameworks. set(CMAKE_C_FRAMEWORK_SEARCH_FLAG -F) set(CMAKE_CXX_FRAMEWORK_SEARCH_FLAG -F) +set(CMAKE_OBJC_FRAMEWORK_SEARCH_FLAG -F) +set(CMAKE_OBJCXX_FRAMEWORK_SEARCH_FLAG -F) set(CMAKE_Fortran_FRAMEWORK_SEARCH_FLAG -F) # default to searching for frameworks first @@ -222,7 +230,7 @@ unset(_apps_paths) include(Platform/UnixPaths) if(_CMAKE_OSX_SYSROOT_PATH AND EXISTS ${_CMAKE_OSX_SYSROOT_PATH}/usr/include) list(APPEND CMAKE_SYSTEM_PREFIX_PATH ${_CMAKE_OSX_SYSROOT_PATH}/usr) - foreach(lang C CXX) + foreach(lang C CXX OBJC OBJCXX) list(APPEND _CMAKE_${lang}_IMPLICIT_INCLUDE_DIRECTORIES_INIT ${_CMAKE_OSX_SYSROOT_PATH}/usr/include) endforeach() endif() diff --git a/Source/CMakeLists.txt b/Source/CMakeLists.txt index 8ed7b2f..4e3e842 100644 --- a/Source/CMakeLists.txt +++ b/Source/CMakeLists.txt @@ -168,6 +168,8 @@ set(SRCS cmBinUtilsWindowsPEObjdumpGetRuntimeDependenciesTool.h cmCacheManager.cxx cmCacheManager.h + cmCheckCustomOutputs.h + cmCheckCustomOutputs.cxx cmCLocaleEnvironmentScope.h cmCLocaleEnvironmentScope.cxx cmCommandArgumentParserHelper.cxx @@ -193,6 +195,7 @@ set(SRCS cmCustomCommandGenerator.h cmCustomCommandLines.cxx cmCustomCommandLines.h + cmCustomCommandTypes.h cmDefinitions.cxx cmDefinitions.h cmDepends.cxx @@ -1142,7 +1145,7 @@ target_link_libraries(cpack CPackLib) # Curses GUI if(BUILD_CursesDialog) - include(${CMake_SOURCE_DIR}/Source/CursesDialog/CMakeLists.txt) + add_subdirectory(CursesDialog) endif() # Qt GUI diff --git a/Source/CMakeVersion.cmake b/Source/CMakeVersion.cmake index a8ced71..0ebfc20 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 15) -set(CMake_VERSION_PATCH 20190921) +set(CMake_VERSION_PATCH 20191001) #set(CMake_VERSION_RC 0) set(CMake_VERSION_IS_DIRTY 0) diff --git a/Source/CTest/cmCTestBuildCommand.cxx b/Source/CTest/cmCTestBuildCommand.cxx index 02b00e3..ce690f9 100644 --- a/Source/CTest/cmCTestBuildCommand.cxx +++ b/Source/CTest/cmCTestBuildCommand.cxx @@ -9,6 +9,7 @@ #include "cmMessageType.h" #include "cmStringAlgorithms.h" #include "cmSystemTools.h" +#include "cm_static_string_view.hxx" #include "cmake.h" #include <cstring> @@ -16,17 +17,15 @@ class cmExecutionStatus; -cmCTestBuildCommand::cmCTestBuildCommand() +void cmCTestBuildCommand::BindArguments() { - this->GlobalGenerator = nullptr; - this->Arguments[ctb_NUMBER_ERRORS] = "NUMBER_ERRORS"; - this->Arguments[ctb_NUMBER_WARNINGS] = "NUMBER_WARNINGS"; - this->Arguments[ctb_TARGET] = "TARGET"; - this->Arguments[ctb_CONFIGURATION] = "CONFIGURATION"; - this->Arguments[ctb_FLAGS] = "FLAGS"; - this->Arguments[ctb_PROJECT_NAME] = "PROJECT_NAME"; - this->Arguments[ctb_LAST] = nullptr; - this->Last = ctb_LAST; + this->cmCTestHandlerCommand::BindArguments(); + this->Bind("NUMBER_ERRORS"_s, this->NumberErrors); + this->Bind("NUMBER_WARNINGS"_s, this->NumberWarnings); + this->Bind("TARGET"_s, this->Target); + this->Bind("CONFIGURATION"_s, this->Configuration); + this->Bind("FLAGS"_s, this->Flags); + this->Bind("PROJECT_NAME"_s, this->ProjectName); } cmCTestBuildCommand::~cmCTestBuildCommand() @@ -60,20 +59,17 @@ cmCTestGenericHandler* cmCTestBuildCommand::InitializeHandler() // const char* ctestBuildConfiguration = this->Makefile->GetDefinition("CTEST_BUILD_CONFIGURATION"); - const char* cmakeBuildConfiguration = - (this->Values[ctb_CONFIGURATION] && *this->Values[ctb_CONFIGURATION]) - ? this->Values[ctb_CONFIGURATION] + const char* cmakeBuildConfiguration = !this->Configuration.empty() + ? this->Configuration.c_str() : ((ctestBuildConfiguration && *ctestBuildConfiguration) ? ctestBuildConfiguration : this->CTest->GetConfigType().c_str()); - const char* cmakeBuildAdditionalFlags = - (this->Values[ctb_FLAGS] && *this->Values[ctb_FLAGS]) - ? this->Values[ctb_FLAGS] + const char* cmakeBuildAdditionalFlags = !this->Flags.empty() + ? this->Flags.c_str() : this->Makefile->GetDefinition("CTEST_BUILD_FLAGS"); - const char* cmakeBuildTarget = - (this->Values[ctb_TARGET] && *this->Values[ctb_TARGET]) - ? this->Values[ctb_TARGET] + const char* cmakeBuildTarget = !this->Target.empty() + ? this->Target.c_str() : this->Makefile->GetDefinition("CTEST_BUILD_TARGET"); if (cmakeGeneratorName && *cmakeGeneratorName) { @@ -153,16 +149,13 @@ bool cmCTestBuildCommand::InitialPass(std::vector<std::string> const& args, cmExecutionStatus& status) { bool ret = cmCTestHandlerCommand::InitialPass(args, status); - if (this->Values[ctb_NUMBER_ERRORS] && *this->Values[ctb_NUMBER_ERRORS]) { + if (!this->NumberErrors.empty()) { this->Makefile->AddDefinition( - this->Values[ctb_NUMBER_ERRORS], - std::to_string(this->Handler->GetTotalErrors())); + this->NumberErrors, std::to_string(this->Handler->GetTotalErrors())); } - if (this->Values[ctb_NUMBER_WARNINGS] && - *this->Values[ctb_NUMBER_WARNINGS]) { + if (!this->NumberWarnings.empty()) { this->Makefile->AddDefinition( - this->Values[ctb_NUMBER_WARNINGS], - std::to_string(this->Handler->GetTotalWarnings())); + this->NumberWarnings, std::to_string(this->Handler->GetTotalWarnings())); } return ret; } diff --git a/Source/CTest/cmCTestBuildCommand.h b/Source/CTest/cmCTestBuildCommand.h index 14f70bf..791e1f0 100644 --- a/Source/CTest/cmCTestBuildCommand.h +++ b/Source/CTest/cmCTestBuildCommand.h @@ -27,7 +27,6 @@ class cmGlobalGenerator; class cmCTestBuildCommand : public cmCTestHandlerCommand { public: - cmCTestBuildCommand(); ~cmCTestBuildCommand() override; /** @@ -49,23 +48,19 @@ public: bool InitialPass(std::vector<std::string> const& args, cmExecutionStatus& status) override; - cmGlobalGenerator* GlobalGenerator; + cmGlobalGenerator* GlobalGenerator = nullptr; protected: cmCTestBuildHandler* Handler; - enum - { - ctb_BUILD = ct_LAST, - ctb_NUMBER_ERRORS, - ctb_NUMBER_WARNINGS, - ctb_TARGET, - ctb_CONFIGURATION, - ctb_FLAGS, - ctb_PROJECT_NAME, - ctb_LAST - }; - + void BindArguments() override; cmCTestGenericHandler* InitializeHandler() override; + + std::string NumberErrors; + std::string NumberWarnings; + std::string Target; + std::string Configuration; + std::string Flags; + std::string ProjectName; }; #endif diff --git a/Source/CTest/cmCTestConfigureCommand.cxx b/Source/CTest/cmCTestConfigureCommand.cxx index 8a56f3d..948b9fb 100644 --- a/Source/CTest/cmCTestConfigureCommand.cxx +++ b/Source/CTest/cmCTestConfigureCommand.cxx @@ -8,25 +8,25 @@ #include "cmMakefile.h" #include "cmStringAlgorithms.h" #include "cmSystemTools.h" +#include "cm_static_string_view.hxx" #include "cmake.h" #include <cstring> #include <sstream> #include <vector> -cmCTestConfigureCommand::cmCTestConfigureCommand() +void cmCTestConfigureCommand::BindArguments() { - this->Arguments[ctc_OPTIONS] = "OPTIONS"; - this->Arguments[ctc_LAST] = nullptr; - this->Last = ctc_LAST; + this->cmCTestHandlerCommand::BindArguments(); + this->Bind("OPTIONS"_s, this->Options); } cmCTestGenericHandler* cmCTestConfigureCommand::InitializeHandler() { std::vector<std::string> options; - if (this->Values[ctc_OPTIONS]) { - cmExpandList(this->Values[ctc_OPTIONS], options); + if (!this->Options.empty()) { + cmExpandList(this->Options, options); } if (this->CTest->GetCTestConfiguration("BuildDirectory").empty()) { diff --git a/Source/CTest/cmCTestConfigureCommand.h b/Source/CTest/cmCTestConfigureCommand.h index 36ca7d5..0bc7848 100644 --- a/Source/CTest/cmCTestConfigureCommand.h +++ b/Source/CTest/cmCTestConfigureCommand.h @@ -23,8 +23,6 @@ class cmCTestGenericHandler; class cmCTestConfigureCommand : public cmCTestHandlerCommand { public: - cmCTestConfigureCommand(); - /** * This is a virtual constructor for the command. */ @@ -42,14 +40,10 @@ public: std::string GetName() const override { return "ctest_configure"; } protected: + void BindArguments() override; cmCTestGenericHandler* InitializeHandler() override; - enum - { - ctc_FIRST = ct_LAST, - ctc_OPTIONS, - ctc_LAST - }; + std::string Options; }; #endif diff --git a/Source/CTest/cmCTestCoverageCommand.cxx b/Source/CTest/cmCTestCoverageCommand.cxx index 07aae76..b66bba7 100644 --- a/Source/CTest/cmCTestCoverageCommand.cxx +++ b/Source/CTest/cmCTestCoverageCommand.cxx @@ -2,14 +2,26 @@ file Copyright.txt or https://cmake.org/licensing for details. */ #include "cmCTestCoverageCommand.h" +#include <set> + +#include "cmAlgorithms.h" #include "cmCTest.h" #include "cmCTestCoverageHandler.h" +#include "cm_static_string_view.hxx" class cmCTestGenericHandler; -cmCTestCoverageCommand::cmCTestCoverageCommand() +void cmCTestCoverageCommand::BindArguments() +{ + this->cmCTestHandlerCommand::BindArguments(); + this->Bind("LABELS"_s, this->Labels); +} + +void cmCTestCoverageCommand::CheckArguments( + std::vector<std::string> const& keywords) { - this->LabelsMentioned = false; + this->LabelsMentioned = + !this->Labels.empty() || cmContains(keywords, "LABELS"); } cmCTestGenericHandler* cmCTestCoverageCommand::InitializeHandler() @@ -24,34 +36,10 @@ cmCTestGenericHandler* cmCTestCoverageCommand::InitializeHandler() // If a LABELS option was given, select only files with the labels. if (this->LabelsMentioned) { - handler->SetLabelFilter(this->Labels); + handler->SetLabelFilter( + std::set<std::string>(this->Labels.begin(), this->Labels.end())); } handler->SetQuiet(this->Quiet); return handler; } - -bool cmCTestCoverageCommand::CheckArgumentKeyword(std::string const& arg) -{ - // Look for arguments specific to this command. - if (arg == "LABELS") { - this->ArgumentDoing = ArgumentDoingLabels; - this->LabelsMentioned = true; - return true; - } - - // Look for other arguments. - return this->Superclass::CheckArgumentKeyword(arg); -} - -bool cmCTestCoverageCommand::CheckArgumentValue(std::string const& arg) -{ - // Handle states specific to this command. - if (this->ArgumentDoing == ArgumentDoingLabels) { - this->Labels.insert(arg); - return true; - } - - // Look for other arguments. - return this->Superclass::CheckArgumentValue(arg); -} diff --git a/Source/CTest/cmCTestCoverageCommand.h b/Source/CTest/cmCTestCoverageCommand.h index 75aefdf..fcffa75 100644 --- a/Source/CTest/cmCTestCoverageCommand.h +++ b/Source/CTest/cmCTestCoverageCommand.h @@ -8,9 +8,9 @@ #include "cmCTestHandlerCommand.h" #include "cmCommand.h" -#include <set> #include <string> #include <utility> +#include <vector> #include <cm/memory> @@ -24,8 +24,6 @@ class cmCTestGenericHandler; class cmCTestCoverageCommand : public cmCTestHandlerCommand { public: - cmCTestCoverageCommand(); - /** * This is a virtual constructor for the command. */ @@ -42,22 +40,13 @@ public: */ std::string GetName() const override { return "ctest_coverage"; } - using Superclass = cmCTestHandlerCommand; - protected: + void BindArguments() override; + void CheckArguments(std::vector<std::string> const& keywords) override; cmCTestGenericHandler* InitializeHandler() override; - bool CheckArgumentKeyword(std::string const& arg) override; - bool CheckArgumentValue(std::string const& arg) override; - - enum - { - ArgumentDoingLabels = Superclass::ArgumentDoingLast1, - ArgumentDoingLast2 - }; - bool LabelsMentioned; - std::set<std::string> Labels; + std::vector<std::string> Labels; }; #endif diff --git a/Source/CTest/cmCTestHandlerCommand.cxx b/Source/CTest/cmCTestHandlerCommand.cxx index 9c5425d..3f9ce4e 100644 --- a/Source/CTest/cmCTestHandlerCommand.cxx +++ b/Source/CTest/cmCTestHandlerCommand.cxx @@ -7,31 +7,16 @@ #include "cmExecutionStatus.h" #include "cmMakefile.h" #include "cmMessageType.h" +#include "cmStringAlgorithms.h" #include "cmSystemTools.h" #include "cmWorkingDirectory.h" +#include "cm_static_string_view.hxx" +#include <algorithm> #include <cstdlib> #include <cstring> #include <sstream> -cmCTestHandlerCommand::cmCTestHandlerCommand() -{ - const size_t INIT_SIZE = 100; - size_t cc; - this->Arguments.reserve(INIT_SIZE); - for (cc = 0; cc < INIT_SIZE; ++cc) { - this->Arguments.push_back(nullptr); - } - this->Arguments[ct_RETURN_VALUE] = "RETURN_VALUE"; - this->Arguments[ct_CAPTURE_CMAKE_ERROR] = "CAPTURE_CMAKE_ERROR"; - this->Arguments[ct_SOURCE] = "SOURCE"; - this->Arguments[ct_BUILD] = "BUILD"; - this->Arguments[ct_SUBMIT_INDEX] = "SUBMIT_INDEX"; - this->Last = ct_LAST; - this->AppendXML = false; - this->Quiet = false; -} - namespace { // class to save and restore the error state for ctest_* commands // if a ctest_* command has a CAPTURE_CMAKE_ERROR then put the error @@ -90,30 +75,30 @@ bool cmCTestHandlerCommand::InitialPass(std::vector<std::string> const& args, // save error state and restore it if needed SaveRestoreErrorState errorState; // Allocate space for argument values. - this->Values.clear(); - this->Values.resize(this->Last, nullptr); + this->BindArguments(); // Process input arguments. - this->ArgumentDoing = ArgumentDoingNone; - // look at all arguments and do not short circuit on the first - // bad one so that CAPTURE_CMAKE_ERROR can override setting the - // global error state - bool foundBadArgument = false; - for (std::string const& arg : args) { - // Check this argument. - if (!this->CheckArgumentKeyword(arg) && !this->CheckArgumentValue(arg)) { - std::ostringstream e; - e << "called with unknown argument \"" << arg << "\"."; - this->SetError(e.str()); - foundBadArgument = true; - } - // note bad argument - if (this->ArgumentDoing == ArgumentDoingError) { - foundBadArgument = true; - } + std::vector<std::string> unparsedArguments; + std::vector<std::string> keywordsMissingValue; + std::vector<std::string> parsedKeywords; + this->Parse(args, &unparsedArguments, &keywordsMissingValue, + &parsedKeywords); + this->CheckArguments(keywordsMissingValue); + + std::sort(parsedKeywords.begin(), parsedKeywords.end()); + auto it = std::adjacent_find(parsedKeywords.begin(), parsedKeywords.end()); + if (it != parsedKeywords.end()) { + this->Makefile->IssueMessage( + MessageType::FATAL_ERROR, + cmStrCat("Called with more than one value for ", *it)); + } + + bool const foundBadArgument = !unparsedArguments.empty(); + if (foundBadArgument) { + this->SetError(cmStrCat("called with unknown argument \"", + unparsedArguments.front(), "\".")); } - bool captureCMakeError = (this->Values[ct_CAPTURE_CMAKE_ERROR] && - *this->Values[ct_CAPTURE_CMAKE_ERROR]); + bool const captureCMakeError = !this->CaptureCMakeError.empty(); // now that arguments are parsed check to see if there is a // CAPTURE_CMAKE_ERROR specified let the errorState object know. if (captureCMakeError) { @@ -123,8 +108,7 @@ bool cmCTestHandlerCommand::InitialPass(std::vector<std::string> const& args, if (foundBadArgument) { // store the cmake error if (captureCMakeError) { - this->Makefile->AddDefinition(this->Values[ct_CAPTURE_CMAKE_ERROR], - "-1"); + this->Makefile->AddDefinition(this->CaptureCMakeError, "-1"); std::string const err = this->GetName() + " " + status.GetError(); if (!cmSystemTools::FindLastString(err.c_str(), "unknown error.")) { cmCTestLog(this->CTest, ERROR_MESSAGE, err << " error from command\n"); @@ -146,10 +130,9 @@ bool cmCTestHandlerCommand::InitialPass(std::vector<std::string> const& args, this->CTest->SetConfigType(ctestConfigType); } - if (this->Values[ct_BUILD]) { + if (!this->Build.empty()) { this->CTest->SetCTestConfiguration( - "BuildDirectory", - cmSystemTools::CollapseFullPath(this->Values[ct_BUILD]).c_str(), + "BuildDirectory", cmSystemTools::CollapseFullPath(this->Build).c_str(), this->Quiet); } else { std::string const& bdir = @@ -163,13 +146,11 @@ bool cmCTestHandlerCommand::InitialPass(std::vector<std::string> const& args, "CTEST_BINARY_DIRECTORY not set" << std::endl;); } } - if (this->Values[ct_SOURCE]) { + if (!this->Source.empty()) { cmCTestLog(this->CTest, DEBUG, - "Set source directory to: " << this->Values[ct_SOURCE] - << std::endl); + "Set source directory to: " << this->Source << std::endl); this->CTest->SetCTestConfiguration( - "SourceDirectory", - cmSystemTools::CollapseFullPath(this->Values[ct_SOURCE]).c_str(), + "SourceDirectory", cmSystemTools::CollapseFullPath(this->Source).c_str(), this->Quiet); } else { this->CTest->SetCTestConfiguration( @@ -192,8 +173,7 @@ bool cmCTestHandlerCommand::InitialPass(std::vector<std::string> const& args, "Cannot instantiate test handler " << this->GetName() << std::endl); if (captureCMakeError) { - this->Makefile->AddDefinition(this->Values[ct_CAPTURE_CMAKE_ERROR], - "-1"); + this->Makefile->AddDefinition(this->CaptureCMakeError, "-1"); std::string const& err = status.GetError(); if (!cmSystemTools::FindLastString(err.c_str(), "unknown error.")) { cmCTestLog(this->CTest, ERROR_MESSAGE, err << " error from command\n"); @@ -203,11 +183,11 @@ bool cmCTestHandlerCommand::InitialPass(std::vector<std::string> const& args, return false; } - handler->SetAppendXML(this->AppendXML); + handler->SetAppendXML(this->Append); handler->PopulateCustomVectors(this->Makefile); - if (this->Values[ct_SUBMIT_INDEX]) { - handler->SetSubmitIndex(atoi(this->Values[ct_SUBMIT_INDEX])); + if (!this->SubmitIndex.empty()) { + handler->SetSubmitIndex(atoi(this->SubmitIndex.c_str())); } cmWorkingDirectory workdir( this->CTest->GetCTestConfiguration("BuildDirectory")); @@ -216,8 +196,7 @@ bool cmCTestHandlerCommand::InitialPass(std::vector<std::string> const& args, this->CTest->GetCTestConfiguration("BuildDirectory") + " : " + std::strerror(workdir.GetLastResult())); if (captureCMakeError) { - this->Makefile->AddDefinition(this->Values[ct_CAPTURE_CMAKE_ERROR], - "-1"); + this->Makefile->AddDefinition(this->CaptureCMakeError, "-1"); cmCTestLog(this->CTest, ERROR_MESSAGE, this->GetName() << " " << status.GetError() << "\n"); // return success because failure is recorded in CAPTURE_CMAKE_ERROR @@ -227,9 +206,8 @@ bool cmCTestHandlerCommand::InitialPass(std::vector<std::string> const& args, } int res = handler->ProcessHandler(); - if (this->Values[ct_RETURN_VALUE] && *this->Values[ct_RETURN_VALUE]) { - this->Makefile->AddDefinition(this->Values[ct_RETURN_VALUE], - std::to_string(res)); + if (!this->ReturnValue.empty()) { + this->Makefile->AddDefinition(this->ReturnValue, std::to_string(res)); } this->ProcessAdditionalValues(handler); // log the error message if there was an error @@ -245,8 +223,7 @@ bool cmCTestHandlerCommand::InitialPass(std::vector<std::string> const& args, } } // store the captured cmake error state 0 or -1 - this->Makefile->AddDefinition(this->Values[ct_CAPTURE_CMAKE_ERROR], - returnString); + this->Makefile->AddDefinition(this->CaptureCMakeError, returnString); } return true; } @@ -255,47 +232,17 @@ void cmCTestHandlerCommand::ProcessAdditionalValues(cmCTestGenericHandler*) { } -bool cmCTestHandlerCommand::CheckArgumentKeyword(std::string const& arg) +void cmCTestHandlerCommand::BindArguments() { - // Look for non-value arguments common to all commands. - if (arg == "APPEND") { - this->ArgumentDoing = ArgumentDoingNone; - this->AppendXML = true; - return true; - } - if (arg == "QUIET") { - this->ArgumentDoing = ArgumentDoingNone; - this->Quiet = true; - return true; - } - - // Check for a keyword in our argument/value table. - for (unsigned int k = 0; k < this->Arguments.size(); ++k) { - if (this->Arguments[k] && arg == this->Arguments[k]) { - this->ArgumentDoing = ArgumentDoingKeyword; - this->ArgumentIndex = k; - return true; - } - } - return false; + this->Bind("APPEND"_s, this->Append); + this->Bind("QUIET"_s, this->Quiet); + this->Bind("RETURN_VALUE"_s, this->ReturnValue); + this->Bind("CAPTURE_CMAKE_ERROR"_s, this->CaptureCMakeError); + this->Bind("SOURCE"_s, this->Source); + this->Bind("BUILD"_s, this->Build); + this->Bind("SUBMIT_INDEX"_s, this->SubmitIndex); } -bool cmCTestHandlerCommand::CheckArgumentValue(std::string const& arg) +void cmCTestHandlerCommand::CheckArguments(std::vector<std::string> const&) { - if (this->ArgumentDoing == ArgumentDoingKeyword) { - this->ArgumentDoing = ArgumentDoingNone; - unsigned int k = this->ArgumentIndex; - if (this->Values[k]) { - std::ostringstream e; - e << "Called with more than one value for " << this->Arguments[k]; - this->Makefile->IssueMessage(MessageType::FATAL_ERROR, e.str()); - this->ArgumentDoing = ArgumentDoingError; - return true; - } - this->Values[k] = arg.c_str(); - cmCTestLog(this->CTest, DEBUG, - "Set " << this->Arguments[k] << " to " << arg << "\n"); - return true; - } - return false; } diff --git a/Source/CTest/cmCTestHandlerCommand.h b/Source/CTest/cmCTestHandlerCommand.h index 79d61f3..5bbc569 100644 --- a/Source/CTest/cmCTestHandlerCommand.h +++ b/Source/CTest/cmCTestHandlerCommand.h @@ -5,9 +5,9 @@ #include "cmConfigure.h" // IWYU pragma: keep +#include "cmArgumentParser.h" #include "cmCTestCommand.h" -#include <stddef.h> #include <string> #include <vector> @@ -19,11 +19,11 @@ class cmExecutionStatus; * * cmCTestHandlerCommand defineds the command to test the project. */ -class cmCTestHandlerCommand : public cmCTestCommand +class cmCTestHandlerCommand + : public cmCTestCommand + , public cmArgumentParser<void> { public: - cmCTestHandlerCommand(); - /** * The name of the command as specified in CMakeList.txt. */ @@ -36,42 +36,22 @@ public: bool InitialPass(std::vector<std::string> const& args, cmExecutionStatus& status) override; - enum - { - ct_NONE, - ct_RETURN_VALUE, - ct_CAPTURE_CMAKE_ERROR, - ct_BUILD, - ct_SOURCE, - ct_SUBMIT_INDEX, - ct_LAST - }; - protected: virtual cmCTestGenericHandler* InitializeHandler() = 0; virtual void ProcessAdditionalValues(cmCTestGenericHandler* handler); // Command argument handling. - virtual bool CheckArgumentKeyword(std::string const& arg); - virtual bool CheckArgumentValue(std::string const& arg); - enum - { - ArgumentDoingNone, - ArgumentDoingError, - ArgumentDoingKeyword, - ArgumentDoingLast1 - }; - int ArgumentDoing; - unsigned int ArgumentIndex; - - bool AppendXML; - bool Quiet; - - std::string ReturnVariable; - std::vector<const char*> Arguments; - std::vector<const char*> Values; - size_t Last; + virtual void BindArguments(); + virtual void CheckArguments(std::vector<std::string> const& keywords); + + bool Append = false; + bool Quiet = false; + std::string CaptureCMakeError; + std::string ReturnValue; + std::string Build; + std::string Source; + std::string SubmitIndex; }; #define CTEST_COMMAND_APPEND_OPTION_DOCS \ diff --git a/Source/CTest/cmCTestMemCheckCommand.cxx b/Source/CTest/cmCTestMemCheckCommand.cxx index 804efa5..abad5fc 100644 --- a/Source/CTest/cmCTestMemCheckCommand.cxx +++ b/Source/CTest/cmCTestMemCheckCommand.cxx @@ -2,18 +2,15 @@ file Copyright.txt or https://cmake.org/licensing for details. */ #include "cmCTestMemCheckCommand.h" -#include <string> -#include <vector> - #include "cmCTest.h" #include "cmCTestMemCheckHandler.h" #include "cmMakefile.h" +#include "cm_static_string_view.hxx" -cmCTestMemCheckCommand::cmCTestMemCheckCommand() +void cmCTestMemCheckCommand::BindArguments() { - this->Arguments[ctm_DEFECT_COUNT] = "DEFECT_COUNT"; - this->Arguments[ctm_LAST] = nullptr; - this->Last = ctm_LAST; + this->cmCTestTestCommand::BindArguments(); + this->Bind("DEFECT_COUNT"_s, this->DefectCount); } cmCTestGenericHandler* cmCTestMemCheckCommand::InitializeActualHandler() @@ -43,9 +40,9 @@ cmCTestGenericHandler* cmCTestMemCheckCommand::InitializeActualHandler() void cmCTestMemCheckCommand::ProcessAdditionalValues( cmCTestGenericHandler* handler) { - if (this->Values[ctm_DEFECT_COUNT] && *this->Values[ctm_DEFECT_COUNT]) { + if (!this->DefectCount.empty()) { this->Makefile->AddDefinition( - this->Values[ctm_DEFECT_COUNT], + this->DefectCount, std::to_string( static_cast<cmCTestMemCheckHandler*>(handler)->GetDefectCount())); } diff --git a/Source/CTest/cmCTestMemCheckCommand.h b/Source/CTest/cmCTestMemCheckCommand.h index 5dad4e7..8f4ffb8 100644 --- a/Source/CTest/cmCTestMemCheckCommand.h +++ b/Source/CTest/cmCTestMemCheckCommand.h @@ -5,6 +5,7 @@ #include "cmConfigure.h" // IWYU pragma: keep +#include <string> #include <utility> #include <cm/memory> @@ -22,8 +23,6 @@ class cmCTestGenericHandler; class cmCTestMemCheckCommand : public cmCTestTestCommand { public: - cmCTestMemCheckCommand(); - /** * This is a virtual constructor for the command. */ @@ -36,15 +35,13 @@ public: } protected: + void BindArguments() override; + cmCTestGenericHandler* InitializeActualHandler() override; void ProcessAdditionalValues(cmCTestGenericHandler* handler) override; - enum - { - ctm_DEFECT_COUNT = ctt_LAST, - ctm_LAST - }; + std::string DefectCount; }; #endif diff --git a/Source/CTest/cmCTestSubmitCommand.cxx b/Source/CTest/cmCTestSubmitCommand.cxx index 2e2cf1a..706b45a 100644 --- a/Source/CTest/cmCTestSubmitCommand.cxx +++ b/Source/CTest/cmCTestSubmitCommand.cxx @@ -2,14 +2,18 @@ file Copyright.txt or https://cmake.org/licensing for details. */ #include "cmCTestSubmitCommand.h" +#include "cmAlgorithms.h" #include "cmCTest.h" #include "cmCTestSubmitHandler.h" #include "cmCommand.h" #include "cmMakefile.h" #include "cmMessageType.h" +#include "cmRange.h" #include "cmStringAlgorithms.h" #include "cmSystemTools.h" +#include "cm_static_string_view.hxx" +#include <set> #include <sstream> #include <utility> @@ -17,18 +21,6 @@ class cmExecutionStatus; -cmCTestSubmitCommand::cmCTestSubmitCommand() -{ - this->PartsMentioned = false; - this->FilesMentioned = false; - this->InternalTest = false; - this->RetryCount = ""; - this->RetryDelay = ""; - this->CDashUpload = false; - this->Arguments[cts_BUILD_ID] = "BUILD_ID"; - this->Last = cts_LAST; -} - /** * This is a virtual constructor for the command. */ @@ -106,13 +98,18 @@ cmCTestGenericHandler* cmCTestSubmitCommand::InitializeHandler() // without any of the default parts. // handler->SelectParts(std::set<cmCTest::Part>()); - handler->SelectFiles(this->Files); + handler->SelectFiles( + std::set<std::string>(this->Files.begin(), this->Files.end())); } // If a PARTS option was given, select only the named parts for submission. // if (this->PartsMentioned) { - handler->SelectParts(this->Parts); + auto parts = + cmMakeRange(this->Parts).transform([this](std::string const& arg) { + return this->CTest->GetPartFromName(arg.c_str()); + }); + handler->SelectParts(std::set<cmCTest::Part>(parts.begin(), parts.end())); } // Pass along any HTTPHEADER to the handler if this option was given. @@ -140,133 +137,61 @@ bool cmCTestSubmitCommand::InitialPass(std::vector<std::string> const& args, bool ret = this->cmCTestHandlerCommand::InitialPass(args, status); - if (this->Values[cts_BUILD_ID] && *this->Values[cts_BUILD_ID]) { - this->Makefile->AddDefinition(this->Values[cts_BUILD_ID], - this->CTest->GetBuildID()); + if (!this->BuildID.empty()) { + this->Makefile->AddDefinition(this->BuildID, this->CTest->GetBuildID()); } return ret; } -bool cmCTestSubmitCommand::CheckArgumentKeyword(std::string const& arg) +void cmCTestSubmitCommand::BindArguments() { if (this->CDashUpload) { // Arguments specific to the CDASH_UPLOAD signature. - if (arg == "CDASH_UPLOAD") { - this->ArgumentDoing = ArgumentDoingCDashUpload; - return true; - } - - if (arg == "CDASH_UPLOAD_TYPE") { - this->ArgumentDoing = ArgumentDoingCDashUploadType; - return true; - } + this->Bind("CDASH_UPLOAD", this->CDashUploadFile); + this->Bind("CDASH_UPLOAD_TYPE", this->CDashUploadType); } else { // Arguments that cannot be used with CDASH_UPLOAD. - if (arg == "PARTS") { - this->ArgumentDoing = ArgumentDoingParts; - this->PartsMentioned = true; - return true; - } - - if (arg == "FILES") { - this->ArgumentDoing = ArgumentDoingFiles; - this->FilesMentioned = true; - return true; - } + this->Bind("PARTS"_s, this->Parts); + this->Bind("FILES"_s, this->Files); } // Arguments used by both modes. - if (arg == "HTTPHEADER") { - this->ArgumentDoing = ArgumentDoingHttpHeader; - return true; - } - - if (arg == "RETRY_COUNT") { - this->ArgumentDoing = ArgumentDoingRetryCount; - return true; - } - - if (arg == "RETRY_DELAY") { - this->ArgumentDoing = ArgumentDoingRetryDelay; - return true; - } - - if (arg == "SUBMIT_URL") { - this->ArgumentDoing = ArgumentDoingSubmitURL; - return true; - } - - if (arg == "INTERNAL_TEST_CHECKSUM") { - this->InternalTest = true; - return true; - } + this->Bind("BUILD_ID"_s, this->BuildID); + this->Bind("HTTPHEADER"_s, this->HttpHeaders); + this->Bind("RETRY_COUNT"_s, this->RetryCount); + this->Bind("RETRY_DELAY"_s, this->RetryDelay); + this->Bind("SUBMIT_URL"_s, this->SubmitURL); + this->Bind("INTERNAL_TEST_CHECKSUM", this->InternalTest); // Look for other arguments. - return this->Superclass::CheckArgumentKeyword(arg); + this->cmCTestHandlerCommand::BindArguments(); } -bool cmCTestSubmitCommand::CheckArgumentValue(std::string const& arg) +void cmCTestSubmitCommand::CheckArguments( + std::vector<std::string> const& keywords) { - // Handle states specific to this command. - if (this->ArgumentDoing == ArgumentDoingParts) { + this->PartsMentioned = !this->Parts.empty() || cmContains(keywords, "PARTS"); + this->FilesMentioned = !this->Files.empty() || cmContains(keywords, "FILES"); + + cmEraseIf(this->Parts, [this](std::string const& arg) -> bool { cmCTest::Part p = this->CTest->GetPartFromName(arg.c_str()); - if (p != cmCTest::PartCount) { - this->Parts.insert(p); - } else { + if (p == cmCTest::PartCount) { std::ostringstream e; e << "Part name \"" << arg << "\" is invalid."; this->Makefile->IssueMessage(MessageType::FATAL_ERROR, e.str()); - this->ArgumentDoing = ArgumentDoingError; + return true; } - return true; - } + return false; + }); - if (this->ArgumentDoing == ArgumentDoingFiles) { - if (cmSystemTools::FileExists(arg)) { - this->Files.insert(arg); - } else { + cmEraseIf(this->Files, [this](std::string const& arg) -> bool { + if (!cmSystemTools::FileExists(arg)) { std::ostringstream e; e << "File \"" << arg << "\" does not exist. Cannot submit " << "a non-existent file."; this->Makefile->IssueMessage(MessageType::FATAL_ERROR, e.str()); - this->ArgumentDoing = ArgumentDoingError; + return true; } - return true; - } - - if (this->ArgumentDoing == ArgumentDoingHttpHeader) { - this->HttpHeaders.push_back(arg); - return true; - } - - if (this->ArgumentDoing == ArgumentDoingRetryCount) { - this->RetryCount = arg; - return true; - } - - if (this->ArgumentDoing == ArgumentDoingRetryDelay) { - this->RetryDelay = arg; - return true; - } - - if (this->ArgumentDoing == ArgumentDoingCDashUpload) { - this->ArgumentDoing = ArgumentDoingNone; - this->CDashUploadFile = arg; - return true; - } - - if (this->ArgumentDoing == ArgumentDoingCDashUploadType) { - this->ArgumentDoing = ArgumentDoingNone; - this->CDashUploadType = arg; - return true; - } - - if (this->ArgumentDoing == ArgumentDoingSubmitURL) { - this->ArgumentDoing = ArgumentDoingNone; - this->SubmitURL = arg; - return true; - } - - // Look for other arguments. - return this->Superclass::CheckArgumentValue(arg); + return false; + }); } diff --git a/Source/CTest/cmCTestSubmitCommand.h b/Source/CTest/cmCTestSubmitCommand.h index 8562207..249f844 100644 --- a/Source/CTest/cmCTestSubmitCommand.h +++ b/Source/CTest/cmCTestSubmitCommand.h @@ -5,11 +5,9 @@ #include "cmConfigure.h" // IWYU pragma: keep -#include "cmCTest.h" #include "cmCTestHandlerCommand.h" #include <memory> -#include <set> #include <string> #include <vector> @@ -26,7 +24,6 @@ class cmExecutionStatus; class cmCTestSubmitCommand : public cmCTestHandlerCommand { public: - cmCTestSubmitCommand(); std::unique_ptr<cmCommand> Clone() override; bool InitialPass(std::vector<std::string> const& args, @@ -37,45 +34,26 @@ public: */ std::string GetName() const override { return "ctest_submit"; } - using Superclass = cmCTestHandlerCommand; - protected: + void BindArguments() override; + void CheckArguments(std::vector<std::string> const& keywords) override; cmCTestGenericHandler* InitializeHandler() override; - bool CheckArgumentKeyword(std::string const& arg) override; - bool CheckArgumentValue(std::string const& arg) override; - - enum - { - ArgumentDoingParts = Superclass::ArgumentDoingLast1, - ArgumentDoingFiles, - ArgumentDoingRetryDelay, - ArgumentDoingRetryCount, - ArgumentDoingCDashUpload, - ArgumentDoingCDashUploadType, - ArgumentDoingHttpHeader, - ArgumentDoingSubmitURL, - ArgumentDoingLast2 - }; - - enum - { - cts_BUILD_ID = ct_LAST, - cts_LAST - }; + bool CDashUpload = false; + bool FilesMentioned = false; + bool InternalTest = false; + bool PartsMentioned = false; - bool PartsMentioned; - std::set<cmCTest::Part> Parts; - bool FilesMentioned; - bool InternalTest; - std::set<std::string> Files; - std::string RetryCount; - std::string RetryDelay; - bool CDashUpload; + std::string BuildID; std::string CDashUploadFile; std::string CDashUploadType; - std::vector<std::string> HttpHeaders; + std::string RetryCount; + std::string RetryDelay; std::string SubmitURL; + + std::vector<std::string> Files; + std::vector<std::string> HttpHeaders; + std::vector<std::string> Parts; }; #endif diff --git a/Source/CTest/cmCTestTestCommand.cxx b/Source/CTest/cmCTestTestCommand.cxx index 3a29ad3..c277db8 100644 --- a/Source/CTest/cmCTestTestCommand.cxx +++ b/Source/CTest/cmCTestTestCommand.cxx @@ -8,30 +8,29 @@ #include "cmDuration.h" #include "cmMakefile.h" #include "cmStringAlgorithms.h" +#include "cm_static_string_view.hxx" #include <chrono> #include <cstdlib> #include <sstream> -#include <vector> -cmCTestTestCommand::cmCTestTestCommand() +void cmCTestTestCommand::BindArguments() { - this->Arguments[ctt_START] = "START"; - this->Arguments[ctt_END] = "END"; - this->Arguments[ctt_STRIDE] = "STRIDE"; - this->Arguments[ctt_EXCLUDE] = "EXCLUDE"; - this->Arguments[ctt_INCLUDE] = "INCLUDE"; - this->Arguments[ctt_EXCLUDE_LABEL] = "EXCLUDE_LABEL"; - this->Arguments[ctt_INCLUDE_LABEL] = "INCLUDE_LABEL"; - this->Arguments[ctt_EXCLUDE_FIXTURE] = "EXCLUDE_FIXTURE"; - this->Arguments[ctt_EXCLUDE_FIXTURE_SETUP] = "EXCLUDE_FIXTURE_SETUP"; - this->Arguments[ctt_EXCLUDE_FIXTURE_CLEANUP] = "EXCLUDE_FIXTURE_CLEANUP"; - this->Arguments[ctt_PARALLEL_LEVEL] = "PARALLEL_LEVEL"; - this->Arguments[ctt_SCHEDULE_RANDOM] = "SCHEDULE_RANDOM"; - this->Arguments[ctt_STOP_TIME] = "STOP_TIME"; - this->Arguments[ctt_TEST_LOAD] = "TEST_LOAD"; - this->Arguments[ctt_LAST] = nullptr; - this->Last = ctt_LAST; + this->cmCTestHandlerCommand::BindArguments(); + this->Bind("START"_s, this->Start); + this->Bind("END"_s, this->End); + this->Bind("STRIDE"_s, this->Stride); + this->Bind("EXCLUDE"_s, this->Exclude); + this->Bind("INCLUDE"_s, this->Include); + this->Bind("EXCLUDE_LABEL"_s, this->ExcludeLabel); + this->Bind("INCLUDE_LABEL"_s, this->IncludeLabel); + this->Bind("EXCLUDE_FIXTURE"_s, this->ExcludeFixture); + this->Bind("EXCLUDE_FIXTURE_SETUP"_s, this->ExcludeFixtureSetup); + this->Bind("EXCLUDE_FIXTURE_CLEANUP"_s, this->ExcludeFixtureCleanup); + this->Bind("PARALLEL_LEVEL"_s, this->ParallelLevel); + this->Bind("SCHEDULE_RANDOM"_s, this->ScheduleRandom); + this->Bind("STOP_TIME"_s, this->StopTime); + this->Bind("TEST_LOAD"_s, this->TestLoad); } cmCTestGenericHandler* cmCTestTestCommand::InitializeHandler() @@ -51,57 +50,44 @@ cmCTestGenericHandler* cmCTestTestCommand::InitializeHandler() } this->CTest->SetTimeOut(timeout); cmCTestGenericHandler* handler = this->InitializeActualHandler(); - if (this->Values[ctt_START] || this->Values[ctt_END] || - this->Values[ctt_STRIDE]) { - std::ostringstream testsToRunString; - if (this->Values[ctt_START]) { - testsToRunString << this->Values[ctt_START]; - } - testsToRunString << ","; - if (this->Values[ctt_END]) { - testsToRunString << this->Values[ctt_END]; - } - testsToRunString << ","; - if (this->Values[ctt_STRIDE]) { - testsToRunString << this->Values[ctt_STRIDE]; - } - handler->SetOption("TestsToRunInformation", - testsToRunString.str().c_str()); + if (!this->Start.empty() || !this->End.empty() || !this->Stride.empty()) { + handler->SetOption( + "TestsToRunInformation", + cmStrCat(this->Start, ',', this->End, ',', this->Stride).c_str()); } - if (this->Values[ctt_EXCLUDE]) { - handler->SetOption("ExcludeRegularExpression", this->Values[ctt_EXCLUDE]); + if (!this->Exclude.empty()) { + handler->SetOption("ExcludeRegularExpression", this->Exclude.c_str()); } - if (this->Values[ctt_INCLUDE]) { - handler->SetOption("IncludeRegularExpression", this->Values[ctt_INCLUDE]); + if (!this->Include.empty()) { + handler->SetOption("IncludeRegularExpression", this->Include.c_str()); } - if (this->Values[ctt_EXCLUDE_LABEL]) { + if (!this->ExcludeLabel.empty()) { handler->SetOption("ExcludeLabelRegularExpression", - this->Values[ctt_EXCLUDE_LABEL]); + this->ExcludeLabel.c_str()); } - if (this->Values[ctt_INCLUDE_LABEL]) { - handler->SetOption("LabelRegularExpression", - this->Values[ctt_INCLUDE_LABEL]); + if (!this->IncludeLabel.empty()) { + handler->SetOption("LabelRegularExpression", this->IncludeLabel.c_str()); } - if (this->Values[ctt_EXCLUDE_FIXTURE]) { + if (!this->ExcludeFixture.empty()) { handler->SetOption("ExcludeFixtureRegularExpression", - this->Values[ctt_EXCLUDE_FIXTURE]); + this->ExcludeFixture.c_str()); } - if (this->Values[ctt_EXCLUDE_FIXTURE_SETUP]) { + if (!this->ExcludeFixtureSetup.empty()) { handler->SetOption("ExcludeFixtureSetupRegularExpression", - this->Values[ctt_EXCLUDE_FIXTURE_SETUP]); + this->ExcludeFixtureSetup.c_str()); } - if (this->Values[ctt_EXCLUDE_FIXTURE_CLEANUP]) { + if (!this->ExcludeFixtureCleanup.empty()) { handler->SetOption("ExcludeFixtureCleanupRegularExpression", - this->Values[ctt_EXCLUDE_FIXTURE_CLEANUP]); + this->ExcludeFixtureCleanup.c_str()); } - if (this->Values[ctt_PARALLEL_LEVEL]) { - handler->SetOption("ParallelLevel", this->Values[ctt_PARALLEL_LEVEL]); + if (!this->ParallelLevel.empty()) { + handler->SetOption("ParallelLevel", this->ParallelLevel.c_str()); } - if (this->Values[ctt_SCHEDULE_RANDOM]) { - handler->SetOption("ScheduleRandom", this->Values[ctt_SCHEDULE_RANDOM]); + if (!this->ScheduleRandom.empty()) { + handler->SetOption("ScheduleRandom", this->ScheduleRandom.c_str()); } - if (this->Values[ctt_STOP_TIME]) { - this->CTest->SetStopTime(this->Values[ctt_STOP_TIME]); + if (!this->StopTime.empty()) { + this->CTest->SetStopTime(this->StopTime); } // Test load is determined by: TEST_LOAD argument, @@ -109,12 +95,12 @@ cmCTestGenericHandler* cmCTestTestCommand::InitializeHandler() // command line argument... in that order. unsigned long testLoad; const char* ctestTestLoad = this->Makefile->GetDefinition("CTEST_TEST_LOAD"); - if (this->Values[ctt_TEST_LOAD] && *this->Values[ctt_TEST_LOAD]) { - if (!cmStrToULong(this->Values[ctt_TEST_LOAD], &testLoad)) { + if (!this->TestLoad.empty()) { + if (!cmStrToULong(this->TestLoad.c_str(), &testLoad)) { testLoad = 0; cmCTestLog(this->CTest, WARNING, - "Invalid value for 'TEST_LOAD' : " - << this->Values[ctt_TEST_LOAD] << std::endl); + "Invalid value for 'TEST_LOAD' : " << this->TestLoad + << std::endl); } } else if (ctestTestLoad && *ctestTestLoad) { if (!cmStrToULong(ctestTestLoad, &testLoad)) { diff --git a/Source/CTest/cmCTestTestCommand.h b/Source/CTest/cmCTestTestCommand.h index a9ba3ab..edd21b7 100644 --- a/Source/CTest/cmCTestTestCommand.h +++ b/Source/CTest/cmCTestTestCommand.h @@ -23,8 +23,6 @@ class cmCTestGenericHandler; class cmCTestTestCommand : public cmCTestHandlerCommand { public: - cmCTestTestCommand(); - /** * This is a virtual constructor for the command. */ @@ -42,29 +40,24 @@ public: std::string GetName() const override { return "ctest_test"; } protected: + void BindArguments() override; virtual cmCTestGenericHandler* InitializeActualHandler(); cmCTestGenericHandler* InitializeHandler() override; - enum - { - ctt_BUILD = ct_LAST, - ctt_RETURN_VALUE, - ctt_START, - ctt_END, - ctt_STRIDE, - ctt_EXCLUDE, - ctt_INCLUDE, - ctt_EXCLUDE_LABEL, - ctt_INCLUDE_LABEL, - ctt_EXCLUDE_FIXTURE, - ctt_EXCLUDE_FIXTURE_SETUP, - ctt_EXCLUDE_FIXTURE_CLEANUP, - ctt_PARALLEL_LEVEL, - ctt_SCHEDULE_RANDOM, - ctt_STOP_TIME, - ctt_TEST_LOAD, - ctt_LAST - }; + std::string Start; + std::string End; + std::string Stride; + std::string Exclude; + std::string Include; + std::string ExcludeLabel; + std::string IncludeLabel; + std::string ExcludeFixture; + std::string ExcludeFixtureSetup; + std::string ExcludeFixtureCleanup; + std::string ParallelLevel; + std::string ScheduleRandom; + std::string StopTime; + std::string TestLoad; }; #endif diff --git a/Source/CTest/cmCTestUpdateCommand.cxx b/Source/CTest/cmCTestUpdateCommand.cxx index 65dc921..673eb9a 100644 --- a/Source/CTest/cmCTestUpdateCommand.cxx +++ b/Source/CTest/cmCTestUpdateCommand.cxx @@ -7,14 +7,11 @@ #include "cmMakefile.h" #include "cmSystemTools.h" -#include <vector> - cmCTestGenericHandler* cmCTestUpdateCommand::InitializeHandler() { - if (this->Values[ct_SOURCE]) { + if (!this->Source.empty()) { this->CTest->SetCTestConfiguration( - "SourceDirectory", - cmSystemTools::CollapseFullPath(this->Values[ct_SOURCE]).c_str(), + "SourceDirectory", cmSystemTools::CollapseFullPath(this->Source).c_str(), this->Quiet); } else { this->CTest->SetCTestConfiguration( diff --git a/Source/CTest/cmCTestUpdateCommand.h b/Source/CTest/cmCTestUpdateCommand.h index 5b0e07e..a4798a5 100644 --- a/Source/CTest/cmCTestUpdateCommand.h +++ b/Source/CTest/cmCTestUpdateCommand.h @@ -23,8 +23,6 @@ class cmCTestGenericHandler; class cmCTestUpdateCommand : public cmCTestHandlerCommand { public: - cmCTestUpdateCommand() {} - /** * This is a virtual constructor for the command. */ diff --git a/Source/CTest/cmCTestUploadCommand.cxx b/Source/CTest/cmCTestUploadCommand.cxx index 59fbf37..9180821 100644 --- a/Source/CTest/cmCTestUploadCommand.cxx +++ b/Source/CTest/cmCTestUploadCommand.cxx @@ -2,61 +2,45 @@ file Copyright.txt or https://cmake.org/licensing for details. */ #include "cmCTestUploadCommand.h" +#include <set> #include <sstream> #include <vector> +#include "cmAlgorithms.h" #include "cmCTest.h" #include "cmCTestUploadHandler.h" #include "cmMakefile.h" #include "cmMessageType.h" #include "cmSystemTools.h" +#include "cm_static_string_view.hxx" -cmCTestGenericHandler* cmCTestUploadCommand::InitializeHandler() -{ - cmCTestUploadHandler* handler = this->CTest->GetUploadHandler(); - handler->Initialize(); - handler->SetFiles(this->Files); - handler->SetQuiet(this->Quiet); - return handler; -} - -bool cmCTestUploadCommand::CheckArgumentKeyword(std::string const& arg) +void cmCTestUploadCommand::BindArguments() { - if (arg == "FILES") { - this->ArgumentDoing = ArgumentDoingFiles; - return true; - } - if (arg == "QUIET") { - this->ArgumentDoing = ArgumentDoingNone; - this->Quiet = true; - return true; - } - if (arg == "CAPTURE_CMAKE_ERROR") { - this->ArgumentDoing = ArgumentDoingCaptureCMakeError; - return true; - } - return false; + this->Bind("FILES"_s, this->Files); + this->Bind("QUIET"_s, this->Quiet); + this->Bind("CAPTURE_CMAKE_ERROR"_s, this->CaptureCMakeError); } -bool cmCTestUploadCommand::CheckArgumentValue(std::string const& arg) +void cmCTestUploadCommand::CheckArguments(std::vector<std::string> const&) { - if (this->ArgumentDoing == ArgumentDoingCaptureCMakeError) { - this->Values[ct_CAPTURE_CMAKE_ERROR] = arg.c_str(); - return true; - } - if (this->ArgumentDoing == ArgumentDoingFiles) { - if (cmSystemTools::FileExists(arg)) { - this->Files.insert(arg); + cmEraseIf(this->Files, [this](std::string const& arg) -> bool { + if (!cmSystemTools::FileExists(arg)) { + std::ostringstream e; + e << "File \"" << arg << "\" does not exist. Cannot submit " + << "a non-existent file."; + this->Makefile->IssueMessage(MessageType::FATAL_ERROR, e.str()); return true; } - std::ostringstream e; - e << "File \"" << arg << "\" does not exist. Cannot submit " - << "a non-existent file."; - this->Makefile->IssueMessage(MessageType::FATAL_ERROR, e.str()); - this->ArgumentDoing = ArgumentDoingError; return false; - } + }); +} - // Look for other arguments. - return this->Superclass::CheckArgumentValue(arg); +cmCTestGenericHandler* cmCTestUploadCommand::InitializeHandler() +{ + cmCTestUploadHandler* handler = this->CTest->GetUploadHandler(); + handler->Initialize(); + handler->SetFiles( + std::set<std::string>(this->Files.begin(), this->Files.end())); + handler->SetQuiet(this->Quiet); + return handler; } diff --git a/Source/CTest/cmCTestUploadCommand.h b/Source/CTest/cmCTestUploadCommand.h index 39314f2..f78f0ec 100644 --- a/Source/CTest/cmCTestUploadCommand.h +++ b/Source/CTest/cmCTestUploadCommand.h @@ -8,9 +8,9 @@ #include "cmCTestHandlerCommand.h" #include "cmCommand.h" -#include <set> #include <string> #include <utility> +#include <vector> #include <cm/memory> @@ -41,22 +41,12 @@ public: */ std::string GetName() const override { return "ctest_upload"; } - using Superclass = cmCTestHandlerCommand; - protected: + void BindArguments() override; + void CheckArguments(std::vector<std::string> const&) override; cmCTestGenericHandler* InitializeHandler() override; - bool CheckArgumentKeyword(std::string const& arg) override; - bool CheckArgumentValue(std::string const& arg) override; - - enum - { - ArgumentDoingFiles = Superclass::ArgumentDoingLast1, - ArgumentDoingCaptureCMakeError, - ArgumentDoingLast2 - }; - - std::set<std::string> Files; + std::vector<std::string> Files; }; #endif diff --git a/Source/CursesDialog/CMakeLists.txt b/Source/CursesDialog/CMakeLists.txt index 270b07e..7009717 100644 --- a/Source/CursesDialog/CMakeLists.txt +++ b/Source/CursesDialog/CMakeLists.txt @@ -1,26 +1,22 @@ # Distributed under the OSI-approved BSD 3-Clause License. See accompanying # file Copyright.txt or https://cmake.org/licensing for details. -set( CURSES_SRCS - CursesDialog/cmCursesOptionsWidget.cxx - CursesDialog/cmCursesBoolWidget.cxx - CursesDialog/cmCursesCacheEntryComposite.cxx - CursesDialog/cmCursesDummyWidget.cxx - CursesDialog/cmCursesFilePathWidget.cxx - CursesDialog/cmCursesForm.cxx - CursesDialog/cmCursesLabelWidget.cxx - CursesDialog/cmCursesLongMessageForm.cxx - CursesDialog/cmCursesMainForm.cxx - CursesDialog/cmCursesPathWidget.cxx - CursesDialog/cmCursesStringWidget.cxx - CursesDialog/cmCursesWidget.cxx - CursesDialog/ccmake.cxx - ) - -include_directories(${CURSES_INCLUDE_PATH}) - - -add_executable(ccmake ${CURSES_SRCS} ) +add_executable(ccmake + ccmake.cxx + cmCursesBoolWidget.cxx + cmCursesCacheEntryComposite.cxx + cmCursesDummyWidget.cxx + cmCursesFilePathWidget.cxx + cmCursesForm.cxx + cmCursesLabelWidget.cxx + cmCursesLongMessageForm.cxx + cmCursesMainForm.cxx + cmCursesOptionsWidget.cxx + cmCursesPathWidget.cxx + cmCursesStringWidget.cxx + cmCursesWidget.cxx + ) +target_include_directories(ccmake PRIVATE ${CURSES_INCLUDE_PATH}) target_link_libraries(ccmake CMakeLib) if(CMAKE_USE_SYSTEM_FORM) find_path(CURSES_FORM_INCLUDE_DIR NAMES form.h HINTS ${CURSES_INCLUDE_PATH} ${CURSES_INCLUDE_PATH}/ncurses) diff --git a/Source/CursesDialog/cmCursesCacheEntryComposite.cxx b/Source/CursesDialog/cmCursesCacheEntryComposite.cxx index 47fe84c..561784c 100644 --- a/Source/CursesDialog/cmCursesCacheEntryComposite.cxx +++ b/Source/CursesDialog/cmCursesCacheEntryComposite.cxx @@ -13,9 +13,11 @@ #include "cmStateTypes.h" #include "cmStringAlgorithms.h" #include "cmSystemTools.h" -#include "cmake.h" + +#include <cm/memory> #include <cassert> +#include <utility> #include <vector> cmCursesCacheEntryComposite::cmCursesCacheEntryComposite( @@ -24,61 +26,65 @@ cmCursesCacheEntryComposite::cmCursesCacheEntryComposite( , LabelWidth(labelwidth) , EntryWidth(entrywidth) { - this->Label = new cmCursesLabelWidget(this->LabelWidth, 1, 1, 1, key); - this->IsNewLabel = new cmCursesLabelWidget(1, 1, 1, 1, " "); - this->Entry = nullptr; - this->Entry = new cmCursesStringWidget(this->EntryWidth, 1, 1, 1); + this->Label = + cm::make_unique<cmCursesLabelWidget>(this->LabelWidth, 1, 1, 1, key); + this->IsNewLabel = cm::make_unique<cmCursesLabelWidget>(1, 1, 1, 1, " "); + this->Entry = + cm::make_unique<cmCursesStringWidget>(this->EntryWidth, 1, 1, 1); } cmCursesCacheEntryComposite::cmCursesCacheEntryComposite( - const std::string& key, cmake* cm, bool isNew, int labelwidth, + const std::string& key, cmState* state, bool isNew, int labelwidth, int entrywidth) : Key(key) , LabelWidth(labelwidth) , EntryWidth(entrywidth) { - this->Label = new cmCursesLabelWidget(this->LabelWidth, 1, 1, 1, key); + this->Label = + cm::make_unique<cmCursesLabelWidget>(this->LabelWidth, 1, 1, 1, key); if (isNew) { - this->IsNewLabel = new cmCursesLabelWidget(1, 1, 1, 1, "*"); + this->IsNewLabel = cm::make_unique<cmCursesLabelWidget>(1, 1, 1, 1, "*"); } else { - this->IsNewLabel = new cmCursesLabelWidget(1, 1, 1, 1, " "); + this->IsNewLabel = cm::make_unique<cmCursesLabelWidget>(1, 1, 1, 1, " "); } - this->Entry = nullptr; - const char* value = cm->GetState()->GetCacheEntryValue(key); + const char* value = state->GetCacheEntryValue(key); assert(value); - switch (cm->GetState()->GetCacheEntryType(key)) { - case cmStateEnums::BOOL: - this->Entry = new cmCursesBoolWidget(this->EntryWidth, 1, 1, 1); - if (cmIsOn(value)) { - static_cast<cmCursesBoolWidget*>(this->Entry)->SetValueAsBool(true); - } else { - static_cast<cmCursesBoolWidget*>(this->Entry)->SetValueAsBool(false); - } + switch (state->GetCacheEntryType(key)) { + case cmStateEnums::BOOL: { + auto bw = cm::make_unique<cmCursesBoolWidget>(this->EntryWidth, 1, 1, 1); + bw->SetValueAsBool(cmIsOn(value)); + this->Entry = std::move(bw); break; - case cmStateEnums::PATH: - this->Entry = new cmCursesPathWidget(this->EntryWidth, 1, 1, 1); - static_cast<cmCursesPathWidget*>(this->Entry)->SetString(value); + } + case cmStateEnums::PATH: { + auto pw = cm::make_unique<cmCursesPathWidget>(this->EntryWidth, 1, 1, 1); + pw->SetString(value); + this->Entry = std::move(pw); break; - case cmStateEnums::FILEPATH: - this->Entry = new cmCursesFilePathWidget(this->EntryWidth, 1, 1, 1); - static_cast<cmCursesFilePathWidget*>(this->Entry)->SetString(value); + } + case cmStateEnums::FILEPATH: { + auto fpw = + cm::make_unique<cmCursesFilePathWidget>(this->EntryWidth, 1, 1, 1); + fpw->SetString(value); + this->Entry = std::move(fpw); break; + } case cmStateEnums::STRING: { - const char* stringsProp = - cm->GetState()->GetCacheEntryProperty(key, "STRINGS"); + const char* stringsProp = state->GetCacheEntryProperty(key, "STRINGS"); if (stringsProp) { - cmCursesOptionsWidget* ow = - new cmCursesOptionsWidget(this->EntryWidth, 1, 1, 1); - this->Entry = ow; - std::vector<std::string> options = cmExpandedList(stringsProp); - for (auto const& opt : options) { + auto ow = + cm::make_unique<cmCursesOptionsWidget>(this->EntryWidth, 1, 1, 1); + for (std::string const& opt : cmExpandedList(stringsProp)) { ow->AddOption(opt); } ow->SetOption(value); + this->Entry = std::move(ow); } else { - this->Entry = new cmCursesStringWidget(this->EntryWidth, 1, 1, 1); - static_cast<cmCursesStringWidget*>(this->Entry)->SetString(value); + auto sw = + cm::make_unique<cmCursesStringWidget>(this->EntryWidth, 1, 1, 1); + sw->SetString(value); + this->Entry = std::move(sw); } break; } @@ -91,12 +97,7 @@ cmCursesCacheEntryComposite::cmCursesCacheEntryComposite( } } -cmCursesCacheEntryComposite::~cmCursesCacheEntryComposite() -{ - delete this->Label; - delete this->IsNewLabel; - delete this->Entry; -} +cmCursesCacheEntryComposite::~cmCursesCacheEntryComposite() = default; const char* cmCursesCacheEntryComposite::GetValue() { diff --git a/Source/CursesDialog/cmCursesCacheEntryComposite.h b/Source/CursesDialog/cmCursesCacheEntryComposite.h index 0a69d3a..a711363 100644 --- a/Source/CursesDialog/cmCursesCacheEntryComposite.h +++ b/Source/CursesDialog/cmCursesCacheEntryComposite.h @@ -5,33 +5,38 @@ #include "cmConfigure.h" // IWYU pragma: keep +#include <memory> #include <string> class cmCursesLabelWidget; class cmCursesWidget; -class cmake; +class cmState; class cmCursesCacheEntryComposite { public: cmCursesCacheEntryComposite(const std::string& key, int labelwidth, int entrywidth); - cmCursesCacheEntryComposite(const std::string& key, cmake* cm, bool isNew, - int labelwidth, int entrywidth); + cmCursesCacheEntryComposite(const std::string& key, cmState* state, + bool isNew, int labelwidth, int entrywidth); ~cmCursesCacheEntryComposite(); cmCursesCacheEntryComposite(cmCursesCacheEntryComposite const&) = delete; cmCursesCacheEntryComposite& operator=(cmCursesCacheEntryComposite const&) = delete; + cmCursesCacheEntryComposite(cmCursesCacheEntryComposite&&) = default; + cmCursesCacheEntryComposite& operator=(cmCursesCacheEntryComposite&&) = + default; + const char* GetValue(); friend class cmCursesMainForm; protected: - cmCursesLabelWidget* Label; - cmCursesLabelWidget* IsNewLabel; - cmCursesWidget* Entry; + std::unique_ptr<cmCursesLabelWidget> Label; + std::unique_ptr<cmCursesLabelWidget> IsNewLabel; + std::unique_ptr<cmCursesWidget> Entry; std::string Key; int LabelWidth; int EntryWidth; diff --git a/Source/CursesDialog/cmCursesMainForm.cxx b/Source/CursesDialog/cmCursesMainForm.cxx index 5f8a19e..219771b 100644 --- a/Source/CursesDialog/cmCursesMainForm.cxx +++ b/Source/CursesDialog/cmCursesMainForm.cxx @@ -2,7 +2,6 @@ file Copyright.txt or https://cmake.org/licensing for details. */ #include "cmCursesMainForm.h" -#include "cmAlgorithms.h" #include "cmCursesCacheEntryComposite.h" #include "cmCursesDummyWidget.h" #include "cmCursesForm.h" @@ -18,6 +17,8 @@ #include "cmVersion.h" #include "cmake.h" +#include <cm/memory> + #include <algorithm> #include <cstdio> #include <cstring> @@ -34,8 +35,6 @@ cmCursesMainForm::cmCursesMainForm(std::vector<std::string> args, , InitialWidth(initWidth) { this->NumberOfPages = 0; - this->Fields = nullptr; - this->Entries = nullptr; this->AdvancedMode = false; this->NumberOfVisibleEntries = 0; this->OkToGenerate = false; @@ -43,7 +42,8 @@ cmCursesMainForm::cmCursesMainForm(std::vector<std::string> args, "Welcome to ccmake, curses based user interface for CMake."); this->HelpMessage.emplace_back(); this->HelpMessage.emplace_back(s_ConstHelpMessage); - this->CMakeInstance = new cmake(cmake::RoleProject, cmState::Project); + this->CMakeInstance = + cm::make_unique<cmake>(cmake::RoleProject, cmState::Project); this->CMakeInstance->SetCMakeEditCommand( cmSystemTools::GetCMakeCursesCommand()); @@ -52,8 +52,6 @@ cmCursesMainForm::cmCursesMainForm(std::vector<std::string> args, cmStrCat(cmSystemTools::GetProgramPath(this->Args[0]), "/cmake"); this->Args[0] = whereCMake; this->CMakeInstance->SetArgs(this->Args); - this->SearchString = ""; - this->OldSearchString = ""; this->SearchMode = false; } @@ -64,27 +62,15 @@ cmCursesMainForm::~cmCursesMainForm() free_form(this->Form); this->Form = nullptr; } - delete[] this->Fields; - - // Clean-up composites - if (this->Entries) { - cmDeleteAll(*this->Entries); - } - delete this->Entries; - if (this->CMakeInstance) { - delete this->CMakeInstance; - this->CMakeInstance = nullptr; - } } // See if a cache entry is in the list of entries in the ui. bool cmCursesMainForm::LookForCacheEntry(const std::string& key) { - return this->Entries && - std::any_of(this->Entries->begin(), this->Entries->end(), - [&key](cmCursesCacheEntryComposite* entry) { - return key == entry->Key; - }); + return std::any_of(this->Entries.begin(), this->Entries.end(), + [&key](cmCursesCacheEntryComposite const& entry) { + return key == entry.Key; + }); } // Create new cmCursesCacheEntryComposite entries from the cache @@ -92,11 +78,10 @@ void cmCursesMainForm::InitializeUI() { // Create a vector of cmCursesCacheEntryComposite's // which contain labels, entries and new entry markers - std::vector<cmCursesCacheEntryComposite*>* newEntries = - new std::vector<cmCursesCacheEntryComposite*>; + std::vector<cmCursesCacheEntryComposite> newEntries; std::vector<std::string> cacheKeys = this->CMakeInstance->GetState()->GetCacheEntryKeys(); - newEntries->reserve(cacheKeys.size()); + newEntries.reserve(cacheKeys.size()); // Count non-internal and non-static entries int count = 0; @@ -112,13 +97,12 @@ void cmCursesMainForm::InitializeUI() int entrywidth = this->InitialWidth - 35; - cmCursesCacheEntryComposite* comp; if (count == 0) { // If cache is empty, display a label saying so and a // dummy entry widget (does not respond to input) - comp = new cmCursesCacheEntryComposite("EMPTY CACHE", 30, 30); - comp->Entry = new cmCursesDummyWidget(1, 1, 1, 1); - newEntries->push_back(comp); + cmCursesCacheEntryComposite comp("EMPTY CACHE", 30, 30); + comp.Entry = cm::make_unique<cmCursesDummyWidget>(1, 1, 1, 1); + newEntries.emplace_back(std::move(comp)); } else { // Create the composites. @@ -132,8 +116,8 @@ void cmCursesMainForm::InitializeUI() } if (!this->LookForCacheEntry(key)) { - newEntries->push_back(new cmCursesCacheEntryComposite( - key, this->CMakeInstance, true, 30, entrywidth)); + newEntries.emplace_back(key, this->CMakeInstance->GetState(), true, 30, + entrywidth); this->OkToGenerate = false; } } @@ -148,18 +132,14 @@ void cmCursesMainForm::InitializeUI() } if (this->LookForCacheEntry(key)) { - newEntries->push_back(new cmCursesCacheEntryComposite( - key, this->CMakeInstance, false, 30, entrywidth)); + newEntries.emplace_back(key, this->CMakeInstance->GetState(), false, + 30, entrywidth); } } } - // Clean old entries - if (this->Entries) { - cmDeleteAll(*this->Entries); - } - delete this->Entries; - this->Entries = newEntries; + // Replace old entries + this->Entries = std::move(newEntries); // Compute fields from composites this->RePost(); @@ -173,18 +153,18 @@ void cmCursesMainForm::RePost() free_form(this->Form); this->Form = nullptr; } - delete[] this->Fields; + this->Fields.clear(); if (this->AdvancedMode) { - this->NumberOfVisibleEntries = this->Entries->size(); + this->NumberOfVisibleEntries = this->Entries.size(); } else { // If normal mode, count only non-advanced entries this->NumberOfVisibleEntries = 0; - for (cmCursesCacheEntryComposite* entry : *this->Entries) { + for (cmCursesCacheEntryComposite& entry : this->Entries) { const char* existingValue = - this->CMakeInstance->GetState()->GetCacheEntryValue(entry->GetValue()); + this->CMakeInstance->GetState()->GetCacheEntryValue(entry.GetValue()); bool advanced = this->CMakeInstance->GetState()->GetCacheEntryPropertyAsBool( - entry->GetValue(), "ADVANCED"); + entry.GetValue(), "ADVANCED"); if (!existingValue || (!this->AdvancedMode && advanced)) { continue; } @@ -197,38 +177,32 @@ void cmCursesMainForm::RePost() } // Assign the fields: 3 for each entry: label, new entry marker // ('*' or ' ') and entry widget - this->Fields = new FIELD*[3 * this->NumberOfVisibleEntries + 1]; - size_t cc; - for (cc = 0; cc < 3 * this->NumberOfVisibleEntries + 1; cc++) { - this->Fields[cc] = nullptr; - } + this->Fields.reserve(3 * this->NumberOfVisibleEntries + 1); // Assign fields - int j = 0; - for (cmCursesCacheEntryComposite* entry : *this->Entries) { + for (cmCursesCacheEntryComposite& entry : this->Entries) { const char* existingValue = - this->CMakeInstance->GetState()->GetCacheEntryValue(entry->GetValue()); + this->CMakeInstance->GetState()->GetCacheEntryValue(entry.GetValue()); bool advanced = this->CMakeInstance->GetState()->GetCacheEntryPropertyAsBool( - entry->GetValue(), "ADVANCED"); + entry.GetValue(), "ADVANCED"); if (!existingValue || (!this->AdvancedMode && advanced)) { continue; } - this->Fields[3 * j] = entry->Label->Field; - this->Fields[3 * j + 1] = entry->IsNewLabel->Field; - this->Fields[3 * j + 2] = entry->Entry->Field; - j++; + this->Fields.push_back(entry.Label->Field); + this->Fields.push_back(entry.IsNewLabel->Field); + this->Fields.push_back(entry.Entry->Field); } // if no cache entries there should still be one dummy field - if (j == 0) { - const auto& front = *this->Entries->front(); - this->Fields[0] = front.Label->Field; - this->Fields[1] = front.IsNewLabel->Field; - this->Fields[2] = front.Entry->Field; + if (this->Fields.empty()) { + const auto& front = this->Entries.front(); + this->Fields.push_back(front.Label->Field); + this->Fields.push_back(front.IsNewLabel->Field); + this->Fields.push_back(front.Entry->Field); this->NumberOfVisibleEntries = 1; } // Has to be null terminated. - this->Fields[3 * this->NumberOfVisibleEntries] = nullptr; + this->Fields.push_back(nullptr); } void cmCursesMainForm::Render(int left, int top, int width, int height) @@ -261,16 +235,16 @@ void cmCursesMainForm::Render(int left, int top, int width, int height) height -= 7; if (this->AdvancedMode) { - this->NumberOfVisibleEntries = this->Entries->size(); + this->NumberOfVisibleEntries = this->Entries.size(); } else { // If normal, display only non-advanced entries this->NumberOfVisibleEntries = 0; - for (cmCursesCacheEntryComposite* entry : *this->Entries) { + for (cmCursesCacheEntryComposite& entry : this->Entries) { const char* existingValue = - this->CMakeInstance->GetState()->GetCacheEntryValue(entry->GetValue()); + this->CMakeInstance->GetState()->GetCacheEntryValue(entry.GetValue()); bool advanced = this->CMakeInstance->GetState()->GetCacheEntryPropertyAsBool( - entry->GetValue(), "ADVANCED"); + entry.GetValue(), "ADVANCED"); if (!existingValue || (!this->AdvancedMode && advanced)) { continue; } @@ -283,12 +257,12 @@ void cmCursesMainForm::Render(int left, int top, int width, int height) if (height > 0) { bool isNewPage; int i = 0; - for (cmCursesCacheEntryComposite* entry : *this->Entries) { + for (cmCursesCacheEntryComposite& entry : this->Entries) { const char* existingValue = - this->CMakeInstance->GetState()->GetCacheEntryValue(entry->GetValue()); + this->CMakeInstance->GetState()->GetCacheEntryValue(entry.GetValue()); bool advanced = this->CMakeInstance->GetState()->GetCacheEntryPropertyAsBool( - entry->GetValue(), "ADVANCED"); + entry.GetValue(), "ADVANCED"); if (!existingValue || (!this->AdvancedMode && advanced)) { continue; } @@ -299,16 +273,16 @@ void cmCursesMainForm::Render(int left, int top, int width, int height) if (isNewPage) { this->NumberOfPages++; } - entry->Label->Move(left, top + row - 1, isNewPage); - entry->IsNewLabel->Move(left + 32, top + row - 1, false); - entry->Entry->Move(left + 33, top + row - 1, false); - entry->Entry->SetPage(this->NumberOfPages); + entry.Label->Move(left, top + row - 1, isNewPage); + entry.IsNewLabel->Move(left + 32, top + row - 1, false); + entry.Entry->Move(left + 33, top + row - 1, false); + entry.Entry->SetPage(this->NumberOfPages); i++; } } // Post the form - this->Form = new_form(this->Fields); + this->Form = new_form(this->Fields.data()); post_form(this->Form); // Update toolbar this->UpdateStatusBar(); @@ -655,28 +629,28 @@ void cmCursesMainForm::RemoveEntry(const char* value) } auto removeIt = - std::find_if(this->Entries->begin(), this->Entries->end(), - [value](cmCursesCacheEntryComposite* entry) -> bool { - const char* val = entry->GetValue(); + std::find_if(this->Entries.begin(), this->Entries.end(), + [value](cmCursesCacheEntryComposite& entry) -> bool { + const char* val = entry.GetValue(); return val != nullptr && !strcmp(value, val); }); - if (removeIt != this->Entries->end()) { + if (removeIt != this->Entries.end()) { this->CMakeInstance->UnwatchUnusedCli(value); - this->Entries->erase(removeIt); + this->Entries.erase(removeIt); } } // copy from the list box to the cache manager void cmCursesMainForm::FillCacheManagerFromUI() { - for (cmCursesCacheEntryComposite* entry : *this->Entries) { - const std::string& cacheKey = entry->Key; + for (cmCursesCacheEntryComposite& entry : this->Entries) { + const std::string& cacheKey = entry.Key; const char* existingValue = this->CMakeInstance->GetState()->GetCacheEntryValue(cacheKey); if (existingValue) { std::string oldValue = existingValue; - std::string newValue = entry->Entry->GetValue(); + std::string newValue = entry.Entry->GetValue(); std::string fixedOldValue; std::string fixedNewValue; cmStateEnums::CacheEntryType t = @@ -762,7 +736,7 @@ void cmCursesMainForm::HandleInput() this->JumpToCacheEntry(this->SearchString.c_str()); this->OldSearchString = this->SearchString; } - this->SearchString = ""; + this->SearchString.clear(); } /* else if ( key == KEY_ESCAPE ) @@ -778,7 +752,7 @@ void cmCursesMainForm::HandleInput() } } else if (key == ctrl('h') || key == KEY_BACKSPACE || key == KEY_DC) { if (!this->SearchString.empty()) { - this->SearchString.resize(this->SearchString.size() - 1); + this->SearchString.pop_back(); } } } else if (currentWidget && !this->SearchMode) { @@ -867,17 +841,9 @@ void cmCursesMainForm::HandleInput() curField, "HELPSTRING"); } if (helpString) { - char* message = new char - [strlen(curField) + strlen(helpString) + - strlen( - "Current option is: \n Help string for this option is: \n") + - 10]; - sprintf( - message, - "Current option is: %s\nHelp string for this option is: %s\n", - curField, helpString); - this->HelpMessage[1] = message; - delete[] message; + this->HelpMessage[1] = + cmStrCat("Current option is: ", curField, '\n', + "Help string for this option is: ", helpString, '\n'); } else { this->HelpMessage[1] = ""; } @@ -973,14 +939,14 @@ void cmCursesMainForm::HandleInput() if (nextCur) { // make the next or prev. current field after deletion - auto nextEntryIt = - std::find_if(this->Entries->begin(), this->Entries->end(), - [&nextVal](cmCursesCacheEntryComposite* entry) { - return nextVal == entry->Key; - }); - - if (nextEntryIt != this->Entries->end()) { - set_current_field(this->Form, (*nextEntryIt)->Entry->Field); + auto nextEntryIt = std::find_if( + this->Entries.begin(), this->Entries.end(), + [&nextVal](cmCursesCacheEntryComposite const& entry) { + return nextVal == entry.Key; + }); + + if (nextEntryIt != this->Entries.end()) { + set_current_field(this->Form, nextEntryIt->Entry->Field); } } } diff --git a/Source/CursesDialog/cmCursesMainForm.h b/Source/CursesDialog/cmCursesMainForm.h index f3194ab..48d1791 100644 --- a/Source/CursesDialog/cmCursesMainForm.h +++ b/Source/CursesDialog/cmCursesMainForm.h @@ -5,15 +5,16 @@ #include "cmConfigure.h" // IWYU pragma: keep +#include "cmCursesCacheEntryComposite.h" #include "cmCursesForm.h" #include "cmCursesStandardIncludes.h" #include "cmStateTypes.h" #include <cstddef> +#include <memory> #include <string> #include <vector> -class cmCursesCacheEntryComposite; class cmake; /** \class cmCursesMainForm @@ -122,7 +123,7 @@ protected: void JumpToCacheEntry(const char* str); // Copies of cache entries stored in the user interface - std::vector<cmCursesCacheEntryComposite*>* Entries; + std::vector<cmCursesCacheEntryComposite> Entries; // Errors produced during last run of cmake std::vector<std::string> Errors; // Command line arguments to be passed to cmake each time @@ -136,11 +137,7 @@ protected: static const char* s_ConstHelpMessage; // Fields displayed. Includes labels, new entry markers, entries - FIELD** Fields; - // Where is source of current project - std::string WhereSource; - // Where is cmake executable - std::string WhereCMake; + std::vector<FIELD*> Fields; // Number of entries shown (depends on mode -normal or advanced-) size_t NumberOfVisibleEntries; bool AdvancedMode; @@ -150,7 +147,7 @@ protected: int NumberOfPages; int InitialWidth; - cmake* CMakeInstance; + std::unique_ptr<cmake> CMakeInstance; std::string SearchString; std::string OldSearchString; diff --git a/Source/CursesDialog/cmCursesStringWidget.cxx b/Source/CursesDialog/cmCursesStringWidget.cxx index 3fc1858..d3a05e8 100644 --- a/Source/CursesDialog/cmCursesStringWidget.cxx +++ b/Source/CursesDialog/cmCursesStringWidget.cxx @@ -9,7 +9,6 @@ #include "cmStateTypes.h" #include <cstdio> -#include <cstring> inline int ctrl(int z) { @@ -35,13 +34,13 @@ void cmCursesStringWidget::OnTab(cmCursesMainForm* /*unused*/, void cmCursesStringWidget::OnReturn(cmCursesMainForm* fm, WINDOW* /*unused*/) { - FORM* form = fm->GetForm(); if (this->InEdit) { cmCursesForm::LogMessage("String widget leaving edit."); this->InEdit = false; fm->PrintKeys(); - delete[] this->OriginalString; + this->OriginalString.clear(); // trick to force forms to update the field buffer + FORM* form = fm->GetForm(); form_driver(form, REQ_NEXT_FIELD); form_driver(form, REQ_PREV_FIELD); this->Done = true; @@ -49,9 +48,7 @@ void cmCursesStringWidget::OnReturn(cmCursesMainForm* fm, WINDOW* /*unused*/) cmCursesForm::LogMessage("String widget entering edit."); this->InEdit = true; fm->PrintKeys(); - char* buf = field_buffer(this->Field, 0); - this->OriginalString = new char[strlen(buf) + 1]; - strcpy(this->OriginalString, buf); + this->OriginalString = field_buffer(this->Field, 0); } } @@ -75,7 +72,7 @@ bool cmCursesStringWidget::HandleInput(int& key, cmCursesMainForm* fm, return false; } - this->OriginalString = nullptr; + this->OriginalString.clear(); this->Done = false; char debugMessage[128]; @@ -113,7 +110,7 @@ bool cmCursesStringWidget::HandleInput(int& key, cmCursesMainForm* fm, key == ctrl('p') || key == KEY_NPAGE || key == ctrl('d') || key == KEY_PPAGE || key == ctrl('u')) { this->InEdit = false; - delete[] this->OriginalString; + this->OriginalString.clear(); // trick to force forms to update the field buffer form_driver(form, REQ_NEXT_FIELD); form_driver(form, REQ_PREV_FIELD); @@ -125,7 +122,7 @@ bool cmCursesStringWidget::HandleInput(int& key, cmCursesMainForm* fm, this->InEdit = false; fm->PrintKeys(); this->SetString(this->OriginalString); - delete[] this->OriginalString; + this->OriginalString.clear(); touchwin(w); wrefresh(w); return true; @@ -188,23 +185,18 @@ bool cmCursesStringWidget::PrintKeys() } if (this->InEdit) { char fmt_s[] = "%s"; - char firstLine[512]; // Clean the toolbar - memset(firstLine, ' ', sizeof(firstLine)); - firstLine[511] = '\0'; curses_move(y - 4, 0); - printw(fmt_s, firstLine); - curses_move(y - 3, 0); - printw(fmt_s, firstLine); - curses_move(y - 2, 0); - printw(fmt_s, firstLine); - curses_move(y - 1, 0); - printw(fmt_s, firstLine); - + clrtoeol(); curses_move(y - 3, 0); printw(fmt_s, "Editing option, press [enter] to confirm"); + clrtoeol(); curses_move(y - 2, 0); printw(fmt_s, " press [esc] to cancel"); + clrtoeol(); + curses_move(y - 1, 0); + clrtoeol(); + return true; } return false; diff --git a/Source/CursesDialog/cmCursesStringWidget.h b/Source/CursesDialog/cmCursesStringWidget.h index 021515b..a41f0e8 100644 --- a/Source/CursesDialog/cmCursesStringWidget.h +++ b/Source/CursesDialog/cmCursesStringWidget.h @@ -23,9 +23,6 @@ class cmCursesStringWidget : public cmCursesWidget public: cmCursesStringWidget(int width, int height, int left, int top); - cmCursesStringWidget(cmCursesStringWidget const&) = delete; - cmCursesStringWidget& operator=(cmCursesStringWidget const&) = delete; - /** * Handle user input. Called by the container of this widget * when this widget has focus. Returns true if the input was @@ -65,7 +62,7 @@ public: protected: // true if the widget is in edit mode bool InEdit; - char* OriginalString; + std::string OriginalString; bool Done; }; diff --git a/Source/cmAddCustomCommandCommand.cxx b/Source/cmAddCustomCommandCommand.cxx index 35db6a4..6e04ce5 100644 --- a/Source/cmAddCustomCommandCommand.cxx +++ b/Source/cmAddCustomCommandCommand.cxx @@ -5,8 +5,10 @@ #include <sstream> #include <unordered_set> +#include "cmCheckCustomOutputs.h" #include "cmCustomCommand.h" #include "cmCustomCommandLines.h" +#include "cmCustomCommandTypes.h" #include "cmExecutionStatus.h" #include "cmGlobalGenerator.h" #include "cmMakefile.h" @@ -14,10 +16,6 @@ #include "cmPolicies.h" #include "cmStringAlgorithms.h" #include "cmSystemTools.h" -#include "cmTarget.h" - -static bool cmAddCustomCommandCommandCheckOutputs( - const std::vector<std::string>& outputs, cmExecutionStatus& status); bool cmAddCustomCommandCommand(std::vector<std::string> const& args, cmExecutionStatus& status) @@ -57,7 +55,7 @@ bool cmAddCustomCommandCommand(std::vector<std::string> const& args, // Save all command lines. cmCustomCommandLines commandLines; - cmTarget::CustomCommandType cctype = cmTarget::POST_BUILD; + cmCustomCommandType cctype = cmCustomCommandType::POST_BUILD; enum tdoing { @@ -141,11 +139,11 @@ bool cmAddCustomCommandCommand(std::vector<std::string> const& args, currentLine.clear(); } } else if (copy == keyPRE_BUILD) { - cctype = cmTarget::PRE_BUILD; + cctype = cmCustomCommandType::PRE_BUILD; } else if (copy == keyPRE_LINK) { - cctype = cmTarget::PRE_LINK; + cctype = cmCustomCommandType::PRE_LINK; } else if (copy == keyPOST_BUILD) { - cctype = cmTarget::POST_BUILD; + cctype = cmCustomCommandType::POST_BUILD; } else if (copy == keyVERBATIM) { verbatim = true; } else if (copy == keyAPPEND) { @@ -307,9 +305,9 @@ bool cmAddCustomCommandCommand(std::vector<std::string> const& args, } // Make sure the output names and locations are safe. - if (!cmAddCustomCommandCommandCheckOutputs(output, status) || - !cmAddCustomCommandCommandCheckOutputs(outputs, status) || - !cmAddCustomCommandCommandCheckOutputs(byproducts, status)) { + if (!cmCheckCustomOutputs(output, "OUTPUT", status) || + !cmCheckCustomOutputs(outputs, "OUTPUTS", status) || + !cmCheckCustomOutputs(byproducts, "BYPRODUCTS", status)) { return false; } @@ -322,8 +320,8 @@ bool cmAddCustomCommandCommand(std::vector<std::string> const& args, // No command for this output exists. status.SetError( - cmStrCat("given APPEND option with output\n\"", output[0], - "\"\nwhich is not already a custom command output.")); + cmStrCat("given APPEND option with output\n ", output[0], + "\nwhich is not already a custom command output.")); return false; } @@ -387,29 +385,3 @@ bool cmAddCustomCommandCommand(std::vector<std::string> const& args, return true; } - -bool cmAddCustomCommandCommandCheckOutputs( - const std::vector<std::string>& outputs, cmExecutionStatus& status) -{ - cmMakefile& mf = status.GetMakefile(); - for (std::string const& o : outputs) { - // Make sure the file will not be generated into the source - // directory during an out of source build. - if (!mf.CanIWriteThisFile(o)) { - std::string e = "attempted to have a file \"" + o + - "\" in a source directory as an output of custom command."; - status.SetError(e); - cmSystemTools::SetFatalErrorOccured(); - return false; - } - - // Make sure the output file name has no invalid characters. - std::string::size_type pos = o.find_first_of("#<>"); - if (pos != std::string::npos) { - status.SetError(cmStrCat("called with OUTPUT containing a \"", o[pos], - "\". This character is not allowed.")); - return false; - } - } - return true; -} diff --git a/Source/cmAddCustomTargetCommand.cxx b/Source/cmAddCustomTargetCommand.cxx index 9fd1234..e27b126 100644 --- a/Source/cmAddCustomTargetCommand.cxx +++ b/Source/cmAddCustomTargetCommand.cxx @@ -4,7 +4,9 @@ #include <utility> +#include "cmCheckCustomOutputs.h" #include "cmCustomCommandLines.h" +#include "cmCustomCommandTypes.h" #include "cmExecutionStatus.h" #include "cmGeneratorExpression.h" #include "cmGlobalGenerator.h" @@ -205,10 +207,15 @@ bool cmAddCustomTargetCommand(std::vector<std::string> const& args, return false; } + // Make sure the byproduct names and locations are safe. + if (!cmCheckCustomOutputs(byproducts, "BYPRODUCTS", status)) { + return false; + } + // Add the utility target to the makefile. bool escapeOldStyle = !verbatim; cmTarget* target = mf.AddUtilityCommand( - targetName, cmMakefile::TargetOrigin::Project, excludeFromAll, + targetName, cmCommandOrigin::Project, excludeFromAll, working_directory.c_str(), byproducts, depends, commandLines, escapeOldStyle, comment, uses_terminal, command_expand_lists, job_pool); diff --git a/Source/cmArgumentParser.cxx b/Source/cmArgumentParser.cxx index 751d117..4c87177 100644 --- a/Source/cmArgumentParser.cxx +++ b/Source/cmArgumentParser.cxx @@ -61,10 +61,14 @@ void Instance::Bind(MultiStringList& val) void Instance::Consume(cm::string_view arg, void* result, std::vector<std::string>* unparsedArguments, - std::vector<std::string>* keywordsMissingValue) + std::vector<std::string>* keywordsMissingValue, + std::vector<std::string>* parsedKeywords) { auto const it = this->Bindings.Find(arg); if (it != this->Bindings.end()) { + if (parsedKeywords != nullptr) { + parsedKeywords->emplace_back(arg); + } it->second(*this, result); if (this->ExpectValue && keywordsMissingValue != nullptr) { keywordsMissingValue->emplace_back(arg); diff --git a/Source/cmArgumentParser.h b/Source/cmArgumentParser.h index b6798bc..9be2488 100644 --- a/Source/cmArgumentParser.h +++ b/Source/cmArgumentParser.h @@ -45,7 +45,8 @@ public: void Consume(cm::string_view arg, void* result, std::vector<std::string>* unparsedArguments, - std::vector<std::string>* keywordsMissingValue); + std::vector<std::string>* keywordsMissingValue, + std::vector<std::string>* parsedKeywords); private: ActionMap const& Bindings; @@ -79,21 +80,25 @@ public: template <typename Range> void Parse(Result& result, Range const& args, std::vector<std::string>* unparsedArguments = nullptr, - std::vector<std::string>* keywordsMissingValue = nullptr) const + std::vector<std::string>* keywordsMissingValue = nullptr, + std::vector<std::string>* parsedKeywords = nullptr) const { ArgumentParser::Instance instance(this->Bindings); for (cm::string_view arg : args) { - instance.Consume(arg, &result, unparsedArguments, keywordsMissingValue); + instance.Consume(arg, &result, unparsedArguments, keywordsMissingValue, + parsedKeywords); } } template <typename Range> Result Parse(Range const& args, std::vector<std::string>* unparsedArguments = nullptr, - std::vector<std::string>* keywordsMissingValue = nullptr) const + std::vector<std::string>* keywordsMissingValue = nullptr, + std::vector<std::string>* parsedKeywords = nullptr) const { Result result; - this->Parse(result, args, unparsedArguments, keywordsMissingValue); + this->Parse(result, args, unparsedArguments, keywordsMissingValue, + parsedKeywords); return result; } @@ -116,11 +121,13 @@ public: template <typename Range> void Parse(Range const& args, std::vector<std::string>* unparsedArguments = nullptr, - std::vector<std::string>* keywordsMissingValue = nullptr) const + std::vector<std::string>* keywordsMissingValue = nullptr, + std::vector<std::string>* parsedKeywords = nullptr) const { ArgumentParser::Instance instance(this->Bindings); for (cm::string_view arg : args) { - instance.Consume(arg, nullptr, unparsedArguments, keywordsMissingValue); + instance.Consume(arg, nullptr, unparsedArguments, keywordsMissingValue, + parsedKeywords); } } diff --git a/Source/cmCPluginAPI.cxx b/Source/cmCPluginAPI.cxx index 9c7beca..78a232e 100644 --- a/Source/cmCPluginAPI.cxx +++ b/Source/cmCPluginAPI.cxx @@ -220,8 +220,10 @@ void CCONV cmAddUtilityCommand(void* arg, const char* utilityName, } // Pass the call to the makefile instance. - mf->AddUtilityCommand(utilityName, cmMakefile::TargetOrigin::Project, - (all ? false : true), nullptr, depends2, commandLines); + std::vector<std::string> no_byproducts; + mf->AddUtilityCommand(utilityName, cmCommandOrigin::Project, + (all ? false : true), nullptr, no_byproducts, depends2, + commandLines); } void CCONV cmAddCustomCommand(void* arg, const char* source, const char* command, int numArgs, @@ -319,16 +321,16 @@ void CCONV cmAddCustomCommandToTarget(void* arg, const char* target, commandLines.push_back(commandLine); // Select the command type. - cmTarget::CustomCommandType cctype = cmTarget::POST_BUILD; + cmCustomCommandType cctype = cmCustomCommandType::POST_BUILD; switch (commandType) { case CM_PRE_BUILD: - cctype = cmTarget::PRE_BUILD; + cctype = cmCustomCommandType::PRE_BUILD; break; case CM_PRE_LINK: - cctype = cmTarget::PRE_LINK; + cctype = cmCustomCommandType::PRE_LINK; break; case CM_POST_BUILD: - cctype = cmTarget::POST_BUILD; + cctype = cmCustomCommandType::POST_BUILD; break; } @@ -561,7 +563,7 @@ void* CCONV cmAddSource(void* arg, void* arg2) // Create the real cmSourceFile instance and copy over saved information. cmSourceFile* rsf = mf->GetOrCreateSource(osf->FullPath); - rsf->GetProperties() = osf->Properties; + rsf->SetProperties(osf->Properties); for (std::string const& d : osf->Depends) { rsf->AddDepend(d); } diff --git a/Source/cmCheckCustomOutputs.cxx b/Source/cmCheckCustomOutputs.cxx new file mode 100644 index 0000000..7645c88 --- /dev/null +++ b/Source/cmCheckCustomOutputs.cxx @@ -0,0 +1,36 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing for details. */ +#include "cmCheckCustomOutputs.h" + +#include "cmExecutionStatus.h" +#include "cmMakefile.h" +#include "cmStringAlgorithms.h" +#include "cmSystemTools.h" + +bool cmCheckCustomOutputs(const std::vector<std::string>& outputs, + cm::string_view keyword, cmExecutionStatus& status) +{ + cmMakefile& mf = status.GetMakefile(); + + for (std::string const& o : outputs) { + // Make sure the file will not be generated into the source + // directory during an out of source build. + if (!mf.CanIWriteThisFile(o)) { + status.SetError( + cmStrCat("attempted to have a file\n ", o, + "\nin a source directory as an output of custom command.")); + cmSystemTools::SetFatalErrorOccured(); + return false; + } + + // Make sure the output file name has no invalid characters. + std::string::size_type pos = o.find_first_of("#<>"); + if (pos != std::string::npos) { + status.SetError(cmStrCat("called with ", keyword, " containing a \"", + o[pos], "\". This character is not allowed.")); + return false; + } + } + + return true; +} diff --git a/Source/cmCheckCustomOutputs.h b/Source/cmCheckCustomOutputs.h new file mode 100644 index 0000000..7c4b3fe --- /dev/null +++ b/Source/cmCheckCustomOutputs.h @@ -0,0 +1,18 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing for details. */ +#ifndef cmCheckCustomOutputs_h +#define cmCheckCustomOutputs_h + +#include "cmConfigure.h" // IWYU pragma: keep + +#include <cm/string_view> + +#include <string> +#include <vector> + +class cmExecutionStatus; + +bool cmCheckCustomOutputs(const std::vector<std::string>& outputs, + cm::string_view keyword, cmExecutionStatus& status); + +#endif diff --git a/Source/cmCommands.cxx b/Source/cmCommands.cxx index 262590b..b1f7db7 100644 --- a/Source/cmCommands.cxx +++ b/Source/cmCommands.cxx @@ -231,43 +231,37 @@ void GetProjectCommands(cmState* state) cmGetSourceFilePropertyCommand); state->AddBuiltinCommand("get_target_property", cmGetTargetPropertyCommand); state->AddBuiltinCommand("get_test_property", cmGetTestPropertyCommand); - state->AddBuiltinCommand("include_directories", - cm::make_unique<cmIncludeDirectoryCommand>()); + state->AddBuiltinCommand("include_directories", cmIncludeDirectoryCommand); state->AddBuiltinCommand("include_regular_expression", cmIncludeRegularExpressionCommand); - state->AddBuiltinCommand("install", cm::make_unique<cmInstallCommand>()); + state->AddBuiltinCommand("install", cmInstallCommand); state->AddBuiltinCommand("install_files", cmInstallFilesCommand); state->AddBuiltinCommand("install_targets", cmInstallTargetsCommand); - state->AddBuiltinCommand("link_directories", - cm::make_unique<cmLinkDirectoriesCommand>()); - state->AddBuiltinCommand("project", cm::make_unique<cmProjectCommand>()); + state->AddBuiltinCommand("link_directories", cmLinkDirectoriesCommand); + state->AddBuiltinCommand("project", cmProjectCommand); state->AddBuiltinCommand("set_source_files_properties", cmSetSourceFilesPropertiesCommand); state->AddBuiltinCommand("set_target_properties", - cm::make_unique<cmSetTargetPropertiesCommand>()); + cmSetTargetPropertiesCommand); state->AddBuiltinCommand("set_tests_properties", cmSetTestsPropertiesCommand); state->AddBuiltinCommand("subdirs", cmSubdirCommand); - state->AddBuiltinCommand( - "target_compile_definitions", - cm::make_unique<cmTargetCompileDefinitionsCommand>()); + state->AddBuiltinCommand("target_compile_definitions", + cmTargetCompileDefinitionsCommand); state->AddBuiltinCommand("target_compile_features", - cm::make_unique<cmTargetCompileFeaturesCommand>()); + cmTargetCompileFeaturesCommand); state->AddBuiltinCommand("target_compile_options", - cm::make_unique<cmTargetCompileOptionsCommand>()); - state->AddBuiltinCommand( - "target_include_directories", - cm::make_unique<cmTargetIncludeDirectoriesCommand>()); + cmTargetCompileOptionsCommand); + state->AddBuiltinCommand("target_include_directories", + cmTargetIncludeDirectoriesCommand); state->AddBuiltinCommand("target_link_libraries", - cm::make_unique<cmTargetLinkLibrariesCommand>()); - state->AddBuiltinCommand("target_sources", - cm::make_unique<cmTargetSourcesCommand>()); + cmTargetLinkLibrariesCommand); + state->AddBuiltinCommand("target_sources", cmTargetSourcesCommand); state->AddBuiltinCommand("try_compile", cm::make_unique<cmTryCompileCommand>()); state->AddBuiltinCommand("try_run", cm::make_unique<cmTryRunCommand>()); - state->AddBuiltinCommand( - "target_precompile_headers", - cm::make_unique<cmTargetPrecompileHeadersCommand>()); + state->AddBuiltinCommand("target_precompile_headers", + cmTargetPrecompileHeadersCommand); #if !defined(CMAKE_BOOTSTRAP) state->AddBuiltinCommand("add_compile_definitions", @@ -275,24 +269,21 @@ void GetProjectCommands(cmState* state) state->AddBuiltinCommand("add_compile_options", cmAddCompileOptionsCommand); state->AddBuiltinCommand("aux_source_directory", cmAuxSourceDirectoryCommand); - state->AddBuiltinCommand("export", cm::make_unique<cmExportCommand>()); + state->AddBuiltinCommand("export", cmExportCommand); state->AddBuiltinCommand("fltk_wrap_ui", cmFLTKWrapUICommand); state->AddBuiltinCommand("include_external_msproject", cmIncludeExternalMSProjectCommand); state->AddBuiltinCommand("install_programs", cmInstallProgramsCommand); state->AddBuiltinCommand("add_link_options", cmAddLinkOptionsCommand); state->AddBuiltinCommand("link_libraries", cmLinkLibrariesCommand); - state->AddBuiltinCommand("target_link_options", - cm::make_unique<cmTargetLinkOptionsCommand>()); + state->AddBuiltinCommand("target_link_options", cmTargetLinkOptionsCommand); state->AddBuiltinCommand("target_link_directories", - cm::make_unique<cmTargetLinkDirectoriesCommand>()); - state->AddBuiltinCommand("load_cache", - cm::make_unique<cmLoadCacheCommand>()); + cmTargetLinkDirectoriesCommand); + state->AddBuiltinCommand("load_cache", cmLoadCacheCommand); state->AddBuiltinCommand("qt_wrap_cpp", cmQTWrapCPPCommand); state->AddBuiltinCommand("qt_wrap_ui", cmQTWrapUICommand); state->AddBuiltinCommand("remove_definitions", cmRemoveDefinitionsCommand); - state->AddBuiltinCommand("source_group", - cm::make_unique<cmSourceGroupCommand>()); + state->AddBuiltinCommand("source_group", cmSourceGroupCommand); state->AddDisallowedCommand( "export_library_dependencies", cmExportLibraryDependenciesCommand, diff --git a/Source/cmCustomCommandGenerator.cxx b/Source/cmCustomCommandGenerator.cxx index ddb855b..7f3e052 100644 --- a/Source/cmCustomCommandGenerator.cxx +++ b/Source/cmCustomCommandGenerator.cxx @@ -19,7 +19,7 @@ namespace { void AppendPaths(const std::vector<std::string>& inputs, - cmGeneratorExpression& ge, cmLocalGenerator* lg, + cmGeneratorExpression const& ge, cmLocalGenerator* lg, std::string const& config, std::vector<std::string>& output) { for (std::string const& in : inputs) { @@ -45,15 +45,15 @@ cmCustomCommandGenerator::cmCustomCommandGenerator(cmCustomCommand const& cc, , LG(lg) , OldStyle(cc.GetEscapeOldStyle()) , MakeVars(cc.GetEscapeAllowMakeVars()) - , GE(new cmGeneratorExpression(cc.GetBacktrace())) , EmulatorsWithArguments(cc.GetCommandLines().size()) { + cmGeneratorExpression ge(cc.GetBacktrace()); + const cmCustomCommandLines& cmdlines = this->CC.GetCommandLines(); for (cmCustomCommandLine const& cmdline : cmdlines) { cmCustomCommandLine argv; for (std::string const& clarg : cmdline) { - std::unique_ptr<cmCompiledGeneratorExpression> cge = - this->GE->Parse(clarg); + std::unique_ptr<cmCompiledGeneratorExpression> cge = ge.Parse(clarg); std::string parsed_arg = cge->Evaluate(this->LG, this->Config); if (this->CC.GetCommandExpandLists()) { cmAppend(argv, cmExpandedList(parsed_arg)); @@ -72,15 +72,14 @@ cmCustomCommandGenerator::cmCustomCommandGenerator(cmCustomCommand const& cc, this->CommandLines.push_back(std::move(argv)); } - AppendPaths(cc.GetByproducts(), *this->GE, this->LG, this->Config, + AppendPaths(cc.GetByproducts(), ge, this->LG, this->Config, this->Byproducts); - AppendPaths(cc.GetDepends(), *this->GE, this->LG, this->Config, - this->Depends); + AppendPaths(cc.GetDepends(), ge, this->LG, this->Config, this->Depends); const std::string& workingdirectory = this->CC.GetWorkingDirectory(); if (!workingdirectory.empty()) { std::unique_ptr<cmCompiledGeneratorExpression> cge = - this->GE->Parse(workingdirectory); + ge.Parse(workingdirectory); this->WorkingDirectory = cge->Evaluate(this->LG, this->Config); // Convert working directory to a full path. if (!this->WorkingDirectory.empty()) { @@ -93,11 +92,6 @@ cmCustomCommandGenerator::cmCustomCommandGenerator(cmCustomCommand const& cc, this->FillEmulatorsWithArguments(); } -cmCustomCommandGenerator::~cmCustomCommandGenerator() -{ - delete this->GE; -} - unsigned int cmCustomCommandGenerator::GetNumberOfCommands() const { return static_cast<unsigned int>(this->CC.GetCommandLines().size()); diff --git a/Source/cmCustomCommandGenerator.h b/Source/cmCustomCommandGenerator.h index d614302..50f292e 100644 --- a/Source/cmCustomCommandGenerator.h +++ b/Source/cmCustomCommandGenerator.h @@ -10,7 +10,6 @@ #include <vector> class cmCustomCommand; -class cmGeneratorExpression; class cmLocalGenerator; class cmCustomCommandGenerator @@ -20,7 +19,6 @@ class cmCustomCommandGenerator cmLocalGenerator* LG; bool OldStyle; bool MakeVars; - cmGeneratorExpression* GE; cmCustomCommandLines CommandLines; std::vector<std::vector<std::string>> EmulatorsWithArguments; std::vector<std::string> Byproducts; @@ -34,7 +32,6 @@ class cmCustomCommandGenerator public: cmCustomCommandGenerator(cmCustomCommand const& cc, std::string config, cmLocalGenerator* lg); - ~cmCustomCommandGenerator(); cmCustomCommandGenerator(const cmCustomCommandGenerator&) = delete; cmCustomCommandGenerator& operator=(const cmCustomCommandGenerator&) = delete; diff --git a/Source/cmCustomCommandTypes.h b/Source/cmCustomCommandTypes.h new file mode 100644 index 0000000..d4bf1f9 --- /dev/null +++ b/Source/cmCustomCommandTypes.h @@ -0,0 +1,39 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing for details. */ +#ifndef cmCustomCommandTypes_h +#define cmCustomCommandTypes_h + +#include "cmConfigure.h" // IWYU pragma: keep + +#include <string> + +/** Target custom command type */ +enum class cmCustomCommandType +{ + PRE_BUILD, + PRE_LINK, + POST_BUILD +}; + +/** Where the command originated from. */ +enum class cmCommandOrigin +{ + Project, + Generator +}; + +/** How to handle custom commands for object libraries */ +enum class cmObjectLibraryCommands +{ + Reject, + Accept +}; + +/** Utility target output source file name. */ +struct cmUtilityOutput +{ + std::string Name; + std::string NameCMP0049; +}; + +#endif diff --git a/Source/cmDependsC.cxx b/Source/cmDependsC.cxx index a380b41..012a0b1 100644 --- a/Source/cmDependsC.cxx +++ b/Source/cmDependsC.cxx @@ -5,7 +5,6 @@ #include "cmsys/FStream.hxx" #include <utility> -#include "cmAlgorithms.h" #include "cmFileTime.h" #include "cmLocalGenerator.h" #include "cmMakefile.h" @@ -67,7 +66,6 @@ cmDependsC::cmDependsC(cmLocalGenerator* lg, const std::string& targetDir, cmDependsC::~cmDependsC() { this->WriteCacheFile(); - cmDeleteAll(this->FileCache); } bool cmDependsC::WriteDependencies(const std::set<std::string>& sources, @@ -172,9 +170,9 @@ bool cmDependsC::WriteDependencies(const std::set<std::string>& sources, // Check whether this file is already in the cache auto fileIt = this->FileCache.find(fullName); if (fileIt != this->FileCache.end()) { - fileIt->second->Used = true; + fileIt->second.Used = true; dependencies.insert(fullName); - for (UnscannedEntry const& inc : fileIt->second->UnscannedEntries) { + for (UnscannedEntry const& inc : fileIt->second.UnscannedEntries) { if (this->Encountered.find(inc.FileName) == this->Encountered.end()) { this->Encountered.insert(inc.FileName); @@ -260,8 +258,7 @@ void cmDependsC::ReadCacheFile() if (res && newer) // cache is newer than the parsed file { - cacheEntry = new cmIncludeLines; - this->FileCache[line] = cacheEntry; + cacheEntry = &this->FileCache[line]; } // file doesn't exist, check that the regular expressions // haven't changed @@ -313,10 +310,10 @@ void cmDependsC::WriteCacheFile() const cacheOut << this->IncludeRegexTransformString << "\n\n"; for (auto const& fileIt : this->FileCache) { - if (fileIt.second->Used) { + if (fileIt.second.Used) { cacheOut << fileIt.first << std::endl; - for (UnscannedEntry const& inc : fileIt.second->UnscannedEntries) { + for (UnscannedEntry const& inc : fileIt.second.UnscannedEntries) { cacheOut << inc.FileName << std::endl; if (inc.QuotedLocation.empty()) { cacheOut << "-" << std::endl; @@ -332,9 +329,8 @@ void cmDependsC::WriteCacheFile() const void cmDependsC::Scan(std::istream& is, const std::string& directory, const std::string& fullName) { - cmIncludeLines* newCacheEntry = new cmIncludeLines; - newCacheEntry->Used = true; - this->FileCache[fullName] = newCacheEntry; + cmIncludeLines& newCacheEntry = this->FileCache[fullName]; + newCacheEntry.Used = true; // Read one line at a time. std::string line; @@ -370,7 +366,7 @@ void cmDependsC::Scan(std::istream& is, const std::string& directory, // This kind of problem will be fixed when a more // preprocessor-like implementation of this scanner is created. if (this->IncludeRegexScan.find(entry.FileName)) { - newCacheEntry->UnscannedEntries.push_back(entry); + newCacheEntry.UnscannedEntries.push_back(entry); if (this->Encountered.find(entry.FileName) == this->Encountered.end()) { this->Encountered.insert(entry.FileName); diff --git a/Source/cmDependsC.h b/Source/cmDependsC.h index cbdc276..7d732d9 100644 --- a/Source/cmDependsC.h +++ b/Source/cmDependsC.h @@ -84,7 +84,7 @@ protected: std::set<std::string> Encountered; std::queue<UnscannedEntry> Unscanned; - std::map<std::string, cmIncludeLines*> FileCache; + std::map<std::string, cmIncludeLines> FileCache; std::map<std::string, std::string> HeaderLocationCache; std::string CacheFileName; diff --git a/Source/cmExportCommand.cxx b/Source/cmExportCommand.cxx index 2713856..79af6e9 100644 --- a/Source/cmExportCommand.cxx +++ b/Source/cmExportCommand.cxx @@ -11,6 +11,7 @@ #include "cmAlgorithms.h" #include "cmArgumentParser.h" +#include "cmExecutionStatus.h" #include "cmExportBuildAndroidMKGenerator.h" #include "cmExportBuildFileGenerator.h" #include "cmExportSet.h" @@ -23,23 +24,31 @@ #include "cmSystemTools.h" #include "cmTarget.h" -class cmExecutionStatus; - #if defined(__HAIKU__) # include <FindDirectory.h> # include <StorageDefs.h> #endif -bool cmExportCommand::InitialPass(std::vector<std::string> const& args, - cmExecutionStatus&) +#if defined(_WIN32) && !defined(__CYGWIN__) +# include <windows.h> +#endif + +static bool HandlePackage(std::vector<std::string> const& args, + cmExecutionStatus& status); + +static void StorePackageRegistry(cmMakefile& mf, std::string const& package, + const char* content, const char* hash); + +bool cmExportCommand(std::vector<std::string> const& args, + cmExecutionStatus& status) { if (args.size() < 2) { - this->SetError("called with too few arguments"); + status.SetError("called with too few arguments"); return false; } if (args[0] == "PACKAGE") { - return this->HandlePackage(args); + return HandlePackage(args, status); } struct Arguments @@ -72,7 +81,7 @@ bool cmExportCommand::InitialPass(std::vector<std::string> const& args, parser.Parse(args, &unknownArgs, &keywordsMissingValue); if (!unknownArgs.empty()) { - this->SetError("Unknown argument: \"" + unknownArgs.front() + "\"."); + status.SetError("Unknown argument: \"" + unknownArgs.front() + "\"."); return false; } @@ -84,7 +93,7 @@ bool cmExportCommand::InitialPass(std::vector<std::string> const& args, } if (arguments.Filename.empty() && fname.empty()) { if (args[0] != "EXPORT") { - this->SetError("FILE <filename> option missing."); + status.SetError("FILE <filename> option missing."); return false; } fname = arguments.ExportSetName + ".cmake"; @@ -95,30 +104,32 @@ bool cmExportCommand::InitialPass(std::vector<std::string> const& args, std::ostringstream e; e << "FILE option given filename \"" << arguments.Filename << "\" which does not have an extension of \".cmake\".\n"; - this->SetError(e.str()); + status.SetError(e.str()); return false; } fname = arguments.Filename; } + cmMakefile& mf = status.GetMakefile(); + // Get the file to write. if (cmSystemTools::FileIsFullPath(fname)) { - if (!this->Makefile->CanIWriteThisFile(fname)) { + if (!mf.CanIWriteThisFile(fname)) { std::ostringstream e; e << "FILE option given filename \"" << fname << "\" which is in the source tree.\n"; - this->SetError(e.str()); + status.SetError(e.str()); return false; } } else { // Interpret relative paths with respect to the current build dir. - std::string dir = this->Makefile->GetCurrentBinaryDirectory(); + std::string const& dir = mf.GetCurrentBinaryDirectory(); fname = dir + "/" + fname; } std::vector<std::string> targets; - cmGlobalGenerator* gg = this->Makefile->GetGlobalGenerator(); + cmGlobalGenerator* gg = mf.GetGlobalGenerator(); cmExportSet* exportSet = nullptr; if (args[0] == "EXPORT") { @@ -127,32 +138,32 @@ bool cmExportCommand::InitialPass(std::vector<std::string> const& args, if (it == setMap.end()) { std::ostringstream e; e << "Export set \"" << arguments.ExportSetName << "\" not found."; - this->SetError(e.str()); + status.SetError(e.str()); return false; } exportSet = &it->second; } else if (!arguments.Targets.empty() || cmContains(keywordsMissingValue, "TARGETS")) { for (std::string const& currentTarget : arguments.Targets) { - if (this->Makefile->IsAlias(currentTarget)) { + if (mf.IsAlias(currentTarget)) { std::ostringstream e; e << "given ALIAS target \"" << currentTarget << "\" which may not be exported."; - this->SetError(e.str()); + status.SetError(e.str()); return false; } if (cmTarget* target = gg->FindTarget(currentTarget)) { if (target->GetType() == cmStateEnums::UTILITY) { - this->SetError("given custom target \"" + currentTarget + - "\" which may not be exported."); + status.SetError("given custom target \"" + currentTarget + + "\" which may not be exported."); return false; } } else { std::ostringstream e; e << "given target \"" << currentTarget << "\" which is not built by this project."; - this->SetError(e.str()); + status.SetError(e.str()); return false; } targets.push_back(currentTarget); @@ -165,7 +176,7 @@ bool cmExportCommand::InitialPass(std::vector<std::string> const& args, } } } else { - this->SetError("EXPORT or TARGETS specifier missing."); + status.SetError("EXPORT or TARGETS specifier missing."); return false; } @@ -184,12 +195,12 @@ bool cmExportCommand::InitialPass(std::vector<std::string> const& args, } else { ebfg->SetTargets(targets); } - this->Makefile->AddExportBuildFileGenerator(ebfg); + mf.AddExportBuildFileGenerator(ebfg); ebfg->SetExportOld(arguments.ExportOld); // Compute the set of configurations exported. std::vector<std::string> configurationTypes; - this->Makefile->GetConfigurations(configurationTypes); + mf.GetConfigurations(configurationTypes); if (configurationTypes.empty()) { configurationTypes.emplace_back(); } @@ -205,7 +216,8 @@ bool cmExportCommand::InitialPass(std::vector<std::string> const& args, return true; } -bool cmExportCommand::HandlePackage(std::vector<std::string> const& args) +static bool HandlePackage(std::vector<std::string> const& args, + cmExecutionStatus& status) { // Parse PACKAGE mode arguments. enum Doing @@ -222,14 +234,14 @@ bool cmExportCommand::HandlePackage(std::vector<std::string> const& args) } else { std::ostringstream e; e << "PACKAGE given unknown argument: " << args[i]; - this->SetError(e.str()); + status.SetError(e.str()); return false; } } // Verify the package name. if (package.empty()) { - this->SetError("PACKAGE must be given a package name."); + status.SetError("PACKAGE must be given a package name."); return false; } const char* packageExpr = "^[A-Za-z0-9_.-]+$"; @@ -238,16 +250,18 @@ bool cmExportCommand::HandlePackage(std::vector<std::string> const& args) std::ostringstream e; e << "PACKAGE given invalid package name \"" << package << "\". " << "Package names must match \"" << packageExpr << "\"."; - this->SetError(e.str()); + status.SetError(e.str()); return false; } + cmMakefile& mf = status.GetMakefile(); + // CMP0090 decides both the default and what variable changes it. - switch (this->Makefile->GetPolicyStatus(cmPolicies::CMP0090)) { + switch (mf.GetPolicyStatus(cmPolicies::CMP0090)) { case cmPolicies::WARN: case cmPolicies::OLD: // Default is to export, but can be disabled. - if (this->Makefile->IsOn("CMAKE_EXPORT_NO_PACKAGE_REGISTRY")) { + if (mf.IsOn("CMAKE_EXPORT_NO_PACKAGE_REGISTRY")) { return true; } break; @@ -255,7 +269,7 @@ bool cmExportCommand::HandlePackage(std::vector<std::string> const& args) case cmPolicies::REQUIRED_ALWAYS: case cmPolicies::NEW: // Default is to not export, but can be enabled. - if (!this->Makefile->IsOn("CMAKE_EXPORT_PACKAGE_REGISTRY")) { + if (!mf.IsOn("CMAKE_EXPORT_PACKAGE_REGISTRY")) { return true; } break; @@ -264,22 +278,17 @@ bool cmExportCommand::HandlePackage(std::vector<std::string> const& args) // We store the current build directory in the registry as a value // named by a hash of its own content. This is deterministic and is // unique with high probability. - const std::string& outDir = this->Makefile->GetCurrentBinaryDirectory(); + const std::string& outDir = mf.GetCurrentBinaryDirectory(); std::string hash = cmSystemTools::ComputeStringMD5(outDir); -#if defined(_WIN32) && !defined(__CYGWIN__) - this->StorePackageRegistryWin(package, outDir.c_str(), hash.c_str()); -#else - this->StorePackageRegistryDir(package, outDir.c_str(), hash.c_str()); -#endif + StorePackageRegistry(mf, package, outDir.c_str(), hash.c_str()); return true; } #if defined(_WIN32) && !defined(__CYGWIN__) -# include <windows.h> -void cmExportCommand::ReportRegistryError(std::string const& msg, - std::string const& key, long err) +static void ReportRegistryError(cmMakefile& mf, std::string const& msg, + std::string const& key, long err) { std::ostringstream e; e << msg << "\n" @@ -291,12 +300,11 @@ void cmExportCommand::ReportRegistryError(std::string const& msg, e << "Windows reported:\n" << " " << cmsys::Encoding::ToNarrow(winmsg); } - this->Makefile->IssueMessage(MessageType::WARNING, e.str()); + mf.IssueMessage(MessageType::WARNING, e.str()); } -void cmExportCommand::StorePackageRegistryWin(std::string const& package, - const char* content, - const char* hash) +static void StorePackageRegistry(cmMakefile& mf, std::string const& package, + const char* content, const char* hash) { std::string key = cmStrCat("Software\\Kitware\\CMake\\Packages\\", package); HKEY hKey; @@ -304,7 +312,7 @@ void cmExportCommand::StorePackageRegistryWin(std::string const& package, RegCreateKeyExW(HKEY_CURRENT_USER, cmsys::Encoding::ToWide(key).c_str(), 0, 0, REG_OPTION_NON_VOLATILE, KEY_SET_VALUE, 0, &hKey, 0); if (err != ERROR_SUCCESS) { - this->ReportRegistryError("Cannot create/open registry key", key, err); + ReportRegistryError(mf, "Cannot create/open registry key", key, err); return; } @@ -317,14 +325,13 @@ void cmExportCommand::StorePackageRegistryWin(std::string const& package, if (err != ERROR_SUCCESS) { std::ostringstream msg; msg << "Cannot set registry value \"" << hash << "\" under key"; - this->ReportRegistryError(msg.str(), key, err); + ReportRegistryError(mf, msg.str(), key, err); return; } } #else -void cmExportCommand::StorePackageRegistryDir(std::string const& package, - const char* content, - const char* hash) +static void StorePackageRegistry(cmMakefile& mf, std::string const& package, + const char* content, const char* hash) { # if defined(__HAIKU__) char dir[B_PATH_NAME_LENGTH]; @@ -356,7 +363,7 @@ void cmExportCommand::StorePackageRegistryDir(std::string const& package, << " " << fname << "\n" << cmSystemTools::GetLastSystemError() << "\n"; /* clang-format on */ - this->Makefile->IssueMessage(MessageType::WARNING, e.str()); + mf.IssueMessage(MessageType::WARNING, e.str()); } } } diff --git a/Source/cmExportCommand.h b/Source/cmExportCommand.h index 819a3c3..9655628 100644 --- a/Source/cmExportCommand.h +++ b/Source/cmExportCommand.h @@ -8,38 +8,9 @@ #include <string> #include <vector> -#include <cm/memory> - -#include "cmCommand.h" - class cmExecutionStatus; -class cmExportCommand : public cmCommand -{ -public: - /** - * This is a virtual constructor for the command. - */ - std::unique_ptr<cmCommand> Clone() override - { - return cm::make_unique<cmExportCommand>(); - } - - /** - * This is called when the command is first encountered in - * the CMakeLists.txt file. - */ - bool InitialPass(std::vector<std::string> const& args, - cmExecutionStatus& status) override; - -private: - bool HandlePackage(std::vector<std::string> const& args); - void StorePackageRegistryWin(std::string const& package, const char* content, - const char* hash); - void StorePackageRegistryDir(std::string const& package, const char* content, - const char* hash); - void ReportRegistryError(std::string const& msg, std::string const& key, - long err); -}; +bool cmExportCommand(std::vector<std::string> const& args, + cmExecutionStatus& status); #endif diff --git a/Source/cmExportFileGenerator.cxx b/Source/cmExportFileGenerator.cxx index f9a28cd..e142560 100644 --- a/Source/cmExportFileGenerator.cxx +++ b/Source/cmExportFileGenerator.cxx @@ -380,7 +380,7 @@ void cmExportFileGenerator::PopulateIncludeDirectoriesInterface( this->ReplaceInstallPrefix(dirs); std::unique_ptr<cmCompiledGeneratorExpression> cge = ge.Parse(dirs); std::string exportDirs = - cge->Evaluate(target->GetLocalGenerator(), "", false, target); + cge->Evaluate(target->GetLocalGenerator(), "", target); if (cge->GetHadContextSensitiveCondition()) { cmLocalGenerator* lg = target->GetLocalGenerator(); diff --git a/Source/cmExportInstallFileGenerator.cxx b/Source/cmExportInstallFileGenerator.cxx index 0009b3a..1e843b6 100644 --- a/Source/cmExportInstallFileGenerator.cxx +++ b/Source/cmExportInstallFileGenerator.cxx @@ -94,6 +94,9 @@ bool cmExportInstallFileGenerator::GenerateMainFile(std::ostream& os) this->PopulateInterfaceProperty("INTERFACE_COMPILE_OPTIONS", gt, cmGeneratorExpression::InstallInterface, properties, missingTargets); + this->PopulateInterfaceProperty("INTERFACE_PRECOMPILE_HEADERS", gt, + cmGeneratorExpression::InstallInterface, + properties, missingTargets); this->PopulateInterfaceProperty("INTERFACE_AUTOUIC_OPTIONS", gt, cmGeneratorExpression::InstallInterface, properties, missingTargets); diff --git a/Source/cmExportTryCompileFileGenerator.cxx b/Source/cmExportTryCompileFileGenerator.cxx index 5631d60..4027d4b 100644 --- a/Source/cmExportTryCompileFileGenerator.cxx +++ b/Source/cmExportTryCompileFileGenerator.cxx @@ -74,9 +74,8 @@ std::string cmExportTryCompileFileGenerator::FindTargets( cmGeneratorTarget gDummyHead(&dummyHead, tgt->GetLocalGenerator()); - std::string result = - cge->Evaluate(tgt->GetLocalGenerator(), this->Config, false, &gDummyHead, - tgt, &dagChecker, language); + std::string result = cge->Evaluate(tgt->GetLocalGenerator(), this->Config, + &gDummyHead, &dagChecker, tgt, language); const std::set<cmGeneratorTarget const*>& allTargets = cge->GetAllTargetsSeen(); diff --git a/Source/cmFileAPICodemodel.cxx b/Source/cmFileAPICodemodel.cxx index 96ea071..fbdb975 100644 --- a/Source/cmFileAPICodemodel.cxx +++ b/Source/cmFileAPICodemodel.cxx @@ -858,47 +858,69 @@ CompileData Target::BuildCompileData(cmSourceFile* sf) fd.Flags.emplace_back(std::move(flags), JBTIndex()); } const std::string COMPILE_OPTIONS("COMPILE_OPTIONS"); - if (const char* coptions = sf->GetProperty(COMPILE_OPTIONS)) { - std::string flags; - lg->AppendCompileOptions( - flags, genexInterpreter.Evaluate(coptions, COMPILE_OPTIONS)); - fd.Flags.emplace_back(std::move(flags), JBTIndex()); + for (BT<std::string> tmpOpt : sf->GetCompileOptions()) { + tmpOpt.Value = genexInterpreter.Evaluate(tmpOpt.Value, COMPILE_OPTIONS); + // After generator evaluation we need to use the AppendCompileOptions + // method so we handle situations where backtrace entries have lists + // and properly escape flags. + std::string tmp; + lg->AppendCompileOptions(tmp, tmpOpt.Value); + BT<std::string> opt(tmp, tmpOpt.Backtrace); + fd.Flags.emplace_back(this->ToJBT(opt)); } // Add include directories from source file properties. { - std::vector<std::string> includes; const std::string INCLUDE_DIRECTORIES("INCLUDE_DIRECTORIES"); - if (const char* cincludes = sf->GetProperty(INCLUDE_DIRECTORIES)) { - const std::string& evaluatedIncludes = - genexInterpreter.Evaluate(cincludes, INCLUDE_DIRECTORIES); - lg->AppendIncludeDirectories(includes, evaluatedIncludes, *sf); - - for (std::string const& include : includes) { - bool const isSystemInclude = this->GT->IsSystemIncludeDirectory( - include, this->Config, fd.Language); - fd.Includes.emplace_back(include, isSystemInclude); + for (BT<std::string> tmpInclude : sf->GetIncludeDirectories()) { + tmpInclude.Value = + genexInterpreter.Evaluate(tmpInclude.Value, INCLUDE_DIRECTORIES); + + // After generator evaluation we need to use the AppendIncludeDirectories + // method so we handle situations where backtrace entries have lists. + std::vector<std::string> tmp; + lg->AppendIncludeDirectories(tmp, tmpInclude.Value, *sf); + for (std::string& i : tmp) { + bool const isSystemInclude = + this->GT->IsSystemIncludeDirectory(i, this->Config, fd.Language); + BT<std::string> include(i, tmpInclude.Backtrace); + fd.Includes.emplace_back(this->ToJBT(include), isSystemInclude); } } } const std::string COMPILE_DEFINITIONS("COMPILE_DEFINITIONS"); - std::set<std::string> fileDefines; - if (const char* defs = sf->GetProperty(COMPILE_DEFINITIONS)) { - lg->AppendDefines(fileDefines, - genexInterpreter.Evaluate(defs, COMPILE_DEFINITIONS)); + std::set<BT<std::string>> fileDefines; + for (BT<std::string> tmpDef : sf->GetCompileDefinitions()) { + tmpDef.Value = + genexInterpreter.Evaluate(tmpDef.Value, COMPILE_DEFINITIONS); + + // After generator evaluation we need to use the AppendDefines method + // so we handle situations where backtrace entries have lists. + std::set<std::string> tmp; + lg->AppendDefines(tmp, tmpDef.Value); + for (const std::string& i : tmp) { + BT<std::string> def(i, tmpDef.Backtrace); + fileDefines.insert(def); + } } + std::set<std::string> configFileDefines; const std::string defPropName = "COMPILE_DEFINITIONS_" + cmSystemTools::UpperCase(this->Config); if (const char* config_defs = sf->GetProperty(defPropName)) { lg->AppendDefines( - fileDefines, + configFileDefines, genexInterpreter.Evaluate(config_defs, COMPILE_DEFINITIONS)); } - fd.Defines.reserve(fileDefines.size()); - for (std::string const& d : fileDefines) { + fd.Defines.reserve(fileDefines.size() + configFileDefines.size()); + + for (BT<std::string> const& def : fileDefines) { + fd.Defines.emplace_back(this->ToJBT(def)); + } + + for (std::string const& d : configFileDefines) { fd.Defines.emplace_back(d, JBTIndex()); } diff --git a/Source/cmGeneratorExpression.cxx b/Source/cmGeneratorExpression.cxx index f49ed2c..1f31069 100644 --- a/Source/cmGeneratorExpression.cxx +++ b/Source/cmGeneratorExpression.cxx @@ -21,40 +21,56 @@ cmGeneratorExpression::cmGeneratorExpression(cmListFileBacktrace backtrace) { } +cmGeneratorExpression::~cmGeneratorExpression() = default; + std::unique_ptr<cmCompiledGeneratorExpression> cmGeneratorExpression::Parse( - std::string const& input) + std::string input) const { return std::unique_ptr<cmCompiledGeneratorExpression>( - new cmCompiledGeneratorExpression(this->Backtrace, input)); + new cmCompiledGeneratorExpression(this->Backtrace, std::move(input))); } std::unique_ptr<cmCompiledGeneratorExpression> cmGeneratorExpression::Parse( - const char* input) + const char* input) const { return this->Parse(std::string(input ? input : "")); } -cmGeneratorExpression::~cmGeneratorExpression() = default; - -const std::string& cmCompiledGeneratorExpression::Evaluate( - cmLocalGenerator* lg, const std::string& config, bool quiet, - const cmGeneratorTarget* headTarget, +std::string cmGeneratorExpression::Evaluate( + std::string input, cmLocalGenerator* lg, const std::string& config, + cmGeneratorTarget const* headTarget, cmGeneratorExpressionDAGChecker* dagChecker, - std::string const& language) const + cmGeneratorTarget const* currentTarget, std::string const& language) { - return this->Evaluate(lg, config, quiet, headTarget, headTarget, dagChecker, + if (Find(input) != std::string::npos) { + cmCompiledGeneratorExpression cge(cmListFileBacktrace(), std::move(input)); + return cge.Evaluate(lg, config, headTarget, dagChecker, currentTarget, language); + } + return input; +} + +std::string cmGeneratorExpression::Evaluate( + const char* input, cmLocalGenerator* lg, const std::string& config, + cmGeneratorTarget const* headTarget, + cmGeneratorExpressionDAGChecker* dagChecker, + cmGeneratorTarget const* currentTarget, std::string const& language) +{ + return input ? Evaluate(std::string(input), lg, config, headTarget, + dagChecker, currentTarget, language) + : ""; } const std::string& cmCompiledGeneratorExpression::Evaluate( - cmLocalGenerator* lg, const std::string& config, bool quiet, - const cmGeneratorTarget* headTarget, const cmGeneratorTarget* currentTarget, + cmLocalGenerator* lg, const std::string& config, + const cmGeneratorTarget* headTarget, cmGeneratorExpressionDAGChecker* dagChecker, - std::string const& language) const + const cmGeneratorTarget* currentTarget, std::string const& language) const { cmGeneratorExpressionContext context( - lg, config, quiet, headTarget, currentTarget ? currentTarget : headTarget, - this->EvaluateForBuildsystem, this->Backtrace, language); + lg, config, this->Quiet, headTarget, + currentTarget ? currentTarget : headTarget, this->EvaluateForBuildsystem, + this->Backtrace, language); return this->EvaluateWithContext(context, dagChecker); } @@ -97,9 +113,10 @@ cmCompiledGeneratorExpression::cmCompiledGeneratorExpression( cmListFileBacktrace backtrace, std::string input) : Backtrace(std::move(backtrace)) , Input(std::move(input)) + , EvaluateForBuildsystem(false) + , Quiet(false) , HadContextSensitiveCondition(false) , HadHeadSensitiveCondition(false) - , EvaluateForBuildsystem(false) { cmGeneratorExpressionLexer l; std::vector<cmGeneratorExpressionToken> tokens = l.Tokenize(this->Input); @@ -377,10 +394,10 @@ void cmCompiledGeneratorExpression::GetMaxLanguageStandard( } const std::string& cmGeneratorExpressionInterpreter::Evaluate( - const char* expression, const std::string& property) + std::string expression, const std::string& property) { this->CompiledGeneratorExpression = - this->GeneratorExpression.Parse(expression); + this->GeneratorExpression.Parse(std::move(expression)); // Specify COMPILE_OPTIONS to DAGchecker, same semantic as COMPILE_FLAGS cmGeneratorExpressionDAGChecker dagChecker( @@ -389,6 +406,12 @@ const std::string& cmGeneratorExpressionInterpreter::Evaluate( nullptr); return this->CompiledGeneratorExpression->Evaluate( - this->LocalGenerator, this->Config, false, this->HeadTarget, &dagChecker, + this->LocalGenerator, this->Config, this->HeadTarget, &dagChecker, nullptr, this->Language); } + +const std::string& cmGeneratorExpressionInterpreter::Evaluate( + const char* expression, const std::string& property) +{ + return this->Evaluate(std::string(expression ? expression : ""), property); +} diff --git a/Source/cmGeneratorExpression.h b/Source/cmGeneratorExpression.h index ef76651..de5c705 100644 --- a/Source/cmGeneratorExpression.h +++ b/Source/cmGeneratorExpression.h @@ -41,8 +41,22 @@ public: cmGeneratorExpression& operator=(cmGeneratorExpression const&) = delete; std::unique_ptr<cmCompiledGeneratorExpression> Parse( - std::string const& input); - std::unique_ptr<cmCompiledGeneratorExpression> Parse(const char* input); + std::string input) const; + std::unique_ptr<cmCompiledGeneratorExpression> Parse( + const char* input) const; + + static std::string Evaluate( + std::string input, cmLocalGenerator* lg, const std::string& config, + cmGeneratorTarget const* headTarget = nullptr, + cmGeneratorExpressionDAGChecker* dagChecker = nullptr, + cmGeneratorTarget const* currentTarget = nullptr, + std::string const& language = std::string()); + static std::string Evaluate( + const char* input, cmLocalGenerator* lg, const std::string& config, + cmGeneratorTarget const* headTarget = nullptr, + cmGeneratorExpressionDAGChecker* dagChecker = nullptr, + cmGeneratorTarget const* currentTarget = nullptr, + std::string const& language = std::string()); enum PreprocessContext { @@ -87,15 +101,10 @@ public: cmCompiledGeneratorExpression const&) = delete; const std::string& Evaluate( - cmLocalGenerator* lg, const std::string& config, bool quiet = false, + cmLocalGenerator* lg, const std::string& config, cmGeneratorTarget const* headTarget = nullptr, - cmGeneratorTarget const* currentTarget = nullptr, cmGeneratorExpressionDAGChecker* dagChecker = nullptr, - std::string const& language = std::string()) const; - const std::string& Evaluate( - cmLocalGenerator* lg, const std::string& config, bool quiet, - cmGeneratorTarget const* headTarget, - cmGeneratorExpressionDAGChecker* dagChecker, + cmGeneratorTarget const* currentTarget = nullptr, std::string const& language = std::string()) const; /** Get set of targets found during evaluations. */ @@ -135,6 +144,8 @@ public: this->EvaluateForBuildsystem = eval; } + void SetQuiet(bool quiet) { this->Quiet = quiet; } + void GetMaxLanguageStandard(cmGeneratorTarget const* tgt, std::map<std::string, std::string>& mapping); @@ -152,6 +163,8 @@ private: std::vector<cmGeneratorExpressionEvaluator*> Evaluators; const std::string Input; bool NeedsEvaluation; + bool EvaluateForBuildsystem; + bool Quiet; mutable std::set<cmGeneratorTarget*> DependTargets; mutable std::set<cmGeneratorTarget const*> AllTargetsSeen; @@ -163,7 +176,6 @@ private: mutable bool HadContextSensitiveCondition; mutable bool HadHeadSensitiveCondition; mutable std::set<cmGeneratorTarget const*> SourceSensitiveTargets; - bool EvaluateForBuildsystem; }; class cmGeneratorExpressionInterpreter @@ -172,11 +184,11 @@ public: cmGeneratorExpressionInterpreter(cmLocalGenerator* localGenerator, std::string config, cmGeneratorTarget const* headTarget, - std::string lang = std::string()) + std::string language = std::string()) : LocalGenerator(localGenerator) , Config(std::move(config)) , HeadTarget(headTarget) - , Language(std::move(lang)) + , Language(std::move(language)) { } @@ -185,13 +197,10 @@ public: cmGeneratorExpressionInterpreter& operator=( cmGeneratorExpressionInterpreter const&) = delete; + const std::string& Evaluate(std::string expression, + const std::string& property); const std::string& Evaluate(const char* expression, const std::string& property); - const std::string& Evaluate(const std::string& expression, - const std::string& property) - { - return this->Evaluate(expression.c_str(), property); - } protected: cmGeneratorExpression GeneratorExpression; diff --git a/Source/cmGeneratorExpressionEvaluationFile.cxx b/Source/cmGeneratorExpressionEvaluationFile.cxx index eb43270..aa2c1a6 100644 --- a/Source/cmGeneratorExpressionEvaluationFile.cxx +++ b/Source/cmGeneratorExpressionEvaluationFile.cxx @@ -37,8 +37,8 @@ void cmGeneratorExpressionEvaluationFile::Generate( { std::string rawCondition = this->Condition->GetInput(); if (!rawCondition.empty()) { - std::string condResult = this->Condition->Evaluate( - lg, config, false, nullptr, nullptr, nullptr, lang); + std::string condResult = + this->Condition->Evaluate(lg, config, nullptr, nullptr, nullptr, lang); if (condResult == "0") { return; } @@ -54,9 +54,9 @@ void cmGeneratorExpressionEvaluationFile::Generate( } std::string outputFileName = this->OutputFileExpr->Evaluate( - lg, config, false, nullptr, nullptr, nullptr, lang); - const std::string& outputContent = inputExpression->Evaluate( - lg, config, false, nullptr, nullptr, nullptr, lang); + lg, config, nullptr, nullptr, nullptr, lang); + const std::string& outputContent = + inputExpression->Evaluate(lg, config, nullptr, nullptr, nullptr, lang); if (cmSystemTools::FileIsFullPath(outputFileName)) { outputFileName = cmSystemTools::CollapseFullPath(outputFileName); @@ -100,8 +100,8 @@ void cmGeneratorExpressionEvaluationFile::CreateOutputFile( gg->GetEnabledLanguages(enabledLanguages); for (std::string const& le : enabledLanguages) { - std::string name = this->OutputFileExpr->Evaluate( - lg, config, false, nullptr, nullptr, nullptr, le); + std::string name = this->OutputFileExpr->Evaluate(lg, config, nullptr, + nullptr, nullptr, le); cmSourceFile* sf = lg->GetMakefile()->GetOrCreateSource( name, false, cmSourceFileLocationKind::Known); // Tell TraceDependencies that the file is not expected to exist diff --git a/Source/cmGeneratorExpressionNode.cxx b/Source/cmGeneratorExpressionNode.cxx index d524867..5a35007 100644 --- a/Source/cmGeneratorExpressionNode.cxx +++ b/Source/cmGeneratorExpressionNode.cxx @@ -45,15 +45,16 @@ std::string cmGeneratorExpressionNode::EvaluateDependentExpression( std::string const& prop, cmLocalGenerator* lg, cmGeneratorExpressionContext* context, cmGeneratorTarget const* headTarget, - cmGeneratorTarget const* currentTarget, - cmGeneratorExpressionDAGChecker* dagChecker) + cmGeneratorExpressionDAGChecker* dagChecker, + cmGeneratorTarget const* currentTarget) { cmGeneratorExpression ge(context->Backtrace); std::unique_ptr<cmCompiledGeneratorExpression> cge = ge.Parse(prop); cge->SetEvaluateForBuildsystem(context->EvaluateForBuildsystem); + cge->SetQuiet(context->Quiet); std::string result = - cge->Evaluate(lg, context->Config, context->Quiet, headTarget, - currentTarget, dagChecker, context->Language); + cge->Evaluate(lg, context->Config, headTarget, dagChecker, currentTarget, + context->Language); if (cge->GetHadContextSensitiveCondition()) { context->HadContextSensitiveCondition = true; } @@ -477,13 +478,13 @@ protected: } return this->EvaluateDependentExpression( - expression, context->LG, context, context->HeadTarget, - context->CurrentTarget, &dagChecker); + expression, context->LG, context, context->HeadTarget, &dagChecker, + context->CurrentTarget); } return this->EvaluateDependentExpression( - expression, context->LG, context, context->HeadTarget, - context->CurrentTarget, dagCheckerParent); + expression, context->LG, context, context->HeadTarget, dagCheckerParent, + context->CurrentTarget); } }; @@ -706,7 +707,8 @@ struct CompilerIdNode : public cmGeneratorExpressionNode }; static const CompilerIdNode cCompilerIdNode("C"), cxxCompilerIdNode("CXX"), - cudaCompilerIdNode("CUDA"), fortranCompilerIdNode("Fortran"); + cudaCompilerIdNode("CUDA"), objcCompilerIdNode("OBJC"), + objcxxCompilerIdNode("OBJCXX"), fortranCompilerIdNode("Fortran"); struct CompilerVersionNode : public cmGeneratorExpressionNode { @@ -770,6 +772,7 @@ struct CompilerVersionNode : public cmGeneratorExpressionNode static const CompilerVersionNode cCompilerVersionNode("C"), cxxCompilerVersionNode("CXX"), cudaCompilerVersionNode("CUDA"), + objcCompilerVersionNode("OBJC"), objcxxCompilerVersionNode("OBJCXX"), fortranCompilerVersionNode("Fortran"); struct PlatformIdNode : public cmGeneratorExpressionNode @@ -1331,7 +1334,7 @@ static const struct TargetPropertyNode : public cmGeneratorExpressionNode if (!interfacePropertyName.empty()) { result = this->EvaluateDependentExpression(result, context->LG, context, - target, target, &dagChecker); + target, &dagChecker, target); std::string linkedTargetsContent = getLinkedTargetsContent( target, interfacePropertyName, context, &dagChecker); if (!linkedTargetsContent.empty()) { @@ -2242,6 +2245,8 @@ const cmGeneratorExpressionNode* cmGeneratorExpressionNode::GetNode( { "NOT", ¬Node }, { "C_COMPILER_ID", &cCompilerIdNode }, { "CXX_COMPILER_ID", &cxxCompilerIdNode }, + { "OBJC_COMPILER_ID", &objcCompilerIdNode }, + { "OBJCXX_COMPILER_ID", &objcxxCompilerIdNode }, { "CUDA_COMPILER_ID", &cudaCompilerIdNode }, { "Fortran_COMPILER_ID", &fortranCompilerIdNode }, { "VERSION_GREATER", &versionGreaterNode }, @@ -2252,6 +2257,8 @@ const cmGeneratorExpressionNode* cmGeneratorExpressionNode::GetNode( { "C_COMPILER_VERSION", &cCompilerVersionNode }, { "CXX_COMPILER_VERSION", &cxxCompilerVersionNode }, { "CUDA_COMPILER_VERSION", &cudaCompilerVersionNode }, + { "OBJC_COMPILER_VERSION", &objcCompilerVersionNode }, + { "OBJCXX_COMPILER_VERSION", &objcxxCompilerVersionNode }, { "Fortran_COMPILER_VERSION", &fortranCompilerVersionNode }, { "PLATFORM_ID", &platformIdNode }, { "COMPILE_FEATURES", &compileFeaturesNode }, diff --git a/Source/cmGeneratorExpressionNode.h b/Source/cmGeneratorExpressionNode.h index 7a36924..13e8484 100644 --- a/Source/cmGeneratorExpressionNode.h +++ b/Source/cmGeneratorExpressionNode.h @@ -43,8 +43,8 @@ struct cmGeneratorExpressionNode static std::string EvaluateDependentExpression( std::string const& prop, cmLocalGenerator* lg, cmGeneratorExpressionContext* context, const cmGeneratorTarget* headTarget, - const cmGeneratorTarget* currentTarget, - cmGeneratorExpressionDAGChecker* dagChecker); + cmGeneratorExpressionDAGChecker* dagChecker, + const cmGeneratorTarget* currentTarget); static const cmGeneratorExpressionNode* GetNode( const std::string& identifier); diff --git a/Source/cmGeneratorTarget.cxx b/Source/cmGeneratorTarget.cxx index 18ca478..07578cc 100644 --- a/Source/cmGeneratorTarget.cxx +++ b/Source/cmGeneratorTarget.cxx @@ -84,16 +84,10 @@ public: virtual ~TargetPropertyEntry() = default; virtual const std::string& Evaluate( - cmLocalGenerator* lg, const std::string& config, bool quiet = false, - cmGeneratorTarget const* headTarget = nullptr, - cmGeneratorTarget const* currentTarget = nullptr, - cmGeneratorExpressionDAGChecker* dagChecker = nullptr, - std::string const& language = std::string()) const = 0; - virtual const std::string& Evaluate( - cmLocalGenerator* lg, const std::string& config, bool quiet, + cmLocalGenerator* lg, const std::string& config, cmGeneratorTarget const* headTarget, cmGeneratorExpressionDAGChecker* dagChecker, - std::string const& language = std::string()) const = 0; + std::string const& language) const = 0; virtual cmListFileBacktrace GetBacktrace() const = 0; virtual std::string const& GetInput() const = 0; @@ -113,23 +107,12 @@ public: { } - const std::string& Evaluate( - cmLocalGenerator* lg, const std::string& config, bool quiet = false, - cmGeneratorTarget const* headTarget = nullptr, - cmGeneratorTarget const* currentTarget = nullptr, - cmGeneratorExpressionDAGChecker* dagChecker = nullptr, - std::string const& language = std::string()) const override - { - return this->ge->Evaluate(lg, config, quiet, headTarget, currentTarget, - dagChecker, language); - } - const std::string& Evaluate( - cmLocalGenerator* lg, const std::string& config, bool quiet, - cmGeneratorTarget const* headTarget, - cmGeneratorExpressionDAGChecker* dagChecker, - std::string const& language = std::string()) const override + const std::string& Evaluate(cmLocalGenerator* lg, const std::string& config, + cmGeneratorTarget const* headTarget, + cmGeneratorExpressionDAGChecker* dagChecker, + std::string const& language) const override { - return this->ge->Evaluate(lg, config, quiet, headTarget, dagChecker, + return this->ge->Evaluate(lg, config, headTarget, dagChecker, nullptr, language); } @@ -161,15 +144,7 @@ public: { } - const std::string& Evaluate(cmLocalGenerator*, const std::string&, bool, - cmGeneratorTarget const*, - cmGeneratorTarget const*, - cmGeneratorExpressionDAGChecker*, - std::string const&) const override - { - return this->PropertyValue; - } - const std::string& Evaluate(cmLocalGenerator*, const std::string&, bool, + const std::string& Evaluate(cmLocalGenerator*, const std::string&, cmGeneratorTarget const*, cmGeneratorExpressionDAGChecker*, std::string const&) const override @@ -198,7 +173,7 @@ cmGeneratorTarget::TargetPropertyEntry* CreateTargetPropertyEntry( return new TargetPropertyEntryGenex(std::move(cge)); } - return new TargetPropertyEntryString(propertyValue, backtrace); + return new TargetPropertyEntryString(propertyValue, std::move(backtrace)); } void CreatePropertyGeneratorExpressions( @@ -245,7 +220,7 @@ EvaluatedTargetPropertyEntry EvaluateTargetPropertyEntry( cmGeneratorTarget::TargetPropertyEntry* entry) { EvaluatedTargetPropertyEntry ee(entry->LinkImplItem, entry->GetBacktrace()); - cmExpandList(entry->Evaluate(thisTarget->GetLocalGenerator(), config, false, + cmExpandList(entry->Evaluate(thisTarget->GetLocalGenerator(), config, thisTarget, dagChecker, lang), ee.Values); if (entry->GetHadContextSensitiveCondition()) { @@ -512,9 +487,8 @@ std::string cmGeneratorTarget::GetOutputName( } // Now evaluate genex and update the previously-prepared map entry. - cmGeneratorExpression ge; - std::unique_ptr<cmCompiledGeneratorExpression> cge = ge.Parse(outName); - i->second = cge->Evaluate(this->LocalGenerator, config); + i->second = + cmGeneratorExpression::Evaluate(outName, this->LocalGenerator, config); } else if (i->second.empty()) { // An empty map entry indicates we have been called recursively // from the above block. @@ -734,9 +708,8 @@ void handleSystemIncludesDep(cmLocalGenerator* lg, { if (const char* dirs = depTgt->GetProperty("INTERFACE_SYSTEM_INCLUDE_DIRECTORIES")) { - cmGeneratorExpression ge; - cmExpandList(ge.Parse(dirs)->Evaluate(lg, config, false, headTarget, - depTgt, dagChecker, language), + cmExpandList(cmGeneratorExpression::Evaluate(dirs, lg, config, headTarget, + dagChecker, depTgt, language), result); } if (!depTgt->IsImported() || excludeImported) { @@ -745,9 +718,8 @@ void handleSystemIncludesDep(cmLocalGenerator* lg, if (const char* dirs = depTgt->GetProperty("INTERFACE_INCLUDE_DIRECTORIES")) { - cmGeneratorExpression ge; - cmExpandList(ge.Parse(dirs)->Evaluate(lg, config, false, headTarget, - depTgt, dagChecker, language), + cmExpandList(cmGeneratorExpression::Evaluate(dirs, lg, config, headTarget, + dagChecker, depTgt, language), result); } } @@ -1117,9 +1089,9 @@ bool cmGeneratorTarget::IsSystemIncludeDirectory( std::vector<std::string> result; for (std::string const& it : this->Target->GetSystemIncludeDirectories()) { - cmGeneratorExpression ge; - cmExpandList(ge.Parse(it)->Evaluate(this->LocalGenerator, config, false, - this, &dagChecker, language), + cmExpandList(cmGeneratorExpression::Evaluate(it, this->LocalGenerator, + config, this, &dagChecker, + nullptr, language), result); } @@ -1225,7 +1197,7 @@ std::string cmGeneratorTarget::EvaluateInterfaceProperty( if (const char* p = this->GetProperty(prop)) { result = cmGeneratorExpressionNode::EvaluateDependentExpression( - p, context->LG, context, headTarget, this, &dagChecker); + p, context->LG, context, headTarget, &dagChecker, this); } if (cmLinkInterfaceLibraries const* iface = @@ -1315,7 +1287,7 @@ void AddObjectEntries(cmGeneratorTarget const* headTarget, EvaluatedTargetPropertyEntry ee(lib, lib.Backtrace); cmExpandList(cge->Evaluate(headTarget->GetLocalGenerator(), config, - false, headTarget, dagChecker), + headTarget, dagChecker), ee.Values); if (cge->GetHadContextSensitiveCondition()) { ee.ContextDependent = true; @@ -1454,11 +1426,14 @@ std::vector<BT<std::string>> cmGeneratorTarget::GetSourceFilePaths( this, linkInterfaceSourcesEntries, files, uniqueSrcs, debugSources); // Collect TARGET_OBJECTS of direct object link-dependencies. - std::vector<EvaluatedTargetPropertyEntry> linkObjectsEntries; - AddObjectEntries(this, config, &dagChecker, linkObjectsEntries); + bool contextDependentObjects = false; std::vector<std::string>::size_type numFilesBefore2 = files.size(); - bool contextDependentObjects = - processSources(this, linkObjectsEntries, files, uniqueSrcs, debugSources); + if (this->GetType() != cmStateEnums::OBJECT_LIBRARY) { + std::vector<EvaluatedTargetPropertyEntry> linkObjectsEntries; + AddObjectEntries(this, config, &dagChecker, linkObjectsEntries); + contextDependentObjects = processSources(this, linkObjectsEntries, files, + uniqueSrcs, debugSources); + } if (!contextDependentDirectSources && !(contextDependentInterfaceSources && numFilesBefore < files.size()) && @@ -2540,12 +2515,11 @@ void cmGeneratorTarget::GetAutoUicOptions(std::vector<std::string>& result, if (!prop) { return; } - cmGeneratorExpression ge; cmGeneratorExpressionDAGChecker dagChecker(this, "AUTOUIC_OPTIONS", nullptr, nullptr); - cmExpandList(ge.Parse(prop)->Evaluate(this->LocalGenerator, config, false, - this, &dagChecker), + cmExpandList(cmGeneratorExpression::Evaluate(prop, this->LocalGenerator, + config, this, &dagChecker), result); } @@ -2815,7 +2789,8 @@ void cmTargetTraceDependencies::CheckCustomCommand(cmCustomCommand const& cc) // Check for target references in generator expressions. for (std::string const& cl : cCmdLine) { const std::unique_ptr<cmCompiledGeneratorExpression> cge = ge.Parse(cl); - cge->Evaluate(this->GeneratorTarget->GetLocalGenerator(), "", true); + cge->SetQuiet(true); + cge->Evaluate(this->GeneratorTarget->GetLocalGenerator(), ""); std::set<cmGeneratorTarget*> geTargets = cge->GetTargets(); targets.insert(geTargets.begin(), geTargets.end()); } @@ -5311,9 +5286,9 @@ void cmGeneratorTarget::ExpandLinkItems( } std::vector<std::string> libs; std::unique_ptr<cmCompiledGeneratorExpression> cge = ge.Parse(value); - cmExpandList(cge->Evaluate(this->LocalGenerator, config, false, headTarget, - this, &dagChecker), - libs); + cmExpandList( + cge->Evaluate(this->LocalGenerator, config, headTarget, &dagChecker, this), + libs); this->LookupLinkItems(libs, cge->GetBacktrace(), items); hadHeadSensitiveCondition = cge->GetHadHeadSensitiveCondition(); } @@ -5576,18 +5551,15 @@ bool cmGeneratorTarget::ComputeOutputDir(const std::string& config, // Select an output directory. if (const char* config_outdir = this->GetProperty(configProp)) { // Use the user-specified per-configuration output directory. - cmGeneratorExpression ge; - std::unique_ptr<cmCompiledGeneratorExpression> cge = - ge.Parse(config_outdir); - out = cge->Evaluate(this->LocalGenerator, config); + out = cmGeneratorExpression::Evaluate(config_outdir, this->LocalGenerator, + config); // Skip per-configuration subdirectory. conf.clear(); } else if (const char* outdir = this->GetProperty(propertyName)) { // Use the user-specified output directory. - cmGeneratorExpression ge; - std::unique_ptr<cmCompiledGeneratorExpression> cge = ge.Parse(outdir); - out = cge->Evaluate(this->LocalGenerator, config); + out = + cmGeneratorExpression::Evaluate(outdir, this->LocalGenerator, config); // Skip per-configuration subdirectory if the value contained a // generator expression. @@ -5655,18 +5627,15 @@ bool cmGeneratorTarget::ComputePDBOutputDir(const std::string& kind, // Select an output directory. if (const char* config_outdir = this->GetProperty(configProp)) { // Use the user-specified per-configuration output directory. - cmGeneratorExpression ge; - std::unique_ptr<cmCompiledGeneratorExpression> cge = - ge.Parse(config_outdir); - out = cge->Evaluate(this->LocalGenerator, config); + out = cmGeneratorExpression::Evaluate(config_outdir, this->LocalGenerator, + config); // Skip per-configuration subdirectory. conf.clear(); } else if (const char* outdir = this->GetProperty(propertyName)) { // Use the user-specified output directory. - cmGeneratorExpression ge; - std::unique_ptr<cmCompiledGeneratorExpression> cge = ge.Parse(outdir); - out = cge->Evaluate(this->LocalGenerator, config); + out = + cmGeneratorExpression::Evaluate(outdir, this->LocalGenerator, config); // Skip per-configuration subdirectory if the value contained a // generator expression. @@ -5721,8 +5690,7 @@ bool cmGeneratorTarget::GetRPATH(const std::string& config, return false; } - cmGeneratorExpression ge; - rpath = ge.Parse(value)->Evaluate(this->LocalGenerator, config); + rpath = cmGeneratorExpression::Evaluate(value, this->LocalGenerator, config); return true; } @@ -6383,7 +6351,7 @@ void cmGeneratorTarget::ComputeLinkImplementationLibraries( cmGeneratorExpression ge(*btIt); std::unique_ptr<cmCompiledGeneratorExpression> const cge = ge.Parse(*le); std::string const& evaluated = - cge->Evaluate(this->LocalGenerator, config, false, head, &dagChecker); + cge->Evaluate(this->LocalGenerator, config, head, &dagChecker); cmExpandList(evaluated, llibs); if (cge->GetHadHeadSensitiveCondition()) { impl.HadHeadSensitiveCondition = true; diff --git a/Source/cmGlobalGenerator.cxx b/Source/cmGlobalGenerator.cxx index 2273c50..a75e2ed 100644 --- a/Source/cmGlobalGenerator.cxx +++ b/Source/cmGlobalGenerator.cxx @@ -2037,10 +2037,18 @@ bool cmGlobalGenerator::IsExcluded(cmLocalGenerator* root, return this->IsExcluded(rootSnp, snp); } -bool cmGlobalGenerator::IsExcluded(cmGeneratorTarget* target) const +bool cmGlobalGenerator::IsExcluded(cmLocalGenerator* root, + cmGeneratorTarget* target) const { - return target->GetType() == cmStateEnums::INTERFACE_LIBRARY || - target->GetPropertyAsBool("EXCLUDE_FROM_ALL"); + if (target->GetType() == cmStateEnums::INTERFACE_LIBRARY) { + return true; + } + if (const char* exclude = target->GetProperty("EXCLUDE_FROM_ALL")) { + return cmIsOn(exclude); + } + // This target is included in its directory. Check whether the + // directory is excluded. + return this->IsExcluded(root, target->GetLocalGenerator()); } void cmGlobalGenerator::GetEnabledLanguages( @@ -2582,7 +2590,7 @@ cmTarget cmGlobalGenerator::CreateGlobalTarget(GlobalTargetInfo const& gti, cmCustomCommand cc(nullptr, no_outputs, no_byproducts, no_depends, gti.CommandLines, nullptr, gti.WorkingDir.c_str()); cc.SetUsesTerminal(gti.UsesTerminal); - target.AddPostBuildCommand(cc); + target.AddPostBuildCommand(std::move(cc)); if (!gti.Message.empty()) { target.SetProperty("EchoString", gti.Message.c_str()); } diff --git a/Source/cmGlobalGenerator.h b/Source/cmGlobalGenerator.h index 372e658..cf9c951 100644 --- a/Source/cmGlobalGenerator.h +++ b/Source/cmGlobalGenerator.h @@ -511,7 +511,7 @@ protected: bool IsExcluded(cmStateSnapshot const& root, cmStateSnapshot const& snp) const; bool IsExcluded(cmLocalGenerator* root, cmLocalGenerator* gen) const; - bool IsExcluded(cmGeneratorTarget* target) const; + bool IsExcluded(cmLocalGenerator* root, cmGeneratorTarget* target) const; virtual void InitializeProgressMarks() {} struct GlobalTargetInfo @@ -555,8 +555,6 @@ protected: cmTarget* FindTargetImpl(std::string const& name) const; cmGeneratorTarget* FindGeneratorTargetImpl(std::string const& name) const; - cmGeneratorTarget* FindImportedGeneratorTargetImpl( - std::string const& name) const; const char* GetPredefinedTargetsFolder(); diff --git a/Source/cmGlobalNinjaGenerator.h b/Source/cmGlobalNinjaGenerator.h index 7aa231e..dca783f 100644 --- a/Source/cmGlobalNinjaGenerator.h +++ b/Source/cmGlobalNinjaGenerator.h @@ -302,9 +302,9 @@ public: return LocalGenerators; } - bool IsExcluded(cmGeneratorTarget* target) + bool IsExcluded(cmLocalGenerator* root, cmGeneratorTarget* target) { - return cmGlobalGenerator::IsExcluded(target); + return cmGlobalGenerator::IsExcluded(root, target); } int GetRuleCmdLength(const std::string& name) { return RuleCmdLength[name]; } diff --git a/Source/cmGlobalUnixMakefileGenerator3.cxx b/Source/cmGlobalUnixMakefileGenerator3.cxx index 0b211b8..4ce3d5e 100644 --- a/Source/cmGlobalUnixMakefileGenerator3.cxx +++ b/Source/cmGlobalUnixMakefileGenerator3.cxx @@ -230,6 +230,14 @@ void cmGlobalUnixMakefileGenerator3::WriteMainMakefile2() depends.push_back(this->EmptyRuleHackDepends); } + // Write and empty all: + lg->WriteMakeRule(makefileStream, "The main recursive all target", "all", + depends, no_commands, true); + + // Write an empty preinstall: + lg->WriteMakeRule(makefileStream, "The main recursive preinstall target", + "preinstall", depends, no_commands, true); + // Write out the "special" stuff lg->WriteSpecialTargetsTop(makefileStream); @@ -460,8 +468,13 @@ void cmGlobalUnixMakefileGenerator3::WriteDirectoryRules2( ruleFileStream << "\n\n"; } - // Write directory-level rules for "all". - this->WriteDirectoryRule2(ruleFileStream, lg, "all", true, false); + if (!lg->IsRootMakefile()) { + // Write directory-level rules for "all". + this->WriteDirectoryRule2(ruleFileStream, lg, "all", true, false); + + // Write directory-level rules for "preinstall". + this->WriteDirectoryRule2(ruleFileStream, lg, "preinstall", true, true); + } // Write directory-level rules for "clean". { @@ -469,9 +482,6 @@ void cmGlobalUnixMakefileGenerator3::WriteDirectoryRules2( lg->AppendDirectoryCleanCommand(cmds); this->WriteDirectoryRule2(ruleFileStream, lg, "clean", false, false, cmds); } - - // Write directory-level rules for "preinstall". - this->WriteDirectoryRule2(ruleFileStream, lg, "preinstall", true, true); } std::vector<cmGlobalGenerator::GeneratedMakeCommand> @@ -685,6 +695,15 @@ void cmGlobalUnixMakefileGenerator3::WriteConvenienceRules2( lg->WriteMakeRule(ruleFileStream, "All Build rule for target.", localName, depends, commands, true); + // add the all/all dependency + if (!this->IsExcluded(this->LocalGenerators[0], gtarget)) { + depends.clear(); + depends.push_back(localName); + commands.clear(); + lg->WriteMakeRule(ruleFileStream, "Include target in all.", "all", + depends, commands, true); + } + // Write the rule. commands.clear(); @@ -739,7 +758,7 @@ void cmGlobalUnixMakefileGenerator3::WriteConvenienceRules2( "Pre-install relink rule for target.", localName, depends, commands, true); - if (!this->IsExcluded(gtarget)) { + if (!this->IsExcluded(this->LocalGenerators[0], gtarget)) { depends.clear(); depends.push_back(localName); commands.clear(); diff --git a/Source/cmGlobalVisualStudio7Generator.cxx b/Source/cmGlobalVisualStudio7Generator.cxx index 92316d3..0915189 100644 --- a/Source/cmGlobalVisualStudio7Generator.cxx +++ b/Source/cmGlobalVisualStudio7Generator.cxx @@ -45,6 +45,14 @@ static cmVS7FlagTable cmVS7ExtraFlagTable[] = { { "", "", "", "", 0 } }; +namespace { +std::string GetSLNFile(cmLocalGenerator* root) +{ + return cmStrCat(root->GetCurrentBinaryDirectory(), '/', + root->GetProjectName(), ".sln"); +} +} + cmGlobalVisualStudio7Generator::cmGlobalVisualStudio7Generator( cmake* cm, std::string const& platformInGeneratorName) : cmGlobalVisualStudioGenerator(cm, platformInGeneratorName) @@ -286,8 +294,10 @@ void cmGlobalVisualStudio7Generator::Generate() this->OutputSLNFile(); // If any solution or project files changed during the generation, // tell Visual Studio to reload them... - if (!cmSystemTools::GetErrorOccuredFlag()) { - this->CallVisualStudioMacro(MacroReload); + if (!cmSystemTools::GetErrorOccuredFlag() && + !this->LocalGenerators.empty()) { + this->CallVisualStudioMacro(MacroReload, + GetSLNFile(this->LocalGenerators[0])); } } @@ -298,8 +308,7 @@ void cmGlobalVisualStudio7Generator::OutputSLNFile( return; } this->CurrentProject = root->GetProjectName(); - std::string fname = cmStrCat(root->GetCurrentBinaryDirectory(), '/', - root->GetProjectName(), ".sln"); + std::string fname = GetSLNFile(root); cmGeneratedFileStream fout(fname.c_str()); fout.SetCopyIfDifferent(true); if (!fout) { @@ -663,10 +672,8 @@ std::set<std::string> cmGlobalVisualStudio7Generator::IsPartOfDefaultBuild( for (std::string const& i : configs) { const char* propertyValue = target->Target->GetMakefile()->GetDefinition(propertyName); - cmGeneratorExpression ge; - std::unique_ptr<cmCompiledGeneratorExpression> cge = - ge.Parse(propertyValue); - if (cmIsOn(cge->Evaluate(target->GetLocalGenerator(), i))) { + if (cmIsOn(cmGeneratorExpression::Evaluate( + propertyValue, target->GetLocalGenerator(), i))) { activeConfigs.insert(i); } } diff --git a/Source/cmGlobalVisualStudio8Generator.cxx b/Source/cmGlobalVisualStudio8Generator.cxx index 255739d..8e6125b 100644 --- a/Source/cmGlobalVisualStudio8Generator.cxx +++ b/Source/cmGlobalVisualStudio8Generator.cxx @@ -96,17 +96,18 @@ bool cmGlobalVisualStudio8Generator::AddCheckTarget() return false; } - const char* no_working_directory = nullptr; - std::vector<std::string> no_depends; std::vector<cmLocalGenerator*> const& generators = this->LocalGenerators; cmLocalVisualStudio7Generator* lg = static_cast<cmLocalVisualStudio7Generator*>(generators[0]); cmMakefile* mf = lg->GetMakefile(); - cmCustomCommandLines noCommandLines; + const char* no_working_directory = nullptr; + std::vector<std::string> no_byproducts; + std::vector<std::string> no_depends; + cmCustomCommandLines no_commands; cmTarget* tgt = mf->AddUtilityCommand( - CMAKE_CHECK_BUILD_SYSTEM_TARGET, cmMakefile::TargetOrigin::Generator, - false, no_working_directory, no_depends, noCommandLines); + CMAKE_CHECK_BUILD_SYSTEM_TARGET, cmCommandOrigin::Generator, false, + no_working_directory, no_byproducts, no_depends, no_commands); cmGeneratorTarget* gt = new cmGeneratorTarget(tgt, lg); lg->AddGeneratorTarget(gt); @@ -152,10 +153,10 @@ bool cmGlobalVisualStudio8Generator::AddCheckTarget() std::vector<std::string> byproducts; byproducts.push_back(cm->GetGlobVerifyStamp()); - mf->AddCustomCommandToTarget(CMAKE_CHECK_BUILD_SYSTEM_TARGET, byproducts, - no_depends, verifyCommandLines, - cmTarget::PRE_BUILD, "Checking File Globs", - no_working_directory, false); + mf->AddCustomCommandToTarget( + CMAKE_CHECK_BUILD_SYSTEM_TARGET, byproducts, no_depends, + verifyCommandLines, cmCustomCommandType::PRE_BUILD, + "Checking File Globs", no_working_directory, false); // Ensure ZERO_CHECK always runs in Visual Studio using MSBuild, // otherwise the prebuild command will not be run. @@ -182,7 +183,6 @@ bool cmGlobalVisualStudio8Generator::AddCheckTarget() // file as the main dependency because it would get // overwritten by the CreateVCProjBuildRule. // (this could be avoided with per-target source files) - std::vector<std::string> no_byproducts; std::string no_main_dependency; cmImplicitDependsList no_implicit_depends; if (cmSourceFile* file = mf->AddCustomCommandToOutput( @@ -284,11 +284,9 @@ bool cmGlobalVisualStudio8Generator::DeployInhibited( cmGeneratorTarget const& target, const char* config) const { bool rVal = false; - if (const char* propStr = target.GetProperty("VS_NO_SOLUTION_DEPLOY")) { - cmGeneratorExpression ge; - std::unique_ptr<cmCompiledGeneratorExpression> cge = ge.Parse(propStr); - std::string prop = cge->Evaluate(target.LocalGenerator, config); - rVal = cmIsOn(prop); + if (const char* prop = target.GetProperty("VS_NO_SOLUTION_DEPLOY")) { + rVal = cmIsOn( + cmGeneratorExpression::Evaluate(prop, target.LocalGenerator, config)); } return rVal; } diff --git a/Source/cmGlobalVisualStudioGenerator.cxx b/Source/cmGlobalVisualStudioGenerator.cxx index 61e8f58..0f939cc 100644 --- a/Source/cmGlobalVisualStudioGenerator.cxx +++ b/Source/cmGlobalVisualStudioGenerator.cxx @@ -183,7 +183,8 @@ void cmGlobalVisualStudioGenerator::AddExtraIDETargets() { // Add a special target that depends on ALL projects for easy build // of one configuration only. - const char* no_working_dir = 0; + const char* no_working_dir = nullptr; + std::vector<std::string> no_byproducts; std::vector<std::string> no_depends; cmCustomCommandLines no_commands; for (auto const& it : this->ProjectMap) { @@ -193,8 +194,8 @@ void cmGlobalVisualStudioGenerator::AddExtraIDETargets() // Use no actual command lines so that the target itself is not // considered always out of date. cmTarget* allBuild = gen[0]->GetMakefile()->AddUtilityCommand( - "ALL_BUILD", cmMakefile::TargetOrigin::Generator, true, no_working_dir, - no_depends, no_commands, false, "Build all projects"); + "ALL_BUILD", cmCommandOrigin::Generator, true, no_working_dir, + no_byproducts, no_depends, no_commands, false, "Build all projects"); cmGeneratorTarget* gt = new cmGeneratorTarget(allBuild, gen[0]); gen[0]->AddGeneratorTarget(gt); @@ -213,7 +214,7 @@ void cmGlobalVisualStudioGenerator::AddExtraIDETargets() tgt->IsImported()) { continue; } - if (!this->IsExcluded(tgt)) { + if (!this->IsExcluded(gen[0], tgt)) { allBuild->AddUtility(tgt->GetName()); } } @@ -288,11 +289,10 @@ void cmGlobalVisualStudioGenerator::ConfigureCMakeVisualStudioMacros() } void cmGlobalVisualStudioGenerator::CallVisualStudioMacro( - MacroName m, const char* vsSolutionFile) + MacroName m, const std::string& vsSolutionFile) { // If any solution or project files changed during the generation, // tell Visual Studio to reload them... - cmMakefile* mf = this->LocalGenerators[0]->GetMakefile(); std::string dir = this->GetUserMacrosDirectory(); // Only really try to call the macro if: @@ -307,27 +307,18 @@ void cmGlobalVisualStudioGenerator::CallVisualStudioMacro( if (cmSystemTools::FileExists(macrosFile.c_str()) && IsVisualStudioMacrosFileRegistered( macrosFile, this->GetUserMacrosRegKeyBase(), nextSubkeyName)) { - std::string topLevelSlnName; - if (vsSolutionFile) { - topLevelSlnName = vsSolutionFile; - } else { - topLevelSlnName = - cmStrCat(mf->GetCurrentBinaryDirectory(), '/', - this->LocalGenerators[0]->GetProjectName(), ".sln"); - } - if (m == MacroReload) { std::vector<std::string> filenames; this->GetFilesReplacedDuringGenerate(filenames); if (!filenames.empty()) { std::string projects = cmJoin(filenames, ";"); cmCallVisualStudioMacro::CallMacro( - topLevelSlnName, CMAKE_VSMACROS_RELOAD_MACRONAME, projects, + vsSolutionFile, CMAKE_VSMACROS_RELOAD_MACRONAME, projects, this->GetCMakeInstance()->GetDebugOutput()); } } else if (m == MacroStop) { cmCallVisualStudioMacro::CallMacro( - topLevelSlnName, CMAKE_VSMACROS_STOP_MACRONAME, "", + vsSolutionFile, CMAKE_VSMACROS_STOP_MACRONAME, "", this->GetCMakeInstance()->GetDebugOutput()); } } @@ -901,7 +892,6 @@ void cmGlobalVisualStudioGenerator::AddSymbolExportCommand( gt->LocalGenerator->ComputeObjectFilenames(mapping, gt); std::string obj_dir = gt->ObjectDirectory; std::string cmakeCommand = cmSystemTools::GetCMakeCommand(); - cmSystemTools::ConvertToWindowsExtendedPath(cmakeCommand); std::string obj_dir_expanded = obj_dir; cmSystemTools::ReplaceString(obj_dir_expanded, this->GetCMakeCFGIntDir(), configName.c_str()); diff --git a/Source/cmGlobalVisualStudioGenerator.h b/Source/cmGlobalVisualStudioGenerator.h index bd615ec..4f2007f 100644 --- a/Source/cmGlobalVisualStudioGenerator.h +++ b/Source/cmGlobalVisualStudioGenerator.h @@ -90,7 +90,7 @@ public: * Call the ReloadProjects macro if necessary based on * GetFilesReplacedDuringGenerate results. */ - void CallVisualStudioMacro(MacroName m, const char* vsSolutionFile = 0); + void CallVisualStudioMacro(MacroName m, const std::string& vsSolutionFile); // return true if target is fortran only bool TargetIsFortranOnly(const cmGeneratorTarget* gt); diff --git a/Source/cmGlobalXCodeGenerator.cxx b/Source/cmGlobalXCodeGenerator.cxx index 643cc99..f42218b 100644 --- a/Source/cmGlobalXCodeGenerator.cxx +++ b/Source/cmGlobalXCodeGenerator.cxx @@ -497,12 +497,14 @@ void cmGlobalXCodeGenerator::AddExtraTargets( { cmMakefile* mf = root->GetMakefile(); - // Add ALL_BUILD const char* no_working_directory = nullptr; + std::vector<std::string> no_byproducts; std::vector<std::string> no_depends; + + // Add ALL_BUILD cmTarget* allbuild = mf->AddUtilityCommand( - "ALL_BUILD", cmMakefile::TargetOrigin::Generator, true, - no_working_directory, no_depends, + "ALL_BUILD", cmCommandOrigin::Generator, true, no_working_directory, + no_byproducts, no_depends, cmMakeSingleCommandLine({ "echo", "Build all projects" })); cmGeneratorTarget* allBuildGt = new cmGeneratorTarget(allbuild, root); @@ -526,8 +528,8 @@ void cmGlobalXCodeGenerator::AddExtraTargets( this->ConvertToRelativeForMake(this->CurrentReRunCMakeMakefile); cmSystemTools::ReplaceString(file, "\\ ", " "); cmTarget* check = mf->AddUtilityCommand( - CMAKE_CHECK_BUILD_SYSTEM_TARGET, cmMakefile::TargetOrigin::Generator, - true, no_working_directory, no_depends, + CMAKE_CHECK_BUILD_SYSTEM_TARGET, cmCommandOrigin::Generator, true, + no_working_directory, no_byproducts, no_depends, cmMakeSingleCommandLine({ "make", "-f", file })); cmGeneratorTarget* checkGt = new cmGeneratorTarget(check, root); @@ -542,9 +544,8 @@ void cmGlobalXCodeGenerator::AddExtraTargets( continue; } - std::string targetName = target->GetName(); - - if (regenerate && (targetName != CMAKE_CHECK_BUILD_SYSTEM_TARGET)) { + if (regenerate && + (target->GetName() != CMAKE_CHECK_BUILD_SYSTEM_TARGET)) { target->Target->AddUtility(CMAKE_CHECK_BUILD_SYSTEM_TARGET); } @@ -555,14 +556,14 @@ void cmGlobalXCodeGenerator::AddExtraTargets( if (target->GetType() == cmStateEnums::OBJECT_LIBRARY) { commandLines.front().back() = // fill placeholder this->PostBuildMakeTarget(target->GetName(), "$(CONFIGURATION)"); - std::vector<std::string> no_byproducts; gen->GetMakefile()->AddCustomCommandToTarget( target->GetName(), no_byproducts, no_depends, commandLines, - cmTarget::POST_BUILD, "Depend check for xcode", dir.c_str(), true, - false, "", "", false, cmMakefile::AcceptObjectLibraryCommands); + cmCustomCommandType::POST_BUILD, "Depend check for xcode", + dir.c_str(), true, false, "", "", false, + cmObjectLibraryCommands::Accept); } - if (!this->IsExcluded(target)) { + if (!this->IsExcluded(gens[0], target)) { allbuild->AddUtility(target->GetName()); } } @@ -2381,10 +2382,8 @@ void cmGlobalXCodeGenerator::CreateBuildSettings(cmGeneratorTarget* gtgt, std::string attribute = prop.substr(16); this->FilterConfigurationAttribute(configName, attribute); if (!attribute.empty()) { - cmGeneratorExpression ge; - std::string processed = - ge.Parse(gtgt->GetProperty(prop)) - ->Evaluate(this->CurrentLocalGenerator, configName); + std::string processed = cmGeneratorExpression::Evaluate( + gtgt->GetProperty(prop), this->CurrentLocalGenerator, configName); buildSettings->AddAttribute(attribute, this->CreateString(processed)); } @@ -3118,10 +3117,9 @@ bool cmGlobalXCodeGenerator::CreateXCodeObjects( std::string attribute = var.substr(22); this->FilterConfigurationAttribute(config.first, attribute); if (!attribute.empty()) { - cmGeneratorExpression ge; - std::string processed = - ge.Parse(this->CurrentMakefile->GetDefinition(var)) - ->Evaluate(this->CurrentLocalGenerator, config.first); + std::string processed = cmGeneratorExpression::Evaluate( + this->CurrentMakefile->GetDefinition(var), + this->CurrentLocalGenerator, config.first); buildSettingsForCfg->AddAttribute(attribute, this->CreateString(processed)); } diff --git a/Source/cmIncludeDirectoryCommand.cxx b/Source/cmIncludeDirectoryCommand.cxx index a4d0a0d..170aea1 100644 --- a/Source/cmIncludeDirectoryCommand.cxx +++ b/Source/cmIncludeDirectoryCommand.cxx @@ -7,24 +7,28 @@ #include <utility> #include "cmAlgorithms.h" +#include "cmExecutionStatus.h" #include "cmGeneratorExpression.h" #include "cmMakefile.h" #include "cmStringAlgorithms.h" #include "cmSystemTools.h" -class cmExecutionStatus; +static void GetIncludes(cmMakefile& mf, const std::string& arg, + std::vector<std::string>& incs); +static void NormalizeInclude(cmMakefile& mf, std::string& inc); -// cmIncludeDirectoryCommand -bool cmIncludeDirectoryCommand::InitialPass( - std::vector<std::string> const& args, cmExecutionStatus&) +bool cmIncludeDirectoryCommand(std::vector<std::string> const& args, + cmExecutionStatus& status) { if (args.empty()) { return true; } + cmMakefile& mf = status.GetMakefile(); + auto i = args.begin(); - bool before = this->Makefile->IsOn("CMAKE_INCLUDE_DIRECTORIES_BEFORE"); + bool before = mf.IsOn("CMAKE_INCLUDE_DIRECTORIES_BEFORE"); bool system = false; if ((*i) == "BEFORE") { @@ -45,13 +49,13 @@ bool cmIncludeDirectoryCommand::InitialPass( continue; } if (i->empty()) { - this->SetError("given empty-string as include directory."); + status.SetError("given empty-string as include directory."); return false; } std::vector<std::string> includes; - this->GetIncludes(*i, includes); + GetIncludes(mf, *i, includes); if (before) { cmAppend(beforeIncludes, includes); @@ -64,9 +68,9 @@ bool cmIncludeDirectoryCommand::InitialPass( } std::reverse(beforeIncludes.begin(), beforeIncludes.end()); - this->Makefile->AddIncludeDirectories(afterIncludes); - this->Makefile->AddIncludeDirectories(beforeIncludes, before); - this->Makefile->AddSystemIncludeDirectories(systemIncludes); + mf.AddIncludeDirectories(afterIncludes); + mf.AddIncludeDirectories(beforeIncludes, before); + mf.AddSystemIncludeDirectories(systemIncludes); return true; } @@ -83,8 +87,8 @@ bool cmIncludeDirectoryCommand::InitialPass( // output from a program and passing it into a command the cleanup doesn't // always happen // -void cmIncludeDirectoryCommand::GetIncludes(const std::string& arg, - std::vector<std::string>& incs) +static void GetIncludes(cmMakefile& mf, const std::string& arg, + std::vector<std::string>& incs) { // break apart any line feed arguments std::string::size_type pos = 0; @@ -92,7 +96,7 @@ void cmIncludeDirectoryCommand::GetIncludes(const std::string& arg, while ((pos = arg.find('\n', lastPos)) != std::string::npos) { if (pos) { std::string inc = arg.substr(lastPos, pos); - this->NormalizeInclude(inc); + NormalizeInclude(mf, inc); if (!inc.empty()) { incs.push_back(std::move(inc)); } @@ -100,13 +104,13 @@ void cmIncludeDirectoryCommand::GetIncludes(const std::string& arg, lastPos = pos + 1; } std::string inc = arg.substr(lastPos); - this->NormalizeInclude(inc); + NormalizeInclude(mf, inc); if (!inc.empty()) { incs.push_back(std::move(inc)); } } -void cmIncludeDirectoryCommand::NormalizeInclude(std::string& inc) +static void NormalizeInclude(cmMakefile& mf, std::string& inc) { std::string::size_type b = inc.find_first_not_of(" \r"); std::string::size_type e = inc.find_last_not_of(" \r"); @@ -119,13 +123,9 @@ void cmIncludeDirectoryCommand::NormalizeInclude(std::string& inc) if (!cmIsOff(inc)) { cmSystemTools::ConvertToUnixSlashes(inc); - - if (!cmSystemTools::FileIsFullPath(inc)) { - if (!cmGeneratorExpression::StartsWithGeneratorExpression(inc)) { - std::string tmp = - cmStrCat(this->Makefile->GetCurrentSourceDirectory(), '/', inc); - inc = tmp; - } + if (!cmSystemTools::FileIsFullPath(inc) && + !cmGeneratorExpression::StartsWithGeneratorExpression(inc)) { + inc = cmStrCat(mf.GetCurrentSourceDirectory(), '/', inc); } } } diff --git a/Source/cmIncludeDirectoryCommand.h b/Source/cmIncludeDirectoryCommand.h index bcaae9d..66caff7 100644 --- a/Source/cmIncludeDirectoryCommand.h +++ b/Source/cmIncludeDirectoryCommand.h @@ -8,40 +8,9 @@ #include <string> #include <vector> -#include <cm/memory> - -#include "cmCommand.h" - class cmExecutionStatus; -/** \class cmIncludeDirectoryCommand - * \brief Add include directories to the build. - * - * cmIncludeDirectoryCommand is used to specify directory locations - * to search for included files. - */ -class cmIncludeDirectoryCommand : public cmCommand -{ -public: - /** - * This is a virtual constructor for the command. - */ - std::unique_ptr<cmCommand> Clone() override - { - return cm::make_unique<cmIncludeDirectoryCommand>(); - } - - /** - * This is called when the command is first encountered in - * the CMakeLists.txt file. - */ - bool InitialPass(std::vector<std::string> const& args, - cmExecutionStatus& status) override; - -protected: - // used internally - void GetIncludes(const std::string& arg, std::vector<std::string>& incs); - void NormalizeInclude(std::string& inc); -}; +bool cmIncludeDirectoryCommand(std::vector<std::string> const& args, + cmExecutionStatus& status); #endif diff --git a/Source/cmInstallCommand.cxx b/Source/cmInstallCommand.cxx index f7050d4..27d0dfb 100644 --- a/Source/cmInstallCommand.cxx +++ b/Source/cmInstallCommand.cxx @@ -4,12 +4,14 @@ #include "cm_static_string_view.hxx" #include "cmsys/Glob.hxx" +#include <cm/memory> #include <cstddef> #include <set> #include <sstream> #include <utility> #include "cmArgumentParser.h" +#include "cmExecutionStatus.h" #include "cmExportSet.h" #include "cmGeneratorExpression.h" #include "cmGlobalGenerator.h" @@ -26,13 +28,61 @@ #include "cmPolicies.h" #include "cmStateTypes.h" #include "cmStringAlgorithms.h" +#include "cmSubcommandTable.h" #include "cmSystemTools.h" #include "cmTarget.h" #include "cmTargetExport.h" -class cmExecutionStatus; +namespace { -static cmInstallTargetGenerator* CreateInstallTargetGenerator( +class Helper +{ +public: + Helper(cmExecutionStatus& status) + : Status(status) + , Makefile(&status.GetMakefile()) + { + this->DefaultComponentName = this->Makefile->GetSafeDefinition( + "CMAKE_INSTALL_DEFAULT_COMPONENT_NAME"); + if (this->DefaultComponentName.empty()) { + this->DefaultComponentName = "Unspecified"; + } + } + + void SetError(std::string const& err) { this->Status.SetError(err); } + + bool MakeFilesFullPath(const char* modeName, + const std::vector<std::string>& relFiles, + std::vector<std::string>& absFiles); + bool CheckCMP0006(bool& failure); + + std::string GetDestination(const cmInstallCommandArguments* args, + const std::string& varName, + const std::string& guess); + std::string GetRuntimeDestination(const cmInstallCommandArguments* args); + std::string GetSbinDestination(const cmInstallCommandArguments* args); + std::string GetArchiveDestination(const cmInstallCommandArguments* args); + std::string GetLibraryDestination(const cmInstallCommandArguments* args); + std::string GetIncludeDestination(const cmInstallCommandArguments* args); + std::string GetSysconfDestination(const cmInstallCommandArguments* args); + std::string GetSharedStateDestination(const cmInstallCommandArguments* args); + std::string GetLocalStateDestination(const cmInstallCommandArguments* args); + std::string GetRunStateDestination(const cmInstallCommandArguments* args); + std::string GetDataRootDestination(const cmInstallCommandArguments* args); + std::string GetDataDestination(const cmInstallCommandArguments* args); + std::string GetInfoDestination(const cmInstallCommandArguments* args); + std::string GetLocaleDestination(const cmInstallCommandArguments* args); + std::string GetManDestination(const cmInstallCommandArguments* args); + std::string GetDocDestination(const cmInstallCommandArguments* args); + std::string GetDestinationForType(const cmInstallCommandArguments* args, + const std::string& type); + + cmExecutionStatus& Status; + cmMakefile* Makefile; + std::string DefaultComponentName; +}; + +cmInstallTargetGenerator* CreateInstallTargetGenerator( cmTarget& target, const cmInstallCommandArguments& args, bool impLib, cmListFileBacktrace const& backtrace, const std::string& destination, bool forceOpt = false, bool namelink = false) @@ -51,7 +101,7 @@ static cmInstallTargetGenerator* CreateInstallTargetGenerator( return g; } -static cmInstallTargetGenerator* CreateInstallTargetGenerator( +cmInstallTargetGenerator* CreateInstallTargetGenerator( cmTarget& target, const cmInstallCommandArguments& args, bool impLib, cmListFileBacktrace const& backtrace, bool forceOpt = false, bool namelink = false) @@ -61,7 +111,7 @@ static cmInstallTargetGenerator* CreateInstallTargetGenerator( namelink); } -static cmInstallFilesGenerator* CreateInstallFilesGenerator( +cmInstallFilesGenerator* CreateInstallFilesGenerator( cmMakefile* mf, const std::vector<std::string>& absFiles, const cmInstallCommandArguments& args, bool programs, const std::string& destination) @@ -74,7 +124,7 @@ static cmInstallFilesGenerator* CreateInstallFilesGenerator( args.GetExcludeFromAll(), args.GetRename().c_str(), args.GetOptional()); } -static cmInstallFilesGenerator* CreateInstallFilesGenerator( +cmInstallFilesGenerator* CreateInstallFilesGenerator( cmMakefile* mf, const std::vector<std::string>& absFiles, const cmInstallCommandArguments& args, bool programs) { @@ -82,68 +132,18 @@ static cmInstallFilesGenerator* CreateInstallFilesGenerator( args.GetDestination()); } -static const std::set<std::string> allowedTypes{ +std::set<std::string> const allowedTypes{ "BIN", "SBIN", "LIB", "INCLUDE", "SYSCONF", "SHAREDSTATE", "LOCALSTATE", "RUNSTATE", "DATA", "INFO", "LOCALE", "MAN", "DOC", }; -// cmInstallCommand -bool cmInstallCommand::InitialPass(std::vector<std::string> const& args, - cmExecutionStatus&) +bool HandleScriptMode(std::vector<std::string> const& args, + cmExecutionStatus& status) { - // Allow calling with no arguments so that arguments may be built up - // using a variable that may be left empty. - if (args.empty()) { - return true; - } - - // Enable the install target. - this->Makefile->GetGlobalGenerator()->EnableInstallTarget(); - - this->DefaultComponentName = - this->Makefile->GetSafeDefinition("CMAKE_INSTALL_DEFAULT_COMPONENT_NAME"); - if (this->DefaultComponentName.empty()) { - this->DefaultComponentName = "Unspecified"; - } - - std::string const& mode = args[0]; - - // Switch among the command modes. - if (mode == "SCRIPT") { - return this->HandleScriptMode(args); - } - if (mode == "CODE") { - return this->HandleScriptMode(args); - } - if (mode == "TARGETS") { - return this->HandleTargetsMode(args); - } - if (mode == "FILES") { - return this->HandleFilesMode(args); - } - if (mode == "PROGRAMS") { - return this->HandleFilesMode(args); - } - if (mode == "DIRECTORY") { - return this->HandleDirectoryMode(args); - } - if (mode == "EXPORT") { - return this->HandleExportMode(args); - } - if (mode == "EXPORT_ANDROID_MK") { - return this->HandleExportAndroidMKMode(args); - } - - // Unknown mode. - std::string e = cmStrCat("called with unknown mode ", args[0]); - this->SetError(e); - return false; -} + Helper helper(status); -bool cmInstallCommand::HandleScriptMode(std::vector<std::string> const& args) -{ - std::string component = this->DefaultComponentName; + std::string component = helper.DefaultComponentName; int componentCount = 0; bool doing_script = false; bool doing_code = false; @@ -163,9 +163,9 @@ bool cmInstallCommand::HandleScriptMode(std::vector<std::string> const& args) } if (componentCount > 1) { - this->SetError("given more than one COMPONENT for the SCRIPT or CODE " - "signature of the INSTALL command. " - "Use multiple INSTALL commands with one COMPONENT each."); + status.SetError("given more than one COMPONENT for the SCRIPT or CODE " + "signature of the INSTALL command. " + "Use multiple INSTALL commands with one COMPONENT each."); return false; } @@ -187,40 +187,43 @@ bool cmInstallCommand::HandleScriptMode(std::vector<std::string> const& args) std::string script = arg; if (!cmSystemTools::FileIsFullPath(script)) { script = - cmStrCat(this->Makefile->GetCurrentSourceDirectory(), '/', arg); + cmStrCat(helper.Makefile->GetCurrentSourceDirectory(), '/', arg); } if (cmSystemTools::FileIsDirectory(script)) { - this->SetError("given a directory as value of SCRIPT argument."); + status.SetError("given a directory as value of SCRIPT argument."); return false; } - this->Makefile->AddInstallGenerator(new cmInstallScriptGenerator( + helper.Makefile->AddInstallGenerator(new cmInstallScriptGenerator( script.c_str(), false, component.c_str(), exclude_from_all)); } else if (doing_code) { doing_code = false; std::string const& code = arg; - this->Makefile->AddInstallGenerator(new cmInstallScriptGenerator( + helper.Makefile->AddInstallGenerator(new cmInstallScriptGenerator( code.c_str(), true, component.c_str(), exclude_from_all)); } } if (doing_script) { - this->SetError("given no value for SCRIPT argument."); + status.SetError("given no value for SCRIPT argument."); return false; } if (doing_code) { - this->SetError("given no value for CODE argument."); + status.SetError("given no value for CODE argument."); return false; } // Tell the global generator about any installation component names // specified. - this->Makefile->GetGlobalGenerator()->AddInstallComponent(component); + helper.Makefile->GetGlobalGenerator()->AddInstallComponent(component); return true; } -bool cmInstallCommand::HandleTargetsMode(std::vector<std::string> const& args) +bool HandleTargetsMode(std::vector<std::string> const& args, + cmExecutionStatus& status) { + Helper helper(status); + // This is the TARGETS mode. std::vector<cmTarget*> targets; @@ -260,21 +263,21 @@ bool cmInstallCommand::HandleTargetsMode(std::vector<std::string> const& args) std::vector<std::string> targetList; std::string exports; std::vector<std::string> unknownArgs; - cmInstallCommandArguments genericArgs(this->DefaultComponentName); + cmInstallCommandArguments genericArgs(helper.DefaultComponentName); genericArgs.Bind("TARGETS"_s, targetList); genericArgs.Bind("EXPORT"_s, exports); genericArgs.Parse(genericArgVector, &unknownArgs); bool success = genericArgs.Finalize(); - cmInstallCommandArguments archiveArgs(this->DefaultComponentName); - cmInstallCommandArguments libraryArgs(this->DefaultComponentName); - cmInstallCommandArguments runtimeArgs(this->DefaultComponentName); - cmInstallCommandArguments objectArgs(this->DefaultComponentName); - cmInstallCommandArguments frameworkArgs(this->DefaultComponentName); - cmInstallCommandArguments bundleArgs(this->DefaultComponentName); - cmInstallCommandArguments privateHeaderArgs(this->DefaultComponentName); - cmInstallCommandArguments publicHeaderArgs(this->DefaultComponentName); - cmInstallCommandArguments resourceArgs(this->DefaultComponentName); + cmInstallCommandArguments archiveArgs(helper.DefaultComponentName); + cmInstallCommandArguments libraryArgs(helper.DefaultComponentName); + cmInstallCommandArguments runtimeArgs(helper.DefaultComponentName); + cmInstallCommandArguments objectArgs(helper.DefaultComponentName); + cmInstallCommandArguments frameworkArgs(helper.DefaultComponentName); + cmInstallCommandArguments bundleArgs(helper.DefaultComponentName); + cmInstallCommandArguments privateHeaderArgs(helper.DefaultComponentName); + cmInstallCommandArguments publicHeaderArgs(helper.DefaultComponentName); + cmInstallCommandArguments resourceArgs(helper.DefaultComponentName); cmInstallCommandIncludesArgument includesArgs; // now parse the args for specific parts of the target (e.g. LIBRARY, @@ -292,7 +295,7 @@ bool cmInstallCommand::HandleTargetsMode(std::vector<std::string> const& args) if (!unknownArgs.empty()) { // Unknown argument. - this->SetError( + status.SetError( cmStrCat("TARGETS given unknown argument \"", unknownArgs[0], "\".")); return false; } @@ -328,7 +331,7 @@ bool cmInstallCommand::HandleTargetsMode(std::vector<std::string> const& args) objectArgs.GetNamelinkOnly() || frameworkArgs.GetNamelinkOnly() || bundleArgs.GetNamelinkOnly() || privateHeaderArgs.GetNamelinkOnly() || publicHeaderArgs.GetNamelinkOnly() || resourceArgs.GetNamelinkOnly()) { - this->SetError( + status.SetError( "TARGETS given NAMELINK_ONLY option not in LIBRARY group. " "The NAMELINK_ONLY option may be specified only following LIBRARY."); return false; @@ -337,7 +340,7 @@ bool cmInstallCommand::HandleTargetsMode(std::vector<std::string> const& args) objectArgs.GetNamelinkSkip() || frameworkArgs.GetNamelinkSkip() || bundleArgs.GetNamelinkSkip() || privateHeaderArgs.GetNamelinkSkip() || publicHeaderArgs.GetNamelinkSkip() || resourceArgs.GetNamelinkSkip()) { - this->SetError( + status.SetError( "TARGETS given NAMELINK_SKIP option not in LIBRARY group. " "The NAMELINK_SKIP option may be specified only following LIBRARY."); return false; @@ -350,15 +353,15 @@ bool cmInstallCommand::HandleTargetsMode(std::vector<std::string> const& args) privateHeaderArgs.HasNamelinkComponent() || publicHeaderArgs.HasNamelinkComponent() || resourceArgs.HasNamelinkComponent()) { - this->SetError( + status.SetError( "TARGETS given NAMELINK_COMPONENT option not in LIBRARY group. " "The NAMELINK_COMPONENT option may be specified only following " "LIBRARY."); return false; } if (libraryArgs.GetNamelinkOnly() && libraryArgs.GetNamelinkSkip()) { - this->SetError("TARGETS given NAMELINK_ONLY and NAMELINK_SKIP. " - "At most one of these two options may be specified."); + status.SetError("TARGETS given NAMELINK_ONLY and NAMELINK_SKIP. " + "At most one of these two options may be specified."); return false; } if (!genericArgs.GetType().empty() || !archiveArgs.GetType().empty() || @@ -366,7 +369,7 @@ bool cmInstallCommand::HandleTargetsMode(std::vector<std::string> const& args) !objectArgs.GetType().empty() || !frameworkArgs.GetType().empty() || !bundleArgs.GetType().empty() || !privateHeaderArgs.GetType().empty() || !publicHeaderArgs.GetType().empty() || !resourceArgs.GetType().empty()) { - this->SetError( + status.SetError( "TARGETS given TYPE option. The TYPE option may only be specified in " " install(FILES) and install(DIRECTORIES)."); return false; @@ -388,17 +391,17 @@ bool cmInstallCommand::HandleTargetsMode(std::vector<std::string> const& args) for (std::string const& tgt : targetList) { - if (this->Makefile->IsAlias(tgt)) { - this->SetError( + if (helper.Makefile->IsAlias(tgt)) { + status.SetError( cmStrCat("TARGETS given target \"", tgt, "\" which is an alias.")); return false; } // Lookup this target in the current directory. - cmTarget* target = this->Makefile->FindLocalNonAliasTarget(tgt); + cmTarget* target = helper.Makefile->FindLocalNonAliasTarget(tgt); if (!target) { // If no local target has been found, find it in the global scope. cmTarget* const global_target = - this->Makefile->GetGlobalGenerator()->FindTarget(tgt, true); + helper.Makefile->GetGlobalGenerator()->FindTarget(tgt, true); if (global_target && !global_target->IsImported()) { target = global_target; } @@ -411,7 +414,7 @@ bool cmInstallCommand::HandleTargetsMode(std::vector<std::string> const& args) target->GetType() != cmStateEnums::MODULE_LIBRARY && target->GetType() != cmStateEnums::OBJECT_LIBRARY && target->GetType() != cmStateEnums::INTERFACE_LIBRARY) { - this->SetError( + status.SetError( cmStrCat("TARGETS given target \"", tgt, "\" which is not an executable, library, or module.")); return false; @@ -420,7 +423,7 @@ bool cmInstallCommand::HandleTargetsMode(std::vector<std::string> const& args) targets.push_back(target); } else { // Did not find the target. - this->SetError( + status.SetError( cmStrCat("TARGETS given target \"", tgt, "\" which does not exist.")); return false; } @@ -472,20 +475,20 @@ bool cmInstallCommand::HandleTargetsMode(std::vector<std::string> const& args) if (!archiveArgs.GetDestination().empty()) { // The import library uses the ARCHIVE properties. archiveGenerator = CreateInstallTargetGenerator( - target, archiveArgs, true, this->Makefile->GetBacktrace()); + target, archiveArgs, true, helper.Makefile->GetBacktrace()); } if (!runtimeArgs.GetDestination().empty()) { // The DLL uses the RUNTIME properties. runtimeGenerator = CreateInstallTargetGenerator( - target, runtimeArgs, false, this->Makefile->GetBacktrace()); + target, runtimeArgs, false, helper.Makefile->GetBacktrace()); } if ((archiveGenerator == nullptr) && (runtimeGenerator == nullptr)) { archiveGenerator = CreateInstallTargetGenerator( - target, archiveArgs, true, this->Makefile->GetBacktrace(), - this->GetArchiveDestination(nullptr)); + target, archiveArgs, true, helper.Makefile->GetBacktrace(), + helper.GetArchiveDestination(nullptr)); runtimeGenerator = CreateInstallTargetGenerator( - target, runtimeArgs, false, this->Makefile->GetBacktrace(), - this->GetRuntimeDestination(nullptr)); + target, runtimeArgs, false, helper.Makefile->GetBacktrace(), + helper.GetRuntimeDestination(nullptr)); } } else { // This is a non-DLL platform. @@ -500,9 +503,9 @@ bool cmInstallCommand::HandleTargetsMode(std::vector<std::string> const& args) // Use the FRAMEWORK properties. if (!frameworkArgs.GetDestination().empty()) { frameworkGenerator = CreateInstallTargetGenerator( - target, frameworkArgs, false, this->Makefile->GetBacktrace()); + target, frameworkArgs, false, helper.Makefile->GetBacktrace()); } else { - this->SetError( + status.SetError( cmStrCat("TARGETS given no FRAMEWORK DESTINATION for shared " "library FRAMEWORK target \"", target.GetName(), "\".")); @@ -512,15 +515,15 @@ bool cmInstallCommand::HandleTargetsMode(std::vector<std::string> const& args) // The shared library uses the LIBRARY properties. if (namelinkMode != cmInstallTargetGenerator::NamelinkModeOnly) { libraryGenerator = CreateInstallTargetGenerator( - target, libraryArgs, false, this->Makefile->GetBacktrace(), - this->GetLibraryDestination(&libraryArgs)); + target, libraryArgs, false, helper.Makefile->GetBacktrace(), + helper.GetLibraryDestination(&libraryArgs)); libraryGenerator->SetNamelinkMode( cmInstallTargetGenerator::NamelinkModeSkip); } if (namelinkMode != cmInstallTargetGenerator::NamelinkModeSkip) { namelinkGenerator = CreateInstallTargetGenerator( - target, libraryArgs, false, this->Makefile->GetBacktrace(), - this->GetLibraryDestination(&libraryArgs), false, true); + target, libraryArgs, false, helper.Makefile->GetBacktrace(), + helper.GetLibraryDestination(&libraryArgs), false, true); namelinkGenerator->SetNamelinkMode( cmInstallTargetGenerator::NamelinkModeOnly); } @@ -541,32 +544,31 @@ bool cmInstallCommand::HandleTargetsMode(std::vector<std::string> const& args) // Use the FRAMEWORK properties. if (!frameworkArgs.GetDestination().empty()) { frameworkGenerator = CreateInstallTargetGenerator( - target, frameworkArgs, false, this->Makefile->GetBacktrace()); + target, frameworkArgs, false, helper.Makefile->GetBacktrace()); } else { - this->SetError( + status.SetError( cmStrCat("TARGETS given no FRAMEWORK DESTINATION for static " "library FRAMEWORK target \"", target.GetName(), "\".")); - return false; } } else { // Static libraries use ARCHIVE properties. archiveGenerator = CreateInstallTargetGenerator( - target, archiveArgs, false, this->Makefile->GetBacktrace(), - this->GetArchiveDestination(&archiveArgs)); + target, archiveArgs, false, helper.Makefile->GetBacktrace(), + helper.GetArchiveDestination(&archiveArgs)); } } break; case cmStateEnums::MODULE_LIBRARY: { // Modules use LIBRARY properties. if (!libraryArgs.GetDestination().empty()) { libraryGenerator = CreateInstallTargetGenerator( - target, libraryArgs, false, this->Makefile->GetBacktrace()); + target, libraryArgs, false, helper.Makefile->GetBacktrace()); libraryGenerator->SetNamelinkMode(namelinkMode); namelinkOnly = (namelinkMode == cmInstallTargetGenerator::NamelinkModeOnly); } else { - this->SetError( + status.SetError( cmStrCat("TARGETS given no LIBRARY DESTINATION for module " "target \"", target.GetName(), "\".")); @@ -578,16 +580,16 @@ bool cmInstallCommand::HandleTargetsMode(std::vector<std::string> const& args) if (!objectArgs.GetDestination().empty()) { // Verify that we know where the objects are to install them. std::string reason; - if (!this->Makefile->GetGlobalGenerator() + if (!helper.Makefile->GetGlobalGenerator() ->HasKnownObjectFileLocation(&reason)) { - this->SetError( + status.SetError( cmStrCat("TARGETS given OBJECT library \"", target.GetName(), "\" whose objects may not be installed", reason, ".")); return false; } objectGenerator = CreateInstallTargetGenerator( - target, objectArgs, false, this->Makefile->GetBacktrace()); + target, objectArgs, false, helper.Makefile->GetBacktrace()); } else { // Installing an OBJECT library without a destination transforms // it to an INTERFACE library. It installs no files but can be @@ -599,29 +601,29 @@ bool cmInstallCommand::HandleTargetsMode(std::vector<std::string> const& args) // Application bundles use the BUNDLE properties. if (!bundleArgs.GetDestination().empty()) { bundleGenerator = CreateInstallTargetGenerator( - target, bundleArgs, false, this->Makefile->GetBacktrace()); + target, bundleArgs, false, helper.Makefile->GetBacktrace()); } else if (!runtimeArgs.GetDestination().empty()) { bool failure = false; - if (this->CheckCMP0006(failure)) { + if (helper.CheckCMP0006(failure)) { // For CMake 2.4 compatibility fallback to the RUNTIME // properties. bundleGenerator = CreateInstallTargetGenerator( - target, runtimeArgs, false, this->Makefile->GetBacktrace()); + target, runtimeArgs, false, helper.Makefile->GetBacktrace()); } else if (failure) { return false; } } if (!bundleGenerator) { - this->SetError(cmStrCat("TARGETS given no BUNDLE DESTINATION for " - "MACOSX_BUNDLE executable target \"", - target.GetName(), "\".")); + status.SetError(cmStrCat("TARGETS given no BUNDLE DESTINATION for " + "MACOSX_BUNDLE executable target \"", + target.GetName(), "\".")); return false; } } else { // Executables use the RUNTIME properties. runtimeGenerator = CreateInstallTargetGenerator( - target, runtimeArgs, false, this->Makefile->GetBacktrace(), - this->GetRuntimeDestination(&runtimeArgs)); + target, runtimeArgs, false, helper.Makefile->GetBacktrace(), + helper.GetRuntimeDestination(&runtimeArgs)); } // On DLL platforms an executable may also have an import @@ -632,7 +634,7 @@ bool cmInstallCommand::HandleTargetsMode(std::vector<std::string> const& args) target.IsExecutableWithExports()) { // The import library uses the ARCHIVE properties. archiveGenerator = CreateInstallTargetGenerator( - target, archiveArgs, true, this->Makefile->GetBacktrace(), true); + target, archiveArgs, true, helper.Makefile->GetBacktrace(), true); } } break; case cmStateEnums::INTERFACE_LIBRARY: @@ -662,42 +664,42 @@ bool cmInstallCommand::HandleTargetsMode(std::vector<std::string> const& args) if ((files) && (*files)) { std::vector<std::string> relFiles = cmExpandedList(files); std::vector<std::string> absFiles; - if (!this->MakeFilesFullPath("PRIVATE_HEADER", relFiles, absFiles)) { + if (!helper.MakeFilesFullPath("PRIVATE_HEADER", relFiles, absFiles)) { return false; } // Create the files install generator. privateHeaderGenerator = CreateInstallFilesGenerator( - this->Makefile, absFiles, privateHeaderArgs, false, - this->GetIncludeDestination(&privateHeaderArgs)); + helper.Makefile, absFiles, privateHeaderArgs, false, + helper.GetIncludeDestination(&privateHeaderArgs)); } files = target.GetProperty("PUBLIC_HEADER"); if ((files) && (*files)) { std::vector<std::string> relFiles = cmExpandedList(files); std::vector<std::string> absFiles; - if (!this->MakeFilesFullPath("PUBLIC_HEADER", relFiles, absFiles)) { + if (!helper.MakeFilesFullPath("PUBLIC_HEADER", relFiles, absFiles)) { return false; } // Create the files install generator. publicHeaderGenerator = CreateInstallFilesGenerator( - this->Makefile, absFiles, publicHeaderArgs, false, - this->GetIncludeDestination(&publicHeaderArgs)); + helper.Makefile, absFiles, publicHeaderArgs, false, + helper.GetIncludeDestination(&publicHeaderArgs)); } files = target.GetProperty("RESOURCE"); if ((files) && (*files)) { std::vector<std::string> relFiles = cmExpandedList(files); std::vector<std::string> absFiles; - if (!this->MakeFilesFullPath("RESOURCE", relFiles, absFiles)) { + if (!helper.MakeFilesFullPath("RESOURCE", relFiles, absFiles)) { return false; } // Create the files install generator. if (!resourceArgs.GetDestination().empty()) { resourceGenerator = CreateInstallFilesGenerator( - this->Makefile, absFiles, resourceArgs, false); + helper.Makefile, absFiles, resourceArgs, false); } else { cmSystemTools::Message( cmStrCat("INSTALL TARGETS - target ", target.GetName(), @@ -721,16 +723,16 @@ bool cmInstallCommand::HandleTargetsMode(std::vector<std::string> const& args) installsPublicHeader || publicHeaderGenerator != nullptr; installsResource = installsResource || resourceGenerator; - this->Makefile->AddInstallGenerator(archiveGenerator); - this->Makefile->AddInstallGenerator(libraryGenerator); - this->Makefile->AddInstallGenerator(namelinkGenerator); - this->Makefile->AddInstallGenerator(runtimeGenerator); - this->Makefile->AddInstallGenerator(objectGenerator); - this->Makefile->AddInstallGenerator(frameworkGenerator); - this->Makefile->AddInstallGenerator(bundleGenerator); - this->Makefile->AddInstallGenerator(privateHeaderGenerator); - this->Makefile->AddInstallGenerator(publicHeaderGenerator); - this->Makefile->AddInstallGenerator(resourceGenerator); + helper.Makefile->AddInstallGenerator(archiveGenerator); + helper.Makefile->AddInstallGenerator(libraryGenerator); + helper.Makefile->AddInstallGenerator(namelinkGenerator); + helper.Makefile->AddInstallGenerator(runtimeGenerator); + helper.Makefile->AddInstallGenerator(objectGenerator); + helper.Makefile->AddInstallGenerator(frameworkGenerator); + helper.Makefile->AddInstallGenerator(bundleGenerator); + helper.Makefile->AddInstallGenerator(privateHeaderGenerator); + helper.Makefile->AddInstallGenerator(publicHeaderGenerator); + helper.Makefile->AddInstallGenerator(resourceGenerator); // Add this install rule to an export if one was specified and // this is not a namelink-only rule. @@ -747,7 +749,7 @@ bool cmInstallCommand::HandleTargetsMode(std::vector<std::string> const& args) te->InterfaceIncludeDirectories = cmJoin(includesArgs.GetIncludeDirs(), ";"); - this->Makefile->GetGlobalGenerator() + helper.Makefile->GetGlobalGenerator() ->GetExportSets()[exports] .AddTargetExport(std::move(te)); } @@ -756,54 +758,57 @@ bool cmInstallCommand::HandleTargetsMode(std::vector<std::string> const& args) // Tell the global generator about any installation component names // specified if (installsArchive) { - this->Makefile->GetGlobalGenerator()->AddInstallComponent( + helper.Makefile->GetGlobalGenerator()->AddInstallComponent( archiveArgs.GetComponent()); } if (installsLibrary) { - this->Makefile->GetGlobalGenerator()->AddInstallComponent( + helper.Makefile->GetGlobalGenerator()->AddInstallComponent( libraryArgs.GetComponent()); } if (installsNamelink) { - this->Makefile->GetGlobalGenerator()->AddInstallComponent( + helper.Makefile->GetGlobalGenerator()->AddInstallComponent( libraryArgs.GetNamelinkComponent()); } if (installsRuntime) { - this->Makefile->GetGlobalGenerator()->AddInstallComponent( + helper.Makefile->GetGlobalGenerator()->AddInstallComponent( runtimeArgs.GetComponent()); } if (installsObject) { - this->Makefile->GetGlobalGenerator()->AddInstallComponent( + helper.Makefile->GetGlobalGenerator()->AddInstallComponent( objectArgs.GetComponent()); } if (installsFramework) { - this->Makefile->GetGlobalGenerator()->AddInstallComponent( + helper.Makefile->GetGlobalGenerator()->AddInstallComponent( frameworkArgs.GetComponent()); } if (installsBundle) { - this->Makefile->GetGlobalGenerator()->AddInstallComponent( + helper.Makefile->GetGlobalGenerator()->AddInstallComponent( bundleArgs.GetComponent()); } if (installsPrivateHeader) { - this->Makefile->GetGlobalGenerator()->AddInstallComponent( + helper.Makefile->GetGlobalGenerator()->AddInstallComponent( privateHeaderArgs.GetComponent()); } if (installsPublicHeader) { - this->Makefile->GetGlobalGenerator()->AddInstallComponent( + helper.Makefile->GetGlobalGenerator()->AddInstallComponent( publicHeaderArgs.GetComponent()); } if (installsResource) { - this->Makefile->GetGlobalGenerator()->AddInstallComponent( + helper.Makefile->GetGlobalGenerator()->AddInstallComponent( resourceArgs.GetComponent()); } return true; } -bool cmInstallCommand::HandleFilesMode(std::vector<std::string> const& args) +bool HandleFilesMode(std::vector<std::string> const& args, + cmExecutionStatus& status) { + Helper helper(status); + // This is the FILES mode. bool programs = (args[0] == "PROGRAMS"); - cmInstallCommandArguments ica(this->DefaultComponentName); + cmInstallCommandArguments ica(helper.DefaultComponentName); std::vector<std::string> files; ica.Bind(programs ? "PROGRAMS"_s : "FILES"_s, files); std::vector<std::string> unknownArgs; @@ -811,14 +816,14 @@ bool cmInstallCommand::HandleFilesMode(std::vector<std::string> const& args) if (!unknownArgs.empty()) { // Unknown argument. - this->SetError( + status.SetError( cmStrCat(args[0], " given unknown argument \"", unknownArgs[0], "\".")); return false; } std::string type = ica.GetType(); if (!type.empty() && allowedTypes.count(type) == 0) { - this->SetError( + status.SetError( cmStrCat(args[0], " given non-type \"", type, "\" with TYPE argument.")); return false; } @@ -832,27 +837,27 @@ bool cmInstallCommand::HandleFilesMode(std::vector<std::string> const& args) if (!ica.GetRename().empty() && filesVector.size() > 1) { // The rename option works only with one file. - this->SetError( + status.SetError( cmStrCat(args[0], " given RENAME option with more than one file.")); return false; } std::vector<std::string> absFiles; - if (!this->MakeFilesFullPath(args[0].c_str(), filesVector, absFiles)) { + if (!helper.MakeFilesFullPath(args[0].c_str(), filesVector, absFiles)) { return false; } - cmPolicies::PolicyStatus status = - this->Makefile->GetPolicyStatus(cmPolicies::CMP0062); + cmPolicies::PolicyStatus policyStatus = + helper.Makefile->GetPolicyStatus(cmPolicies::CMP0062); - cmGlobalGenerator* gg = this->Makefile->GetGlobalGenerator(); + cmGlobalGenerator* gg = helper.Makefile->GetGlobalGenerator(); for (std::string const& file : filesVector) { if (gg->IsExportedTargetsFile(file)) { const char* modal = nullptr; std::ostringstream e; MessageType messageType = MessageType::AUTHOR_WARNING; - switch (status) { + switch (policyStatus) { case cmPolicies::WARN: e << cmPolicies::GetPolicyWarning(cmPolicies::CMP0062) << "\n"; modal = "should"; @@ -872,7 +877,7 @@ bool cmInstallCommand::HandleFilesMode(std::vector<std::string> const& args) << " not be installed with the " "install() command. Use the install(EXPORT) mechanism " "instead. See the cmake-packages(7) manual for more.\n"; - this->Makefile->IssueMessage(messageType, e.str()); + helper.Makefile->IssueMessage(messageType, e.str()); if (messageType == MessageType::FATAL_ERROR) { return false; } @@ -885,34 +890,36 @@ bool cmInstallCommand::HandleFilesMode(std::vector<std::string> const& args) } if (!type.empty() && !ica.GetDestination().empty()) { - this->SetError(cmStrCat(args[0], - " given both TYPE and DESTINATION arguments. " - "You may only specify one.")); + status.SetError(cmStrCat(args[0], + " given both TYPE and DESTINATION arguments. " + "You may only specify one.")); return false; } - std::string destination = this->GetDestinationForType(&ica, type); + std::string destination = helper.GetDestinationForType(&ica, type); if (destination.empty()) { // A destination is required. - this->SetError(cmStrCat(args[0], " given no DESTINATION!")); + status.SetError(cmStrCat(args[0], " given no DESTINATION!")); return false; } // Create the files install generator. - this->Makefile->AddInstallGenerator(CreateInstallFilesGenerator( - this->Makefile, absFiles, ica, programs, destination)); + helper.Makefile->AddInstallGenerator(CreateInstallFilesGenerator( + helper.Makefile, absFiles, ica, programs, destination)); // Tell the global generator about any installation component names // specified. - this->Makefile->GetGlobalGenerator()->AddInstallComponent( + helper.Makefile->GetGlobalGenerator()->AddInstallComponent( ica.GetComponent()); return true; } -bool cmInstallCommand::HandleDirectoryMode( - std::vector<std::string> const& args) +bool HandleDirectoryMode(std::vector<std::string> const& args, + cmExecutionStatus& status) { + Helper helper(status); + enum Doing { DoingNone, @@ -937,14 +944,14 @@ bool cmInstallCommand::HandleDirectoryMode( std::string permissions_file; std::string permissions_dir; std::vector<std::string> configurations; - std::string component = this->DefaultComponentName; + std::string component = helper.DefaultComponentName; std::string literal_args; std::string type; for (unsigned int i = 1; i < args.size(); ++i) { if (args[i] == "DESTINATION") { if (in_match_mode) { - this->SetError(cmStrCat(args[0], " does not allow \"", args[i], - "\" after PATTERN or REGEX.")); + status.SetError(cmStrCat(args[0], " does not allow \"", args[i], + "\" after PATTERN or REGEX.")); return false; } @@ -952,8 +959,8 @@ bool cmInstallCommand::HandleDirectoryMode( doing = DoingDestination; } else if (args[i] == "TYPE") { if (in_match_mode) { - this->SetError(cmStrCat(args[0], " does not allow \"", args[i], - "\" after PATTERN or REGEX.")); + status.SetError(cmStrCat(args[0], " does not allow \"", args[i], + "\" after PATTERN or REGEX.")); return false; } @@ -961,8 +968,8 @@ bool cmInstallCommand::HandleDirectoryMode( doing = DoingType; } else if (args[i] == "OPTIONAL") { if (in_match_mode) { - this->SetError(cmStrCat(args[0], " does not allow \"", args[i], - "\" after PATTERN or REGEX.")); + status.SetError(cmStrCat(args[0], " does not allow \"", args[i], + "\" after PATTERN or REGEX.")); return false; } @@ -971,8 +978,8 @@ bool cmInstallCommand::HandleDirectoryMode( doing = DoingNone; } else if (args[i] == "MESSAGE_NEVER") { if (in_match_mode) { - this->SetError(cmStrCat(args[0], " does not allow \"", args[i], - "\" after PATTERN or REGEX.")); + status.SetError(cmStrCat(args[0], " does not allow \"", args[i], + "\" after PATTERN or REGEX.")); return false; } @@ -990,16 +997,16 @@ bool cmInstallCommand::HandleDirectoryMode( } else if (args[i] == "EXCLUDE") { // Add this property to the current match rule. if (!in_match_mode || doing == DoingPattern || doing == DoingRegex) { - this->SetError(cmStrCat(args[0], " does not allow \"", args[i], - "\" before a PATTERN or REGEX is given.")); + status.SetError(cmStrCat(args[0], " does not allow \"", args[i], + "\" before a PATTERN or REGEX is given.")); return false; } literal_args += " EXCLUDE"; doing = DoingNone; } else if (args[i] == "PERMISSIONS") { if (!in_match_mode) { - this->SetError(cmStrCat(args[0], " does not allow \"", args[i], - "\" before a PATTERN or REGEX is given.")); + status.SetError(cmStrCat(args[0], " does not allow \"", args[i], + "\" before a PATTERN or REGEX is given.")); return false; } @@ -1008,8 +1015,8 @@ bool cmInstallCommand::HandleDirectoryMode( doing = DoingPermsMatch; } else if (args[i] == "FILE_PERMISSIONS") { if (in_match_mode) { - this->SetError(cmStrCat(args[0], " does not allow \"", args[i], - "\" after PATTERN or REGEX.")); + status.SetError(cmStrCat(args[0], " does not allow \"", args[i], + "\" after PATTERN or REGEX.")); return false; } @@ -1017,8 +1024,8 @@ bool cmInstallCommand::HandleDirectoryMode( doing = DoingPermsFile; } else if (args[i] == "DIRECTORY_PERMISSIONS") { if (in_match_mode) { - this->SetError(cmStrCat(args[0], " does not allow \"", args[i], - "\" after PATTERN or REGEX.")); + status.SetError(cmStrCat(args[0], " does not allow \"", args[i], + "\" after PATTERN or REGEX.")); return false; } @@ -1026,8 +1033,8 @@ bool cmInstallCommand::HandleDirectoryMode( doing = DoingPermsDir; } else if (args[i] == "USE_SOURCE_PERMISSIONS") { if (in_match_mode) { - this->SetError(cmStrCat(args[0], " does not allow \"", args[i], - "\" after PATTERN or REGEX.")); + status.SetError(cmStrCat(args[0], " does not allow \"", args[i], + "\" after PATTERN or REGEX.")); return false; } @@ -1036,8 +1043,8 @@ bool cmInstallCommand::HandleDirectoryMode( doing = DoingNone; } else if (args[i] == "FILES_MATCHING") { if (in_match_mode) { - this->SetError(cmStrCat(args[0], " does not allow \"", args[i], - "\" after PATTERN or REGEX.")); + status.SetError(cmStrCat(args[0], " does not allow \"", args[i], + "\" after PATTERN or REGEX.")); return false; } @@ -1046,8 +1053,8 @@ bool cmInstallCommand::HandleDirectoryMode( doing = DoingNone; } else if (args[i] == "CONFIGURATIONS") { if (in_match_mode) { - this->SetError(cmStrCat(args[0], " does not allow \"", args[i], - "\" after PATTERN or REGEX.")); + status.SetError(cmStrCat(args[0], " does not allow \"", args[i], + "\" after PATTERN or REGEX.")); return false; } @@ -1055,8 +1062,8 @@ bool cmInstallCommand::HandleDirectoryMode( doing = DoingConfigurations; } else if (args[i] == "COMPONENT") { if (in_match_mode) { - this->SetError(cmStrCat(args[0], " does not allow \"", args[i], - "\" after PATTERN or REGEX.")); + status.SetError(cmStrCat(args[0], " does not allow \"", args[i], + "\" after PATTERN or REGEX.")); return false; } @@ -1064,8 +1071,8 @@ bool cmInstallCommand::HandleDirectoryMode( doing = DoingComponent; } else if (args[i] == "EXCLUDE_FROM_ALL") { if (in_match_mode) { - this->SetError(cmStrCat(args[0], " does not allow \"", args[i], - "\" after PATTERN or REGEX.")); + status.SetError(cmStrCat(args[0], " does not allow \"", args[i], + "\" after PATTERN or REGEX.")); return false; } exclude_from_all = true; @@ -1076,14 +1083,14 @@ bool cmInstallCommand::HandleDirectoryMode( std::string::size_type gpos = cmGeneratorExpression::Find(dir); if (gpos != 0 && !cmSystemTools::FileIsFullPath(dir)) { dir = - cmStrCat(this->Makefile->GetCurrentSourceDirectory(), '/', args[i]); + cmStrCat(helper.Makefile->GetCurrentSourceDirectory(), '/', args[i]); } // Make sure the name is a directory. if (cmSystemTools::FileExists(dir) && !cmSystemTools::FileIsDirectory(dir)) { - this->SetError(cmStrCat(args[0], " given non-directory \"", args[i], - "\" to install.")); + status.SetError(cmStrCat(args[0], " given non-directory \"", args[i], + "\" to install.")); return false; } @@ -1096,8 +1103,8 @@ bool cmInstallCommand::HandleDirectoryMode( doing = DoingNone; } else if (doing == DoingType) { if (allowedTypes.count(args[i]) == 0) { - this->SetError(cmStrCat(args[0], " given non-type \"", args[i], - "\" with TYPE argument.")); + status.SetError(cmStrCat(args[0], " given non-type \"", args[i], + "\" with TYPE argument.")); return false; } @@ -1133,15 +1140,15 @@ bool cmInstallCommand::HandleDirectoryMode( // Check the requested permission. if (!cmInstallCommandArguments::CheckPermissions(args[i], permissions_file)) { - this->SetError(cmStrCat(args[0], " given invalid file permission \"", - args[i], "\".")); + status.SetError(cmStrCat(args[0], " given invalid file permission \"", + args[i], "\".")); return false; } } else if (doing == DoingPermsDir) { // Check the requested permission. if (!cmInstallCommandArguments::CheckPermissions(args[i], permissions_dir)) { - this->SetError(cmStrCat( + status.SetError(cmStrCat( args[0], " given invalid directory permission \"", args[i], "\".")); return false; } @@ -1149,13 +1156,13 @@ bool cmInstallCommand::HandleDirectoryMode( // Check the requested permission. if (!cmInstallCommandArguments::CheckPermissions(args[i], literal_args)) { - this->SetError( + status.SetError( cmStrCat(args[0], " given invalid permission \"", args[i], "\".")); return false; } } else { // Unknown argument. - this->SetError( + status.SetError( cmStrCat(args[0], " given unknown argument \"", args[i], "\".")); return false; } @@ -1174,40 +1181,42 @@ bool cmInstallCommand::HandleDirectoryMode( if (!destination) { if (type.empty()) { // A destination is required. - this->SetError(cmStrCat(args[0], " given no DESTINATION!")); + status.SetError(cmStrCat(args[0], " given no DESTINATION!")); return false; } - destinationStr = this->GetDestinationForType(nullptr, type); + destinationStr = helper.GetDestinationForType(nullptr, type); destination = destinationStr.c_str(); } else if (!type.empty()) { - this->SetError(cmStrCat(args[0], - " given both TYPE and DESTINATION " - "arguments. You may only specify one.")); + status.SetError(cmStrCat(args[0], + " given both TYPE and DESTINATION " + "arguments. You may only specify one.")); return false; } cmInstallGenerator::MessageLevel message = - cmInstallGenerator::SelectMessageLevel(this->Makefile, message_never); + cmInstallGenerator::SelectMessageLevel(helper.Makefile, message_never); // Create the directory install generator. - this->Makefile->AddInstallGenerator(new cmInstallDirectoryGenerator( + helper.Makefile->AddInstallGenerator(new cmInstallDirectoryGenerator( dirs, destination, permissions_file.c_str(), permissions_dir.c_str(), configurations, component.c_str(), message, exclude_from_all, literal_args.c_str(), optional)); // Tell the global generator about any installation component names // specified. - this->Makefile->GetGlobalGenerator()->AddInstallComponent(component); + helper.Makefile->GetGlobalGenerator()->AddInstallComponent(component); return true; } -bool cmInstallCommand::HandleExportAndroidMKMode( - std::vector<std::string> const& args) +bool HandleExportAndroidMKMode(std::vector<std::string> const& args, + cmExecutionStatus& status) { #ifndef CMAKE_BOOTSTRAP + Helper helper(status); + // This is the EXPORT mode. - cmInstallCommandArguments ica(this->DefaultComponentName); + cmInstallCommandArguments ica(helper.DefaultComponentName); std::string exp; std::string name_space; @@ -1224,7 +1233,7 @@ bool cmInstallCommand::HandleExportAndroidMKMode( if (!unknownArgs.empty()) { // Unknown argument. - this->SetError( + status.SetError( cmStrCat(args[0], " given unknown argument \"", unknownArgs[0], "\".")); return false; } @@ -1236,30 +1245,30 @@ bool cmInstallCommand::HandleExportAndroidMKMode( // Make sure there is a destination. if (ica.GetDestination().empty()) { // A destination is required. - this->SetError(cmStrCat(args[0], " given no DESTINATION!")); + status.SetError(cmStrCat(args[0], " given no DESTINATION!")); return false; } // Check the file name. std::string fname = filename; if (fname.find_first_of(":/\\") != std::string::npos) { - this->SetError(cmStrCat(args[0], " given invalid export file name \"", - fname, - "\". The FILE argument may not contain a path. " - "Specify the path in the DESTINATION argument.")); + status.SetError(cmStrCat(args[0], " given invalid export file name \"", + fname, + "\". The FILE argument may not contain a path. " + "Specify the path in the DESTINATION argument.")); return false; } // Check the file extension. if (!fname.empty() && cmSystemTools::GetFilenameLastExtension(fname) != ".mk") { - this->SetError(cmStrCat( + status.SetError(cmStrCat( args[0], " given invalid export file name \"", fname, R"(". The FILE argument must specify a name ending in ".mk".)")); return false; } if (fname.find_first_of(":/\\") != std::string::npos) { - this->SetError( + status.SetError( cmStrCat(args[0], " given export name \"", exp, "\". " "This name cannot be safely converted to a file name. " @@ -1273,10 +1282,10 @@ bool cmInstallCommand::HandleExportAndroidMKMode( } cmExportSet& exportSet = - this->Makefile->GetGlobalGenerator()->GetExportSets()[exp]; + helper.Makefile->GetGlobalGenerator()->GetExportSets()[exp]; cmInstallGenerator::MessageLevel message = - cmInstallGenerator::SelectMessageLevel(this->Makefile); + cmInstallGenerator::SelectMessageLevel(helper.Makefile); // Create the export install generator. cmInstallExportGenerator* exportGenerator = new cmInstallExportGenerator( @@ -1284,20 +1293,23 @@ bool cmInstallCommand::HandleExportAndroidMKMode( ica.GetConfigurations(), ica.GetComponent().c_str(), message, ica.GetExcludeFromAll(), fname.c_str(), name_space.c_str(), exportOld, true); - this->Makefile->AddInstallGenerator(exportGenerator); + helper.Makefile->AddInstallGenerator(exportGenerator); return true; #else static_cast<void>(args); - this->SetError("EXPORT_ANDROID_MK not supported in bootstrap cmake"); + status.SetError("EXPORT_ANDROID_MK not supported in bootstrap cmake"); return false; #endif } -bool cmInstallCommand::HandleExportMode(std::vector<std::string> const& args) +bool HandleExportMode(std::vector<std::string> const& args, + cmExecutionStatus& status) { + Helper helper(status); + // This is the EXPORT mode. - cmInstallCommandArguments ica(this->DefaultComponentName); + cmInstallCommandArguments ica(helper.DefaultComponentName); std::string exp; std::string name_space; @@ -1314,7 +1326,7 @@ bool cmInstallCommand::HandleExportMode(std::vector<std::string> const& args) if (!unknownArgs.empty()) { // Unknown argument. - this->SetError( + status.SetError( cmStrCat(args[0], " given unknown argument \"", unknownArgs[0], "\".")); return false; } @@ -1326,25 +1338,25 @@ bool cmInstallCommand::HandleExportMode(std::vector<std::string> const& args) // Make sure there is a destination. if (ica.GetDestination().empty()) { // A destination is required. - this->SetError(cmStrCat(args[0], " given no DESTINATION!")); + status.SetError(cmStrCat(args[0], " given no DESTINATION!")); return false; } // Check the file name. std::string fname = filename; if (fname.find_first_of(":/\\") != std::string::npos) { - this->SetError(cmStrCat(args[0], " given invalid export file name \"", - fname, - "\". " - "The FILE argument may not contain a path. " - "Specify the path in the DESTINATION argument.")); + status.SetError(cmStrCat(args[0], " given invalid export file name \"", + fname, + "\". " + "The FILE argument may not contain a path. " + "Specify the path in the DESTINATION argument.")); return false; } // Check the file extension. if (!fname.empty() && cmSystemTools::GetFilenameLastExtension(fname) != ".cmake") { - this->SetError( + status.SetError( cmStrCat(args[0], " given invalid export file name \"", fname, "\". " "The FILE argument must specify a name ending in \".cmake\".")); @@ -1356,7 +1368,7 @@ bool cmInstallCommand::HandleExportMode(std::vector<std::string> const& args) fname = cmStrCat(exp, ".cmake"); if (fname.find_first_of(":/\\") != std::string::npos) { - this->SetError(cmStrCat( + status.SetError(cmStrCat( args[0], " given export name \"", exp, "\". " "This name cannot be safely converted to a file name. " @@ -1367,17 +1379,17 @@ bool cmInstallCommand::HandleExportMode(std::vector<std::string> const& args) } cmExportSet& exportSet = - this->Makefile->GetGlobalGenerator()->GetExportSets()[exp]; + helper.Makefile->GetGlobalGenerator()->GetExportSets()[exp]; if (exportOld) { for (auto const& te : exportSet.GetTargetExports()) { cmTarget* tgt = - this->Makefile->GetGlobalGenerator()->FindTarget(te->TargetName); + helper.Makefile->GetGlobalGenerator()->FindTarget(te->TargetName); const bool newCMP0022Behavior = (tgt && tgt->GetPolicyStatusCMP0022() != cmPolicies::WARN && tgt->GetPolicyStatusCMP0022() != cmPolicies::OLD); if (!newCMP0022Behavior) { - this->SetError(cmStrCat( + status.SetError(cmStrCat( "INSTALL(EXPORT) given keyword \"" "EXPORT_LINK_INTERFACE_LIBRARIES\", but target \"", te->TargetName, "\" does not have policy CMP0022 set to NEW.")); @@ -1387,7 +1399,7 @@ bool cmInstallCommand::HandleExportMode(std::vector<std::string> const& args) } cmInstallGenerator::MessageLevel message = - cmInstallGenerator::SelectMessageLevel(this->Makefile); + cmInstallGenerator::SelectMessageLevel(helper.Makefile); // Create the export install generator. cmInstallExportGenerator* exportGenerator = new cmInstallExportGenerator( @@ -1395,14 +1407,14 @@ bool cmInstallCommand::HandleExportMode(std::vector<std::string> const& args) ica.GetConfigurations(), ica.GetComponent().c_str(), message, ica.GetExcludeFromAll(), fname.c_str(), name_space.c_str(), exportOld, false); - this->Makefile->AddInstallGenerator(exportGenerator); + helper.Makefile->AddInstallGenerator(exportGenerator); return true; } -bool cmInstallCommand::MakeFilesFullPath( - const char* modeName, const std::vector<std::string>& relFiles, - std::vector<std::string>& absFiles) +bool Helper::MakeFilesFullPath(const char* modeName, + const std::vector<std::string>& relFiles, + std::vector<std::string>& absFiles) { for (std::string const& relFile : relFiles) { std::string file = relFile; @@ -1424,7 +1436,7 @@ bool cmInstallCommand::MakeFilesFullPath( return true; } -bool cmInstallCommand::CheckCMP0006(bool& failure) +bool Helper::CheckCMP0006(bool& failure) { switch (this->Makefile->GetPolicyStatus(cmPolicies::CMP0006)) { case cmPolicies::WARN: @@ -1449,9 +1461,9 @@ bool cmInstallCommand::CheckCMP0006(bool& failure) return false; } -std::string cmInstallCommand::GetDestination( - const cmInstallCommandArguments* args, const std::string& varName, - const std::string& guess) +std::string Helper::GetDestination(const cmInstallCommandArguments* args, + const std::string& varName, + const std::string& guess) { if (args && !args->GetDestination().empty()) { return args->GetDestination(); @@ -1463,55 +1475,54 @@ std::string cmInstallCommand::GetDestination( return guess; } -std::string cmInstallCommand::GetRuntimeDestination( +std::string Helper::GetRuntimeDestination( const cmInstallCommandArguments* args) { return this->GetDestination(args, "CMAKE_INSTALL_BINDIR", "bin"); } -std::string cmInstallCommand::GetSbinDestination( - const cmInstallCommandArguments* args) +std::string Helper::GetSbinDestination(const cmInstallCommandArguments* args) { return this->GetDestination(args, "CMAKE_INSTALL_SBINDIR", "sbin"); } -std::string cmInstallCommand::GetArchiveDestination( +std::string Helper::GetArchiveDestination( const cmInstallCommandArguments* args) { return this->GetDestination(args, "CMAKE_INSTALL_LIBDIR", "lib"); } -std::string cmInstallCommand::GetLibraryDestination( +std::string Helper::GetLibraryDestination( const cmInstallCommandArguments* args) { return this->GetDestination(args, "CMAKE_INSTALL_LIBDIR", "lib"); } -std::string cmInstallCommand::GetIncludeDestination( +std::string Helper::GetIncludeDestination( const cmInstallCommandArguments* args) { return this->GetDestination(args, "CMAKE_INSTALL_INCLUDEDIR", "include"); } -std::string cmInstallCommand::GetSysconfDestination( +std::string Helper::GetSysconfDestination( const cmInstallCommandArguments* args) { return this->GetDestination(args, "CMAKE_INSTALL_SYSCONFDIR", "etc"); } -std::string cmInstallCommand::GetSharedStateDestination( +std::string Helper::GetSharedStateDestination( const cmInstallCommandArguments* args) { return this->GetDestination(args, "CMAKE_INSTALL_SHAREDSTATEDIR", "com"); } -std::string cmInstallCommand::GetLocalStateDestination( +std::string Helper::GetLocalStateDestination( const cmInstallCommandArguments* args) { return this->GetDestination(args, "CMAKE_INSTALL_LOCALSTATEDIR", "var"); } -std::string cmInstallCommand::GetRunStateDestination( +std::string Helper::GetRunStateDestination( const cmInstallCommandArguments* args) { return this->GetDestination(args, "CMAKE_INSTALL_RUNSTATEDIR", @@ -1519,49 +1530,44 @@ std::string cmInstallCommand::GetRunStateDestination( "/run"); } -std::string cmInstallCommand::GetDataRootDestination( +std::string Helper::GetDataRootDestination( const cmInstallCommandArguments* args) { return this->GetDestination(args, "CMAKE_INSTALL_DATAROOTDIR", "share"); } -std::string cmInstallCommand::GetDataDestination( - const cmInstallCommandArguments* args) +std::string Helper::GetDataDestination(const cmInstallCommandArguments* args) { return this->GetDestination(args, "CMAKE_INSTALL_DATADIR", this->GetDataRootDestination(nullptr)); } -std::string cmInstallCommand::GetInfoDestination( - const cmInstallCommandArguments* args) +std::string Helper::GetInfoDestination(const cmInstallCommandArguments* args) { return this->GetDestination(args, "CMAKE_INSTALL_INFODIR", this->GetDataRootDestination(nullptr) + "/info"); } -std::string cmInstallCommand::GetLocaleDestination( - const cmInstallCommandArguments* args) +std::string Helper::GetLocaleDestination(const cmInstallCommandArguments* args) { return this->GetDestination(args, "CMAKE_INSTALL_LOCALEDIR", this->GetDataRootDestination(nullptr) + "/locale"); } -std::string cmInstallCommand::GetManDestination( - const cmInstallCommandArguments* args) +std::string Helper::GetManDestination(const cmInstallCommandArguments* args) { return this->GetDestination(args, "CMAKE_INSTALL_MANDIR", this->GetDataRootDestination(nullptr) + "/man"); } -std::string cmInstallCommand::GetDocDestination( - const cmInstallCommandArguments* args) +std::string Helper::GetDocDestination(const cmInstallCommandArguments* args) { return this->GetDestination(args, "CMAKE_INSTALL_DOCDIR", this->GetDataRootDestination(nullptr) + "/doc"); } -std::string cmInstallCommand::GetDestinationForType( +std::string Helper::GetDestinationForType( const cmInstallCommandArguments* args, const std::string& type) { if (args && !args->GetDestination().empty()) { @@ -1608,3 +1614,31 @@ std::string cmInstallCommand::GetDestinationForType( } return ""; } + +} // namespace + +bool cmInstallCommand(std::vector<std::string> const& args, + cmExecutionStatus& status) +{ + // Allow calling with no arguments so that arguments may be built up + // using a variable that may be left empty. + if (args.empty()) { + return true; + } + + // Enable the install target. + status.GetMakefile().GetGlobalGenerator()->EnableInstallTarget(); + + static cmSubcommandTable const subcommand{ + { "SCRIPT"_s, HandleScriptMode }, + { "CODE"_s, HandleScriptMode }, + { "TARGETS"_s, HandleTargetsMode }, + { "FILES"_s, HandleFilesMode }, + { "PROGRAMS"_s, HandleFilesMode }, + { "DIRECTORY"_s, HandleDirectoryMode }, + { "EXPORT"_s, HandleExportMode }, + { "EXPORT_ANDROID_MK"_s, HandleExportAndroidMKMode }, + }; + + return subcommand(args[0], args, status); +} diff --git a/Source/cmInstallCommand.h b/Source/cmInstallCommand.h index 9ffb842..32f00ce 100644 --- a/Source/cmInstallCommand.h +++ b/Source/cmInstallCommand.h @@ -8,71 +8,9 @@ #include <string> #include <vector> -#include <cm/memory> - -#include "cmCommand.h" - class cmExecutionStatus; -class cmInstallCommandArguments; - -/** \class cmInstallCommand - * \brief Specifies where to install some files - * - * cmInstallCommand is a general-purpose interface command for - * specifying install rules. - */ -class cmInstallCommand : public cmCommand -{ -public: - /** - * This is a virtual constructor for the command. - */ - std::unique_ptr<cmCommand> Clone() override - { - return cm::make_unique<cmInstallCommand>(); - } - - /** - * This is called when the command is first encountered in - * the CMakeLists.txt file. - */ - bool InitialPass(std::vector<std::string> const& args, - cmExecutionStatus& status) override; - -private: - bool HandleScriptMode(std::vector<std::string> const& args); - bool HandleTargetsMode(std::vector<std::string> const& args); - bool HandleFilesMode(std::vector<std::string> const& args); - bool HandleDirectoryMode(std::vector<std::string> const& args); - bool HandleExportMode(std::vector<std::string> const& args); - bool HandleExportAndroidMKMode(std::vector<std::string> const& args); - bool MakeFilesFullPath(const char* modeName, - const std::vector<std::string>& relFiles, - std::vector<std::string>& absFiles); - bool CheckCMP0006(bool& failure); - - std::string GetDestination(const cmInstallCommandArguments* args, - const std::string& varName, - const std::string& guess); - std::string GetRuntimeDestination(const cmInstallCommandArguments* args); - std::string GetSbinDestination(const cmInstallCommandArguments* args); - std::string GetArchiveDestination(const cmInstallCommandArguments* args); - std::string GetLibraryDestination(const cmInstallCommandArguments* args); - std::string GetIncludeDestination(const cmInstallCommandArguments* args); - std::string GetSysconfDestination(const cmInstallCommandArguments* args); - std::string GetSharedStateDestination(const cmInstallCommandArguments* args); - std::string GetLocalStateDestination(const cmInstallCommandArguments* args); - std::string GetRunStateDestination(const cmInstallCommandArguments* args); - std::string GetDataRootDestination(const cmInstallCommandArguments* args); - std::string GetDataDestination(const cmInstallCommandArguments* args); - std::string GetInfoDestination(const cmInstallCommandArguments* args); - std::string GetLocaleDestination(const cmInstallCommandArguments* args); - std::string GetManDestination(const cmInstallCommandArguments* args); - std::string GetDocDestination(const cmInstallCommandArguments* args); - std::string GetDestinationForType(const cmInstallCommandArguments* args, - const std::string& type); - std::string DefaultComponentName; -}; +bool cmInstallCommand(std::vector<std::string> const& args, + cmExecutionStatus& status); #endif diff --git a/Source/cmInstallDirectoryGenerator.cxx b/Source/cmInstallDirectoryGenerator.cxx index 1d8210c..259c7f7 100644 --- a/Source/cmInstallDirectoryGenerator.cxx +++ b/Source/cmInstallDirectoryGenerator.cxx @@ -9,8 +9,6 @@ #include "cmStringAlgorithms.h" #include "cmSystemTools.h" -#include <memory> - cmInstallDirectoryGenerator::cmInstallDirectoryGenerator( std::vector<std::string> const& dirs, const char* dest, const char* file_permissions, const char* dir_permissions, @@ -64,10 +62,9 @@ void cmInstallDirectoryGenerator::GenerateScriptForConfig( std::ostream& os, const std::string& config, Indent indent) { std::vector<std::string> dirs; - cmGeneratorExpression ge; for (std::string const& d : this->Directories) { - std::unique_ptr<cmCompiledGeneratorExpression> cge = ge.Parse(d); - cmExpandList(cge->Evaluate(this->LocalGenerator, config), dirs); + cmExpandList( + cmGeneratorExpression::Evaluate(d, this->LocalGenerator, config), dirs); } // Make sure all dirs have absolute paths. @@ -97,6 +94,6 @@ void cmInstallDirectoryGenerator::AddDirectoryInstallRule( std::string cmInstallDirectoryGenerator::GetDestination( std::string const& config) const { - cmGeneratorExpression ge; - return ge.Parse(this->Destination)->Evaluate(this->LocalGenerator, config); + return cmGeneratorExpression::Evaluate(this->Destination, + this->LocalGenerator, config); } diff --git a/Source/cmInstallFilesGenerator.cxx b/Source/cmInstallFilesGenerator.cxx index c4048d4..f5b69a5 100644 --- a/Source/cmInstallFilesGenerator.cxx +++ b/Source/cmInstallFilesGenerator.cxx @@ -6,8 +6,6 @@ #include "cmInstallType.h" #include "cmStringAlgorithms.h" -#include <memory> - class cmLocalGenerator; cmInstallFilesGenerator::cmInstallFilesGenerator( @@ -51,8 +49,8 @@ bool cmInstallFilesGenerator::Compute(cmLocalGenerator* lg) std::string cmInstallFilesGenerator::GetDestination( std::string const& config) const { - cmGeneratorExpression ge; - return ge.Parse(this->Destination)->Evaluate(this->LocalGenerator, config); + return cmGeneratorExpression::Evaluate(this->Destination, + this->LocalGenerator, config); } void cmInstallFilesGenerator::AddFilesInstallRule( @@ -82,10 +80,9 @@ void cmInstallFilesGenerator::GenerateScriptForConfig( std::ostream& os, const std::string& config, Indent indent) { std::vector<std::string> files; - cmGeneratorExpression ge; for (std::string const& f : this->Files) { - std::unique_ptr<cmCompiledGeneratorExpression> cge = ge.Parse(f); - cmExpandList(cge->Evaluate(this->LocalGenerator, config), files); + cmExpandList( + cmGeneratorExpression::Evaluate(f, this->LocalGenerator, config), files); } this->AddFilesInstallRule(os, config, indent, files); } diff --git a/Source/cmInstallScriptGenerator.cxx b/Source/cmInstallScriptGenerator.cxx index b7b7817..ea29455 100644 --- a/Source/cmInstallScriptGenerator.cxx +++ b/Source/cmInstallScriptGenerator.cxx @@ -2,7 +2,6 @@ file Copyright.txt or https://cmake.org/licensing for details. */ #include "cmInstallScriptGenerator.h" -#include <memory> #include <ostream> #include <vector> @@ -79,11 +78,9 @@ void cmInstallScriptGenerator::GenerateScriptForConfig( std::ostream& os, const std::string& config, Indent indent) { if (this->AllowGenex) { - cmGeneratorExpression ge; - std::unique_ptr<cmCompiledGeneratorExpression> cge = - ge.Parse(this->Script); this->AddScriptInstallRule(os, indent, - cge->Evaluate(this->LocalGenerator, config)); + cmGeneratorExpression::Evaluate( + this->Script, this->LocalGenerator, config)); } else { this->AddScriptInstallRule(os, indent, this->Script); } diff --git a/Source/cmInstallTargetGenerator.cxx b/Source/cmInstallTargetGenerator.cxx index 90b0e1d..0cd04cc 100644 --- a/Source/cmInstallTargetGenerator.cxx +++ b/Source/cmInstallTargetGenerator.cxx @@ -4,7 +4,6 @@ #include <cassert> #include <map> -#include <memory> #include <set> #include <sstream> #include <utility> @@ -375,9 +374,8 @@ void cmInstallTargetGenerator::GetInstallObjectNames( std::string cmInstallTargetGenerator::GetDestination( std::string const& config) const { - cmGeneratorExpression ge; - return ge.Parse(this->Destination) - ->Evaluate(this->Target->GetLocalGenerator(), config); + return cmGeneratorExpression::Evaluate( + this->Destination, this->Target->GetLocalGenerator(), config); } std::string cmInstallTargetGenerator::GetInstallFilename( diff --git a/Source/cmJsonObjects.cxx b/Source/cmJsonObjects.cxx index 52e28d3..79180cb 100644 --- a/Source/cmJsonObjects.cxx +++ b/Source/cmJsonObjects.cxx @@ -32,7 +32,6 @@ #include <functional> #include <limits> #include <map> -#include <memory> #include <set> #include <string> #include <unordered_map> @@ -357,10 +356,8 @@ static Json::Value DumpCTestInfo(cmLocalGenerator* lg, cmTest* testInfo, } // Remove any config specific variables from the output. - cmGeneratorExpression ge; - auto cge = ge.Parse(command); - const std::string& processed = cge->Evaluate(lg, config); - result[kCTEST_COMMAND] = processed; + result[kCTEST_COMMAND] = + cmGeneratorExpression::Evaluate(command, lg, config); // Build up the list of properties that may have been specified Json::Value properties = Json::arrayValue; @@ -369,9 +366,8 @@ static Json::Value DumpCTestInfo(cmLocalGenerator* lg, cmTest* testInfo, entry[kKEY_KEY] = prop.first; // Remove config variables from the value too. - auto cge_value = ge.Parse(prop.second); - const std::string& processed_value = cge_value->Evaluate(lg, config); - entry[kVALUE_KEY] = processed_value; + entry[kVALUE_KEY] = + cmGeneratorExpression::Evaluate(prop.second, lg, config); properties.append(entry); } result[kPROPERTIES_KEY] = properties; diff --git a/Source/cmLinkDirectoriesCommand.cxx b/Source/cmLinkDirectoriesCommand.cxx index 57b69c8..2914046 100644 --- a/Source/cmLinkDirectoriesCommand.cxx +++ b/Source/cmLinkDirectoriesCommand.cxx @@ -4,6 +4,7 @@ #include <sstream> +#include "cmExecutionStatus.h" #include "cmGeneratorExpression.h" #include "cmMakefile.h" #include "cmMessageType.h" @@ -11,17 +12,18 @@ #include "cmStringAlgorithms.h" #include "cmSystemTools.h" -class cmExecutionStatus; +static void AddLinkDir(cmMakefile& mf, std::string const& dir, + std::vector<std::string>& directories); -// cmLinkDirectoriesCommand -bool cmLinkDirectoriesCommand::InitialPass( - std::vector<std::string> const& args, cmExecutionStatus&) +bool cmLinkDirectoriesCommand(std::vector<std::string> const& args, + cmExecutionStatus& status) { if (args.empty()) { return true; } - bool before = this->Makefile->IsOn("CMAKE_LINK_DIRECTORIES_BEFORE"); + cmMakefile& mf = status.GetMakefile(); + bool before = mf.IsOn("CMAKE_LINK_DIRECTORIES_BEFORE"); auto i = args.cbegin(); if ((*i) == "BEFORE") { @@ -34,16 +36,16 @@ bool cmLinkDirectoriesCommand::InitialPass( std::vector<std::string> directories; for (; i != args.cend(); ++i) { - this->AddLinkDir(*i, directories); + AddLinkDir(mf, *i, directories); } - this->Makefile->AddLinkDirectory(cmJoin(directories, ";"), before); + mf.AddLinkDirectory(cmJoin(directories, ";"), before); return true; } -void cmLinkDirectoriesCommand::AddLinkDir( - std::string const& dir, std::vector<std::string>& directories) +static void AddLinkDir(cmMakefile& mf, std::string const& dir, + std::vector<std::string>& directories) { std::string unixPath = dir; cmSystemTools::ConvertToUnixSlashes(unixPath); @@ -56,10 +58,10 @@ void cmLinkDirectoriesCommand::AddLinkDir( << " " << unixPath << "\n" << "as a link directory.\n"; /* clang-format on */ - switch (this->Makefile->GetPolicyStatus(cmPolicies::CMP0015)) { + switch (mf.GetPolicyStatus(cmPolicies::CMP0015)) { case cmPolicies::WARN: e << cmPolicies::GetPolicyWarning(cmPolicies::CMP0015); - this->Makefile->IssueMessage(MessageType::AUTHOR_WARNING, e.str()); + mf.IssueMessage(MessageType::AUTHOR_WARNING, e.str()); break; case cmPolicies::OLD: // OLD behavior does not convert @@ -67,7 +69,7 @@ void cmLinkDirectoriesCommand::AddLinkDir( case cmPolicies::REQUIRED_IF_USED: case cmPolicies::REQUIRED_ALWAYS: e << cmPolicies::GetRequiredPolicyError(cmPolicies::CMP0015); - this->Makefile->IssueMessage(MessageType::FATAL_ERROR, e.str()); + mf.IssueMessage(MessageType::FATAL_ERROR, e.str()); CM_FALLTHROUGH; case cmPolicies::NEW: // NEW behavior converts @@ -75,9 +77,7 @@ void cmLinkDirectoriesCommand::AddLinkDir( break; } if (convertToAbsolute) { - std::string tmp = - cmStrCat(this->Makefile->GetCurrentSourceDirectory(), '/', unixPath); - unixPath = tmp; + unixPath = cmStrCat(mf.GetCurrentSourceDirectory(), '/', unixPath); } } directories.push_back(unixPath); diff --git a/Source/cmLinkDirectoriesCommand.h b/Source/cmLinkDirectoriesCommand.h index 489d90f..a7caa5c 100644 --- a/Source/cmLinkDirectoriesCommand.h +++ b/Source/cmLinkDirectoriesCommand.h @@ -8,41 +8,9 @@ #include <string> #include <vector> -#include <cm/memory> - -#include "cmCommand.h" - class cmExecutionStatus; -/** \class cmLinkDirectoriesCommand - * \brief Define a list of directories containing files to link. - * - * cmLinkDirectoriesCommand is used to specify a list - * of directories containing files to link into executable(s). - * Note that the command supports the use of CMake built-in variables - * such as CMAKE_BINARY_DIR and CMAKE_SOURCE_DIR. - */ -class cmLinkDirectoriesCommand : public cmCommand -{ -public: - /** - * This is a virtual constructor for the command. - */ - std::unique_ptr<cmCommand> Clone() override - { - return cm::make_unique<cmLinkDirectoriesCommand>(); - } - - /** - * This is called when the command is first encountered in - * the CMakeLists.txt file. - */ - bool InitialPass(std::vector<std::string> const& args, - cmExecutionStatus& status) override; - -private: - void AddLinkDir(std::string const& dir, - std::vector<std::string>& directories); -}; +bool cmLinkDirectoriesCommand(std::vector<std::string> const& args, + cmExecutionStatus& status); #endif diff --git a/Source/cmLoadCacheCommand.cxx b/Source/cmLoadCacheCommand.cxx index 3fd7343..cca1633 100644 --- a/Source/cmLoadCacheCommand.cxx +++ b/Source/cmLoadCacheCommand.cxx @@ -3,24 +3,30 @@ #include "cmLoadCacheCommand.h" #include "cmsys/FStream.hxx" +#include <set> +#include "cmExecutionStatus.h" #include "cmMakefile.h" #include "cmStateTypes.h" #include "cmSystemTools.h" #include "cmake.h" -class cmExecutionStatus; +static bool ReadWithPrefix(std::vector<std::string> const& args, + cmExecutionStatus& status); -// cmLoadCacheCommand -bool cmLoadCacheCommand::InitialPass(std::vector<std::string> const& args, - cmExecutionStatus&) +static void CheckLine(cmMakefile& mf, std::string const& prefix, + std::set<std::string> const& variablesToRead, + const char* line); + +bool cmLoadCacheCommand(std::vector<std::string> const& args, + cmExecutionStatus& status) { if (args.empty()) { - this->SetError("called with wrong number of arguments."); + status.SetError("called with wrong number of arguments."); } if (args.size() >= 2 && args[1] == "READ_WITH_PREFIX") { - return this->ReadWithPrefix(args); + return ReadWithPrefix(args, status); } // Cache entries to be excluded from the import list. @@ -59,24 +65,26 @@ bool cmLoadCacheCommand::InitialPass(std::vector<std::string> const& args, } } + cmMakefile& mf = status.GetMakefile(); + // Loop over each build directory listed in the arguments. Each // directory has a cache file. for (std::string const& arg : args) { if ((arg == "EXCLUDE") || (arg == "INCLUDE_INTERNALS")) { break; } - this->Makefile->GetCMakeInstance()->LoadCache(arg, false, excludes, - includes); + mf.GetCMakeInstance()->LoadCache(arg, false, excludes, includes); } return true; } -bool cmLoadCacheCommand::ReadWithPrefix(std::vector<std::string> const& args) +static bool ReadWithPrefix(std::vector<std::string> const& args, + cmExecutionStatus& status) { // Make sure we have a prefix. if (args.size() < 3) { - this->SetError("READ_WITH_PREFIX form must specify a prefix."); + status.SetError("READ_WITH_PREFIX form must specify a prefix."); return false; } @@ -84,17 +92,19 @@ bool cmLoadCacheCommand::ReadWithPrefix(std::vector<std::string> const& args) std::string cacheFile = args[0] + "/CMakeCache.txt"; if (!cmSystemTools::FileExists(cacheFile)) { std::string e = "Cannot load cache file from " + cacheFile; - this->SetError(e); + status.SetError(e); return false; } // Prepare the table of variables to read. - this->Prefix = args[2]; - this->VariablesToRead.insert(args.begin() + 3, args.end()); + std::string const prefix = args[2]; + std::set<std::string> const variablesToRead(args.begin() + 3, args.end()); // Read the cache file. cmsys::ifstream fin(cacheFile.c_str()); + cmMakefile& mf = status.GetMakefile(); + // This is a big hack read loop to overcome a buggy ifstream // implementation on HP-UX. This should work on all platforms even // for small buffer sizes. @@ -123,7 +133,7 @@ bool cmLoadCacheCommand::ReadWithPrefix(std::vector<std::string> const& args) } if (i != end) { // Completed a line. - this->CheckLine(line.c_str()); + CheckLine(mf, prefix, variablesToRead, line.c_str()); line.clear(); // Skip the newline character. @@ -134,13 +144,15 @@ bool cmLoadCacheCommand::ReadWithPrefix(std::vector<std::string> const& args) } if (!line.empty()) { // Partial last line. - this->CheckLine(line.c_str()); + CheckLine(mf, prefix, variablesToRead, line.c_str()); } return true; } -void cmLoadCacheCommand::CheckLine(const char* line) +static void CheckLine(cmMakefile& mf, std::string const& prefix, + std::set<std::string> const& variablesToRead, + const char* line) { // Check one line of the cache file. std::string var; @@ -148,14 +160,14 @@ void cmLoadCacheCommand::CheckLine(const char* line) cmStateEnums::CacheEntryType type = cmStateEnums::UNINITIALIZED; if (cmake::ParseCacheEntry(line, var, value, type)) { // Found a real entry. See if this one was requested. - if (this->VariablesToRead.find(var) != this->VariablesToRead.end()) { + if (variablesToRead.find(var) != variablesToRead.end()) { // This was requested. Set this variable locally with the given // prefix. - var = this->Prefix + var; + var = prefix + var; if (!value.empty()) { - this->Makefile->AddDefinition(var, value); + mf.AddDefinition(var, value); } else { - this->Makefile->RemoveDefinition(var); + mf.RemoveDefinition(var); } } } diff --git a/Source/cmLoadCacheCommand.h b/Source/cmLoadCacheCommand.h index 37f0372..7cee663 100644 --- a/Source/cmLoadCacheCommand.h +++ b/Source/cmLoadCacheCommand.h @@ -5,45 +5,12 @@ #include "cmConfigure.h" // IWYU pragma: keep -#include <set> #include <string> #include <vector> -#include <cm/memory> - -#include "cmCommand.h" - class cmExecutionStatus; -/** \class cmLoadCacheCommand - * \brief load a cache file - * - * cmLoadCacheCommand loads the non internal values of a cache file - */ -class cmLoadCacheCommand : public cmCommand -{ -public: - /** - * This is a virtual constructor for the command. - */ - std::unique_ptr<cmCommand> Clone() override - { - return cm::make_unique<cmLoadCacheCommand>(); - } - - /** - * This is called when the command is first encountered in - * the CMakeLists.txt file. - */ - bool InitialPass(std::vector<std::string> const& args, - cmExecutionStatus& status) override; - -protected: - std::set<std::string> VariablesToRead; - std::string Prefix; - - bool ReadWithPrefix(std::vector<std::string> const& args); - void CheckLine(const char* line); -}; +bool cmLoadCacheCommand(std::vector<std::string> const& args, + cmExecutionStatus& status); #endif diff --git a/Source/cmLocalGenerator.cxx b/Source/cmLocalGenerator.cxx index e059548..6e6ca51 100644 --- a/Source/cmLocalGenerator.cxx +++ b/Source/cmLocalGenerator.cxx @@ -7,6 +7,7 @@ #include "cmCustomCommand.h" #include "cmCustomCommandGenerator.h" #include "cmCustomCommandLines.h" +#include "cmCustomCommandTypes.h" #include "cmGeneratedFileStream.h" #include "cmGeneratorExpression.h" #include "cmGeneratorExpressionEvaluationFile.h" @@ -48,7 +49,6 @@ #include <functional> #include <initializer_list> #include <iterator> -#include <memory> #include <sstream> #include <unordered_set> #include <utility> @@ -925,10 +925,8 @@ void cmLocalGenerator::AddCompileOptions(std::vector<BT<std::string>>& flags, // to ON if (char const* jmcExprGen = target->GetProperty("VS_JUST_MY_CODE_DEBUGGING")) { - cmGeneratorExpression ge; - std::unique_ptr<cmCompiledGeneratorExpression> cge = - ge.Parse(jmcExprGen); - std::string isJMCEnabled = cge->Evaluate(this, config); + std::string isJMCEnabled = + cmGeneratorExpression::Evaluate(jmcExprGen, this, config); if (cmIsOn(isJMCEnabled)) { std::vector<std::string> optVec = cmExpandedList(jmc); std::string jmcFlags; @@ -1657,7 +1655,7 @@ void cmLocalGenerator::AddArchitectureFlags(std::string& flags, const char* deploymentTargetFlag = this->Makefile->GetDefinition(deploymentTargetFlagVar); if (!archs.empty() && !lang.empty() && - (lang[0] == 'C' || lang[0] == 'F')) { + (lang[0] == 'C' || lang[0] == 'F' || lang[0] == 'O')) { for (std::string const& arch : archs) { flags += " -arch "; flags += arch; @@ -1710,11 +1708,8 @@ void cmLocalGenerator::AddLanguageFlags(std::string& flags, if (!msvcRuntimeLibraryValue) { msvcRuntimeLibraryValue = msvcRuntimeLibraryDefault; } - cmGeneratorExpression ge; - std::unique_ptr<cmCompiledGeneratorExpression> cge = - ge.Parse(msvcRuntimeLibraryValue); - std::string const msvcRuntimeLibrary = - cge->Evaluate(this, config, false, target); + std::string const msvcRuntimeLibrary = cmGeneratorExpression::Evaluate( + msvcRuntimeLibraryValue, this, config, target); if (!msvcRuntimeLibrary.empty()) { if (const char* msvcRuntimeLibraryOptions = this->Makefile->GetDefinition( @@ -1947,10 +1942,20 @@ void cmLocalGenerator::AddCompilerRequirementFlag( langStdMap["CXX"].emplace_back("11"); langStdMap["CXX"].emplace_back("98"); + langStdMap["OBJCXX"].emplace_back("20"); + langStdMap["OBJCXX"].emplace_back("17"); + langStdMap["OBJCXX"].emplace_back("14"); + langStdMap["OBJCXX"].emplace_back("11"); + langStdMap["OBJCXX"].emplace_back("98"); + langStdMap["C"].emplace_back("11"); langStdMap["C"].emplace_back("99"); langStdMap["C"].emplace_back("90"); + langStdMap["OBJC"].emplace_back("11"); + langStdMap["OBJC"].emplace_back("99"); + langStdMap["OBJC"].emplace_back("90"); + langStdMap["CUDA"].emplace_back("14"); langStdMap["CUDA"].emplace_back("11"); langStdMap["CUDA"].emplace_back("98"); @@ -2362,7 +2367,7 @@ void cmLocalGenerator::AddPchDependencies(cmGeneratorTarget* target, if (this->GetGlobalGenerator()->IsMultiConfig()) { this->Makefile->AddCustomCommandToTarget( target->GetName(), outputs, no_deps, commandLines, - cmTarget::PRE_BUILD, no_message, no_current_dir); + cmCustomCommandType::PRE_BUILD, no_message, no_current_dir); } else { cmImplicitDependsList no_implicit_depends; cmSourceFile* copy_rule = this->Makefile->AddCustomCommandToOutput( diff --git a/Source/cmLocalNinjaGenerator.cxx b/Source/cmLocalNinjaGenerator.cxx index 0fe385f..fed8be5 100644 --- a/Source/cmLocalNinjaGenerator.cxx +++ b/Source/cmLocalNinjaGenerator.cxx @@ -89,7 +89,9 @@ void cmLocalNinjaGenerator::Generate() if (tg) { tg->Generate(); // Add the target to "all" if required. - if (!this->GetGlobalNinjaGenerator()->IsExcluded(target)) { + if (!this->GetGlobalNinjaGenerator()->IsExcluded( + this->GetGlobalNinjaGenerator()->GetLocalGenerators()[0], + target)) { this->GetGlobalNinjaGenerator()->AddDependencyToAll(target); } } @@ -608,12 +610,10 @@ void cmLocalNinjaGenerator::AdditionalCleanFiles() this->Makefile->GetProperty("ADDITIONAL_CLEAN_FILES")) { std::vector<std::string> cleanFiles; { - cmGeneratorExpression ge; - auto cge = ge.Parse(prop_value); - cmExpandList( - cge->Evaluate(this, - this->Makefile->GetSafeDefinition("CMAKE_BUILD_TYPE")), - cleanFiles); + cmExpandList(cmGeneratorExpression::Evaluate( + prop_value, this, + this->Makefile->GetSafeDefinition("CMAKE_BUILD_TYPE")), + cleanFiles); } std::string const& binaryDir = this->GetCurrentBinaryDirectory(); cmGlobalNinjaGenerator* gg = this->GetGlobalNinjaGenerator(); diff --git a/Source/cmLocalNinjaGenerator.h b/Source/cmLocalNinjaGenerator.h index 08bec70..f64534c 100644 --- a/Source/cmLocalNinjaGenerator.h +++ b/Source/cmLocalNinjaGenerator.h @@ -93,7 +93,6 @@ private: void WriteProcessedMakefile(std::ostream& os); void WritePools(std::ostream& os); - void WriteCustomCommandRule(); void WriteCustomCommandBuildStatement(cmCustomCommand const* cc, const cmNinjaDeps& orderOnlyDeps); diff --git a/Source/cmLocalUnixMakefileGenerator3.cxx b/Source/cmLocalUnixMakefileGenerator3.cxx index a7be04e..1d87e93 100644 --- a/Source/cmLocalUnixMakefileGenerator3.cxx +++ b/Source/cmLocalUnixMakefileGenerator3.cxx @@ -1083,12 +1083,10 @@ void cmLocalUnixMakefileGenerator3::AppendDirectoryCleanCommand( // Look for additional files registered for cleaning in this directory. if (const char* prop_value = this->Makefile->GetProperty("ADDITIONAL_CLEAN_FILES")) { - cmGeneratorExpression ge; - std::unique_ptr<cmCompiledGeneratorExpression> cge = ge.Parse(prop_value); - cmExpandList( - cge->Evaluate(this, - this->Makefile->GetSafeDefinition("CMAKE_BUILD_TYPE")), - cleanFiles); + cmExpandList(cmGeneratorExpression::Evaluate( + prop_value, this, + this->Makefile->GetSafeDefinition("CMAKE_BUILD_TYPE")), + cleanFiles); } if (cleanFiles.empty()) { return; diff --git a/Source/cmMakefile.cxx b/Source/cmMakefile.cxx index 34081ed..d9fbe9d 100644 --- a/Source/cmMakefile.cxx +++ b/Source/cmMakefile.cxx @@ -38,7 +38,9 @@ #include "cmState.h" #include "cmStateDirectory.h" #include "cmStateTypes.h" +#include "cmStringAlgorithms.h" #include "cmSystemTools.h" +#include "cmTarget.h" #include "cmTargetLinkLibraryType.h" #include "cmTest.h" #include "cmTestGenerator.h" // IWYU pragma: keep @@ -837,13 +839,8 @@ bool cmMakefile::ValidateCustomCommand( return true; } -cmTarget* cmMakefile::AddCustomCommandToTarget( - const std::string& target, const std::vector<std::string>& byproducts, - const std::vector<std::string>& depends, - const cmCustomCommandLines& commandLines, cmTarget::CustomCommandType type, - const char* comment, const char* workingDir, bool escapeOldStyle, - bool uses_terminal, const std::string& depfile, const std::string& job_pool, - bool command_expand_lists, ObjectLibraryCommands objLibraryCommands) +cmTarget* cmMakefile::GetCustomCommandTarget( + const std::string& target, cmObjectLibraryCommands objLibCommands) const { // Find the target to which to add the custom command. auto ti = this->Targets.find(target); @@ -884,7 +881,7 @@ cmTarget* cmMakefile::AddCustomCommandToTarget( } cmTarget* t = &ti->second; - if (objLibraryCommands == RejectObjectLibraryCommands && + if (objLibCommands == cmObjectLibraryCommands::Reject && t->GetType() == cmStateEnums::OBJECT_LIBRARY) { std::ostringstream e; e << "Target \"" << target @@ -902,8 +899,21 @@ cmTarget* cmMakefile::AddCustomCommandToTarget( return nullptr; } + return t; +} + +cmTarget* cmMakefile::AddCustomCommandToTarget( + const std::string& target, const std::vector<std::string>& byproducts, + const std::vector<std::string>& depends, + const cmCustomCommandLines& commandLines, cmCustomCommandType type, + const char* comment, const char* workingDir, bool escapeOldStyle, + bool uses_terminal, const std::string& depfile, const std::string& job_pool, + bool command_expand_lists, cmObjectLibraryCommands objLibCommands) +{ + cmTarget* t = this->GetCustomCommandTarget(target, objLibCommands); + // Validate custom commands. - if (!this->ValidateCustomCommand(commandLines)) { + if (!t || !this->ValidateCustomCommand(commandLines)) { return t; } @@ -920,7 +930,7 @@ cmTarget* cmMakefile::AddCustomCommandToTarget( void cmMakefile::CommitCustomCommandToTarget( cmTarget* target, const std::vector<std::string>& byproducts, const std::vector<std::string>& depends, - const cmCustomCommandLines& commandLines, cmTarget::CustomCommandType type, + const cmCustomCommandLines& commandLines, cmCustomCommandType type, const char* comment, const char* workingDir, bool escapeOldStyle, bool uses_terminal, const std::string& depfile, const std::string& job_pool, bool command_expand_lists) @@ -936,47 +946,18 @@ void cmMakefile::CommitCustomCommandToTarget( cc.SetDepfile(depfile); cc.SetJobPool(job_pool); switch (type) { - case cmTarget::PRE_BUILD: - target->AddPreBuildCommand(cc); + case cmCustomCommandType::PRE_BUILD: + target->AddPreBuildCommand(std::move(cc)); break; - case cmTarget::PRE_LINK: - target->AddPreLinkCommand(cc); + case cmCustomCommandType::PRE_LINK: + target->AddPreLinkCommand(std::move(cc)); break; - case cmTarget::POST_BUILD: - target->AddPostBuildCommand(cc); + case cmCustomCommandType::POST_BUILD: + target->AddPostBuildCommand(std::move(cc)); break; } - this->UpdateOutputToSourceMap(byproducts, target); -} -void cmMakefile::UpdateOutputToSourceMap( - std::vector<std::string> const& byproducts, cmTarget* target) -{ - for (std::string const& o : byproducts) { - this->UpdateOutputToSourceMap(o, target); - } -} - -void cmMakefile::UpdateOutputToSourceMap(std::string const& byproduct, - cmTarget* target) -{ - SourceEntry entry; - entry.Sources.Target = target; - - auto pr = this->OutputToSource.emplace(byproduct, entry); - if (!pr.second) { - SourceEntry& current = pr.first->second; - // Has the target already been set? - if (!current.Sources.Target) { - current.Sources.Target = target; - } else { - // Multiple custom commands/targets produce the same output (source file - // or target). See also comment in other UpdateOutputToSourceMap - // overload. - // - // TODO: Warn the user about this case. - } - } + this->AddTargetByproducts(target, byproducts); } cmSourceFile* cmMakefile::AddCustomCommandToOutput( @@ -1102,47 +1083,10 @@ cmSourceFile* cmMakefile::CommitCustomCommandToOutput( cc->SetDepfile(depfile); cc->SetJobPool(job_pool); file->SetCustomCommand(std::move(cc)); - this->UpdateOutputToSourceMap(outputs, file, false); - this->UpdateOutputToSourceMap(byproducts, file, true); - } - return file; -} - -void cmMakefile::UpdateOutputToSourceMap( - std::vector<std::string> const& outputs, cmSourceFile* source, - bool byproduct) -{ - for (std::string const& o : outputs) { - this->UpdateOutputToSourceMap(o, source, byproduct); - } -} -void cmMakefile::UpdateOutputToSourceMap(std::string const& output, - cmSourceFile* source, bool byproduct) -{ - SourceEntry entry; - entry.Sources.Source = source; - entry.Sources.SourceIsByproduct = byproduct; - - auto pr = this->OutputToSource.emplace(output, entry); - if (!pr.second) { - SourceEntry& current = pr.first->second; - // Outputs take precedence over byproducts - if (!current.Sources.Source || - (current.Sources.SourceIsByproduct && !byproduct)) { - current.Sources.Source = source; - current.Sources.SourceIsByproduct = false; - } else { - // Multiple custom commands produce the same output but may - // be attached to a different source file (MAIN_DEPENDENCY). - // LinearGetSourceFileWithOutput would return the first one, - // so keep the mapping for the first one. - // - // TODO: Warn the user about this case. However, the VS 8 generator - // triggers it for separate generate.stamp rules in ZERO_CHECK and - // individual targets. - } + this->AddSourceOutputs(file, outputs, byproducts); } + return file; } void cmMakefile::AddCustomCommandOldStyle( @@ -1157,9 +1101,9 @@ void cmMakefile::AddCustomCommandOldStyle( // same then it added a post-build rule to the target. Preserve // this behavior. std::vector<std::string> no_byproducts; - this->AddCustomCommandToTarget(target, no_byproducts, depends, - commandLines, cmTarget::POST_BUILD, comment, - nullptr); + this->AddCustomCommandToTarget( + target, no_byproducts, depends, commandLines, + cmCustomCommandType::POST_BUILD, comment, nullptr); return; } @@ -1247,85 +1191,73 @@ void cmMakefile::CommitAppendCustomCommandToOutput( } } -cmTarget* cmMakefile::AddUtilityCommand( - const std::string& utilityName, TargetOrigin origin, bool excludeFromAll, - const char* workingDirectory, const std::vector<std::string>& depends, - const cmCustomCommandLines& commandLines, bool escapeOldStyle, - const char* comment, bool uses_terminal, bool command_expand_lists, - const std::string& job_pool) +cmUtilityOutput cmMakefile::GetUtilityOutput(cmTarget* target) { - std::vector<std::string> no_byproducts; - return this->AddUtilityCommand( - utilityName, origin, excludeFromAll, workingDirectory, no_byproducts, - depends, commandLines, escapeOldStyle, comment, uses_terminal, - command_expand_lists, job_pool); + std::string force = cmStrCat(this->GetCurrentBinaryDirectory(), + "/CMakeFiles/", target->GetName()); + std::string forceCMP0049 = target->GetSourceCMP0049(force); + { + cmSourceFile* sf = nullptr; + if (!forceCMP0049.empty()) { + sf = this->GetOrCreateSource(forceCMP0049, false, + cmSourceFileLocationKind::Known); + } + // The output is not actually created so mark it symbolic. + if (sf) { + sf->SetProperty("SYMBOLIC", "1"); + } else { + cmSystemTools::Error("Could not get source file entry for " + force); + } + } + return { std::move(force), std::move(forceCMP0049) }; } cmTarget* cmMakefile::AddUtilityCommand( - const std::string& utilityName, TargetOrigin origin, bool excludeFromAll, + const std::string& utilityName, cmCommandOrigin origin, bool excludeFromAll, const char* workingDirectory, const std::vector<std::string>& byproducts, const std::vector<std::string>& depends, const cmCustomCommandLines& commandLines, bool escapeOldStyle, const char* comment, bool uses_terminal, bool command_expand_lists, const std::string& job_pool) { - // Create a target instance for this utility. - cmTarget* target = this->AddNewTarget(cmStateEnums::UTILITY, utilityName); - target->SetIsGeneratorProvided(origin == TargetOrigin::Generator); - if (excludeFromAll || this->GetPropertyAsBool("EXCLUDE_FROM_ALL")) { - target->SetProperty("EXCLUDE_FROM_ALL", "TRUE"); - } + cmTarget* target = + this->AddNewUtilityTarget(utilityName, origin, excludeFromAll); // Validate custom commands. - if (!this->ValidateCustomCommand(commandLines) || - (commandLines.empty() && depends.empty())) { + if ((commandLines.empty() && depends.empty()) || + !this->ValidateCustomCommand(commandLines)) { return target; } + // Get the output name of the utility target and mark it generated. + cmUtilityOutput force = this->GetUtilityOutput(target); + this->GetOrCreateGeneratedSource(force.Name); + // Always create the byproduct sources and mark them generated. this->CreateGeneratedSources(byproducts); - std::string force = - cmStrCat(this->GetCurrentBinaryDirectory(), "/CMakeFiles/", utilityName); - this->CreateGeneratedSource(force); - std::string forceCMP0049 = target->GetSourceCMP0049(force); - { - cmSourceFile* sf = nullptr; - if (!forceCMP0049.empty()) { - sf = this->GetOrCreateSource(forceCMP0049, false, - cmSourceFileLocationKind::Known); - } - // The output is not actually created so mark it symbolic. - if (sf) { - sf->SetProperty("SYMBOLIC", "1"); - } else { - cmSystemTools::Error("Could not get source file entry for " + force); - } - } - if (!comment) { // Use an empty comment to avoid generation of default comment. comment = ""; } - this->CommitUtilityCommand(target, force, forceCMP0049, workingDirectory, - byproducts, depends, commandLines, escapeOldStyle, - comment, uses_terminal, command_expand_lists, - job_pool); + this->CommitUtilityCommand(target, force, workingDirectory, byproducts, + depends, commandLines, escapeOldStyle, comment, + uses_terminal, command_expand_lists, job_pool); return target; } void cmMakefile::CommitUtilityCommand( - cmTarget* target, const std::string& force, const std::string& forceCMP0049, - const char* workingDirectory, const std::vector<std::string>& byproducts, + cmTarget* target, const cmUtilityOutput& force, const char* workingDirectory, + const std::vector<std::string>& byproducts, const std::vector<std::string>& depends, const cmCustomCommandLines& commandLines, bool escapeOldStyle, const char* comment, bool uses_terminal, bool command_expand_lists, const std::string& job_pool) { std::vector<std::string> forced; - forced.push_back(force); + forced.push_back(force.Name); std::string no_main_dependency; cmImplicitDependsList no_implicit_depends; bool no_replace = false; @@ -1333,11 +1265,11 @@ void cmMakefile::CommitUtilityCommand( forced, byproducts, depends, no_main_dependency, no_implicit_depends, commandLines, comment, workingDirectory, no_replace, escapeOldStyle, uses_terminal, command_expand_lists, /*depfile=*/"", job_pool); - if (!forceCMP0049.empty()) { - target->AddSource(forceCMP0049); + if (!force.NameCMP0049.empty()) { + target->AddSource(force.NameCMP0049); } if (sf) { - this->UpdateOutputToSourceMap(byproducts, target); + this->AddTargetByproducts(target, byproducts); } } @@ -1845,7 +1777,7 @@ void cmMakefile::AddSubDirectory(const std::string& srcPath, cmMakefile* subMf = new cmMakefile(this->GlobalGenerator, newSnapshot); this->GetGlobalGenerator()->AddMakefile(subMf); - if (excludeFromAll || this->GetPropertyAsBool("EXCLUDE_FROM_ALL")) { + if (excludeFromAll) { subMf->SetProperty("EXCLUDE_FROM_ALL", "TRUE"); } @@ -2120,9 +2052,7 @@ cmTarget* cmMakefile::AddLibrary(const std::string& lname, // over changes in CMakeLists.txt, making the information stale and // hence useless. target->ClearDependencyInformation(*this); - if (excludeFromAll || - (type != cmStateEnums::INTERFACE_LIBRARY && - this->GetPropertyAsBool("EXCLUDE_FROM_ALL"))) { + if (excludeFromAll) { target->SetProperty("EXCLUDE_FROM_ALL", "TRUE"); } target->AddSources(srcs); @@ -2135,7 +2065,7 @@ cmTarget* cmMakefile::AddExecutable(const std::string& exeName, bool excludeFromAll) { cmTarget* target = this->AddNewTarget(cmStateEnums::EXECUTABLE, exeName); - if (excludeFromAll || this->GetPropertyAsBool("EXCLUDE_FROM_ALL")) { + if (excludeFromAll) { target->SetProperty("EXCLUDE_FROM_ALL", "TRUE"); } target->AddSources(srcs); @@ -2156,6 +2086,18 @@ cmTarget* cmMakefile::AddNewTarget(cmStateEnums::TargetType type, return &it->second; } +cmTarget* cmMakefile::AddNewUtilityTarget(const std::string& utilityName, + cmCommandOrigin origin, + bool excludeFromAll) +{ + cmTarget* target = this->AddNewTarget(cmStateEnums::UTILITY, utilityName); + target->SetIsGeneratorProvided(origin == cmCommandOrigin::Generator); + if (excludeFromAll) { + target->SetProperty("EXCLUDE_FROM_ALL", "TRUE"); + } + return target; +} + namespace { bool AnyOutputMatches(const std::string& name, const std::vector<std::string>& outputs) @@ -2286,6 +2228,76 @@ bool cmMakefile::MightHaveCustomCommand(const std::string& name) const return false; } +void cmMakefile::AddTargetByproducts( + cmTarget* target, const std::vector<std::string>& byproducts) +{ + for (std::string const& o : byproducts) { + this->UpdateOutputToSourceMap(o, target); + } +} + +void cmMakefile::AddSourceOutputs(cmSourceFile* source, + const std::vector<std::string>& outputs, + const std::vector<std::string>& byproducts) +{ + for (std::string const& o : outputs) { + this->UpdateOutputToSourceMap(o, source, false); + } + for (std::string const& o : byproducts) { + this->UpdateOutputToSourceMap(o, source, true); + } +} + +void cmMakefile::UpdateOutputToSourceMap(std::string const& byproduct, + cmTarget* target) +{ + SourceEntry entry; + entry.Sources.Target = target; + + auto pr = this->OutputToSource.emplace(byproduct, entry); + if (!pr.second) { + SourceEntry& current = pr.first->second; + // Has the target already been set? + if (!current.Sources.Target) { + current.Sources.Target = target; + } else { + // Multiple custom commands/targets produce the same output (source file + // or target). See also comment in other UpdateOutputToSourceMap + // overload. + // + // TODO: Warn the user about this case. + } + } +} + +void cmMakefile::UpdateOutputToSourceMap(std::string const& output, + cmSourceFile* source, bool byproduct) +{ + SourceEntry entry; + entry.Sources.Source = source; + entry.Sources.SourceIsByproduct = byproduct; + + auto pr = this->OutputToSource.emplace(output, entry); + if (!pr.second) { + SourceEntry& current = pr.first->second; + // Outputs take precedence over byproducts + if (!current.Sources.Source || + (current.Sources.SourceIsByproduct && !byproduct)) { + current.Sources.Source = source; + current.Sources.SourceIsByproduct = false; + } else { + // Multiple custom commands produce the same output but may + // be attached to a different source file (MAIN_DEPENDENCY). + // LinearGetSourceFileWithOutput would return the first one, + // so keep the mapping for the first one. + // + // TODO: Warn the user about this case. However, the VS 8 generator + // triggers it for separate generate.stamp rules in ZERO_CHECK and + // individual targets. + } + } +} + #if !defined(CMAKE_BOOTSTRAP) cmSourceGroup* cmMakefile::GetSourceGroup( const std::vector<std::string>& name) const @@ -3530,19 +3542,20 @@ cmSourceFile* cmMakefile::GetOrCreateSource(const std::string& sourceName, return this->CreateSource(sourceName, generated, kind); } -void cmMakefile::CreateGeneratedSource(const std::string& output) +cmSourceFile* cmMakefile::GetOrCreateGeneratedSource( + const std::string& sourceName) { - if (cmSourceFile* out = this->GetOrCreateSource( - output, true, cmSourceFileLocationKind::Known)) { - out->SetProperty("GENERATED", "1"); - } + cmSourceFile* sf = + this->GetOrCreateSource(sourceName, true, cmSourceFileLocationKind::Known); + sf->SetProperty("GENERATED", "1"); + return sf; } void cmMakefile::CreateGeneratedSources( const std::vector<std::string>& outputs) { for (std::string const& output : outputs) { - this->CreateGeneratedSource(output); + this->GetOrCreateGeneratedSource(output); } } @@ -4615,9 +4628,9 @@ bool cmMakefile::AddRequiredTargetFeature(cmTarget* target, target->AppendProperty("COMPILE_FEATURES", feature.c_str()); - return lang == "C" - ? this->AddRequiredTargetCFeature(target, feature, error) - : this->AddRequiredTargetCxxFeature(target, feature, error); + return lang == "C" || lang == "OBJC" + ? this->AddRequiredTargetCFeature(target, feature, lang, error) + : this->AddRequiredTargetCxxFeature(target, feature, lang, error); } bool cmMakefile::CompileFeatureKnown(cmTarget const* target, @@ -4709,30 +4722,33 @@ bool cmMakefile::HaveStandardAvailable(cmTarget const* target, std::string const& lang, const std::string& feature) const { - return lang == "C" ? this->HaveCStandardAvailable(target, feature) - : this->HaveCxxStandardAvailable(target, feature); + return lang == "C" || lang == "OBJC" + ? this->HaveCStandardAvailable(target, feature, lang) + : this->HaveCxxStandardAvailable(target, feature, lang); } bool cmMakefile::HaveCStandardAvailable(cmTarget const* target, - const std::string& feature) const + const std::string& feature, + std::string const& lang) const { const char* defaultCStandard = - this->GetDefinition("CMAKE_C_STANDARD_DEFAULT"); + this->GetDefinition(cmStrCat("CMAKE_", lang, "_STANDARD_DEFAULT")); if (!defaultCStandard) { this->IssueMessage( MessageType::INTERNAL_ERROR, - "CMAKE_C_STANDARD_DEFAULT is not set. COMPILE_FEATURES support " - "not fully configured for this compiler."); + cmStrCat("CMAKE_", lang, + "_STANDARD_DEFAULT is not set. COMPILE_FEATURES support " + "not fully configured for this compiler.")); // Return true so the caller does not try to lookup the default standard. return true; } if (std::find_if(cm::cbegin(C_STANDARDS), cm::cend(C_STANDARDS), cmStrCmp(defaultCStandard)) == cm::cend(C_STANDARDS)) { - std::ostringstream e; - e << "The CMAKE_C_STANDARD_DEFAULT variable contains an " - "invalid value: \"" - << defaultCStandard << "\"."; - this->IssueMessage(MessageType::INTERNAL_ERROR, e.str()); + const std::string e = cmStrCat("The CMAKE_", lang, + "_STANDARD_DEFAULT variable contains an " + "invalid value: \"", + defaultCStandard, "\"."); + this->IssueMessage(MessageType::INTERNAL_ERROR, e); return false; } @@ -4740,19 +4756,20 @@ bool cmMakefile::HaveCStandardAvailable(cmTarget const* target, bool needC99 = false; bool needC11 = false; - this->CheckNeededCLanguage(feature, needC90, needC99, needC11); + this->CheckNeededCLanguage(feature, lang, needC90, needC99, needC11); - const char* existingCStandard = target->GetProperty("C_STANDARD"); + const char* existingCStandard = + target->GetProperty(cmStrCat(lang, "_STANDARD")); if (!existingCStandard) { existingCStandard = defaultCStandard; } if (std::find_if(cm::cbegin(C_STANDARDS), cm::cend(C_STANDARDS), cmStrCmp(existingCStandard)) == cm::cend(C_STANDARDS)) { - std::ostringstream e; - e << "The C_STANDARD property on target \"" << target->GetName() - << "\" contained an invalid value: \"" << existingCStandard << "\"."; - this->IssueMessage(MessageType::FATAL_ERROR, e.str()); + const std::string e = cmStrCat( + "The ", lang, "_STANDARD property on target \"", target->GetName(), + "\" contained an invalid value: \"", existingCStandard, "\"."); + this->IssueMessage(MessageType::FATAL_ERROR, e); return false; } @@ -4783,7 +4800,7 @@ bool cmMakefile::IsLaterStandard(std::string const& lang, std::string const& lhs, std::string const& rhs) { - if (lang == "C") { + if (lang == "C" || lang == "OBJC") { const char* const* rhsIt = std::find_if( cm::cbegin(C_STANDARDS), cm::cend(C_STANDARDS), cmStrCmp(rhs)); @@ -4798,25 +4815,26 @@ bool cmMakefile::IsLaterStandard(std::string const& lang, } bool cmMakefile::HaveCxxStandardAvailable(cmTarget const* target, - const std::string& feature) const + const std::string& feature, + std::string const& lang) const { const char* defaultCxxStandard = - this->GetDefinition("CMAKE_CXX_STANDARD_DEFAULT"); + this->GetDefinition(cmStrCat("CMAKE_", lang, "_STANDARD_DEFAULT")); if (!defaultCxxStandard) { this->IssueMessage( MessageType::INTERNAL_ERROR, - "CMAKE_CXX_STANDARD_DEFAULT is not set. COMPILE_FEATURES support " - "not fully configured for this compiler."); + cmStrCat("CMAKE_", lang, + "_STANDARD_DEFAULT is not set. COMPILE_FEATURES support " + "not fully configured for this compiler.")); // Return true so the caller does not try to lookup the default standard. return true; } if (std::find_if(cm::cbegin(CXX_STANDARDS), cm::cend(CXX_STANDARDS), cmStrCmp(defaultCxxStandard)) == cm::cend(CXX_STANDARDS)) { - std::ostringstream e; - e << "The CMAKE_CXX_STANDARD_DEFAULT variable contains an " - "invalid value: \"" - << defaultCxxStandard << "\"."; - this->IssueMessage(MessageType::INTERNAL_ERROR, e.str()); + const std::string e = + cmStrCat("The CMAKE_", lang, "_STANDARD_DEFAULT variable contains an ", + "invalid value: \"", defaultCxxStandard, "\"."); + this->IssueMessage(MessageType::INTERNAL_ERROR, e); return false; } @@ -4825,10 +4843,11 @@ bool cmMakefile::HaveCxxStandardAvailable(cmTarget const* target, bool needCxx14 = false; bool needCxx17 = false; bool needCxx20 = false; - this->CheckNeededCxxLanguage(feature, needCxx98, needCxx11, needCxx14, + this->CheckNeededCxxLanguage(feature, lang, needCxx98, needCxx11, needCxx14, needCxx17, needCxx20); - const char* existingCxxStandard = target->GetProperty("CXX_STANDARD"); + const char* existingCxxStandard = + target->GetProperty(cmStrCat(lang, "_STANDARD")); if (!existingCxxStandard) { existingCxxStandard = defaultCxxStandard; } @@ -4837,10 +4856,10 @@ bool cmMakefile::HaveCxxStandardAvailable(cmTarget const* target, std::find_if(cm::cbegin(CXX_STANDARDS), cm::cend(CXX_STANDARDS), cmStrCmp(existingCxxStandard)); if (existingCxxLevel == cm::cend(CXX_STANDARDS)) { - std::ostringstream e; - e << "The CXX_STANDARD property on target \"" << target->GetName() - << "\" contained an invalid value: \"" << existingCxxStandard << "\"."; - this->IssueMessage(MessageType::FATAL_ERROR, e.str()); + const std::string e = cmStrCat( + "The ", lang, "_STANDARD property on target \"", target->GetName(), + "\" contained an invalid value: \"", existingCxxStandard, "\"."); + this->IssueMessage(MessageType::FATAL_ERROR, e); return false; } @@ -4858,32 +4877,33 @@ bool cmMakefile::HaveCxxStandardAvailable(cmTarget const* target, } void cmMakefile::CheckNeededCxxLanguage(const std::string& feature, + std::string const& lang, bool& needCxx98, bool& needCxx11, bool& needCxx14, bool& needCxx17, bool& needCxx20) const { if (const char* propCxx98 = - this->GetDefinition("CMAKE_CXX98_COMPILE_FEATURES")) { + this->GetDefinition(cmStrCat("CMAKE_", lang, "98_COMPILE_FEATURES"))) { std::vector<std::string> props = cmExpandedList(propCxx98); needCxx98 = cmContains(props, feature); } if (const char* propCxx11 = - this->GetDefinition("CMAKE_CXX11_COMPILE_FEATURES")) { + this->GetDefinition(cmStrCat("CMAKE_", lang, "11_COMPILE_FEATURES"))) { std::vector<std::string> props = cmExpandedList(propCxx11); needCxx11 = cmContains(props, feature); } if (const char* propCxx14 = - this->GetDefinition("CMAKE_CXX14_COMPILE_FEATURES")) { + this->GetDefinition(cmStrCat("CMAKE_", lang, "14_COMPILE_FEATURES"))) { std::vector<std::string> props = cmExpandedList(propCxx14); needCxx14 = cmContains(props, feature); } if (const char* propCxx17 = - this->GetDefinition("CMAKE_CXX17_COMPILE_FEATURES")) { + this->GetDefinition(cmStrCat("CMAKE_", lang, "17_COMPILE_FEATURES"))) { std::vector<std::string> props = cmExpandedList(propCxx17); needCxx17 = cmContains(props, feature); } if (const char* propCxx20 = - this->GetDefinition("CMAKE_CXX20_COMPILE_FEATURES")) { + this->GetDefinition(cmStrCat("CMAKE_", lang, "20_COMPILE_FEATURES"))) { std::vector<std::string> props = cmExpandedList(propCxx20); needCxx20 = cmContains(props, feature); } @@ -4891,6 +4911,7 @@ void cmMakefile::CheckNeededCxxLanguage(const std::string& feature, bool cmMakefile::AddRequiredTargetCxxFeature(cmTarget* target, const std::string& feature, + std::string const& lang, std::string* error) const { bool needCxx98 = false; @@ -4899,13 +4920,14 @@ bool cmMakefile::AddRequiredTargetCxxFeature(cmTarget* target, bool needCxx17 = false; bool needCxx20 = false; - this->CheckNeededCxxLanguage(feature, needCxx98, needCxx11, needCxx14, + this->CheckNeededCxxLanguage(feature, lang, needCxx98, needCxx11, needCxx14, needCxx17, needCxx20); - const char* existingCxxStandard = target->GetProperty("CXX_STANDARD"); + const char* existingCxxStandard = + target->GetProperty(cmStrCat(lang, "_STANDARD")); if (existingCxxStandard == nullptr) { const char* defaultCxxStandard = - this->GetDefinition("CMAKE_CXX_STANDARD_DEFAULT"); + this->GetDefinition(cmStrCat("CMAKE_", lang, "_STANDARD_DEFAULT")); if (defaultCxxStandard && *defaultCxxStandard) { existingCxxStandard = defaultCxxStandard; } @@ -4916,14 +4938,14 @@ bool cmMakefile::AddRequiredTargetCxxFeature(cmTarget* target, std::find_if(cm::cbegin(CXX_STANDARDS), cm::cend(CXX_STANDARDS), cmStrCmp(existingCxxStandard)); if (existingCxxLevel == cm::cend(CXX_STANDARDS)) { - std::ostringstream e; - e << "The CXX_STANDARD property on target \"" << target->GetName() - << "\" contained an invalid value: \"" << existingCxxStandard << "\"."; + const std::string e = cmStrCat( + "The ", lang, "_STANDARD property on target \"", target->GetName(), + "\" contained an invalid value: \"", existingCxxStandard, "\"."); if (error) { - *error = e.str(); + *error = e; } else { - this->GetCMakeInstance()->IssueMessage(MessageType::FATAL_ERROR, - e.str(), this->Backtrace); + this->GetCMakeInstance()->IssueMessage(MessageType::FATAL_ERROR, e, + this->Backtrace); } return false; } @@ -4964,7 +4986,7 @@ bool cmMakefile::AddRequiredTargetCxxFeature(cmTarget* target, // Ensure the C++ language level is high enough to support // the needed C++ features. if (!existingCxxLevel || existingCxxLevel < needCxxLevel) { - target->SetProperty("CXX_STANDARD", *needCxxLevel); + target->SetProperty(cmStrCat(lang, "_STANDARD"), *needCxxLevel); } // Ensure the CUDA language level is high enough to support @@ -4978,21 +5000,21 @@ bool cmMakefile::AddRequiredTargetCxxFeature(cmTarget* target, } void cmMakefile::CheckNeededCLanguage(const std::string& feature, - bool& needC90, bool& needC99, - bool& needC11) const + std::string const& lang, bool& needC90, + bool& needC99, bool& needC11) const { if (const char* propC90 = - this->GetDefinition("CMAKE_C90_COMPILE_FEATURES")) { + this->GetDefinition(cmStrCat("CMAKE_", lang, "90_COMPILE_FEATURES"))) { std::vector<std::string> props = cmExpandedList(propC90); needC90 = cmContains(props, feature); } if (const char* propC99 = - this->GetDefinition("CMAKE_C99_COMPILE_FEATURES")) { + this->GetDefinition(cmStrCat("CMAKE_", lang, "99_COMPILE_FEATURES"))) { std::vector<std::string> props = cmExpandedList(propC99); needC99 = cmContains(props, feature); } if (const char* propC11 = - this->GetDefinition("CMAKE_C11_COMPILE_FEATURES")) { + this->GetDefinition(cmStrCat("CMAKE_", lang, "11_COMPILE_FEATURES"))) { std::vector<std::string> props = cmExpandedList(propC11); needC11 = cmContains(props, feature); } @@ -5000,18 +5022,20 @@ void cmMakefile::CheckNeededCLanguage(const std::string& feature, bool cmMakefile::AddRequiredTargetCFeature(cmTarget* target, const std::string& feature, + std::string const& lang, std::string* error) const { bool needC90 = false; bool needC99 = false; bool needC11 = false; - this->CheckNeededCLanguage(feature, needC90, needC99, needC11); + this->CheckNeededCLanguage(feature, lang, needC90, needC99, needC11); - const char* existingCStandard = target->GetProperty("C_STANDARD"); + const char* existingCStandard = + target->GetProperty(cmStrCat(lang, "_STANDARD")); if (existingCStandard == nullptr) { const char* defaultCStandard = - this->GetDefinition("CMAKE_C_STANDARD_DEFAULT"); + this->GetDefinition(cmStrCat("CMAKE_", lang, "_STANDARD_DEFAULT")); if (defaultCStandard && *defaultCStandard) { existingCStandard = defaultCStandard; } @@ -5019,14 +5043,14 @@ bool cmMakefile::AddRequiredTargetCFeature(cmTarget* target, if (existingCStandard) { if (std::find_if(cm::cbegin(C_STANDARDS), cm::cend(C_STANDARDS), cmStrCmp(existingCStandard)) == cm::cend(C_STANDARDS)) { - std::ostringstream e; - e << "The C_STANDARD property on target \"" << target->GetName() - << "\" contained an invalid value: \"" << existingCStandard << "\"."; + const std::string e = cmStrCat( + "The ", lang, "_STANDARD property on target \"", target->GetName(), + "\" contained an invalid value: \"", existingCStandard, "\"."); if (error) { - *error = e.str(); + *error = e; } else { - this->GetCMakeInstance()->IssueMessage(MessageType::FATAL_ERROR, - e.str(), this->Backtrace); + this->GetCMakeInstance()->IssueMessage(MessageType::FATAL_ERROR, e, + this->Backtrace); } return false; } @@ -5057,11 +5081,11 @@ bool cmMakefile::AddRequiredTargetCFeature(cmTarget* target, } if (setC11) { - target->SetProperty("C_STANDARD", "11"); + target->SetProperty(cmStrCat(lang, "_STANDARD"), "11"); } else if (setC99) { - target->SetProperty("C_STANDARD", "99"); + target->SetProperty(cmStrCat(lang, "_STANDARD"), "99"); } else if (setC90) { - target->SetProperty("C_STANDARD", "90"); + target->SetProperty(cmStrCat(lang, "_STANDARD"), "90"); } return true; } diff --git a/Source/cmMakefile.h b/Source/cmMakefile.h index db37477..3a17aaf 100644 --- a/Source/cmMakefile.h +++ b/Source/cmMakefile.h @@ -20,6 +20,7 @@ #include <cm/string_view> #include "cmAlgorithms.h" +#include "cmCustomCommandTypes.h" #include "cmListFileCache.h" #include "cmMessageType.h" #include "cmNewLineStyle.h" @@ -28,7 +29,10 @@ #include "cmStateSnapshot.h" #include "cmStateTypes.h" #include "cmStringAlgorithms.h" -#include "cmTarget.h" + +// IWYU does not see that 'std::unordered_map<std::string, cmTarget>' +// will not compile without the complete type. +#include "cmTarget.h" // IWYU pragma: keep #if !defined(CMAKE_BOOTSTRAP) # include "cmSourceGroup.h" @@ -164,22 +168,21 @@ public: */ void FinalPass(); - /** How to handle custom commands for object libraries */ - enum ObjectLibraryCommands - { - RejectObjectLibraryCommands, - AcceptObjectLibraryCommands - }; + /** + * Get the target for PRE_BUILD, PRE_LINK, or POST_BUILD commands. + */ + cmTarget* GetCustomCommandTarget( + const std::string& target, cmObjectLibraryCommands objLibCommands) const; /** Add a custom command to the build. */ cmTarget* AddCustomCommandToTarget( const std::string& target, const std::vector<std::string>& byproducts, const std::vector<std::string>& depends, - const cmCustomCommandLines& commandLines, cmTarget::CustomCommandType type, + const cmCustomCommandLines& commandLines, cmCustomCommandType type, const char* comment, const char* workingDir, bool escapeOldStyle = true, bool uses_terminal = false, const std::string& depfile = "", const std::string& job_pool = "", bool command_expand_lists = false, - ObjectLibraryCommands objLibraryCommands = RejectObjectLibraryCommands); + cmObjectLibraryCommands objLibCommands = cmObjectLibraryCommands::Reject); cmSourceFile* AddCustomCommandToOutput( const std::string& output, const std::vector<std::string>& depends, const std::string& main_dependency, @@ -209,6 +212,19 @@ public: const cmCustomCommandLines& commandLines); /** + * Add target byproducts. + */ + void AddTargetByproducts(cmTarget* target, + const std::vector<std::string>& byproducts); + + /** + * Add source file outputs. + */ + void AddSourceOutputs(cmSourceFile* source, + const std::vector<std::string>& outputs, + const std::vector<std::string>& byproducts); + + /** * Add a define flag to the build. */ void AddDefineFlag(std::string const& definition); @@ -225,6 +241,10 @@ public: cmTarget* AddNewTarget(cmStateEnums::TargetType type, const std::string& name); + /** Create a target instance for the utility. */ + cmTarget* AddNewUtilityTarget(const std::string& utilityName, + cmCommandOrigin origin, bool excludeFromAll); + /** * Add an executable to the build. */ @@ -232,26 +252,19 @@ public: const std::vector<std::string>& srcs, bool excludeFromAll = false); - /** Where the target originated from. */ - enum class TargetOrigin - { - Project, - Generator - }; + /** + * Return the utility target output source file name and the CMP0049 name. + */ + cmUtilityOutput GetUtilityOutput(cmTarget* target); /** * Add a utility to the build. A utility target is a command that * is run every time the target is built. */ cmTarget* AddUtilityCommand( - const std::string& utilityName, TargetOrigin origin, bool excludeFromAll, - const char* workingDirectory, const std::vector<std::string>& depends, - const cmCustomCommandLines& commandLines, bool escapeOldStyle = true, - const char* comment = nullptr, bool uses_terminal = false, - bool command_expand_lists = false, const std::string& job_pool = ""); - cmTarget* AddUtilityCommand( - const std::string& utilityName, TargetOrigin origin, bool excludeFromAll, - const char* workingDirectory, const std::vector<std::string>& byproducts, + const std::string& utilityName, cmCommandOrigin origin, + bool excludeFromAll, const char* workingDirectory, + const std::vector<std::string>& byproducts, const std::vector<std::string>& depends, const cmCustomCommandLines& commandLines, bool escapeOldStyle = true, const char* comment = nullptr, bool uses_terminal = false, @@ -455,6 +468,12 @@ public: const std::string& sourceName, bool generated = false, cmSourceFileLocationKind kind = cmSourceFileLocationKind::Ambiguous); + /** Get a cmSourceFile pointer for a given source name and always mark the + * file as generated, if the name is not found, then create the source file + * and return it. + */ + cmSourceFile* GetOrCreateGeneratedSource(const std::string& sourceName); + void AddTargetObject(std::string const& tgtName, std::string const& objFile); /** @@ -1036,13 +1055,12 @@ private: friend bool cmCMakePolicyCommand(std::vector<std::string> const& args, cmExecutionStatus& status); class IncludeScope; - friend class IncludeScope; - class ListFileScope; + class ListFileScope; friend class ListFileScope; - class BuildsystemFileScope; + class BuildsystemFileScope; friend class BuildsystemFileScope; // CMP0053 == old @@ -1061,10 +1079,12 @@ private: bool ValidateCustomCommand(const cmCustomCommandLines& commandLines) const; + void CreateGeneratedSources(const std::vector<std::string>& outputs); + void CommitCustomCommandToTarget( cmTarget* target, const std::vector<std::string>& byproducts, const std::vector<std::string>& depends, - const cmCustomCommandLines& commandLines, cmTarget::CustomCommandType type, + const cmCustomCommandLines& commandLines, cmCustomCommandType type, const char* comment, const char* workingDir, bool escapeOldStyle, bool uses_terminal, const std::string& depfile, const std::string& job_pool, bool command_expand_lists); @@ -1083,8 +1103,7 @@ private: const cmImplicitDependsList& implicit_depends, const cmCustomCommandLines& commandLines); - void CommitUtilityCommand(cmTarget* target, const std::string& force, - const std::string& forceCMP0049, + void CommitUtilityCommand(cmTarget* target, const cmUtilityOutput& force, const char* workingDirectory, const std::vector<std::string>& byproducts, const std::vector<std::string>& depends, @@ -1093,9 +1112,6 @@ private: bool uses_terminal, bool command_expand_lists, const std::string& job_pool); - void CreateGeneratedSource(const std::string& output); - void CreateGeneratedSources(const std::vector<std::string>& outputs); - /** * See LinearGetSourceFileWithOutput for background information */ @@ -1120,12 +1136,7 @@ private: using OutputToSourceMap = std::unordered_map<std::string, SourceEntry>; OutputToSourceMap OutputToSource; - void UpdateOutputToSourceMap(std::vector<std::string> const& byproducts, - cmTarget* target); void UpdateOutputToSourceMap(std::string const& byproduct, cmTarget* target); - - void UpdateOutputToSourceMap(std::vector<std::string> const& outputs, - cmSourceFile* source, bool byproduct); void UpdateOutputToSourceMap(std::string const& output, cmSourceFile* source, bool byproduct); @@ -1135,22 +1146,28 @@ private: bool MightHaveCustomCommand(const std::string& name) const; bool AddRequiredTargetCFeature(cmTarget* target, const std::string& feature, + std::string const& lang, std::string* error = nullptr) const; bool AddRequiredTargetCxxFeature(cmTarget* target, const std::string& feature, + std::string const& lang, std::string* error = nullptr) const; - void CheckNeededCLanguage(const std::string& feature, bool& needC90, + void CheckNeededCLanguage(const std::string& feature, + std::string const& lang, bool& needC90, bool& needC99, bool& needC11) const; - void CheckNeededCxxLanguage(const std::string& feature, bool& needCxx98, + void CheckNeededCxxLanguage(const std::string& feature, + std::string const& lang, bool& needCxx98, bool& needCxx11, bool& needCxx14, bool& needCxx17, bool& needCxx20) const; bool HaveCStandardAvailable(cmTarget const* target, - const std::string& feature) const; + const std::string& feature, + std::string const& lang) const; bool HaveCxxStandardAvailable(cmTarget const* target, - const std::string& feature) const; + const std::string& feature, + std::string const& lang) const; void CheckForUnusedVariables() const; diff --git a/Source/cmMakefileTargetGenerator.cxx b/Source/cmMakefileTargetGenerator.cxx index 482af22..d352f8e 100644 --- a/Source/cmMakefileTargetGenerator.cxx +++ b/Source/cmMakefileTargetGenerator.cxx @@ -154,11 +154,10 @@ void cmMakefileTargetGenerator::WriteTargetBuildRules() auto evaluatedFiles = [this](const char* prop_value) -> std::vector<std::string> { std::vector<std::string> files; - cmGeneratorExpression ge; - std::unique_ptr<cmCompiledGeneratorExpression> cge = ge.Parse(prop_value); - cmExpandList(cge->Evaluate(this->LocalGenerator, this->ConfigName, false, - this->GeneratorTarget, nullptr, nullptr), - files); + cmExpandList( + cmGeneratorExpression::Evaluate(prop_value, this->LocalGenerator, + this->ConfigName, this->GeneratorTarget), + files); return files; }; diff --git a/Source/cmNinjaTargetGenerator.cxx b/Source/cmNinjaTargetGenerator.cxx index 2ab5adf..613e7aa 100644 --- a/Source/cmNinjaTargetGenerator.cxx +++ b/Source/cmNinjaTargetGenerator.cxx @@ -1317,14 +1317,11 @@ void cmNinjaTargetGenerator::AdditionalCleanFiles() this->GeneratorTarget->GetProperty("ADDITIONAL_CLEAN_FILES")) { cmLocalNinjaGenerator* lg = this->LocalGenerator; std::vector<std::string> cleanFiles; - { - cmGeneratorExpression ge; - auto cge = ge.Parse(prop_value); - cmExpandList(cge->Evaluate( - lg, this->Makefile->GetSafeDefinition("CMAKE_BUILD_TYPE"), - false, this->GeneratorTarget, nullptr, nullptr), - cleanFiles); - } + cmExpandList(cmGeneratorExpression::Evaluate( + prop_value, lg, + this->Makefile->GetSafeDefinition("CMAKE_BUILD_TYPE"), + this->GeneratorTarget), + cleanFiles); std::string const& binaryDir = lg->GetCurrentBinaryDirectory(); cmGlobalNinjaGenerator* gg = lg->GetGlobalNinjaGenerator(); for (std::string const& cleanFile : cleanFiles) { diff --git a/Source/cmProjectCommand.cxx b/Source/cmProjectCommand.cxx index 5fa6a21..373e767 100644 --- a/Source/cmProjectCommand.cxx +++ b/Source/cmProjectCommand.cxx @@ -10,6 +10,7 @@ #include <limits> #include <utility> +#include "cmExecutionStatus.h" #include "cmMakefile.h" #include "cmMessageType.h" #include "cmPolicies.h" @@ -17,40 +18,39 @@ #include "cmStringAlgorithms.h" #include "cmSystemTools.h" -class cmExecutionStatus; +static bool IncludeByVariable(cmExecutionStatus& status, + const std::string& variable); +static void TopLevelCMakeVarCondSet(cmMakefile& mf, std::string const& name, + std::string const& value); -// cmProjectCommand -bool cmProjectCommand::InitialPass(std::vector<std::string> const& args, - cmExecutionStatus&) +bool cmProjectCommand(std::vector<std::string> const& args, + cmExecutionStatus& status) { if (args.empty()) { - this->SetError("PROJECT called with incorrect number of arguments"); + status.SetError("PROJECT called with incorrect number of arguments"); return false; } - if (!this->IncludeByVariable("CMAKE_PROJECT_INCLUDE_BEFORE")) { + cmMakefile& mf = status.GetMakefile(); + if (!IncludeByVariable(status, "CMAKE_PROJECT_INCLUDE_BEFORE")) { return false; } std::string const& projectName = args[0]; - this->Makefile->SetProjectName(projectName); + mf.SetProjectName(projectName); - this->Makefile->AddCacheDefinition( - projectName + "_BINARY_DIR", - this->Makefile->GetCurrentBinaryDirectory().c_str(), - "Value Computed by CMake", cmStateEnums::STATIC); - this->Makefile->AddCacheDefinition( - projectName + "_SOURCE_DIR", - this->Makefile->GetCurrentSourceDirectory().c_str(), - "Value Computed by CMake", cmStateEnums::STATIC); + mf.AddCacheDefinition(projectName + "_BINARY_DIR", + mf.GetCurrentBinaryDirectory().c_str(), + "Value Computed by CMake", cmStateEnums::STATIC); + mf.AddCacheDefinition(projectName + "_SOURCE_DIR", + mf.GetCurrentSourceDirectory().c_str(), + "Value Computed by CMake", cmStateEnums::STATIC); - this->Makefile->AddDefinition("PROJECT_BINARY_DIR", - this->Makefile->GetCurrentBinaryDirectory()); - this->Makefile->AddDefinition("PROJECT_SOURCE_DIR", - this->Makefile->GetCurrentSourceDirectory()); + mf.AddDefinition("PROJECT_BINARY_DIR", mf.GetCurrentBinaryDirectory()); + mf.AddDefinition("PROJECT_SOURCE_DIR", mf.GetCurrentSourceDirectory()); - this->Makefile->AddDefinition("PROJECT_NAME", projectName); + mf.AddDefinition("PROJECT_NAME", projectName); // Set the CMAKE_PROJECT_NAME variable to be the highest-level // project name in the tree. If there are two project commands @@ -58,12 +58,10 @@ bool cmProjectCommand::InitialPass(std::vector<std::string> const& args, // CMakeLists.txt file, then go with the last one, so that // CMAKE_PROJECT_NAME will match PROJECT_NAME, and cmake --build // will work. - if (!this->Makefile->GetDefinition("CMAKE_PROJECT_NAME") || - (this->Makefile->IsRootMakefile())) { - this->Makefile->AddDefinition("CMAKE_PROJECT_NAME", projectName); - this->Makefile->AddCacheDefinition( - "CMAKE_PROJECT_NAME", projectName.c_str(), "Value Computed by CMake", - cmStateEnums::STATIC); + if (!mf.GetDefinition("CMAKE_PROJECT_NAME") || mf.IsRootMakefile()) { + mf.AddDefinition("CMAKE_PROJECT_NAME", projectName); + mf.AddCacheDefinition("CMAKE_PROJECT_NAME", projectName.c_str(), + "Value Computed by CMake", cmStateEnums::STATIC); } bool haveVersion = false; @@ -90,9 +88,8 @@ bool cmProjectCommand::InitialPass(std::vector<std::string> const& args, for (size_t i = 1; i < args.size(); ++i) { if (args[i] == "LANGUAGES") { if (haveLanguages) { - this->Makefile->IssueMessage( - MessageType::FATAL_ERROR, - "LANGUAGES may be specified at most once."); + mf.IssueMessage(MessageType::FATAL_ERROR, + "LANGUAGES may be specified at most once."); cmSystemTools::SetFatalErrorOccured(); return true; } @@ -106,12 +103,12 @@ bool cmProjectCommand::InitialPass(std::vector<std::string> const& args, "the following parameters must be specified after LANGUAGES " "keyword: ", cmJoin(languages, ", "), '.'); - this->Makefile->IssueMessage(MessageType::WARNING, msg); + mf.IssueMessage(MessageType::WARNING, msg); } } else if (args[i] == "VERSION") { if (haveVersion) { - this->Makefile->IssueMessage(MessageType::FATAL_ERROR, - "VERSION may be specified at most once."); + mf.IssueMessage(MessageType::FATAL_ERROR, + "VERSION may be specified at most once."); cmSystemTools::SetFatalErrorOccured(); return true; } @@ -120,8 +117,8 @@ bool cmProjectCommand::InitialPass(std::vector<std::string> const& args, missedValueReporter(); } doing = DoingVersion; - missedValueReporter = [this, &resetReporter]() { - this->Makefile->IssueMessage( + missedValueReporter = [&mf, &resetReporter]() { + mf.IssueMessage( MessageType::WARNING, "VERSION keyword not followed by a value or was followed by a " "value that expanded to nothing."); @@ -129,9 +126,8 @@ bool cmProjectCommand::InitialPass(std::vector<std::string> const& args, }; } else if (args[i] == "DESCRIPTION") { if (haveDescription) { - this->Makefile->IssueMessage( - MessageType::FATAL_ERROR, - "DESCRIPTION may be specified at most once."); + mf.IssueMessage(MessageType::FATAL_ERROR, + "DESCRIPTION may be specified at most once."); cmSystemTools::SetFatalErrorOccured(); return true; } @@ -140,8 +136,8 @@ bool cmProjectCommand::InitialPass(std::vector<std::string> const& args, missedValueReporter(); } doing = DoingDescription; - missedValueReporter = [this, &resetReporter]() { - this->Makefile->IssueMessage( + missedValueReporter = [&mf, &resetReporter]() { + mf.IssueMessage( MessageType::WARNING, "DESCRIPTION keyword not followed by a value or was followed " "by a value that expanded to nothing."); @@ -149,16 +145,15 @@ bool cmProjectCommand::InitialPass(std::vector<std::string> const& args, }; } else if (args[i] == "HOMEPAGE_URL") { if (haveHomepage) { - this->Makefile->IssueMessage( - MessageType::FATAL_ERROR, - "HOMEPAGE_URL may be specified at most once."); + mf.IssueMessage(MessageType::FATAL_ERROR, + "HOMEPAGE_URL may be specified at most once."); cmSystemTools::SetFatalErrorOccured(); return true; } haveHomepage = true; doing = DoingHomepage; - missedValueReporter = [this, &resetReporter]() { - this->Makefile->IssueMessage( + missedValueReporter = [&mf, &resetReporter]() { + mf.IssueMessage( MessageType::WARNING, "HOMEPAGE_URL keyword not followed by a value or was followed " "by a value that expanded to nothing."); @@ -190,10 +185,9 @@ bool cmProjectCommand::InitialPass(std::vector<std::string> const& args, if ((haveVersion || haveDescription || haveHomepage) && !haveLanguages && !languages.empty()) { - this->Makefile->IssueMessage( - MessageType::FATAL_ERROR, - "project with VERSION, DESCRIPTION or HOMEPAGE_URL must " - "use LANGUAGES before language names."); + mf.IssueMessage(MessageType::FATAL_ERROR, + "project with VERSION, DESCRIPTION or HOMEPAGE_URL must " + "use LANGUAGES before language names."); cmSystemTools::SetFatalErrorOccured(); return true; } @@ -202,13 +196,12 @@ bool cmProjectCommand::InitialPass(std::vector<std::string> const& args, } cmPolicies::PolicyStatus const cmp0048 = - this->Makefile->GetPolicyStatus(cmPolicies::CMP0048); + mf.GetPolicyStatus(cmPolicies::CMP0048); if (haveVersion) { // Set project VERSION variables to given values if (cmp0048 == cmPolicies::OLD || cmp0048 == cmPolicies::WARN) { - this->Makefile->IssueMessage( - MessageType::FATAL_ERROR, - "VERSION not allowed unless CMP0048 is set to NEW"); + mf.IssueMessage(MessageType::FATAL_ERROR, + "VERSION not allowed unless CMP0048 is set to NEW"); cmSystemTools::SetFatalErrorOccured(); return true; } @@ -217,13 +210,13 @@ bool cmProjectCommand::InitialPass(std::vector<std::string> const& args, R"(^([0-9]+(\.[0-9]+(\.[0-9]+(\.[0-9]+)?)?)?)?$)"); if (!vx.find(version)) { std::string e = R"(VERSION ")" + version + R"(" format invalid.)"; - this->Makefile->IssueMessage(MessageType::FATAL_ERROR, e); + mf.IssueMessage(MessageType::FATAL_ERROR, e); cmSystemTools::SetFatalErrorOccured(); return true; } cmPolicies::PolicyStatus const cmp0096 = - this->Makefile->GetPolicyStatus(cmPolicies::CMP0096); + mf.GetPolicyStatus(cmPolicies::CMP0096); constexpr std::size_t MAX_VERSION_COMPONENTS = 4u; std::string version_string; @@ -257,34 +250,30 @@ bool cmProjectCommand::InitialPass(std::vector<std::string> const& args, std::string vv; vv = projectName + "_VERSION"; - this->Makefile->AddDefinition("PROJECT_VERSION", version_string); - this->Makefile->AddDefinition(vv, version_string); + mf.AddDefinition("PROJECT_VERSION", version_string); + mf.AddDefinition(vv, version_string); vv = projectName + "_VERSION_MAJOR"; - this->Makefile->AddDefinition("PROJECT_VERSION_MAJOR", - version_components[0]); - this->Makefile->AddDefinition(vv, version_components[0]); + mf.AddDefinition("PROJECT_VERSION_MAJOR", version_components[0]); + mf.AddDefinition(vv, version_components[0]); vv = projectName + "_VERSION_MINOR"; - this->Makefile->AddDefinition("PROJECT_VERSION_MINOR", - version_components[1]); - this->Makefile->AddDefinition(vv, version_components[1]); + mf.AddDefinition("PROJECT_VERSION_MINOR", version_components[1]); + mf.AddDefinition(vv, version_components[1]); vv = projectName + "_VERSION_PATCH"; - this->Makefile->AddDefinition("PROJECT_VERSION_PATCH", - version_components[2]); - this->Makefile->AddDefinition(vv, version_components[2]); + mf.AddDefinition("PROJECT_VERSION_PATCH", version_components[2]); + mf.AddDefinition(vv, version_components[2]); vv = projectName + "_VERSION_TWEAK"; - this->Makefile->AddDefinition("PROJECT_VERSION_TWEAK", - version_components[3]); - this->Makefile->AddDefinition(vv, version_components[3]); + mf.AddDefinition("PROJECT_VERSION_TWEAK", version_components[3]); + mf.AddDefinition(vv, version_components[3]); // Also, try set top level variables - TopLevelCMakeVarCondSet("CMAKE_PROJECT_VERSION", version_string.c_str()); - TopLevelCMakeVarCondSet("CMAKE_PROJECT_VERSION_MAJOR", - version_components[0].c_str()); - TopLevelCMakeVarCondSet("CMAKE_PROJECT_VERSION_MINOR", - version_components[1].c_str()); - TopLevelCMakeVarCondSet("CMAKE_PROJECT_VERSION_PATCH", - version_components[2].c_str()); - TopLevelCMakeVarCondSet("CMAKE_PROJECT_VERSION_TWEAK", - version_components[3].c_str()); + TopLevelCMakeVarCondSet(mf, "CMAKE_PROJECT_VERSION", version_string); + TopLevelCMakeVarCondSet(mf, "CMAKE_PROJECT_VERSION_MAJOR", + version_components[0]); + TopLevelCMakeVarCondSet(mf, "CMAKE_PROJECT_VERSION_MINOR", + version_components[1]); + TopLevelCMakeVarCondSet(mf, "CMAKE_PROJECT_VERSION_PATCH", + version_components[2]); + TopLevelCMakeVarCondSet(mf, "CMAKE_PROJECT_VERSION_TWEAK", + version_components[3]); } else if (cmp0048 != cmPolicies::OLD) { // Set project VERSION variables to empty std::vector<std::string> vv = { "PROJECT_VERSION", @@ -297,7 +286,7 @@ bool cmProjectCommand::InitialPass(std::vector<std::string> const& args, projectName + "_VERSION_MINOR", projectName + "_VERSION_PATCH", projectName + "_VERSION_TWEAK" }; - if (this->Makefile->IsRootMakefile()) { + if (mf.IsRootMakefile()) { vv.emplace_back("CMAKE_PROJECT_VERSION"); vv.emplace_back("CMAKE_PROJECT_VERSION_MAJOR"); vv.emplace_back("CMAKE_PROJECT_VERSION_MINOR"); @@ -306,7 +295,7 @@ bool cmProjectCommand::InitialPass(std::vector<std::string> const& args, } std::string vw; for (std::string const& i : vv) { - const char* const v = this->Makefile->GetDefinition(i); + const char* const v = mf.GetDefinition(i); if (v && *v) { if (cmp0048 == cmPolicies::WARN) { if (!injectedProjectCommand) { @@ -314,51 +303,54 @@ bool cmProjectCommand::InitialPass(std::vector<std::string> const& args, vw += i; } } else { - this->Makefile->AddDefinition(i, ""); + mf.AddDefinition(i, ""); } } } if (!vw.empty()) { - this->Makefile->IssueMessage( + mf.IssueMessage( MessageType::AUTHOR_WARNING, cmStrCat(cmPolicies::GetPolicyWarning(cmPolicies::CMP0048), "\nThe following variable(s) would be set to empty:", vw)); } } - this->Makefile->AddDefinition("PROJECT_DESCRIPTION", description); - this->Makefile->AddDefinition(projectName + "_DESCRIPTION", description); - TopLevelCMakeVarCondSet("CMAKE_PROJECT_DESCRIPTION", description.c_str()); + mf.AddDefinition("PROJECT_DESCRIPTION", description); + mf.AddDefinition(projectName + "_DESCRIPTION", description); + TopLevelCMakeVarCondSet(mf, "CMAKE_PROJECT_DESCRIPTION", description); - this->Makefile->AddDefinition("PROJECT_HOMEPAGE_URL", homepage); - this->Makefile->AddDefinition(projectName + "_HOMEPAGE_URL", homepage); - TopLevelCMakeVarCondSet("CMAKE_PROJECT_HOMEPAGE_URL", homepage.c_str()); + mf.AddDefinition("PROJECT_HOMEPAGE_URL", homepage); + mf.AddDefinition(projectName + "_HOMEPAGE_URL", homepage); + TopLevelCMakeVarCondSet(mf, "CMAKE_PROJECT_HOMEPAGE_URL", homepage); if (languages.empty()) { // if no language is specified do c and c++ languages = { "C", "CXX" }; } - this->Makefile->EnableLanguage(languages, false); + mf.EnableLanguage(languages, false); - if (!this->IncludeByVariable("CMAKE_PROJECT_INCLUDE")) { + if (!IncludeByVariable(status, "CMAKE_PROJECT_INCLUDE")) { return false; } - if (!this->IncludeByVariable("CMAKE_PROJECT_" + projectName + "_INCLUDE")) { + if (!IncludeByVariable(status, + "CMAKE_PROJECT_" + projectName + "_INCLUDE")) { return false; } return true; } -bool cmProjectCommand::IncludeByVariable(const std::string& variable) +static bool IncludeByVariable(cmExecutionStatus& status, + const std::string& variable) { - const char* const include = this->Makefile->GetDefinition(variable); + cmMakefile& mf = status.GetMakefile(); + const char* const include = mf.GetDefinition(variable); if (!include) { return true; } - const bool readit = this->Makefile->ReadDependentFile(include); + const bool readit = mf.ReadDependentFile(include); if (readit) { return true; } @@ -367,24 +359,20 @@ bool cmProjectCommand::IncludeByVariable(const std::string& variable) return true; } - std::string m = cmStrCat("could not find file:\n" - " ", - include); - this->SetError(m); + status.SetError(cmStrCat("could not find file:\n ", include)); return false; } -void cmProjectCommand::TopLevelCMakeVarCondSet(const char* const name, - const char* const value) +static void TopLevelCMakeVarCondSet(cmMakefile& mf, std::string const& name, + std::string const& value) { // Set the CMAKE_PROJECT_XXX variable to be the highest-level // project name in the tree. If there are two project commands // in the same CMakeLists.txt file, and it is the top level // CMakeLists.txt file, then go with the last one. - if (!this->Makefile->GetDefinition(name) || - (this->Makefile->IsRootMakefile())) { - this->Makefile->AddDefinition(name, value); - this->Makefile->AddCacheDefinition(name, value, "Value Computed by CMake", - cmStateEnums::STATIC); + if (!mf.GetDefinition(name) || mf.IsRootMakefile()) { + mf.AddDefinition(name, value); + mf.AddCacheDefinition(name, value.c_str(), "Value Computed by CMake", + cmStateEnums::STATIC); } } diff --git a/Source/cmProjectCommand.h b/Source/cmProjectCommand.h index ffbd330..c06b459 100644 --- a/Source/cmProjectCommand.h +++ b/Source/cmProjectCommand.h @@ -8,41 +8,9 @@ #include <string> #include <vector> -#include <cm/memory> - -#include "cmCommand.h" - class cmExecutionStatus; -/** \class cmProjectCommand - * \brief Specify the name for this build project. - * - * cmProjectCommand is used to specify a name for this build project. - * It is defined once per set of CMakeList.txt files (including - * all subdirectories). Currently it just sets the name of the workspace - * file for Microsoft Visual C++ - */ -class cmProjectCommand : public cmCommand -{ -public: - /** - * This is a virtual constructor for the command. - */ - std::unique_ptr<cmCommand> Clone() override - { - return cm::make_unique<cmProjectCommand>(); - } - - /** - * This is called when the command is first encountered in - * the CMakeLists.txt file. - */ - bool InitialPass(std::vector<std::string> const& args, - cmExecutionStatus& status) override; - -private: - bool IncludeByVariable(const std::string& variable); - void TopLevelCMakeVarCondSet(const char* name, const char* value); -}; +bool cmProjectCommand(std::vector<std::string> const& args, + cmExecutionStatus& status); #endif diff --git a/Source/cmQtAutoGen.cxx b/Source/cmQtAutoGen.cxx index 57c8825..0f0e864 100644 --- a/Source/cmQtAutoGen.cxx +++ b/Source/cmQtAutoGen.cxx @@ -72,7 +72,6 @@ void MergeOptions(std::vector<std::string>& baseOpts, // - Class definitions unsigned int const cmQtAutoGen::ParallelMax = 64; -std::string const cmQtAutoGen::ListSep = "<<<S>>>"; cm::string_view cmQtAutoGen::GeneratorName(GenT genType) { @@ -162,6 +161,16 @@ std::string cmQtAutoGen::QuotedCommand(std::vector<std::string> const& command) return res; } +std::string cmQtAutoGen::FileNameWithoutLastExtension(cm::string_view filename) +{ + auto slashPos = filename.rfind('/'); + if (slashPos != cm::string_view::npos) { + filename.remove_prefix(slashPos + 1); + } + auto dotPos = filename.rfind('.'); + return std::string(filename.substr(0, dotPos)); +} + std::string cmQtAutoGen::ParentDir(cm::string_view filename) { auto slashPos = filename.rfind('/'); diff --git a/Source/cmQtAutoGen.h b/Source/cmQtAutoGen.h index 2a28c1e..d070b79 100644 --- a/Source/cmQtAutoGen.h +++ b/Source/cmQtAutoGen.h @@ -17,7 +17,7 @@ class cmQtAutoGen { public: - /// @brief Integer version + /** Integer version. */ struct IntegerVersion { unsigned int Major = 0; @@ -43,6 +43,7 @@ public: } }; + /** Compiler features. */ class CompilerFeatures { public: @@ -52,7 +53,7 @@ public: }; using CompilerFeaturesHandle = std::shared_ptr<CompilerFeatures>; - /// @brief AutoGen generator type + /** AutoGen generator type. */ enum class GenT { GEN, // AUTOGEN @@ -61,8 +62,6 @@ public: RCC // AUTORCC }; - /// @brief Nested lists separator - static std::string const ListSep; /// @brief Maximum number of parallel threads/processes in a generator static unsigned int const ParallelMax; @@ -80,6 +79,9 @@ public: static std::string QuotedCommand(std::vector<std::string> const& command); + /// @brief Returns the file name without path and extension (thread safe) + static std::string FileNameWithoutLastExtension(cm::string_view filename); + /// @brief Returns the parent directory of the file (thread safe) static std::string ParentDir(cm::string_view filename); diff --git a/Source/cmQtAutoGenGlobalInitializer.cxx b/Source/cmQtAutoGenGlobalInitializer.cxx index 576a034..751ad50 100644 --- a/Source/cmQtAutoGenGlobalInitializer.cxx +++ b/Source/cmQtAutoGenGlobalInitializer.cxx @@ -3,6 +3,7 @@ #include "cmQtAutoGenGlobalInitializer.h" #include "cmCustomCommandLines.h" +#include "cmCustomCommandTypes.h" #include "cmDuration.h" #include "cmGeneratorTarget.h" #include "cmLocalGenerator.h" @@ -154,7 +155,7 @@ void cmQtAutoGenGlobalInitializer::GetOrCreateGlobalTarget( // Create utility target cmTarget* target = makefile->AddUtilityCommand( - name, cmMakefile::TargetOrigin::Generator, true, + name, cmCommandOrigin::Generator, true, makefile->GetHomeOutputDirectory().c_str() /*work dir*/, std::vector<std::string>() /*output*/, std::vector<std::string>() /*depends*/, cmCustomCommandLines(), false, diff --git a/Source/cmQtAutoGenInitializer.cxx b/Source/cmQtAutoGenInitializer.cxx index 9045722..d7b9fa2 100644 --- a/Source/cmQtAutoGenInitializer.cxx +++ b/Source/cmQtAutoGenInitializer.cxx @@ -8,7 +8,8 @@ #include "cmAlgorithms.h" #include "cmCustomCommand.h" #include "cmCustomCommandLines.h" -#include "cmFilePathChecksum.h" +#include "cmCustomCommandTypes.h" +#include "cmGeneratedFileStream.h" #include "cmGeneratorExpression.h" #include "cmGeneratorTarget.h" #include "cmGlobalGenerator.h" @@ -17,7 +18,6 @@ #include "cmLocalGenerator.h" #include "cmMakefile.h" #include "cmMessageType.h" -#include "cmOutputConverter.h" #include "cmPolicies.h" #include "cmSourceFile.h" #include "cmSourceFileLocationKind.h" @@ -27,34 +27,37 @@ #include "cmStringAlgorithms.h" #include "cmSystemTools.h" #include "cmTarget.h" +#include "cm_jsoncpp_value.h" +#include "cm_jsoncpp_writer.h" #include "cmake.h" #include "cmsys/SystemInformation.hxx" -#include <algorithm> #include <cstddef> #include <deque> #include <initializer_list> #include <map> +#include <ostream> #include <set> #include <string> #include <unordered_set> #include <utility> #include <vector> +#include <cm/algorithm> +#include <cm/iterator> #include <cm/memory> namespace { -std::size_t GetParallelCPUCount() +unsigned int GetParallelCPUCount() { - static std::size_t count = 0; + static unsigned int count = 0; // Detect only on the first call if (count == 0) { cmsys::SystemInformation info; info.RunCPUCheck(); - count = info.GetNumberOfPhysicalCPU(); - count = std::max<std::size_t>(count, 1); - count = std::min<std::size_t>(count, cmQtAutoGen::ParallelMax); + count = + cm::clamp(info.GetNumberOfPhysicalCPU(), 1u, cmQtAutoGen::ParallelMax); } return count; } @@ -124,7 +127,7 @@ bool StaticLibraryCycle(cmGeneratorTarget const* targetOrigin, return cycle; } -/** Sanitizes file search paths */ +/** Sanitizes file search paths. */ class SearchPathSanitizer { public: @@ -158,99 +161,134 @@ std::vector<std::string> SearchPathSanitizer::operator()( } return res; } -} // End of unnamed namespace -cmQtAutoGenInitializer::InfoWriter::InfoWriter(std::string const& filename) +/** @brief Writes a CMake info file. */ +class InfoWriter { - Ofs_.SetCopyIfDifferent(true); - Ofs_.Open(filename, false, true); -} +public: + // -- Single value + void Set(std::string const& key, std::string const& value) + { + Value_[key] = value; + } + void SetConfig(std::string const& key, + cmQtAutoGenInitializer::ConfigString const& cfgStr); + void SetBool(std::string const& key, bool value) { Value_[key] = value; } + void SetUInt(std::string const& key, unsigned int value) + { + Value_[key] = value; + } + + // -- Array utility + template <typename CONT> + static bool MakeArray(Json::Value& jval, CONT const& container); + + template <typename CONT> + static void MakeStringArray(Json::Value& jval, CONT const& container); + + // -- Array value + template <typename CONT> + void SetArray(std::string const& key, CONT const& container); + template <typename CONT> + void SetConfigArray( + std::string const& key, + cmQtAutoGenInitializer::ConfigStrings<CONT> const& cfgStr); + + // -- Array of arrays + template <typename CONT, typename FUNC> + void SetArrayArray(std::string const& key, CONT const& container, FUNC func); -template <class IT> -std::string cmQtAutoGenInitializer::InfoWriter::ListJoin(IT it_begin, - IT it_end) + // -- Save to json file + bool Save(std::string const& filename); + +private: + Json::Value Value_; +}; + +void InfoWriter::SetConfig(std::string const& key, + cmQtAutoGenInitializer::ConfigString const& cfgStr) { - std::string res; - for (IT it = it_begin; it != it_end; ++it) { - if (it != it_begin) { - res += ';'; - } - for (const char* c = it->c_str(); *c; ++c) { - if (*c == '"') { - // Escape the double quote to avoid ending the argument. - res += "\\\""; - } else if (*c == '$') { - // Escape the dollar to avoid expanding variables. - res += "\\$"; - } else if (*c == '\\') { - // Escape the backslash to avoid other escapes. - res += "\\\\"; - } else if (*c == ';') { - // Escape the semicolon to avoid list expansion. - res += "\\;"; - } else { - // Other characters will be parsed correctly. - res += *c; - } - } + Set(key, cfgStr.Default); + for (auto const& item : cfgStr.Config) { + Set(cmStrCat(key, '_', item.first), item.second); } - return res; } -inline std::string cmQtAutoGenInitializer::InfoWriter::ConfigKey( - cm::string_view key, std::string const& config) +template <typename CONT> +bool InfoWriter::MakeArray(Json::Value& jval, CONT const& container) { - return cmStrCat(key, "_", config); + jval = Json::arrayValue; + std::size_t const listSize = cm::size(container); + if (listSize == 0) { + return false; + } + jval.resize(static_cast<unsigned int>(listSize)); + return true; } -void cmQtAutoGenInitializer::InfoWriter::Write(cm::string_view key, - std::string const& value) -{ - Ofs_ << "set(" << key << " " << cmOutputConverter::EscapeForCMake(value) - << ")\n"; -}; - -void cmQtAutoGenInitializer::InfoWriter::WriteUInt(cm::string_view key, - unsigned int value) +template <typename CONT> +void InfoWriter::MakeStringArray(Json::Value& jval, CONT const& container) { - Ofs_ << "set(" << key << " " << value << ")\n"; -}; + if (MakeArray(jval, container)) { + Json::ArrayIndex ii = 0; + for (std::string const& item : container) { + jval[ii++] = item; + } + } +} -template <class C> -void cmQtAutoGenInitializer::InfoWriter::WriteStrings(cm::string_view key, - C const& container) +template <typename CONT> +void InfoWriter::SetArray(std::string const& key, CONT const& container) { - Ofs_ << "set(" << key << " \"" - << ListJoin(container.begin(), container.end()) << "\")\n"; + MakeStringArray(Value_[key], container); } -void cmQtAutoGenInitializer::InfoWriter::WriteConfig( - cm::string_view key, std::map<std::string, std::string> const& map) +template <typename CONT, typename FUNC> +void InfoWriter::SetArrayArray(std::string const& key, CONT const& container, + FUNC func) { - for (auto const& item : map) { - Write(ConfigKey(key, item.first), item.second); + Json::Value& jval = Value_[key]; + if (MakeArray(jval, container)) { + Json::ArrayIndex ii = 0; + for (auto const& citem : container) { + Json::Value& aval = jval[ii++]; + aval = Json::arrayValue; + func(aval, citem); + } } -}; +} -template <class C> -void cmQtAutoGenInitializer::InfoWriter::WriteConfigStrings( - cm::string_view key, std::map<std::string, C> const& map) +template <typename CONT> +void InfoWriter::SetConfigArray( + std::string const& key, + cmQtAutoGenInitializer::ConfigStrings<CONT> const& cfgStr) { - for (auto const& item : map) { - WriteStrings(ConfigKey(key, item.first), item.second); + SetArray(key, cfgStr.Default); + for (auto const& item : cfgStr.Config) { + SetArray(cmStrCat(key, '_', item.first), item.second); } } -void cmQtAutoGenInitializer::InfoWriter::WriteNestedLists( - cm::string_view key, std::vector<std::vector<std::string>> const& lists) +bool InfoWriter::Save(std::string const& filename) { - std::vector<std::string> seplist; - seplist.reserve(lists.size()); - for (std::vector<std::string> const& list : lists) { - seplist.push_back(cmStrCat("{", ListJoin(list.begin(), list.end()), "}")); + cmGeneratedFileStream fileStream; + fileStream.SetCopyIfDifferent(true); + fileStream.Open(filename, false, true); + if (!fileStream) { + return false; + } + + Json::StyledStreamWriter jsonWriter; + try { + jsonWriter.write(fileStream, Value_); + } catch (...) { + return false; } - Write(key, cmJoin(seplist, cmQtAutoGen::ListSep)); -}; + + return fileStream.Close(); +} + +} // End of unnamed namespace cmQtAutoGenInitializer::cmQtAutoGenInitializer( cmQtAutoGenGlobalInitializer* globalInitializer, @@ -262,6 +300,7 @@ cmQtAutoGenInitializer::cmQtAutoGenInitializer( , GlobalGen(genTarget->GetGlobalGenerator()) , LocalGen(genTarget->GetLocalGenerator()) , Makefile(genTarget->Makefile) + , PathCheckSum(genTarget->Makefile) , QtVersion(qtVersion) { AutogenTarget.GlobalTarget = globalAutogenTarget; @@ -281,12 +320,20 @@ bool cmQtAutoGenInitializer::InitCustomTargets() } // Verbosity - this->Verbosity = this->Makefile->GetSafeDefinition("CMAKE_AUTOGEN_VERBOSE"); - if (!this->Verbosity.empty()) { - unsigned long iVerb = 0; - if (!cmStrToULong(this->Verbosity, &iVerb)) { - // Non numeric verbosity - this->Verbosity = cmIsOn(this->Verbosity) ? "1" : "0"; + { + std::string def = + this->Makefile->GetSafeDefinition("CMAKE_AUTOGEN_VERBOSE"); + if (!def.empty()) { + unsigned long iVerb = 0; + if (cmStrToULong(def, &iVerb)) { + // Numeric verbosity + this->Verbosity = static_cast<unsigned int>(iVerb); + } else { + // Non numeric verbosity + if (cmIsOn(def)) { + this->Verbosity = 1; + } + } } } @@ -353,16 +400,11 @@ bool cmQtAutoGenInitializer::InitCustomTargets() cmSystemTools::ConvertToUnixSlashes(this->Dir.Work); // Include directory - this->Dir.Include = cmStrCat(this->Dir.Build, "/include"); + ConfigFileNames(this->Dir.Include, cmStrCat(this->Dir.Build, "/include"), + ""); + this->Dir.IncludeGenExp = this->Dir.Include.Default; if (this->MultiConfig) { - this->Dir.Include += "_$<CONFIG>"; - } - // Per config include directories - if (this->MultiConfig) { - for (std::string const& cfg : this->ConfigsList) { - std::string& dir = this->Dir.ConfigInclude[cfg]; - dir = cmStrCat(this->Dir.Build, "/include_", cfg); - } + this->Dir.IncludeGenExp += "_$<CONFIG>"; } } @@ -383,36 +425,31 @@ bool cmQtAutoGenInitializer::InitCustomTargets() cmStrCat(this->GenTarget->GetName(), "_autogen"); // Autogen target parallel processing - this->AutogenTarget.Parallel = - this->GenTarget->GetSafeProperty("AUTOGEN_PARALLEL"); - if (this->AutogenTarget.Parallel.empty() || - (this->AutogenTarget.Parallel == "AUTO")) { - // Autodetect number of CPUs - this->AutogenTarget.Parallel = std::to_string(GetParallelCPUCount()); + { + std::string prop = this->GenTarget->GetSafeProperty("AUTOGEN_PARALLEL"); + if (prop.empty() || (prop == "AUTO")) { + // Autodetect number of CPUs + this->AutogenTarget.Parallel = GetParallelCPUCount(); + } else { + this->AutogenTarget.Parallel = 1; + } } // Autogen target info and settings files { + // Info file this->AutogenTarget.InfoFile = - cmStrCat(this->Dir.Info, "/AutogenInfo.cmake"); - - this->AutogenTarget.SettingsFile = - cmStrCat(this->Dir.Info, "/AutogenOldSettings.txt"); + cmStrCat(this->Dir.Info, "/AutogenInfo.json"); - if (this->MultiConfig) { - for (std::string const& cfg : this->ConfigsList) { - std::string& filename = this->AutogenTarget.ConfigSettingsFile[cfg]; - filename = - AppendFilenameSuffix(this->AutogenTarget.SettingsFile, "_" + cfg); - this->AddCleanFile(filename); - } - } else { - this->AddCleanFile(this->AutogenTarget.SettingsFile); - } + // Used settings file + ConfigFileNames(this->AutogenTarget.SettingsFile, + cmStrCat(this->Dir.Info, "/AutogenUsed"), ".txt"); + ConfigFileClean(this->AutogenTarget.SettingsFile); - this->AutogenTarget.ParseCacheFile = - cmStrCat(this->Dir.Info, "/ParseCache.txt"); - this->AddCleanFile(this->AutogenTarget.ParseCacheFile); + // Parse cache file + ConfigFileNames(this->AutogenTarget.ParseCacheFile, + cmStrCat(this->Dir.Info, "/ParseCache"), ".txt"); + ConfigFileClean(this->AutogenTarget.ParseCacheFile); } // Autogen target: Compute user defined dependencies @@ -435,9 +472,15 @@ bool cmQtAutoGenInitializer::InitCustomTargets() } } - // CMAKE_AUTOMOC_RELAXED_MODE deprecation warning if (this->Moc.Enabled) { + // Path prefix + if (cmIsOn(this->GenTarget->GetSafeProperty("AUTOMOC_PATH_PREFIX"))) { + this->Moc.PathPrefix = true; + } + + // CMAKE_AUTOMOC_RELAXED_MODE if (this->Makefile->IsOn("CMAKE_AUTOMOC_RELAXED_MODE")) { + this->Moc.RelaxedMode = true; this->Makefile->IssueMessage( MessageType::AUTHOR_WARNING, cmStrCat("AUTOMOC: CMAKE_AUTOMOC_RELAXED_MODE is " @@ -445,6 +488,32 @@ bool cmQtAutoGenInitializer::InitCustomTargets() "disabling it and converting the target ", this->GenTarget->GetName(), " to regular mode.")); } + + // Options + cmExpandList(this->GenTarget->GetSafeProperty("AUTOMOC_MOC_OPTIONS"), + this->Moc.Options); + // Filters + cmExpandList(this->GenTarget->GetSafeProperty("AUTOMOC_MACRO_NAMES"), + this->Moc.MacroNames); + { + auto filterList = cmExpandedList( + this->GenTarget->GetSafeProperty("AUTOMOC_DEPEND_FILTERS")); + if ((filterList.size() % 2) != 0) { + cmSystemTools::Error( + cmStrCat("AutoMoc: AUTOMOC_DEPEND_FILTERS predefs size ", + filterList.size(), " is not a multiple of 2.")); + return false; + } + this->Moc.DependFilters.reserve(1 + (filterList.size() / 2)); + this->Moc.DependFilters.emplace_back( + "Q_PLUGIN_METADATA", + "[\n][ \t]*Q_PLUGIN_METADATA[ \t]*\\(" + "[^\\)]*FILE[ \t]*\"([^\"]+)\""); + for (std::size_t ii = 0; ii != filterList.size(); ii += 2) { + this->Moc.DependFilters.emplace_back(filterList[ii], + filterList[ii + 1]); + } + } } } @@ -455,7 +524,7 @@ bool cmQtAutoGenInitializer::InitCustomTargets() // Add autogen include directory to the origin target INCLUDE_DIRECTORIES if (this->MocOrUicEnabled() || (this->Rcc.Enabled && this->MultiConfig)) { - this->GenTarget->AddIncludeDirectory(this->Dir.Include, true); + this->GenTarget->AddIncludeDirectory(this->Dir.IncludeGenExp, true); } // Scan files @@ -479,20 +548,27 @@ bool cmQtAutoGenInitializer::InitCustomTargets() bool cmQtAutoGenInitializer::InitMoc() { // Mocs compilation file - this->Moc.MocsCompilation = + this->Moc.CompilationFile = cmStrCat(this->Dir.Build, "/mocs_compilation.cpp"); - // Moc predefs command + // Moc predefs if (this->GenTarget->GetPropertyAsBool("AUTOMOC_COMPILER_PREDEFINES") && (this->QtVersion >= IntegerVersion(5, 8))) { - this->Moc.PredefsCmd = this->Makefile->GetSafeDefinition( - "CMAKE_CXX_COMPILER_PREDEFINES_COMMAND"); + // Command + cmExpandList(this->Makefile->GetSafeDefinition( + "CMAKE_CXX_COMPILER_PREDEFINES_COMMAND"), + this->Moc.PredefsCmd); + // Header + if (!this->Moc.PredefsCmd.empty()) { + ConfigFileNames(this->Moc.PredefsFile, + cmStrCat(this->Dir.Build, "/moc_predefs"), ".h"); + } } // Moc includes { SearchPathSanitizer sanitizer(this->Makefile); - auto GetIncludeDirs = + auto getDirs = [this, &sanitizer](std::string const& cfg) -> std::vector<std::string> { // Get the include dirs for this target, without stripping the implicit // include dirs off, see issue #13667. @@ -504,22 +580,22 @@ bool cmQtAutoGenInitializer::InitMoc() }; // Default configuration include directories - this->Moc.Includes = GetIncludeDirs(this->ConfigDefault); + this->Moc.Includes.Default = getDirs(this->ConfigDefault); // Other configuration settings if (this->MultiConfig) { for (std::string const& cfg : this->ConfigsList) { - std::vector<std::string> dirs = GetIncludeDirs(cfg); - if (dirs != this->Moc.Includes) { - this->Moc.ConfigIncludes[cfg] = std::move(dirs); + std::vector<std::string> dirs = getDirs(cfg); + if (dirs == this->Moc.Includes.Default) { + continue; } + this->Moc.Includes.Config[cfg] = std::move(dirs); } } } // Moc compile definitions { - auto GetCompileDefinitions = - [this](std::string const& cfg) -> std::set<std::string> { + auto getDefs = [this](std::string const& cfg) -> std::set<std::string> { std::set<std::string> defines; this->LocalGen->GetTargetDefines(this->GenTarget, cfg, "CXX", defines); #ifdef _WIN32 @@ -532,14 +608,15 @@ bool cmQtAutoGenInitializer::InitMoc() }; // Default configuration defines - this->Moc.Defines = GetCompileDefinitions(this->ConfigDefault); + this->Moc.Defines.Default = getDefs(this->ConfigDefault); // Other configuration defines if (this->MultiConfig) { for (std::string const& cfg : this->ConfigsList) { - std::set<std::string> defines = GetCompileDefinitions(cfg); - if (defines != this->Moc.Defines) { - this->Moc.ConfigDefines[cfg] = std::move(defines); + std::set<std::string> defines = getDefs(cfg); + if (defines == this->Moc.Defines.Default) { + continue; } + this->Moc.Defines.Config[cfg] = std::move(defines); } } } @@ -572,23 +649,22 @@ bool cmQtAutoGenInitializer::InitUic() } // Uic target options { - auto UicGetOpts = - [this](std::string const& cfg) -> std::vector<std::string> { + auto getOpts = [this](std::string const& cfg) -> std::vector<std::string> { std::vector<std::string> opts; this->GenTarget->GetAutoUicOptions(opts, cfg); return opts; }; - // Default settings - this->Uic.Options = UicGetOpts(this->ConfigDefault); - - // Configuration specific settings + // Default options + this->Uic.Options.Default = getOpts(this->ConfigDefault); + // Configuration specific options if (this->MultiConfig) { for (std::string const& cfg : this->ConfigsList) { - std::vector<std::string> options = UicGetOpts(cfg); - if (options != this->Uic.Options) { - this->Uic.ConfigOptions[cfg] = std::move(options); + std::vector<std::string> options = getOpts(cfg); + if (options == this->Uic.Options.Default) { + continue; } + this->Uic.Options.Config[cfg] = std::move(options); } } } @@ -822,8 +898,7 @@ bool cmQtAutoGenInitializer::InitScanFiles() // Check if the .ui file has uic options std::string const uicOpts = sf->GetSafeProperty(kw.AUTOUIC_OPTIONS); if (!uicOpts.empty()) { - this->Uic.FileFiles.push_back(fullPath); - this->Uic.FileOptions.push_back(cmExpandedList(uicOpts)); + this->Uic.UiFiles.emplace_back(fullPath, cmExpandedList(uicOpts)); } } else { // Register skipped .ui file @@ -887,31 +962,19 @@ bool cmQtAutoGenInitializer::InitScanFiles() } } // Path checksum and file names - { - cmFilePathChecksum const fpathCheckSum(this->Makefile); - for (Qrc& qrc : this->Rcc.Qrcs) { - qrc.PathChecksum = fpathCheckSum.getPart(qrc.QrcFile); - // RCC output file name - qrc.RccFile = cmStrCat(this->Dir.Build, '/', qrc.PathChecksum, "/qrc_", - qrc.QrcName, ".cpp"); - { - cm::string_view const baseSuffix = - qrc.Unique ? cm::string_view() : cm::string_view(qrc.PathChecksum); - std::string const base = - cmStrCat(this->Dir.Info, "/RCC", qrc.QrcName, baseSuffix); - qrc.LockFile = cmStrCat(base, ".lock"); - qrc.InfoFile = cmStrCat(base, "Info.cmake"); - qrc.SettingsFile = cmStrCat(base, "Settings.txt"); - if (this->MultiConfig) { - for (std::string const& cfg : this->ConfigsList) { - qrc.ConfigSettingsFile[cfg] = - AppendFilenameSuffix(qrc.SettingsFile, "_" + cfg); - } - } - } - } - } - // RCC options + for (Qrc& qrc : this->Rcc.Qrcs) { + // Path checksum + qrc.QrcPathChecksum = this->PathCheckSum.getPart(qrc.QrcFile); + // Output file name + qrc.OutputFile = cmStrCat(this->Dir.Build, '/', qrc.QrcPathChecksum, + "/qrc_", qrc.QrcName, ".cpp"); + std::string const base = cmStrCat(this->Dir.Info, "/AutoRcc_", + qrc.QrcName, '_', qrc.QrcPathChecksum); + qrc.LockFile = cmStrCat(base, "_Lock.lock"); + qrc.InfoFile = cmStrCat(base, "_Info.json"); + ConfigFileNames(qrc.SettingsFile, cmStrCat(base, "_Used"), ".txt"); + } + // rcc options for (Qrc& qrc : this->Rcc.Qrcs) { // Target options std::vector<std::string> opts = optionsTarget; @@ -921,7 +984,7 @@ bool cmQtAutoGenInitializer::InitScanFiles() // Replace '-' with '_'. The former is not valid for symbol names. std::replace(name.begin(), name.end(), '-', '_'); if (!qrc.Unique) { - name += cmStrCat('_', qrc.PathChecksum); + name += cmStrCat('_', qrc.QrcPathChecksum); } std::vector<std::string> nameOpts; nameOpts.emplace_back("-name"); @@ -932,7 +995,7 @@ bool cmQtAutoGenInitializer::InitScanFiles() RccMergeOptions(opts, qrc.Options, modernQt); qrc.Options = std::move(opts); } - // RCC resources + // rcc resources for (Qrc& qrc : this->Rcc.Qrcs) { if (!qrc.Generated) { std::string error; @@ -957,8 +1020,8 @@ bool cmQtAutoGenInitializer::InitAutogenTarget() // Files provided by the autogen target std::vector<std::string> autogenProvides; if (this->Moc.Enabled) { - this->AddGeneratedSource(this->Moc.MocsCompilation, this->Moc, true); - autogenProvides.push_back(this->Moc.MocsCompilation); + this->AddGeneratedSource(this->Moc.CompilationFile, this->Moc, true); + autogenProvides.push_back(this->Moc.CompilationFile); } // Compose target comment @@ -1023,7 +1086,7 @@ bool cmQtAutoGenInitializer::InitAutogenTarget() this->Dir.Work.c_str()); cc.SetEscapeOldStyle(false); cc.SetEscapeAllowMakeVars(true); - this->GenTarget->Target->AddPreBuildCommand(cc); + this->GenTarget->Target->AddPreBuildCommand(std::move(cc)); } else { // Add link library target dependencies to the autogen target @@ -1055,7 +1118,7 @@ bool cmQtAutoGenInitializer::InitAutogenTarget() // Create autogen target cmTarget* autogenTarget = this->Makefile->AddUtilityCommand( - this->AutogenTarget.Name, cmMakefile::TargetOrigin::Generator, true, + this->AutogenTarget.Name, cmCommandOrigin::Generator, true, this->Dir.Work.c_str(), /*byproducts=*/autogenProvides, std::vector<std::string>(this->AutogenTarget.DependFiles.begin(), this->AutogenTarget.DependFiles.end()), @@ -1100,10 +1163,10 @@ bool cmQtAutoGenInitializer::InitRccTargets() // Register info file as generated by CMake this->Makefile->AddCMakeOutputFile(qrc.InfoFile); // Register file at target - this->AddGeneratedSource(qrc.RccFile, this->Rcc); + this->AddGeneratedSource(qrc.OutputFile, this->Rcc); std::vector<std::string> ccOutput; - ccOutput.push_back(qrc.RccFile); + ccOutput.push_back(qrc.OutputFile); std::vector<std::string> ccDepends; // Add the .qrc and info file to the custom command dependencies @@ -1133,13 +1196,12 @@ bool cmQtAutoGenInitializer::InitRccTargets() { ccName = cmStrCat(this->GenTarget->GetName(), "_arcc_", qrc.QrcName); if (!qrc.Unique) { - ccName += cmStrCat('_', qrc.PathChecksum); + ccName += cmStrCat('_', qrc.QrcPathChecksum); } cmTarget* autoRccTarget = this->Makefile->AddUtilityCommand( - ccName, cmMakefile::TargetOrigin::Generator, true, - this->Dir.Work.c_str(), ccOutput, ccDepends, commandLines, false, - ccComment.c_str()); + ccName, cmCommandOrigin::Generator, true, this->Dir.Work.c_str(), + ccOutput, ccDepends, commandLines, false, ccComment.c_str()); // Create autogen generator target this->LocalGen->AddGeneratorTarget( @@ -1212,229 +1274,185 @@ bool cmQtAutoGenInitializer::SetupCustomTargets() bool cmQtAutoGenInitializer::SetupWriteAutogenInfo() { - InfoWriter ofs(this->AutogenTarget.InfoFile); - if (ofs) { - // Utility lambdas - auto MfDef = [this](const char* key) { - return this->Makefile->GetSafeDefinition(key); - }; + // Utility lambdas + auto MfDef = [this](std::string const& key) { + return this->Makefile->GetSafeDefinition(key); + }; - // Write common settings - ofs.Write("# Meta\n"); - ofs.Write("AM_MULTI_CONFIG", this->MultiConfig ? "TRUE" : "FALSE"); - ofs.Write("AM_PARALLEL", this->AutogenTarget.Parallel); - ofs.Write("AM_VERBOSITY", this->Verbosity); - - ofs.Write("# Directories\n"); - ofs.Write("AM_CMAKE_SOURCE_DIR", MfDef("CMAKE_SOURCE_DIR")); - ofs.Write("AM_CMAKE_BINARY_DIR", MfDef("CMAKE_BINARY_DIR")); - ofs.Write("AM_CMAKE_CURRENT_SOURCE_DIR", - MfDef("CMAKE_CURRENT_SOURCE_DIR")); - ofs.Write("AM_CMAKE_CURRENT_BINARY_DIR", - MfDef("CMAKE_CURRENT_BINARY_DIR")); - ofs.Write("AM_BUILD_DIR", this->Dir.Build); - ofs.Write("AM_INCLUDE_DIR", this->Dir.Include); - ofs.WriteConfig("AM_INCLUDE_DIR", this->Dir.ConfigInclude); - - std::vector<std::string> headers; - std::vector<std::string> headersFlags; - std::vector<std::string> headersBuildPaths; - std::vector<std::string> sources; - std::vector<std::string> sourcesFlags; - std::set<std::string> moc_skip; - std::set<std::string> uic_skip; - - // Filter headers - { - auto headerCount = this->AutogenTarget.Headers.size(); - headers.reserve(headerCount); - headersFlags.reserve(headerCount); + // Filtered headers and sources + std::set<std::string> moc_skip; + std::set<std::string> uic_skip; + std::vector<MUFile const*> headers; + std::vector<MUFile const*> sources; - std::vector<MUFile const*> sortedHeaders; - { - sortedHeaders.reserve(headerCount); - for (auto const& pair : this->AutogenTarget.Headers) { - sortedHeaders.emplace_back(pair.second.get()); - } - std::sort(sortedHeaders.begin(), sortedHeaders.end(), - [](MUFile const* a, MUFile const* b) { - return (a->FullPath < b->FullPath); - }); + // Filter headers + { + headers.reserve(this->AutogenTarget.Headers.size()); + for (auto const& pair : this->AutogenTarget.Headers) { + MUFile const* const muf = pair.second.get(); + if (muf->Generated && !this->CMP0071Accept) { + continue; } - - for (MUFile const* const muf : sortedHeaders) { - if (muf->Generated && !this->CMP0071Accept) { - continue; - } - if (muf->SkipMoc) { - moc_skip.insert(muf->FullPath); - } - if (muf->SkipUic) { - uic_skip.insert(muf->FullPath); - } - if (muf->MocIt || muf->UicIt) { - headers.emplace_back(muf->FullPath); - headersFlags.emplace_back( - cmStrCat(muf->MocIt ? 'M' : 'm', muf->UicIt ? 'U' : 'u')); - } + if (muf->SkipMoc) { + moc_skip.insert(muf->FullPath); } - } - // Header build paths - { - cmFilePathChecksum const fpathCheckSum(this->Makefile); - std::unordered_set<std::string> emitted; - for (std::string const& hdr : headers) { - std::string const basePath = - cmStrCat(fpathCheckSum.getPart(hdr), "/moc_", - cmSystemTools::GetFilenameWithoutLastExtension(hdr)); - std::string suffix; - for (int ii = 0; ii != 1024; ++ii) { - std::string path = cmStrCat(basePath, suffix, ".cpp"); - if (emitted.emplace(path).second) { - headersBuildPaths.emplace_back(std::move(path)); - break; - } - suffix = cmStrCat('_', ii + 1); - } + if (muf->SkipUic) { + uic_skip.insert(muf->FullPath); + } + if (muf->MocIt || muf->UicIt) { + headers.emplace_back(muf); } } + std::sort(headers.begin(), headers.end(), + [](MUFile const* a, MUFile const* b) { + return (a->FullPath < b->FullPath); + }); + } - // Filter sources - { - auto sourcesCount = this->AutogenTarget.Sources.size(); - sources.reserve(sourcesCount); - sourcesFlags.reserve(sourcesCount); - - std::vector<MUFile const*> sorted; - sorted.reserve(sourcesCount); - for (auto const& pair : this->AutogenTarget.Sources) { - sorted.emplace_back(pair.second.get()); + // Filter sources + { + sources.reserve(this->AutogenTarget.Sources.size()); + for (auto const& pair : this->AutogenTarget.Sources) { + MUFile const* const muf = pair.second.get(); + if (muf->Generated && !this->CMP0071Accept) { + continue; } - std::sort(sorted.begin(), sorted.end(), - [](MUFile const* a, MUFile const* b) { - return (a->FullPath < b->FullPath); - }); - - for (MUFile const* const muf : sorted) { - if (muf->Generated && !this->CMP0071Accept) { - continue; - } - if (muf->SkipMoc) { - moc_skip.insert(muf->FullPath); - } - if (muf->SkipUic) { - uic_skip.insert(muf->FullPath); - } - if (muf->MocIt || muf->UicIt) { - sources.emplace_back(muf->FullPath); - sourcesFlags.emplace_back( - cmStrCat(muf->MocIt ? 'M' : 'm', muf->UicIt ? 'U' : 'u')); - } + if (muf->SkipMoc) { + moc_skip.insert(muf->FullPath); + } + if (muf->SkipUic) { + uic_skip.insert(muf->FullPath); + } + if (muf->MocIt || muf->UicIt) { + sources.emplace_back(muf); } } + std::sort(sources.begin(), sources.end(), + [](MUFile const* a, MUFile const* b) { + return (a->FullPath < b->FullPath); + }); + } - ofs.Write("# Qt\n"); - ofs.WriteUInt("AM_QT_VERSION_MAJOR", this->QtVersion.Major); - ofs.Write("AM_QT_MOC_EXECUTABLE", this->Moc.Executable); - ofs.Write("AM_QT_UIC_EXECUTABLE", this->Uic.Executable); - - ofs.Write("# Files\n"); - ofs.Write("AM_CMAKE_EXECUTABLE", cmSystemTools::GetCMakeCommand()); - ofs.Write("AM_SETTINGS_FILE", this->AutogenTarget.SettingsFile); - ofs.WriteConfig("AM_SETTINGS_FILE", - this->AutogenTarget.ConfigSettingsFile); - ofs.Write("AM_PARSE_CACHE_FILE", this->AutogenTarget.ParseCacheFile); - ofs.WriteStrings("AM_HEADERS", headers); - ofs.WriteStrings("AM_HEADERS_FLAGS", headersFlags); - ofs.WriteStrings("AM_HEADERS_BUILD_PATHS", headersBuildPaths); - ofs.WriteStrings("AM_SOURCES", sources); - ofs.WriteStrings("AM_SOURCES_FLAGS", sourcesFlags); - - // Write moc settings - if (this->Moc.Enabled) { - ofs.Write("# MOC settings\n"); - ofs.WriteStrings("AM_MOC_SKIP", moc_skip); - ofs.WriteStrings("AM_MOC_DEFINITIONS", this->Moc.Defines); - ofs.WriteConfigStrings("AM_MOC_DEFINITIONS", this->Moc.ConfigDefines); - ofs.WriteStrings("AM_MOC_INCLUDES", this->Moc.Includes); - ofs.WriteConfigStrings("AM_MOC_INCLUDES", this->Moc.ConfigIncludes); - ofs.Write("AM_MOC_OPTIONS", - this->GenTarget->GetSafeProperty("AUTOMOC_MOC_OPTIONS")); - ofs.Write("AM_MOC_RELAXED_MODE", MfDef("CMAKE_AUTOMOC_RELAXED_MODE")); - ofs.Write("AM_MOC_PATH_PREFIX", - this->GenTarget->GetSafeProperty("AUTOMOC_PATH_PREFIX")); - ofs.Write("AM_MOC_MACRO_NAMES", - this->GenTarget->GetSafeProperty("AUTOMOC_MACRO_NAMES")); - ofs.Write("AM_MOC_DEPEND_FILTERS", - this->GenTarget->GetSafeProperty("AUTOMOC_DEPEND_FILTERS")); - ofs.Write("AM_MOC_PREDEFS_CMD", this->Moc.PredefsCmd); - } - - // Write uic settings - if (this->Uic.Enabled) { - // Add skipped .ui files - uic_skip.insert(this->Uic.SkipUi.begin(), this->Uic.SkipUi.end()); + // Info writer + InfoWriter info; + + // General + info.SetBool("MULTI_CONFIG", this->MultiConfig); + info.SetUInt("PARALLEL", this->AutogenTarget.Parallel); + info.SetUInt("VERBOSITY", this->Verbosity); + + // Directories + info.Set("CMAKE_SOURCE_DIR", MfDef("CMAKE_SOURCE_DIR")); + info.Set("CMAKE_BINARY_DIR", MfDef("CMAKE_BINARY_DIR")); + info.Set("CMAKE_CURRENT_SOURCE_DIR", MfDef("CMAKE_CURRENT_SOURCE_DIR")); + info.Set("CMAKE_CURRENT_BINARY_DIR", MfDef("CMAKE_CURRENT_BINARY_DIR")); + info.Set("BUILD_DIR", this->Dir.Build); + info.SetConfig("INCLUDE_DIR", this->Dir.Include); + + info.SetUInt("QT_VERSION_MAJOR", this->QtVersion.Major); + info.Set("QT_MOC_EXECUTABLE", this->Moc.Executable); + info.Set("QT_UIC_EXECUTABLE", this->Uic.Executable); + + info.Set("CMAKE_EXECUTABLE", cmSystemTools::GetCMakeCommand()); + info.SetConfig("SETTINGS_FILE", this->AutogenTarget.SettingsFile); + info.SetConfig("PARSE_CACHE_FILE", this->AutogenTarget.ParseCacheFile); + info.SetArray("HEADER_EXTENSIONS", + this->Makefile->GetCMakeInstance()->GetHeaderExtensions()); + info.SetArrayArray( + "HEADERS", headers, [this](Json::Value& jval, MUFile const* muf) { + jval.resize(3u); + jval[0u] = muf->FullPath; + jval[1u] = cmStrCat(muf->MocIt ? 'M' : 'm', muf->UicIt ? 'U' : 'u'); + jval[2u] = this->GetMocBuildPath(*muf); + }); + info.SetArrayArray( + "SOURCES", sources, [](Json::Value& jval, MUFile const* muf) { + jval.resize(2u); + jval[0u] = muf->FullPath; + jval[1u] = cmStrCat(muf->MocIt ? 'M' : 'm', muf->UicIt ? 'U' : 'u'); + }); + + // Write moc settings + if (this->Moc.Enabled) { + info.SetArray("MOC_SKIP", moc_skip); + info.SetConfigArray("MOC_DEFINITIONS", this->Moc.Defines); + info.SetConfigArray("MOC_INCLUDES", this->Moc.Includes); + info.SetArray("MOC_OPTIONS", this->Moc.Options); + info.SetBool("MOC_RELAXED_MODE", this->Moc.RelaxedMode); + info.SetBool("MOC_PATH_PREFIX", this->Moc.PathPrefix); + info.SetArray("MOC_MACRO_NAMES", this->Moc.MacroNames); + info.SetArrayArray( + "MOC_DEPEND_FILTERS", this->Moc.DependFilters, + [](Json::Value& jval, std::pair<std::string, std::string> const& pair) { + jval.resize(2u); + jval[0u] = pair.first; + jval[1u] = pair.second; + }); + info.Set("MOC_COMPILATION_FILE", this->Moc.CompilationFile); + info.SetArray("MOC_PREDEFS_CMD", this->Moc.PredefsCmd); + info.SetConfig("MOC_PREDEFS_FILE", this->Moc.PredefsFile); + } - ofs.Write("# UIC settings\n"); - ofs.WriteStrings("AM_UIC_SKIP", uic_skip); - ofs.WriteStrings("AM_UIC_TARGET_OPTIONS", this->Uic.Options); - ofs.WriteConfigStrings("AM_UIC_TARGET_OPTIONS", this->Uic.ConfigOptions); - ofs.WriteStrings("AM_UIC_OPTIONS_FILES", this->Uic.FileFiles); - ofs.WriteNestedLists("AM_UIC_OPTIONS_OPTIONS", this->Uic.FileOptions); - ofs.WriteStrings("AM_UIC_SEARCH_PATHS", this->Uic.SearchPaths); - } - } else { - cmSystemTools::Error(cmStrCat("AutoGen: Could not write file ", - this->AutogenTarget.InfoFile)); - return false; + // Write uic settings + if (this->Uic.Enabled) { + // Add skipped .ui files + uic_skip.insert(this->Uic.SkipUi.begin(), this->Uic.SkipUi.end()); + + info.SetArray("UIC_SKIP", uic_skip); + info.SetArrayArray("UIC_UI_FILES", this->Uic.UiFiles, + [](Json::Value& jval, UicT::UiFileT const& uiFile) { + jval.resize(2u); + jval[0u] = uiFile.first; + InfoWriter::MakeStringArray(jval[1u], uiFile.second); + }); + info.SetConfigArray("UIC_OPTIONS", this->Uic.Options); + info.SetArray("UIC_SEARCH_PATHS", this->Uic.SearchPaths); } + info.Save(this->AutogenTarget.InfoFile); + return true; } bool cmQtAutoGenInitializer::SetupWriteRccInfo() { for (Qrc const& qrc : this->Rcc.Qrcs) { - InfoWriter ofs(qrc.InfoFile); - if (ofs) { - // Utility lambdas - auto MfDef = [this](const char* key) { - return this->Makefile->GetSafeDefinition(key); - }; + // Utility lambdas + auto MfDef = [this](std::string const& key) { + return this->Makefile->GetSafeDefinition(key); + }; - // Write - ofs.Write("# Configurations\n"); - ofs.Write("ARCC_MULTI_CONFIG", this->MultiConfig ? "TRUE" : "FALSE"); - ofs.Write("ARCC_VERBOSITY", this->Verbosity); - ofs.Write("# Settings file\n"); - ofs.Write("ARCC_SETTINGS_FILE", qrc.SettingsFile); - ofs.WriteConfig("ARCC_SETTINGS_FILE", qrc.ConfigSettingsFile); - - ofs.Write("# Directories\n"); - ofs.Write("ARCC_CMAKE_SOURCE_DIR", MfDef("CMAKE_SOURCE_DIR")); - ofs.Write("ARCC_CMAKE_BINARY_DIR", MfDef("CMAKE_BINARY_DIR")); - ofs.Write("ARCC_BUILD_DIR", this->Dir.Build); - ofs.Write("ARCC_INCLUDE_DIR", this->Dir.Include); - ofs.WriteConfig("ARCC_INCLUDE_DIR", this->Dir.ConfigInclude); - - ofs.Write("# Rcc executable\n"); - ofs.Write("ARCC_RCC_EXECUTABLE", this->Rcc.Executable); - ofs.WriteStrings("ARCC_RCC_LIST_OPTIONS", - this->Rcc.ExecutableFeatures->ListOptions); - - ofs.Write("# Rcc job\n"); - ofs.Write("ARCC_LOCK_FILE", qrc.LockFile); - ofs.Write("ARCC_SOURCE", qrc.QrcFile); - ofs.Write("ARCC_OUTPUT_CHECKSUM", qrc.PathChecksum); - ofs.Write("ARCC_OUTPUT_NAME", - cmSystemTools::GetFilenameName(qrc.RccFile)); - ofs.WriteStrings("ARCC_OPTIONS", qrc.Options); - ofs.WriteStrings("ARCC_INPUTS", qrc.Resources); - } else { - cmSystemTools::Error( - cmStrCat("AutoRcc: Could not write file ", qrc.InfoFile)); - return false; - } + InfoWriter info; + + // General + info.SetBool("MULTI_CONFIG", this->MultiConfig); + info.SetUInt("VERBOSITY", this->Verbosity); + + // Files + info.Set("LOCK_FILE", qrc.LockFile); + info.SetConfig("SETTINGS_FILE", qrc.SettingsFile); + + // Directories + info.Set("CMAKE_SOURCE_DIR", MfDef("CMAKE_SOURCE_DIR")); + info.Set("CMAKE_BINARY_DIR", MfDef("CMAKE_BINARY_DIR")); + info.Set("CMAKE_CURRENT_SOURCE_DIR", MfDef("CMAKE_CURRENT_SOURCE_DIR")); + info.Set("CMAKE_CURRENT_BINARY_DIR", MfDef("CMAKE_CURRENT_BINARY_DIR")); + info.Set("BUILD_DIR", this->Dir.Build); + info.SetConfig("INCLUDE_DIR", this->Dir.Include); + + // rcc executable + info.Set("RCC_EXECUTABLE", this->Rcc.Executable); + info.SetArray("RCC_LIST_OPTIONS", + this->Rcc.ExecutableFeatures->ListOptions); + + // qrc file + info.Set("SOURCE", qrc.QrcFile); + info.Set("OUTPUT_CHECKSUM", qrc.QrcPathChecksum); + info.Set("OUTPUT_NAME", cmSystemTools::GetFilenameName(qrc.OutputFile)); + info.SetArray("OPTIONS", qrc.Options); + info.SetArray("INPUTS", qrc.Resources); + + info.Save(qrc.InfoFile); } return true; @@ -1507,64 +1525,82 @@ void cmQtAutoGenInitializer::AddCleanFile(std::string const& fileName) fileName.c_str(), false); } -static unsigned int CharPtrToUInt(const char* const input) +void cmQtAutoGenInitializer::ConfigFileNames(ConfigString& configString, + cm::string_view prefix, + cm::string_view suffix) { - unsigned long tmp = 0; - if (input != nullptr && cmStrToULong(input, &tmp)) { - return static_cast<unsigned int>(tmp); + configString.Default = cmStrCat(prefix, suffix); + if (this->MultiConfig) { + for (auto const& cfg : this->ConfigsList) { + configString.Config[cfg] = cmStrCat(prefix, '_', cfg, suffix); + } } - return 0; } -static std::vector<cmQtAutoGen::IntegerVersion> GetKnownQtVersions( - cmGeneratorTarget const* genTarget) +void cmQtAutoGenInitializer::ConfigFileClean(ConfigString& configString) { - // Qt version variable prefixes - static std::initializer_list< - std::pair<cm::string_view, cm::string_view>> const keys{ - { "Qt6Core_VERSION_MAJOR", "Qt6Core_VERSION_MINOR" }, - { "Qt5Core_VERSION_MAJOR", "Qt5Core_VERSION_MINOR" }, - { "QT_VERSION_MAJOR", "QT_VERSION_MINOR" }, - }; - - std::vector<cmQtAutoGen::IntegerVersion> result; - result.reserve(keys.size() * 2); - - // Adds a version to the result (nullptr safe) - auto addVersion = [&result](const char* major, const char* minor) { - cmQtAutoGen::IntegerVersion ver(CharPtrToUInt(major), - CharPtrToUInt(minor)); - if (ver.Major != 0) { - result.emplace_back(ver); + this->AddCleanFile(configString.Default); + if (this->MultiConfig) { + for (auto const& pair : configString.Config) { + this->AddCleanFile(pair.second); } - }; - - cmMakefile* makefile = genTarget->Makefile; - - // Read versions from variables - for (auto const& keyPair : keys) { - addVersion(makefile->GetDefinition(std::string(keyPair.first)), - makefile->GetDefinition(std::string(keyPair.second))); - } - - // Read versions from directory properties - for (auto const& keyPair : keys) { - addVersion(makefile->GetProperty(std::string(keyPair.first)), - makefile->GetProperty(std::string(keyPair.second))); } - - return result; } std::pair<cmQtAutoGen::IntegerVersion, unsigned int> cmQtAutoGenInitializer::GetQtVersion(cmGeneratorTarget const* target) { + // Converts a char ptr to an unsigned int value + auto toUInt = [](const char* const input) -> unsigned int { + unsigned long tmp = 0; + if (input != nullptr && cmStrToULong(input, &tmp)) { + return static_cast<unsigned int>(tmp); + } + return 0u; + }; + + // Initialize return value to a default std::pair<IntegerVersion, unsigned int> res( IntegerVersion(), - CharPtrToUInt(target->GetLinkInterfaceDependentStringProperty( - "QT_MAJOR_VERSION", ""))); + toUInt(target->GetLinkInterfaceDependentStringProperty("QT_MAJOR_VERSION", + ""))); + + // Acquire known Qt versions + std::vector<cmQtAutoGen::IntegerVersion> knownQtVersions; + { + // Qt version variable prefixes + static std::initializer_list< + std::pair<cm::string_view, cm::string_view>> const keys{ + { "Qt6Core_VERSION_MAJOR", "Qt6Core_VERSION_MINOR" }, + { "Qt5Core_VERSION_MAJOR", "Qt5Core_VERSION_MINOR" }, + { "QT_VERSION_MAJOR", "QT_VERSION_MINOR" }, + }; - auto knownQtVersions = GetKnownQtVersions(target); + knownQtVersions.reserve(keys.size() * 2); + + // Adds a version to the result (nullptr safe) + auto addVersion = [&knownQtVersions, &toUInt](const char* major, + const char* minor) { + cmQtAutoGen::IntegerVersion ver(toUInt(major), toUInt(minor)); + if (ver.Major != 0) { + knownQtVersions.emplace_back(ver); + } + }; + + // Read versions from variables + for (auto const& keyPair : keys) { + addVersion(target->Makefile->GetDefinition(std::string(keyPair.first)), + target->Makefile->GetDefinition(std::string(keyPair.second))); + } + + // Read versions from directory properties + for (auto const& keyPair : keys) { + addVersion(target->Makefile->GetProperty(std::string(keyPair.first)), + target->Makefile->GetProperty(std::string(keyPair.second))); + } + } + + // Evaluate known Qt versions if (!knownQtVersions.empty()) { if (res.second == 0) { // No specific version was requested by the target: @@ -1583,6 +1619,30 @@ cmQtAutoGenInitializer::GetQtVersion(cmGeneratorTarget const* target) return res; } +std::string cmQtAutoGenInitializer::GetMocBuildPath(MUFile const& muf) +{ + std::string res; + if (!muf.MocIt) { + return res; + } + { + std::string const basePath = + cmStrCat(this->PathCheckSum.getPart(muf.FullPath), "/moc_", + FileNameWithoutLastExtension(muf.FullPath)); + std::string suffix; + constexpr std::size_t num_tries_max = 256; + for (std::size_t ii = 0; ii != num_tries_max; ++ii) { + res = cmStrCat(basePath, suffix, ".cpp"); + if (this->Moc.EmittedBuildPaths.emplace(res).second) { + break; + } + // Compute new suffix + suffix = cmStrCat('_', ii + 1); + } + } + return res; +} + bool cmQtAutoGenInitializer::GetQtExecutable(GenVarsT& genVars, const std::string& executable, bool ignoreMissingTarget) const diff --git a/Source/cmQtAutoGenInitializer.h b/Source/cmQtAutoGenInitializer.h index 7ce9fad..0580ddb 100644 --- a/Source/cmQtAutoGenInitializer.h +++ b/Source/cmQtAutoGenInitializer.h @@ -4,17 +4,16 @@ #define cmQtAutoGenInitializer_h #include "cmConfigure.h" // IWYU pragma: keep -#include "cmGeneratedFileStream.h" +#include "cmFilePathChecksum.h" #include "cmQtAutoGen.h" #include <cm/string_view> -#include <map> #include <memory> -#include <ostream> #include <set> #include <string> #include <unordered_map> +#include <unordered_set> #include <utility> #include <vector> @@ -26,29 +25,47 @@ class cmQtAutoGenGlobalInitializer; class cmSourceFile; class cmTarget; -/// @brief Initializes the QtAutoGen generators +/** \class cmQtAutoGenerator + * \brief Initializes the QtAutoGen generators + */ class cmQtAutoGenInitializer : public cmQtAutoGen { public: - /// @brief Rcc job information + /** String value with per configuration variants. */ + class ConfigString + { + public: + std::string Default; + std::unordered_map<std::string, std::string> Config; + }; + + /** String values with per configuration variants. */ + template <typename C> + class ConfigStrings + { + public: + C Default; + std::unordered_map<std::string, C> Config; + }; + + /** rcc job. */ class Qrc { public: std::string LockFile; std::string QrcFile; std::string QrcName; - std::string PathChecksum; + std::string QrcPathChecksum; std::string InfoFile; - std::string SettingsFile; - std::map<std::string, std::string> ConfigSettingsFile; - std::string RccFile; + ConfigString SettingsFile; + std::string OutputFile; bool Generated = false; bool Unique = false; std::vector<std::string> Options; std::vector<std::string> Resources; }; - /// @brief Moc/Uic file + /** moc and/or uic file. */ struct MUFile { std::string FullPath; @@ -61,7 +78,7 @@ public: }; using MUFileHandle = std::unique_ptr<MUFile>; - /// @brief Abstract moc/uic/rcc generator variables base class + /** Abstract moc/uic/rcc generator variables base class. */ struct GenVarsT { bool Enabled = false; @@ -74,47 +91,13 @@ public: std::string Executable; CompilerFeaturesHandle ExecutableFeatures; - /// @brief Constructor GenVarsT(GenT gen) : Gen(gen) , GenNameUpper(cmQtAutoGen::GeneratorNameUpper(gen)){}; }; - /// @brief Writes a CMake info file - class InfoWriter - { - public: - /// @brief Open the given file - InfoWriter(std::string const& filename); - - /// @return True if the file is open - explicit operator bool() const { return static_cast<bool>(Ofs_); } - - void Write(cm::string_view text) { Ofs_ << text; } - void Write(cm::string_view, std::string const& value); - void WriteUInt(cm::string_view, unsigned int value); - - template <class C> - void WriteStrings(cm::string_view, C const& container); - void WriteConfig(cm::string_view, - std::map<std::string, std::string> const& map); - template <class C> - void WriteConfigStrings(cm::string_view, - std::map<std::string, C> const& map); - void WriteNestedLists(cm::string_view, - std::vector<std::vector<std::string>> const& lists); - - private: - template <class IT> - static std::string ListJoin(IT it_begin, IT it_end); - static std::string ConfigKey(cm::string_view, std::string const& config); - - private: - cmGeneratedFileStream Ofs_; - }; - public: - /// @return The detected Qt version and the required Qt major version + /** @return The detected Qt version and the required Qt major version. */ static std::pair<IntegerVersion, unsigned int> GetQtVersion( cmGeneratorTarget const* genTarget); @@ -128,7 +111,7 @@ public: bool SetupCustomTargets(); private: - /// @brief If moc or uic is enabled, the autogen target will be generated + /** If moc or uic is enabled, the autogen target will be generated. */ bool MocOrUicEnabled() const { return (this->Moc.Enabled || this->Uic.Enabled); @@ -152,6 +135,12 @@ private: cm::string_view genNameUpper); void AddCleanFile(std::string const& fileName); + void ConfigFileNames(ConfigString& configString, cm::string_view prefix, + cm::string_view suffix); + void ConfigFileClean(ConfigString& configString); + + std::string GetMocBuildPath(MUFile const& muf); + bool GetQtExecutable(GenVarsT& genVars, const std::string& executable, bool ignoreMissingTarget) const; @@ -161,39 +150,39 @@ private: cmGlobalGenerator* GlobalGen = nullptr; cmLocalGenerator* LocalGen = nullptr; cmMakefile* Makefile = nullptr; + cmFilePathChecksum const PathCheckSum; - // Configuration + // -- Configuration IntegerVersion QtVersion; + unsigned int Verbosity = 0; bool MultiConfig = false; + bool CMP0071Accept = false; + bool CMP0071Warn = false; std::string ConfigDefault; std::vector<std::string> ConfigsList; - std::string Verbosity; std::string TargetsFolder; - bool CMP0071Accept = false; - bool CMP0071Warn = false; - /// @brief Common directories + /** Common directories. */ struct { std::string Info; std::string Build; std::string Work; - std::string Include; - std::map<std::string, std::string> ConfigInclude; + ConfigString Include; + std::string IncludeGenExp; } Dir; - /// @brief Autogen target variables + /** Autogen target variables. */ struct { std::string Name; bool GlobalTarget = false; // Settings - std::string Parallel; + unsigned int Parallel = 1; // Configuration files std::string InfoFile; - std::string SettingsFile; - std::string ParseCacheFile; - std::map<std::string, std::string> ConfigSettingsFile; + ConfigString SettingsFile; + ConfigString ParseCacheFile; // Dependencies bool DependOrigin = false; std::set<std::string> DependFiles; @@ -204,45 +193,53 @@ private: std::vector<MUFile*> FilesGenerated; } AutogenTarget; - /// @brief Moc only variables + /** moc variables. */ struct MocT : public GenVarsT { - std::string PredefsCmd; - std::vector<std::string> Includes; - std::map<std::string, std::vector<std::string>> ConfigIncludes; - std::set<std::string> Defines; - std::map<std::string, std::set<std::string>> ConfigDefines; - std::string MocsCompilation; - - /// @brief Constructor MocT() : GenVarsT(GenT::MOC){}; + + bool RelaxedMode = false; + bool PathPrefix = false; + std::string CompilationFile; + // Compiler implicit pre defines + std::vector<std::string> PredefsCmd; + ConfigString PredefsFile; + // Defines + ConfigStrings<std::set<std::string>> Defines; + // Includes + ConfigStrings<std::vector<std::string>> Includes; + // Options + std::vector<std::string> Options; + // Filters + std::vector<std::string> MacroNames; + std::vector<std::pair<std::string, std::string>> DependFilters; + // Utility + std::unordered_set<std::string> EmittedBuildPaths; } Moc; - /// @brief Uic only variables + /** uic variables. */ struct UicT : public GenVarsT { - std::set<std::string> SkipUi; - std::vector<std::string> SearchPaths; - std::vector<std::string> Options; - std::map<std::string, std::vector<std::string>> ConfigOptions; - std::vector<std::string> FileFiles; - std::vector<std::vector<std::string>> FileOptions; + using UiFileT = std::pair<std::string, std::vector<std::string>>; - /// @brief Constructor UicT() : GenVarsT(GenT::UIC){}; + + std::set<std::string> SkipUi; + std::vector<UiFileT> UiFiles; + ConfigStrings<std::vector<std::string>> Options; + std::vector<std::string> SearchPaths; } Uic; - /// @brief Rcc only variables + /** rcc variables. */ struct RccT : public GenVarsT { - bool GlobalTarget = false; - std::vector<Qrc> Qrcs; - - /// @brief Constructor RccT() : GenVarsT(GenT::RCC){}; + + bool GlobalTarget = false; + std::vector<Qrc> Qrcs; } Rcc; }; diff --git a/Source/cmQtAutoGenerator.cxx b/Source/cmQtAutoGenerator.cxx index eb829fa..1320b07 100644 --- a/Source/cmQtAutoGenerator.cxx +++ b/Source/cmQtAutoGenerator.cxx @@ -2,19 +2,11 @@ file Copyright.txt or https://cmake.org/licensing for details. */ #include "cmQtAutoGenerator.h" -#include <cm/memory> - -#include "cmsys/FStream.hxx" - -#include "cmGlobalGenerator.h" -#include "cmMakefile.h" #include "cmQtAutoGen.h" -#include "cmState.h" -#include "cmStateDirectory.h" -#include "cmStateSnapshot.h" #include "cmStringAlgorithms.h" #include "cmSystemTools.h" -#include "cmake.h" +#include "cm_jsoncpp_reader.h" +#include "cmsys/FStream.hxx" cmQtAutoGenerator::Logger::Logger() { @@ -44,13 +36,10 @@ cmQtAutoGenerator::Logger::Logger() cmQtAutoGenerator::Logger::~Logger() = default; -void cmQtAutoGenerator::Logger::RaiseVerbosity(std::string const& value) +void cmQtAutoGenerator::Logger::RaiseVerbosity(unsigned int value) { - unsigned long verbosity = 0; - if (cmStrToULong(value, &verbosity)) { - if (this->Verbosity_ < verbosity) { - this->Verbosity_ = static_cast<unsigned int>(verbosity); - } + if (this->Verbosity_ < value) { + this->Verbosity_ = value; } } @@ -214,64 +203,214 @@ bool cmQtAutoGenerator::FileDiffers(std::string const& filename, return differs; } -cmQtAutoGenerator::cmQtAutoGenerator() = default; +cmQtAutoGenerator::cmQtAutoGenerator(GenT genType) + : GenType_(genType) +{ +} cmQtAutoGenerator::~cmQtAutoGenerator() = default; -bool cmQtAutoGenerator::Run(std::string const& infoFile, - std::string const& config) +bool cmQtAutoGenerator::InfoT::Read(std::istream& istr) { - // Info settings - InfoFile_ = infoFile; - cmSystemTools::ConvertToUnixSlashes(InfoFile_); - if (!InfoFileTime_.Load(InfoFile_)) { - cmSystemTools::Stderr(cmStrCat("AutoGen: The info file ", - Quoted(InfoFile_), " is not readable\n")); + try { + istr >> Json_; + } catch (...) { return false; } - InfoDir_ = cmSystemTools::GetFilenamePath(infoFile); - InfoConfig_ = config; + return true; +} - bool success = false; - { - cmake cm(cmake::RoleScript, cmState::Unknown); - cm.SetHomeOutputDirectory(InfoDir()); - cm.SetHomeDirectory(InfoDir()); - cm.GetCurrentSnapshot().SetDefaultDefinitions(); - cmGlobalGenerator gg(&cm); - - cmStateSnapshot snapshot = cm.GetCurrentSnapshot(); - snapshot.GetDirectory().SetCurrentBinary(InfoDir()); - snapshot.GetDirectory().SetCurrentSource(InfoDir()); - - auto makefile = cm::make_unique<cmMakefile>(&gg, snapshot); - // The OLD/WARN behavior for policy CMP0053 caused a speed regression. - // https://gitlab.kitware.com/cmake/cmake/issues/17570 - makefile->SetPolicyVersion("3.9", std::string()); - gg.SetCurrentMakefile(makefile.get()); - success = this->Init(makefile.get()); +bool cmQtAutoGenerator::InfoT::GetJsonArray(std::vector<std::string>& list, + Json::Value const& jval) +{ + Json::ArrayIndex const arraySize = jval.size(); + if (arraySize == 0) { + return false; } - if (success) { - success = this->Process(); + + bool picked = false; + list.reserve(list.size() + arraySize); + for (Json::ArrayIndex ii = 0; ii != arraySize; ++ii) { + Json::Value const& ival = jval[ii]; + if (ival.isString()) { + list.emplace_back(ival.asString()); + picked = true; + } } - return success; + return picked; +} + +bool cmQtAutoGenerator::InfoT::GetJsonArray( + std::unordered_set<std::string>& list, Json::Value const& jval) +{ + Json::ArrayIndex const arraySize = jval.size(); + if (arraySize == 0) { + return false; + } + + bool picked = false; + list.reserve(list.size() + arraySize); + for (Json::ArrayIndex ii = 0; ii != arraySize; ++ii) { + Json::Value const& ival = jval[ii]; + if (ival.isString()) { + list.emplace(ival.asString()); + picked = true; + } + } + return picked; +} + +std::string cmQtAutoGenerator::InfoT::ConfigKey(cm::string_view key) const +{ + return cmStrCat(key, '_', Gen_.InfoConfig()); +} + +bool cmQtAutoGenerator::InfoT::GetString(std::string const& key, + std::string& value, + bool required) const +{ + Json::Value const& jval = Json_[key]; + if (!jval.isString()) { + if (!jval.isNull() || required) { + return LogError(cmStrCat(key, " is not a string.")); + } + } else { + value = jval.asString(); + if (value.empty() && required) { + return LogError(cmStrCat(key, " is empty.")); + } + } + return true; +} + +bool cmQtAutoGenerator::InfoT::GetStringConfig(std::string const& key, + std::string& value, + bool required) const +{ + { // Try config + std::string const configKey = ConfigKey(key); + Json::Value const& jval = Json_[configKey]; + if (!jval.isNull()) { + if (!jval.isString()) { + return LogError(cmStrCat(configKey, " is not a string.")); + } + value = jval.asString(); + if (required && value.empty()) { + return LogError(cmStrCat(configKey, " is empty.")); + } + return true; + } + } + // Try plain + return GetString(key, value, required); +} + +bool cmQtAutoGenerator::InfoT::GetBool(std::string const& key, bool& value, + bool required) const +{ + Json::Value const& jval = Json_[key]; + if (jval.isBool()) { + value = jval.asBool(); + } else { + if (!jval.isNull() || required) { + return LogError(cmStrCat(key, " is not a boolean.")); + } + } + return true; +} + +bool cmQtAutoGenerator::InfoT::GetUInt(std::string const& key, + unsigned int& value, + bool required) const +{ + Json::Value const& jval = Json_[key]; + if (jval.isUInt()) { + value = jval.asUInt(); + } else { + if (!jval.isNull() || required) { + return LogError(cmStrCat(key, " is not an unsigned integer.")); + } + } + return true; +} + +bool cmQtAutoGenerator::InfoT::GetArray(std::string const& key, + std::vector<std::string>& list, + bool required) const +{ + Json::Value const& jval = Json_[key]; + if (!jval.isArray()) { + if (!jval.isNull() || required) { + return LogError(cmStrCat(key, " is not an array.")); + } + } + return GetJsonArray(list, jval) || !required; +} + +bool cmQtAutoGenerator::InfoT::GetArray(std::string const& key, + std::unordered_set<std::string>& list, + bool required) const +{ + Json::Value const& jval = Json_[key]; + if (!jval.isArray()) { + if (!jval.isNull() || required) { + return LogError(cmStrCat(key, " is not an array.")); + } + } + return GetJsonArray(list, jval) || !required; } -std::string cmQtAutoGenerator::SettingsFind(std::string const& content, - const char* key) +bool cmQtAutoGenerator::InfoT::GetArrayConfig(std::string const& key, + std::vector<std::string>& list, + bool required) const { - std::string prefix = cmStrCat(key, ':'); - std::string::size_type pos = content.find(prefix); - if (pos != std::string::npos) { + { // Try config + std::string const configKey = ConfigKey(key); + Json::Value const& jval = Json_[configKey]; + if (!jval.isNull()) { + if (!jval.isArray()) { + return LogError(cmStrCat(configKey, " is not an array string.")); + } + if (!GetJsonArray(list, jval) && required) { + return LogError(cmStrCat(configKey, " is empty.")); + } + return true; + } + } + // Try plain + return GetArray(key, list, required); +} + +bool cmQtAutoGenerator::InfoT::LogError(GenT genType, + cm::string_view message) const +{ + Gen_.Log().Error(genType, + cmStrCat("Info error in info file\n", + Quoted(Gen_.InfoFile()), ":\n", message)); + return false; +} + +bool cmQtAutoGenerator::InfoT::LogError(cm::string_view message) const +{ + return LogError(Gen_.GenType_, message); +} + +std::string cmQtAutoGenerator::SettingsFind(cm::string_view content, + cm::string_view key) +{ + cm::string_view res; + std::string const prefix = cmStrCat(key, ':'); + cm::string_view::size_type pos = content.find(prefix); + if (pos != cm::string_view::npos) { pos += prefix.size(); if (pos < content.size()) { - std::string::size_type posE = content.find('\n', pos); - if ((posE != std::string::npos) && (posE != pos)) { - return content.substr(pos, posE - pos); + cm::string_view::size_type posE = content.find('\n', pos); + if ((posE != cm::string_view::npos) && (posE != pos)) { + res = content.substr(pos, posE - pos); } } } - return std::string(); + return std::string(res); } std::string cmQtAutoGenerator::MessagePath(cm::string_view path) const @@ -286,3 +425,66 @@ std::string cmQtAutoGenerator::MessagePath(cm::string_view path) const } return cmQtAutoGen::Quoted(res); } + +bool cmQtAutoGenerator::Run(cm::string_view infoFile, cm::string_view config) +{ + // Info config + InfoConfig_ = std::string(config); + + // Info file + InfoFile_ = std::string(infoFile); + cmSystemTools::CollapseFullPath(InfoFile_); + InfoDir_ = cmSystemTools::GetFilenamePath(InfoFile_); + + // Load info file time + if (!InfoFileTime_.Load(InfoFile_)) { + cmSystemTools::Stderr(cmStrCat("AutoGen: The info file ", + Quoted(InfoFile_), " is not readable\n")); + return false; + } + + { + InfoT info(*this); + + // Read info file + { + cmsys::ifstream ifs(InfoFile_.c_str(), + (std::ios::in | std::ios::binary)); + if (!ifs) { + Log().Error( + GenType_, + cmStrCat("Could not to open info file ", Quoted(InfoFile_))); + return false; + } + if (!info.Read(ifs)) { + Log().Error(GenType_, + cmStrCat("Could not read info file ", Quoted(InfoFile_))); + return false; + } + } + + // -- Read common info settings + { + unsigned int verbosity = 0; + // Info: setup project directories + if (!info.GetUInt("VERBOSITY", verbosity, false) || + !info.GetString("CMAKE_SOURCE_DIR", ProjectDirs_.Source, true) || + !info.GetString("CMAKE_BINARY_DIR", ProjectDirs_.Binary, true) || + !info.GetString("CMAKE_CURRENT_SOURCE_DIR", + ProjectDirs_.CurrentSource, true) || + !info.GetString("CMAKE_CURRENT_BINARY_DIR", + ProjectDirs_.CurrentBinary, true)) { + return false; + } + Logger_.RaiseVerbosity(verbosity); + } + + // -- Call virtual init from info method. + if (!this->InitFromInfo(info)) { + return false; + } + } + + // Call virtual process method. + return this->Process(); +} diff --git a/Source/cmQtAutoGenerator.h b/Source/cmQtAutoGenerator.h index f60acb0..71ee9ce 100644 --- a/Source/cmQtAutoGenerator.h +++ b/Source/cmQtAutoGenerator.h @@ -7,15 +7,16 @@ #include "cmFileTime.h" #include "cmQtAutoGen.h" +#include "cm_jsoncpp_value.h" #include <cm/string_view> +#include <istream> #include <mutex> #include <string> +#include <unordered_set> #include <vector> -class cmMakefile; - /** \class cmQtAutoGenerator * \brief Base class for QtAutoGen generators */ @@ -34,7 +35,7 @@ public: // -- Verbosity unsigned int Verbosity() const { return this->Verbosity_; } void SetVerbosity(unsigned int value) { this->Verbosity_ = value; } - void RaiseVerbosity(std::string const& value); + void RaiseVerbosity(unsigned int value); bool Verbose() const { return (this->Verbosity_ != 0); } void SetVerbose(bool value) { this->Verbosity_ = value ? 1 : 0; } // -- Color output @@ -80,39 +81,97 @@ public: public: // -- Constructors - cmQtAutoGenerator(); + cmQtAutoGenerator(GenT genType); virtual ~cmQtAutoGenerator(); cmQtAutoGenerator(cmQtAutoGenerator const&) = delete; cmQtAutoGenerator& operator=(cmQtAutoGenerator const&) = delete; - // -- Run - bool Run(std::string const& infoFile, std::string const& config); - - // -- InfoFile + // -- Info options std::string const& InfoFile() const { return InfoFile_; } - cmFileTime const& InfoFileTime() const { return InfoFileTime_; } std::string const& InfoDir() const { return InfoDir_; } + cmFileTime const& InfoFileTime() const { return InfoFileTime_; } std::string const& InfoConfig() const { return InfoConfig_; } + // -- Info file parsing + /** Info file reader class. */ + class InfoT + { + public: + InfoT(cmQtAutoGenerator& gen) + : Gen_(gen) + { + } + + /** Read json data from a stream. */ + bool Read(std::istream& istr); + + /** Returns false if the JSON value isn't a string. */ + bool GetString(std::string const& key, std::string& value, + bool required) const; + bool GetStringConfig(std::string const& key, std::string& value, + bool required) const; + bool GetBool(std::string const& key, bool& value, bool required) const; + bool GetUInt(std::string const& key, unsigned int& value, + bool required) const; + /** Returns false if the JSON value isn't an array. */ + bool GetArray(std::string const& key, std::vector<std::string>& list, + bool required) const; + bool GetArray(std::string const& key, + std::unordered_set<std::string>& list, bool required) const; + bool GetArrayConfig(std::string const& key, std::vector<std::string>& list, + bool required) const; + + Json::Value const& GetValue(std::string const& key) const + { + return Json_[key]; + } + + /** Returns true if strings were appended to the list. */ + static bool GetJsonArray(std::vector<std::string>& list, + Json::Value const& jval); + /** Returns true if strings were found in the JSON array. */ + static bool GetJsonArray(std::unordered_set<std::string>& list, + Json::Value const& jval); + + bool LogError(GenT genType, cm::string_view message) const; + bool LogError(cm::string_view message) const; + + private: + std::string ConfigKey(cm::string_view key) const; + + private: + Json::Value Json_; + cmQtAutoGenerator& Gen_; + }; + + // -- Settings file + static std::string SettingsFind(cm::string_view content, + cm::string_view key); + // -- Directories ProjectDirsT const& ProjectDirs() const { return ProjectDirs_; } - - // -- Utility - static std::string SettingsFind(std::string const& content, const char* key); std::string MessagePath(cm::string_view path) const; + // -- Run + bool Run(cm::string_view infoFile, cm::string_view config); + protected: // -- Abstract processing interface - virtual bool Init(cmMakefile* makefile) = 0; + virtual bool InitFromInfo(InfoT const& info) = 0; virtual bool Process() = 0; - ProjectDirsT& ProjectDirsRef() { return ProjectDirs_; } + // - Utility classes + Logger const& Log() const { return Logger_; } private: - // -- Info settings + // -- Generator type + GenT GenType_; + // -- Logging + Logger Logger_; + // -- Info file std::string InfoFile_; - cmFileTime InfoFileTime_; std::string InfoDir_; + cmFileTime InfoFileTime_; std::string InfoConfig_; // -- Directories ProjectDirsT ProjectDirs_; diff --git a/Source/cmQtAutoMocUic.cxx b/Source/cmQtAutoMocUic.cxx index 5cd1ba1..3b0f717 100644 --- a/Source/cmQtAutoMocUic.cxx +++ b/Source/cmQtAutoMocUic.cxx @@ -2,31 +2,570 @@ file Copyright.txt or https://cmake.org/licensing for details. */ #include "cmQtAutoMocUic.h" -#include <algorithm> -#include <set> -#include <utility> - -#include <cm/memory> - #include "cmAlgorithms.h" #include "cmCryptoHash.h" +#include "cmFileTime.h" #include "cmGeneratedFileStream.h" -#include "cmMakefile.h" #include "cmQtAutoGen.h" +#include "cmQtAutoGenerator.h" #include "cmStringAlgorithms.h" #include "cmSystemTools.h" -#include "cmake.h" +#include "cmWorkerPool.h" +#include "cm_jsoncpp_value.h" #include "cmsys/FStream.hxx" +#include "cmsys/RegularExpression.hxx" + +#include <cm/memory> +#include <cm/string_view> + +#include <algorithm> +#include <atomic> +#include <cstddef> +#include <map> +#include <mutex> +#include <set> +#include <string> +#include <unordered_map> +#include <unordered_set> +#include <utility> +#include <vector> #if defined(__APPLE__) # include <unistd.h> #endif -static constexpr std::size_t MocUnderscoreLength = 4; // Length of "moc_" -static constexpr std::size_t UiUnderscoreLength = 3; // Length of "ui_" +namespace { + +constexpr std::size_t MocUnderscoreLength = 4; // Length of "moc_" +constexpr std::size_t UiUnderscoreLength = 3; // Length of "ui_" + +/** \class cmQtAutoMocUicT + * \brief AUTOMOC and AUTOUIC generator + */ +class cmQtAutoMocUicT : public cmQtAutoGenerator +{ +public: + cmQtAutoMocUicT(); + ~cmQtAutoMocUicT() override; + + cmQtAutoMocUicT(cmQtAutoMocUicT const&) = delete; + cmQtAutoMocUicT& operator=(cmQtAutoMocUicT const&) = delete; + +public: + // -- Types + + /** Include string with sub parts. */ + struct IncludeKeyT + { + IncludeKeyT(std::string const& key, std::size_t basePrefixLength); + + std::string Key; // Full include string + std::string Dir; // Include directory + std::string Base; // Base part of the include file name + }; + + /** Search key plus regular expression pair. */ + struct KeyExpT + { + KeyExpT(std::string key, std::string const& exp) + : Key(std::move(key)) + , Exp(exp) + { + } + + std::string Key; + cmsys::RegularExpression Exp; + }; + + /** Source file parsing cache. */ + class ParseCacheT + { + public: + // -- Types + + /** Entry of the file parsing cache. */ + struct FileT + { + void Clear(); + + struct MocT + { + std::string Macro; + struct IncludeT + { + std::vector<IncludeKeyT> Underscore; + std::vector<IncludeKeyT> Dot; + } Include; + std::vector<std::string> Depends; + } Moc; + + struct UicT + { + std::vector<IncludeKeyT> Include; + std::vector<std::string> Depends; + } Uic; + }; + using FileHandleT = std::shared_ptr<FileT>; + using GetOrInsertT = std::pair<FileHandleT, bool>; + + public: + ParseCacheT(); + ~ParseCacheT(); + + bool ReadFromFile(std::string const& fileName); + bool WriteToFile(std::string const& fileName); + + //! Always returns a valid handle + GetOrInsertT GetOrInsert(std::string const& fileName); + + private: + std::unordered_map<std::string, FileHandleT> Map_; + }; + + /** Source file data. */ + class SourceFileT + { + public: + SourceFileT(std::string fileName) + : FileName(std::move(fileName)) + { + } + + public: + std::string FileName; + cmFileTime FileTime; + ParseCacheT::FileHandleT ParseData; + std::string BuildPath; + bool IsHeader = false; + bool Moc = false; + bool Uic = false; + }; + using SourceFileHandleT = std::shared_ptr<SourceFileT>; + using SourceFileMapT = std::map<std::string, SourceFileHandleT>; + + /** Meta compiler file mapping information. */ + struct MappingT + { + SourceFileHandleT SourceFile; + std::string OutputFile; + std::string IncludeString; + std::vector<SourceFileHandleT> IncluderFiles; + }; + using MappingHandleT = std::shared_ptr<MappingT>; + using MappingMapT = std::map<std::string, MappingHandleT>; + + /** Common settings. */ + class BaseSettingsT + { + public: + // -- Constructors + BaseSettingsT(); + ~BaseSettingsT(); + + BaseSettingsT(BaseSettingsT const&) = delete; + BaseSettingsT& operator=(BaseSettingsT const&) = delete; + + // -- Attributes + // - Config + bool MultiConfig = false; + unsigned int QtVersionMajor = 4; + unsigned int ThreadCount = 0; + // - Directories + std::string AutogenBuildDir; + std::string AutogenIncludeDir; + // - Files + std::string CMakeExecutable; + cmFileTime CMakeExecutableTime; + std::string ParseCacheFile; + std::vector<std::string> HeaderExtensions; + }; + + /** Shared common variables. */ + class BaseEvalT + { + public: + // -- Parse Cache + bool ParseCacheChanged = false; + cmFileTime ParseCacheTime; + ParseCacheT ParseCache; + + // -- Sources + SourceFileMapT Headers; + SourceFileMapT Sources; + }; + + /** Moc settings. */ + class MocSettingsT + { + public: + // -- Constructors + MocSettingsT(); + ~MocSettingsT(); + + MocSettingsT(MocSettingsT const&) = delete; + MocSettingsT& operator=(MocSettingsT const&) = delete; + + // -- Const methods + bool skipped(std::string const& fileName) const; + std::string MacrosString() const; + + // -- Attributes + bool Enabled = false; + bool SettingsChanged = false; + bool RelaxedMode = false; + bool PathPrefix = false; + cmFileTime ExecutableTime; + std::string Executable; + std::string CompFileAbs; + std::string PredefsFileAbs; + std::unordered_set<std::string> SkipList; + std::vector<std::string> IncludePaths; + std::vector<std::string> Definitions; + std::vector<std::string> OptionsIncludes; + std::vector<std::string> OptionsDefinitions; + std::vector<std::string> OptionsExtra; + std::vector<std::string> PredefsCmd; + std::vector<KeyExpT> DependFilters; + std::vector<KeyExpT> MacroFilters; + cmsys::RegularExpression RegExpInclude; + }; + + /** Moc shared variables. */ + class MocEvalT + { + public: + // -- predefines file + cmFileTime PredefsTime; + // -- Mappings + MappingMapT HeaderMappings; + MappingMapT SourceMappings; + MappingMapT Includes; + // -- Discovered files + SourceFileMapT HeadersDiscovered; + // -- Output directories + std::unordered_set<std::string> OutputDirs; + // -- Mocs compilation + bool CompUpdated = false; + std::vector<std::string> CompFiles; + }; + + /** Uic settings. */ + class UicSettingsT + { + public: + struct UiFile + { + std::vector<std::string> Options; + }; + + public: + UicSettingsT(); + ~UicSettingsT(); + + UicSettingsT(UicSettingsT const&) = delete; + UicSettingsT& operator=(UicSettingsT const&) = delete; + + // -- Const methods + bool skipped(std::string const& fileName) const; + + // -- Attributes + bool Enabled = false; + bool SettingsChanged = false; + cmFileTime ExecutableTime; + std::string Executable; + std::unordered_set<std::string> SkipList; + std::vector<std::string> Options; + std::unordered_map<std::string, UiFile> UiFiles; + std::vector<std::string> SearchPaths; + cmsys::RegularExpression RegExpInclude; + }; + + /** Uic shared variables. */ + class UicEvalT + { + public: + // -- Discovered files + SourceFileMapT UiFiles; + // -- Mappings + MappingMapT Includes; + // -- Output directories + std::unordered_set<std::string> OutputDirs; + }; + + /** Abstract job class for concurrent job processing. */ + class JobT : public cmWorkerPool::JobT + { + protected: + /** Protected default constructor. */ + JobT(bool fence = false) + : cmWorkerPool::JobT(fence) + { + } + + //! Get the generator. Only valid during Process() call! + cmQtAutoMocUicT* Gen() const + { + return static_cast<cmQtAutoMocUicT*>(UserData()); + }; + + // -- Accessors. Only valid during Process() call! + Logger const& Log() const { return Gen()->Log(); } + BaseSettingsT const& BaseConst() const { return Gen()->BaseConst(); } + BaseEvalT& BaseEval() const { return Gen()->BaseEval(); } + MocSettingsT const& MocConst() const { return Gen()->MocConst(); } + MocEvalT& MocEval() const { return Gen()->MocEval(); } + UicSettingsT const& UicConst() const { return Gen()->UicConst(); } + UicEvalT& UicEval() const { return Gen()->UicEval(); } + + // -- Logging + std::string MessagePath(cm::string_view path) const + { + return Gen()->MessagePath(path); + } + // - Error logging with automatic abort + void LogError(GenT genType, cm::string_view message) const; + void LogCommandError(GenT genType, cm::string_view message, + std::vector<std::string> const& command, + std::string const& output) const; + + /** @brief Run an external process. Use only during Process() call! */ + bool RunProcess(GenT genType, cmWorkerPool::ProcessResultT& result, + std::vector<std::string> const& command, + std::string* infoMessage = nullptr); + }; + + /** Fence job utility class. */ + class JobFenceT : public JobT + { + public: + JobFenceT() + : JobT(true) + { + } + void Process() override{}; + }; + + /** Generate moc_predefs.h. */ + class JobMocPredefsT : public JobFenceT + { + void Process() override; + bool Update(std::string* reason) const; + }; + + /** File parse job base class. */ + class JobParseT : public JobT + { + public: + JobParseT(SourceFileHandleT fileHandle) + : FileHandle(std::move(fileHandle)) + { + } + + protected: + bool ReadFile(); + void CreateKeys(std::vector<IncludeKeyT>& container, + std::set<std::string> const& source, + std::size_t basePrefixLength); + void MocMacro(); + void MocDependecies(); + void MocIncludes(); + void UicIncludes(); + + protected: + SourceFileHandleT FileHandle; + std::string Content; + }; + + /** Header file parse job. */ + class JobParseHeaderT : public JobParseT + { + public: + using JobParseT::JobParseT; + void Process() override; + }; + + /** Source file parse job. */ + class JobParseSourceT : public JobParseT + { + public: + using JobParseT::JobParseT; + void Process() override; + }; + + /** Evaluate cached file parse data - moc. */ + class JobEvalCacheT : public JobT + { + protected: + std::string MessageSearchLocations() const; + std::vector<std::string> SearchLocations; + }; + + /** Evaluate cached file parse data - moc. */ + class JobEvalCacheMocT : public JobEvalCacheT + { + void Process() override; + bool EvalHeader(SourceFileHandleT source); + bool EvalSource(SourceFileHandleT const& source); + bool FindIncludedHeader(SourceFileHandleT& headerHandle, + cm::string_view includerDir, + cm::string_view includeBase); + bool RegisterIncluded(std::string const& includeString, + SourceFileHandleT includerFileHandle, + SourceFileHandleT sourceFileHandle) const; + void RegisterMapping(MappingHandleT mappingHandle) const; + std::string MessageHeader(cm::string_view headerBase) const; + }; + + /** Evaluate cached file parse data - uic. */ + class JobEvalCacheUicT : public JobEvalCacheT + { + void Process() override; + bool EvalFile(SourceFileHandleT const& sourceFileHandle); + bool FindIncludedUi(cm::string_view sourceDirPrefix, + cm::string_view includePrefix); + bool RegisterMapping(std::string const& includeString, + SourceFileHandleT includerFileHandle); + + std::string UiName; + SourceFileHandleT UiFileHandle; + }; + + /** Evaluate cached file parse data - finish */ + class JobEvalCacheFinishT : public JobFenceT + { + void Process() override; + }; + + /** Dependency probing base job. */ + class JobProbeDepsT : public JobT + { + }; + + /** Probes file dependencies and generates moc compile jobs. */ + class JobProbeDepsMocT : public JobProbeDepsT + { + void Process() override; + bool Generate(MappingHandleT const& mapping, bool compFile) const; + bool Probe(MappingT const& mapping, std::string* reason) const; + std::pair<std::string, cmFileTime> FindDependency( + std::string const& sourceDir, std::string const& includeString) const; + }; + + /** Probes file dependencies and generates uic compile jobs. */ + class JobProbeDepsUicT : public JobProbeDepsT + { + void Process() override; + bool Probe(MappingT const& mapping, std::string* reason) const; + }; + + /** Dependency probing finish job. */ + class JobProbeDepsFinishT : public JobFenceT + { + void Process() override; + }; -cmQtAutoMocUic::IncludeKeyT::IncludeKeyT(std::string const& key, - std::size_t basePrefixLength) + /** Meta compiler base job. */ + class JobCompileT : public JobT + { + public: + JobCompileT(MappingHandleT uicMapping, std::unique_ptr<std::string> reason) + : Mapping(std::move(uicMapping)) + , Reason(std::move(reason)) + { + } + + protected: + MappingHandleT Mapping; + std::unique_ptr<std::string> Reason; + }; + + /** moc compiles a file. */ + class JobCompileMocT : public JobCompileT + { + public: + using JobCompileT::JobCompileT; + void Process() override; + }; + + /** uic compiles a file. */ + class JobCompileUicT : public JobCompileT + { + public: + using JobCompileT::JobCompileT; + void Process() override; + }; + + /** Generate mocs_compilation.cpp. */ + class JobMocsCompilationT : public JobFenceT + { + private: + void Process() override; + }; + + /** @brief The last job. */ + class JobFinishT : public JobFenceT + { + private: + void Process() override; + }; + + // -- Const settings interface + BaseSettingsT const& BaseConst() const { return this->BaseConst_; } + BaseEvalT& BaseEval() { return this->BaseEval_; } + MocSettingsT const& MocConst() const { return this->MocConst_; } + MocEvalT& MocEval() { return this->MocEval_; } + UicSettingsT const& UicConst() const { return this->UicConst_; } + UicEvalT& UicEval() { return this->UicEval_; } + + // -- Parallel job processing interface + cmWorkerPool& WorkerPool() { return WorkerPool_; } + void AbortError() { Abort(true); } + void AbortSuccess() { Abort(false); } + + // -- Utility + std::string AbsoluteBuildPath(cm::string_view relativePath) const; + std::string AbsoluteIncludePath(cm::string_view relativePath) const; + template <class JOBTYPE> + void CreateParseJobs(SourceFileMapT const& sourceMap); + std::string CollapseFullPathTS(std::string const& path) const; + +private: + // -- Abstract processing interface + bool InitFromInfo(InfoT const& info) override; + void InitJobs(); + bool Process() override; + // -- Settings file + void SettingsFileRead(); + bool SettingsFileWrite(); + // -- Parse cache + void ParseCacheRead(); + bool ParseCacheWrite(); + // -- Thread processing + void Abort(bool error); + // -- Generation + bool CreateDirectories(); + +private: + // -- Settings + BaseSettingsT BaseConst_; + BaseEvalT BaseEval_; + MocSettingsT MocConst_; + MocEvalT MocEval_; + UicSettingsT UicConst_; + UicEvalT UicEval_; + // -- Settings file + std::string SettingsFile_; + std::string SettingsStringMoc_; + std::string SettingsStringUic_; + // -- Worker thread pool + std::atomic<bool> JobError_ = ATOMIC_VAR_INIT(false); + cmWorkerPool WorkerPool_; + // -- Concurrent processing + mutable std::mutex CMakeLibMutex_; +}; + +cmQtAutoMocUicT::IncludeKeyT::IncludeKeyT(std::string const& key, + std::size_t basePrefixLength) : Key(key) , Dir(SubDirPrefix(key)) , Base(cmSystemTools::GetFilenameWithoutLastExtension(key)) @@ -36,7 +575,7 @@ cmQtAutoMocUic::IncludeKeyT::IncludeKeyT(std::string const& key, } } -void cmQtAutoMocUic::ParseCacheT::FileT::Clear() +void cmQtAutoMocUicT::ParseCacheT::FileT::Clear() { Moc.Macro.clear(); Moc.Include.Underscore.clear(); @@ -47,18 +586,8 @@ void cmQtAutoMocUic::ParseCacheT::FileT::Clear() Uic.Depends.clear(); } -cmQtAutoMocUic::ParseCacheT::FileHandleT cmQtAutoMocUic::ParseCacheT::Get( - std::string const& fileName) const -{ - auto it = Map_.find(fileName); - if (it != Map_.end()) { - return it->second; - } - return FileHandleT(); -} - -cmQtAutoMocUic::ParseCacheT::GetOrInsertT -cmQtAutoMocUic::ParseCacheT::GetOrInsert(std::string const& fileName) +cmQtAutoMocUicT::ParseCacheT::GetOrInsertT +cmQtAutoMocUicT::ParseCacheT::GetOrInsert(std::string const& fileName) { // Find existing entry { @@ -74,15 +603,10 @@ cmQtAutoMocUic::ParseCacheT::GetOrInsert(std::string const& fileName) }; } -cmQtAutoMocUic::ParseCacheT::ParseCacheT() = default; -cmQtAutoMocUic::ParseCacheT::~ParseCacheT() = default; - -void cmQtAutoMocUic::ParseCacheT::Clear() -{ - Map_.clear(); -} +cmQtAutoMocUicT::ParseCacheT::ParseCacheT() = default; +cmQtAutoMocUicT::ParseCacheT::~ParseCacheT() = default; -bool cmQtAutoMocUic::ParseCacheT::ReadFromFile(std::string const& fileName) +bool cmQtAutoMocUicT::ParseCacheT::ReadFromFile(std::string const& fileName) { cmsys::ifstream fin(fileName.c_str()); if (!fin) { @@ -145,7 +669,7 @@ bool cmQtAutoMocUic::ParseCacheT::ReadFromFile(std::string const& fileName) return true; } -bool cmQtAutoMocUic::ParseCacheT::WriteToFile(std::string const& fileName) +bool cmQtAutoMocUicT::ParseCacheT::WriteToFile(std::string const& fileName) { cmGeneratedFileStream ofs(fileName); if (!ofs) { @@ -177,24 +701,24 @@ bool cmQtAutoMocUic::ParseCacheT::WriteToFile(std::string const& fileName) return ofs.Close(); } -cmQtAutoMocUic::BaseSettingsT::BaseSettingsT() = default; -cmQtAutoMocUic::BaseSettingsT::~BaseSettingsT() = default; +cmQtAutoMocUicT::BaseSettingsT::BaseSettingsT() = default; +cmQtAutoMocUicT::BaseSettingsT::~BaseSettingsT() = default; -cmQtAutoMocUic::MocSettingsT::MocSettingsT() +cmQtAutoMocUicT::MocSettingsT::MocSettingsT() { RegExpInclude.compile( "(^|\n)[ \t]*#[ \t]*include[ \t]+" "[\"<](([^ \">]+/)?moc_[^ \">/]+\\.cpp|[^ \">]+\\.moc)[\">]"); } -cmQtAutoMocUic::MocSettingsT::~MocSettingsT() = default; +cmQtAutoMocUicT::MocSettingsT::~MocSettingsT() = default; -bool cmQtAutoMocUic::MocSettingsT::skipped(std::string const& fileName) const +bool cmQtAutoMocUicT::MocSettingsT::skipped(std::string const& fileName) const { return (!Enabled || (SkipList.find(fileName) != SkipList.end())); } -std::string cmQtAutoMocUic::MocSettingsT::MacrosString() const +std::string cmQtAutoMocUicT::MocSettingsT::MacrosString() const { std::string res; const auto itB = MacroFilters.cbegin(); @@ -216,27 +740,27 @@ std::string cmQtAutoMocUic::MocSettingsT::MacrosString() const return res; } -cmQtAutoMocUic::UicSettingsT::UicSettingsT() +cmQtAutoMocUicT::UicSettingsT::UicSettingsT() { RegExpInclude.compile("(^|\n)[ \t]*#[ \t]*include[ \t]+" "[\"<](([^ \">]+/)?ui_[^ \">/]+\\.h)[\">]"); } -cmQtAutoMocUic::UicSettingsT::~UicSettingsT() = default; +cmQtAutoMocUicT::UicSettingsT::~UicSettingsT() = default; -bool cmQtAutoMocUic::UicSettingsT::skipped(std::string const& fileName) const +bool cmQtAutoMocUicT::UicSettingsT::skipped(std::string const& fileName) const { return (!Enabled || (SkipList.find(fileName) != SkipList.end())); } -void cmQtAutoMocUic::JobT::LogError(GenT genType, - cm::string_view message) const +void cmQtAutoMocUicT::JobT::LogError(GenT genType, + cm::string_view message) const { Gen()->AbortError(); Gen()->Log().Error(genType, message); } -void cmQtAutoMocUic::JobT::LogCommandError( +void cmQtAutoMocUicT::JobT::LogCommandError( GenT genType, cm::string_view message, std::vector<std::string> const& command, std::string const& output) const { @@ -244,10 +768,10 @@ void cmQtAutoMocUic::JobT::LogCommandError( Gen()->Log().ErrorCommand(genType, message, command, output); } -bool cmQtAutoMocUic::JobT::RunProcess(GenT genType, - cmWorkerPool::ProcessResultT& result, - std::vector<std::string> const& command, - std::string* infoMessage) +bool cmQtAutoMocUicT::JobT::RunProcess(GenT genType, + cmWorkerPool::ProcessResultT& result, + std::vector<std::string> const& command, + std::string* infoMessage) { // Log command if (Log().Verbose()) { @@ -265,7 +789,7 @@ bool cmQtAutoMocUic::JobT::RunProcess(GenT genType, BaseConst().AutogenBuildDir); } -void cmQtAutoMocUic::JobMocPredefsT::Process() +void cmQtAutoMocUicT::JobMocPredefsT::Process() { // (Re)generate moc_predefs.h on demand std::unique_ptr<std::string> reason; @@ -327,7 +851,7 @@ void cmQtAutoMocUic::JobMocPredefsT::Process() } } -bool cmQtAutoMocUic::JobMocPredefsT::Update(std::string* reason) const +bool cmQtAutoMocUicT::JobMocPredefsT::Update(std::string* reason) const { // Test if the file exists if (!MocEval().PredefsTime.Load(MocConst().PredefsFileAbs)) { @@ -366,7 +890,7 @@ bool cmQtAutoMocUic::JobMocPredefsT::Update(std::string* reason) const return false; } -bool cmQtAutoMocUic::JobParseT::ReadFile() +bool cmQtAutoMocUicT::JobParseT::ReadFile() { // Clear old parse information FileHandle->ParseData->Clear(); @@ -393,9 +917,9 @@ bool cmQtAutoMocUic::JobParseT::ReadFile() return true; } -void cmQtAutoMocUic::JobParseT::CreateKeys(std::vector<IncludeKeyT>& container, - std::set<std::string> const& source, - std::size_t basePrefixLength) +void cmQtAutoMocUicT::JobParseT::CreateKeys( + std::vector<IncludeKeyT>& container, std::set<std::string> const& source, + std::size_t basePrefixLength) { if (source.empty()) { return; @@ -406,7 +930,7 @@ void cmQtAutoMocUic::JobParseT::CreateKeys(std::vector<IncludeKeyT>& container, } } -void cmQtAutoMocUic::JobParseT::MocMacro() +void cmQtAutoMocUicT::JobParseT::MocMacro() { for (KeyExpT const& filter : MocConst().MacroFilters) { // Run a simple find string check @@ -423,7 +947,7 @@ void cmQtAutoMocUic::JobParseT::MocMacro() } } -void cmQtAutoMocUic::JobParseT::MocDependecies() +void cmQtAutoMocUicT::JobParseT::MocDependecies() { if (MocConst().DependFilters.empty()) { return; @@ -464,7 +988,7 @@ void cmQtAutoMocUic::JobParseT::MocDependecies() } } -void cmQtAutoMocUic::JobParseT::MocIncludes() +void cmQtAutoMocUicT::JobParseT::MocIncludes() { if (Content.find("moc") == std::string::npos) { return; @@ -497,7 +1021,7 @@ void cmQtAutoMocUic::JobParseT::MocIncludes() CreateKeys(Include.Dot, dot, 0); } -void cmQtAutoMocUic::JobParseT::UicIncludes() +void cmQtAutoMocUicT::JobParseT::UicIncludes() { if (Content.find("ui_") == std::string::npos) { return; @@ -517,7 +1041,7 @@ void cmQtAutoMocUic::JobParseT::UicIncludes() CreateKeys(FileHandle->ParseData->Uic.Include, includes, UiUnderscoreLength); } -void cmQtAutoMocUic::JobParseHeaderT::Process() +void cmQtAutoMocUicT::JobParseHeaderT::Process() { if (!ReadFile()) { return; @@ -533,7 +1057,7 @@ void cmQtAutoMocUic::JobParseHeaderT::Process() } } -void cmQtAutoMocUic::JobParseSourceT::Process() +void cmQtAutoMocUicT::JobParseSourceT::Process() { if (!ReadFile()) { return; @@ -550,7 +1074,7 @@ void cmQtAutoMocUic::JobParseSourceT::Process() } } -std::string cmQtAutoMocUic::JobEvalCacheT::MessageSearchLocations() const +std::string cmQtAutoMocUicT::JobEvalCacheT::MessageSearchLocations() const { std::string res; res.reserve(512); @@ -562,7 +1086,7 @@ std::string cmQtAutoMocUic::JobEvalCacheT::MessageSearchLocations() const return res; } -void cmQtAutoMocUic::JobEvalCacheMocT::Process() +void cmQtAutoMocUicT::JobEvalCacheMocT::Process() { // Evaluate headers for (auto const& pair : BaseEval().Headers) { @@ -578,7 +1102,7 @@ void cmQtAutoMocUic::JobEvalCacheMocT::Process() } } -bool cmQtAutoMocUic::JobEvalCacheMocT::EvalHeader(SourceFileHandleT source) +bool cmQtAutoMocUicT::JobEvalCacheMocT::EvalHeader(SourceFileHandleT source) { SourceFileT const& sourceFile = *source; auto const& parseData = sourceFile.ParseData->Moc; @@ -605,7 +1129,7 @@ bool cmQtAutoMocUic::JobEvalCacheMocT::EvalHeader(SourceFileHandleT source) return true; } -bool cmQtAutoMocUic::JobEvalCacheMocT::EvalSource( +bool cmQtAutoMocUicT::JobEvalCacheMocT::EvalSource( SourceFileHandleT const& source) { SourceFileT const& sourceFile = *source; @@ -806,7 +1330,7 @@ bool cmQtAutoMocUic::JobEvalCacheMocT::EvalSource( return true; } -bool cmQtAutoMocUic::JobEvalCacheMocT::FindIncludedHeader( +bool cmQtAutoMocUicT::JobEvalCacheMocT::FindIncludedHeader( SourceFileHandleT& headerHandle, cm::string_view includerDir, cm::string_view includeBase) { @@ -871,7 +1395,7 @@ bool cmQtAutoMocUic::JobEvalCacheMocT::FindIncludedHeader( return false; } -bool cmQtAutoMocUic::JobEvalCacheMocT::RegisterIncluded( +bool cmQtAutoMocUicT::JobEvalCacheMocT::RegisterIncluded( std::string const& includeString, SourceFileHandleT includerFileHandle, SourceFileHandleT sourceFileHandle) const { @@ -919,7 +1443,7 @@ bool cmQtAutoMocUic::JobEvalCacheMocT::RegisterIncluded( return true; } -void cmQtAutoMocUic::JobEvalCacheMocT::RegisterMapping( +void cmQtAutoMocUicT::JobEvalCacheMocT::RegisterMapping( MappingHandleT mappingHandle) const { auto& regMap = mappingHandle->SourceFile->IsHeader @@ -938,14 +1462,14 @@ void cmQtAutoMocUic::JobEvalCacheMocT::RegisterMapping( } } -std::string cmQtAutoMocUic::JobEvalCacheMocT::MessageHeader( +std::string cmQtAutoMocUicT::JobEvalCacheMocT::MessageHeader( cm::string_view headerBase) const { return MessagePath(cmStrCat( headerBase, ".{", cmJoin(this->BaseConst().HeaderExtensions, ","), '}')); } -void cmQtAutoMocUic::JobEvalCacheUicT::Process() +void cmQtAutoMocUicT::JobEvalCacheUicT::Process() { // Prepare buffers SearchLocations.reserve((UicConst().SearchPaths.size() + 1) * 2); @@ -964,7 +1488,7 @@ void cmQtAutoMocUic::JobEvalCacheUicT::Process() } } -bool cmQtAutoMocUic::JobEvalCacheUicT::EvalFile( +bool cmQtAutoMocUicT::JobEvalCacheUicT::EvalFile( SourceFileHandleT const& sourceFileHandle) { SourceFileT const& sourceFile = *sourceFileHandle; @@ -999,7 +1523,7 @@ bool cmQtAutoMocUic::JobEvalCacheUicT::EvalFile( return true; } -bool cmQtAutoMocUic::JobEvalCacheUicT::FindIncludedUi( +bool cmQtAutoMocUicT::JobEvalCacheUicT::FindIncludedUi( cm::string_view sourceDirPrefix, cm::string_view includePrefix) { // Clear locations buffer @@ -1053,7 +1577,7 @@ bool cmQtAutoMocUic::JobEvalCacheUicT::FindIncludedUi( return false; } -bool cmQtAutoMocUic::JobEvalCacheUicT::RegisterMapping( +bool cmQtAutoMocUicT::JobEvalCacheUicT::RegisterMapping( std::string const& includeString, SourceFileHandleT includerFileHandle) { auto& Includes = Gen()->UicEval().Includes; @@ -1098,7 +1622,7 @@ bool cmQtAutoMocUic::JobEvalCacheUicT::RegisterMapping( return true; } -void cmQtAutoMocUic::JobEvalCacheFinishT::Process() +void cmQtAutoMocUicT::JobEvalCacheFinishT::Process() { // Add discovered header parse jobs Gen()->CreateParseJobs<JobParseHeaderT>(MocEval().HeadersDiscovered); @@ -1118,7 +1642,7 @@ void cmQtAutoMocUic::JobEvalCacheFinishT::Process() } } -void cmQtAutoMocUic::JobProbeDepsMocT::Process() +void cmQtAutoMocUicT::JobProbeDepsMocT::Process() { // Create moc header jobs for (auto const& pair : MocEval().HeaderMappings) { @@ -1140,8 +1664,8 @@ void cmQtAutoMocUic::JobProbeDepsMocT::Process() } } -bool cmQtAutoMocUic::JobProbeDepsMocT::Generate(MappingHandleT const& mapping, - bool compFile) const +bool cmQtAutoMocUicT::JobProbeDepsMocT::Generate(MappingHandleT const& mapping, + bool compFile) const { std::unique_ptr<std::string> reason; if (Log().Verbose()) { @@ -1160,8 +1684,8 @@ bool cmQtAutoMocUic::JobProbeDepsMocT::Generate(MappingHandleT const& mapping, return true; } -bool cmQtAutoMocUic::JobProbeDepsMocT::Probe(MappingT const& mapping, - std::string* reason) const +bool cmQtAutoMocUicT::JobProbeDepsMocT::Probe(MappingT const& mapping, + std::string* reason) const { std::string const& sourceFile = mapping.SourceFile->FileName; std::string const& outputFile = mapping.OutputFile; @@ -1251,7 +1775,7 @@ bool cmQtAutoMocUic::JobProbeDepsMocT::Probe(MappingT const& mapping, } std::pair<std::string, cmFileTime> -cmQtAutoMocUic::JobProbeDepsMocT::FindDependency( +cmQtAutoMocUicT::JobProbeDepsMocT::FindDependency( std::string const& sourceDir, std::string const& includeString) const { using ResPair = std::pair<std::string, cmFileTime>; @@ -1273,7 +1797,7 @@ cmQtAutoMocUic::JobProbeDepsMocT::FindDependency( return ResPair(); } -void cmQtAutoMocUic::JobProbeDepsUicT::Process() +void cmQtAutoMocUicT::JobProbeDepsUicT::Process() { for (auto const& pair : Gen()->UicEval().Includes) { MappingHandleT const& mapping = pair.second; @@ -1292,8 +1816,8 @@ void cmQtAutoMocUic::JobProbeDepsUicT::Process() } } -bool cmQtAutoMocUic::JobProbeDepsUicT::Probe(MappingT const& mapping, - std::string* reason) const +bool cmQtAutoMocUicT::JobProbeDepsUicT::Probe(MappingT const& mapping, + std::string* reason) const { std::string const& sourceFile = mapping.SourceFile->FileName; std::string const& outputFile = mapping.OutputFile; @@ -1342,7 +1866,7 @@ bool cmQtAutoMocUic::JobProbeDepsUicT::Probe(MappingT const& mapping, return false; } -void cmQtAutoMocUic::JobProbeDepsFinishT::Process() +void cmQtAutoMocUicT::JobProbeDepsFinishT::Process() { // Create output directories { @@ -1378,7 +1902,7 @@ void cmQtAutoMocUic::JobProbeDepsFinishT::Process() Gen()->WorkerPool().EmplaceJob<JobFinishT>(); } -void cmQtAutoMocUic::JobCompileMocT::Process() +void cmQtAutoMocUicT::JobCompileMocT::Process() { std::string const& sourceFile = Mapping->SourceFile->FileName; std::string const& outputFile = Mapping->OutputFile; @@ -1455,7 +1979,7 @@ void cmQtAutoMocUic::JobCompileMocT::Process() } } -void cmQtAutoMocUic::JobCompileUicT::Process() +void cmQtAutoMocUicT::JobCompileUicT::Process() { std::string const& sourceFile = Mapping->SourceFile->FileName; std::string const& outputFile = Mapping->OutputFile; @@ -1464,10 +1988,10 @@ void cmQtAutoMocUic::JobCompileUicT::Process() std::vector<std::string> cmd; cmd.push_back(UicConst().Executable); { - std::vector<std::string> allOpts = UicConst().TargetOptions; - auto optionIt = UicConst().Options.find(sourceFile); - if (optionIt != UicConst().Options.end()) { - UicMergeOptions(allOpts, optionIt->second, + std::vector<std::string> allOpts = UicConst().Options; + auto optionIt = UicConst().UiFiles.find(sourceFile); + if (optionIt != UicConst().UiFiles.end()) { + UicMergeOptions(allOpts, optionIt->second.Options, (BaseConst().QtVersionMajor == 5)); } cmAppend(cmd, allOpts); @@ -1498,7 +2022,7 @@ void cmQtAutoMocUic::JobCompileUicT::Process() } } -void cmQtAutoMocUic::JobMocsCompilationT::Process() +void cmQtAutoMocUicT::JobMocsCompilationT::Process() { // Compose mocs compilation file content std::string content = @@ -1543,343 +2067,313 @@ void cmQtAutoMocUic::JobMocsCompilationT::Process() } } -void cmQtAutoMocUic::JobFinishT::Process() +void cmQtAutoMocUicT::JobFinishT::Process() { Gen()->AbortSuccess(); } -cmQtAutoMocUic::cmQtAutoMocUic() = default; -cmQtAutoMocUic::~cmQtAutoMocUic() = default; +cmQtAutoMocUicT::cmQtAutoMocUicT() + : cmQtAutoGenerator(GenT::GEN) +{ +} +cmQtAutoMocUicT::~cmQtAutoMocUicT() = default; -bool cmQtAutoMocUic::Init(cmMakefile* makefile) +bool cmQtAutoMocUicT::InitFromInfo(InfoT const& info) { - // Utility lambdas - auto InfoGet = [makefile](cm::string_view key) { - return makefile->GetSafeDefinition(std::string(key)); - }; - auto InfoGetBool = [makefile](cm::string_view key) { - return makefile->IsOn(std::string(key)); - }; - auto InfoGetList = - [makefile](cm::string_view key) -> std::vector<std::string> { - return cmExpandedList(makefile->GetSafeDefinition(std::string(key))); - }; - auto InfoGetLists = - [makefile](cm::string_view key) -> std::vector<std::vector<std::string>> { - std::vector<std::vector<std::string>> lists; - { - std::string const value = makefile->GetSafeDefinition(std::string(key)); - std::string::size_type pos = 0; - while (pos < value.size()) { - std::string::size_type next = value.find(ListSep, pos); - std::string::size_type length = - (next != std::string::npos) ? next - pos : value.size() - pos; - // Remove enclosing braces - if (length >= 2) { - std::string::const_iterator itBeg = value.begin() + (pos + 1); - std::string::const_iterator itEnd = itBeg + (length - 2); - lists.emplace_back(cmExpandedList(std::string(itBeg, itEnd))); - } - pos += length; - pos += ListSep.size(); - } - } - return lists; - }; - auto InfoGetConfig = [makefile, this](cm::string_view key) -> std::string { - if (const char* valueConf = - makefile->GetDefinition(cmStrCat(key, '_', InfoConfig()))) { - return std::string(valueConf); - } - return makefile->GetSafeDefinition(std::string(key)); - }; - auto InfoGetConfigList = - [&InfoGetConfig](cm::string_view key) -> std::vector<std::string> { - return cmExpandedList(InfoGetConfig(key)); - }; - auto LogInfoError = [this](cm::string_view msg) -> bool { - this->Log().Error(GenT::GEN, - cmStrCat("In ", Quoted(this->InfoFile()), ":\n", msg)); + // -- Required settings + if (!info.GetBool("MULTI_CONFIG", BaseConst_.MultiConfig, true) || + !info.GetUInt("QT_VERSION_MAJOR", BaseConst_.QtVersionMajor, true) || + !info.GetUInt("PARALLEL", BaseConst_.ThreadCount, false) || + !info.GetString("BUILD_DIR", BaseConst_.AutogenBuildDir, true) || + !info.GetStringConfig("INCLUDE_DIR", BaseConst_.AutogenIncludeDir, + true) || + !info.GetString("CMAKE_EXECUTABLE", BaseConst_.CMakeExecutable, true) || + !info.GetStringConfig("PARSE_CACHE_FILE", BaseConst_.ParseCacheFile, + true) || + !info.GetStringConfig("SETTINGS_FILE", SettingsFile_, true) || + !info.GetArray("HEADER_EXTENSIONS", BaseConst_.HeaderExtensions, true) || + !info.GetString("QT_MOC_EXECUTABLE", MocConst_.Executable, false) || + !info.GetString("QT_UIC_EXECUTABLE", UicConst_.Executable, false)) { return false; - }; - auto MatchSizes = [&LogInfoError](cm::string_view keyA, cm::string_view keyB, - std::size_t sizeA, - std::size_t sizeB) -> bool { - if (sizeA == sizeB) { - return true; - } - return LogInfoError(cmStrCat("Lists sizes mismatch ", keyA, '(', sizeA, - ") ", keyB, '(', sizeB, ')')); - }; - - // -- Read info file - if (!makefile->ReadListFile(InfoFile())) { - return LogInfoError("File processing failed"); } - // -- Meta - Logger_.RaiseVerbosity(InfoGet("AM_VERBOSITY")); - BaseConst_.MultiConfig = InfoGetBool("AM_MULTI_CONFIG"); - { - unsigned long num = 1; - if (cmStrToULong(InfoGet("AM_PARALLEL"), &num)) { - num = std::max<unsigned long>(num, 1); - num = std::min<unsigned long>(num, ParallelMax); - } - WorkerPool_.SetThreadCount(static_cast<unsigned int>(num)); - } - BaseConst_.HeaderExtensions = - makefile->GetCMakeInstance()->GetHeaderExtensions(); - - // - Files and directories - ProjectDirsRef().Source = InfoGet("AM_CMAKE_SOURCE_DIR"); - ProjectDirsRef().Binary = InfoGet("AM_CMAKE_BINARY_DIR"); - ProjectDirsRef().CurrentSource = InfoGet("AM_CMAKE_CURRENT_SOURCE_DIR"); - ProjectDirsRef().CurrentBinary = InfoGet("AM_CMAKE_CURRENT_BINARY_DIR"); - BaseConst_.AutogenBuildDir = InfoGet("AM_BUILD_DIR"); - if (BaseConst_.AutogenBuildDir.empty()) { - return LogInfoError("Autogen build directory missing."); - } - BaseConst_.AutogenIncludeDir = InfoGetConfig("AM_INCLUDE_DIR"); - if (BaseConst_.AutogenIncludeDir.empty()) { - return LogInfoError("Autogen include directory missing."); - } - BaseConst_.CMakeExecutable = InfoGetConfig("AM_CMAKE_EXECUTABLE"); - if (BaseConst_.CMakeExecutable.empty()) { - return LogInfoError("CMake executable file name missing."); - } + // -- Checks if (!BaseConst_.CMakeExecutableTime.Load(BaseConst_.CMakeExecutable)) { - return LogInfoError(cmStrCat("The CMake executable ", - MessagePath(BaseConst_.CMakeExecutable), - " does not exist.")); - } - BaseConst_.ParseCacheFile = InfoGetConfig("AM_PARSE_CACHE_FILE"); - if (BaseConst_.ParseCacheFile.empty()) { - return LogInfoError("Parse cache file name missing."); + return info.LogError(cmStrCat("The CMake executable ", + MessagePath(BaseConst_.CMakeExecutable), + " does not exist.")); } - // - Settings file - SettingsFile_ = InfoGetConfig("AM_SETTINGS_FILE"); - if (SettingsFile_.empty()) { - return LogInfoError("Settings file name missing."); - } - - // - Qt environment - { - unsigned long qtv = BaseConst_.QtVersionMajor; - if (cmStrToULong(InfoGet("AM_QT_VERSION_MAJOR"), &qtv)) { - BaseConst_.QtVersionMajor = static_cast<unsigned int>(qtv); - } - } + // -- Evaluate values + BaseConst_.ThreadCount = std::min(BaseConst_.ThreadCount, ParallelMax); + WorkerPool_.SetThreadCount(BaseConst_.ThreadCount); - // - Moc - MocConst_.Executable = InfoGet("AM_QT_MOC_EXECUTABLE"); - if (!MocConst().Executable.empty()) { + // -- Moc + if (!MocConst_.Executable.empty()) { + // -- Moc is enabled MocConst_.Enabled = true; - // Load the executable file time - if (!MocConst_.ExecutableTime.Load(MocConst_.Executable)) { - return LogInfoError(cmStrCat("The moc executable ", - MessagePath(MocConst_.Executable), - " does not exist.")); - } - for (std::string& sfl : InfoGetList("AM_MOC_SKIP")) { - MocConst_.SkipList.insert(std::move(sfl)); - } - MocConst_.Definitions = InfoGetConfigList("AM_MOC_DEFINITIONS"); - MocConst_.IncludePaths = InfoGetConfigList("AM_MOC_INCLUDES"); - MocConst_.OptionsExtra = InfoGetList("AM_MOC_OPTIONS"); - MocConst_.RelaxedMode = InfoGetBool("AM_MOC_RELAXED_MODE"); - MocConst_.PathPrefix = InfoGetBool("AM_MOC_PATH_PREFIX"); + // -- Temporary buffers + struct + { + std::vector<std::string> MacroNames; + std::vector<std::string> DependFilters; + } tmp; + + // -- Required settings + if (!info.GetBool("MOC_RELAXED_MODE", MocConst_.RelaxedMode, false) || + !info.GetBool("MOC_PATH_PREFIX", MocConst_.PathPrefix, true) || + !info.GetArray("MOC_SKIP", MocConst_.SkipList, false) || + !info.GetArrayConfig("MOC_DEFINITIONS", MocConst_.Definitions, + false) || + !info.GetArrayConfig("MOC_INCLUDES", MocConst_.IncludePaths, false) || + !info.GetArray("MOC_OPTIONS", MocConst_.OptionsExtra, false) || + !info.GetStringConfig("MOC_COMPILATION_FILE", MocConst_.CompFileAbs, + true) || + !info.GetArray("MOC_PREDEFS_CMD", MocConst_.PredefsCmd, false) || + !info.GetStringConfig("MOC_PREDEFS_FILE", MocConst_.PredefsFileAbs, + !MocConst_.PredefsCmd.empty()) || + !info.GetArray("MOC_MACRO_NAMES", tmp.MacroNames, true) || + !info.GetArray("MOC_DEPEND_FILTERS", tmp.DependFilters, false)) { + return false; + } - for (std::string const& item : InfoGetList("AM_MOC_MACRO_NAMES")) { + // -- Evaluate settings + for (std::string const& item : tmp.MacroNames) { MocConst_.MacroFilters.emplace_back( item, ("[\n][ \t]*{?[ \t]*" + item).append("[^a-zA-Z0-9_]")); } + // Dependency filters { - auto addFilter = [this, &LogInfoError](std::string const& key, - std::string const& exp) -> bool { - auto filterErr = [&LogInfoError, &key, - &exp](cm::string_view err) -> bool { - return LogInfoError(cmStrCat("AUTOMOC_DEPEND_FILTERS: ", err, '\n', - " Key: ", Quoted(key), '\n', - " Exp: ", Quoted(exp), '\n')); + Json::Value const& val = info.GetValue("MOC_DEPEND_FILTERS"); + if (!val.isArray()) { + return info.LogError("MOC_DEPEND_FILTERS JSON value is not an array."); + } + Json::ArrayIndex const arraySize = val.size(); + for (Json::ArrayIndex ii = 0; ii != arraySize; ++ii) { + // Test entry closure + auto testEntry = [&info, ii](bool test, cm::string_view msg) -> bool { + if (!test) { + info.LogError( + cmStrCat("MOC_DEPEND_FILTERS filter ", ii, ": ", msg)); + } + return !test; }; - if (key.empty()) { - return filterErr("Key is empty"); - } - if (exp.empty()) { - return filterErr("Regular expression is empty"); + + Json::Value const& pairVal = val[ii]; + + if (testEntry(pairVal.isArray(), "JSON value is not an array.") || + testEntry(pairVal.size() == 2, "JSON array size invalid.")) { + return false; } - this->MocConst_.DependFilters.emplace_back(key, exp); - if (!this->MocConst_.DependFilters.back().Exp.is_valid()) { - return filterErr("Regular expression compiling failed"); + + Json::Value const& keyVal = pairVal[0u]; + Json::Value const& expVal = pairVal[1u]; + if (testEntry(keyVal.isString(), + "JSON value for keyword is not a string.") || + testEntry(expVal.isString(), + "JSON value for regular expression is not a string.")) { + return false; } - return true; - }; - // Insert default filter for Q_PLUGIN_METADATA - if (BaseConst().QtVersionMajor != 4) { - if (!addFilter("Q_PLUGIN_METADATA", - "[\n][ \t]*Q_PLUGIN_METADATA[ \t]*\\(" - "[^\\)]*FILE[ \t]*\"([^\"]+)\"")) { + std::string const key = keyVal.asString(); + std::string const exp = expVal.asString(); + if (testEntry(!key.empty(), "Keyword is empty.") || + testEntry(!exp.empty(), "Regular expression is empty.")) { return false; } - } - // Insert user defined dependency filters - std::vector<std::string> flts = InfoGetList("AM_MOC_DEPEND_FILTERS"); - if ((flts.size() % 2) != 0) { - return LogInfoError( - "AUTOMOC_DEPEND_FILTERS list size is not a multiple of 2"); - } - for (auto itC = flts.begin(), itE = flts.end(); itC != itE; itC += 2) { - if (!addFilter(*itC, *(itC + 1))) { + + this->MocConst_.DependFilters.emplace_back(key, exp); + if (testEntry( + this->MocConst_.DependFilters.back().Exp.is_valid(), + cmStrCat("Regular expression compilation failed.\nKeyword: ", + Quoted(key), "\nExpression: ", Quoted(exp)))) { return false; } } } - MocConst_.PredefsCmd = InfoGetList("AM_MOC_PREDEFS_CMD"); + // Check if moc executable exists (by reading the file time) + if (!MocConst_.ExecutableTime.Load(MocConst_.Executable)) { + return info.LogError(cmStrCat("The moc executable ", + MessagePath(MocConst_.Executable), + " does not exist.")); + } } - // - Uic - UicConst_.Executable = InfoGet("AM_QT_UIC_EXECUTABLE"); - if (!UicConst().Executable.empty()) { + // -- Uic + if (!UicConst_.Executable.empty()) { + // Uic is enabled UicConst_.Enabled = true; - // Load the executable file time - if (!UicConst_.ExecutableTime.Load(UicConst_.Executable)) { - return LogInfoError(cmStrCat("The uic executable ", - MessagePath(UicConst_.Executable), - " does not exist.")); - } - for (std::string& sfl : InfoGetList("AM_UIC_SKIP")) { - UicConst_.SkipList.insert(std::move(sfl)); + + // -- Required settings + if (!info.GetArray("UIC_SKIP", UicConst_.SkipList, false) || + !info.GetArray("UIC_SEARCH_PATHS", UicConst_.SearchPaths, false) || + !info.GetArrayConfig("UIC_OPTIONS", UicConst_.Options, false)) { + return false; } - UicConst_.SearchPaths = InfoGetList("AM_UIC_SEARCH_PATHS"); - UicConst_.TargetOptions = InfoGetConfigList("AM_UIC_TARGET_OPTIONS"); + // .ui files { - cm::string_view const keyFiles = "AM_UIC_OPTIONS_FILES"; - cm::string_view const keyOpts = "AM_UIC_OPTIONS_OPTIONS"; - auto sources = InfoGetList(keyFiles); - auto options = InfoGetLists(keyOpts); - if (!MatchSizes(keyFiles, keyOpts, sources.size(), options.size())) { - return false; + Json::Value const& val = info.GetValue("UIC_UI_FILES"); + if (!val.isArray()) { + return info.LogError("UIC_UI_FILES JSON value is not an array."); } - auto fitEnd = sources.cend(); - auto fit = sources.begin(); - auto oit = options.begin(); - while (fit != fitEnd) { - UicConst_.Options[*fit] = std::move(*oit); - ++fit; - ++oit; + Json::ArrayIndex const arraySize = val.size(); + for (Json::ArrayIndex ii = 0; ii != arraySize; ++ii) { + // Test entry closure + auto testEntry = [&info, ii](bool test, cm::string_view msg) -> bool { + if (!test) { + info.LogError(cmStrCat("UIC_UI_FILES entry ", ii, ": ", msg)); + } + return !test; + }; + + Json::Value const& entry = val[ii]; + if (testEntry(entry.isArray(), "JSON value is not an array.") || + testEntry(entry.size() == 2, "JSON array size invalid.")) { + return false; + } + + Json::Value const& entryName = entry[0u]; + Json::Value const& entryOptions = entry[1u]; + if (testEntry(entryName.isString(), + "JSON value for name is not a string.") || + testEntry(entryOptions.isArray(), + "JSON value for options is not an array.")) { + return false; + } + + auto& uiFile = UicConst_.UiFiles[entryName.asString()]; + InfoT::GetJsonArray(uiFile.Options, entryOptions); } } + + // -- Evaluate settings + // Check if uic executable exists (by reading the file time) + if (!UicConst_.ExecutableTime.Load(UicConst_.Executable)) { + return info.LogError(cmStrCat("The uic executable ", + MessagePath(UicConst_.Executable), + " does not exist.")); + } } - // Headers + // -- Headers { - // Get file lists - cm::string_view const keyFiles = "AM_HEADERS"; - cm::string_view const keyFlags = "AM_HEADERS_FLAGS"; - std::vector<std::string> files = InfoGetList(keyFiles); - std::vector<std::string> flags = InfoGetList(keyFlags); - std::vector<std::string> builds; - if (!MatchSizes(keyFiles, keyFlags, files.size(), flags.size())) { - return false; - } - if (MocConst().Enabled) { - cm::string_view const keyPaths = "AM_HEADERS_BUILD_PATHS"; - builds = InfoGetList(keyPaths); - if (!MatchSizes(keyFiles, keyPaths, files.size(), builds.size())) { + Json::Value const& val = info.GetValue("HEADERS"); + if (!val.isArray()) { + return info.LogError("HEADERS JSON value is not an array."); + } + Json::ArrayIndex const arraySize = val.size(); + for (Json::ArrayIndex ii = 0; ii != arraySize; ++ii) { + // Test entry closure + auto testEntry = [&info, ii](bool test, cm::string_view msg) -> bool { + if (!test) { + info.LogError(cmStrCat("HEADERS entry ", ii, ": ", msg)); + } + return !test; + }; + + Json::Value const& entry = val[ii]; + if (testEntry(entry.isArray(), "JSON value is not an array.") || + testEntry(entry.size() == 3, "JSON array size invalid.")) { return false; } - } - // Process file lists - for (std::size_t ii = 0; ii != files.size(); ++ii) { - std::string& fileName(files[ii]); - std::string const& fileFlags(flags[ii]); - if (fileFlags.size() != 2) { - LogInfoError(cmStrCat("Invalid flags string size ", fileFlags.size(), - "in ", keyFlags)); + Json::Value const& entryName = entry[0u]; + Json::Value const& entryFlags = entry[1u]; + Json::Value const& entryBuild = entry[2u]; + if (testEntry(entryName.isString(), + "JSON value for name is not a string.") || + testEntry(entryFlags.isString(), + "JSON value for flags is not a string.") || + testEntry(entryBuild.isString(), + "JSON value for build path is not a string.")) { return false; } - cmFileTime fileTime; - if (!fileTime.Load(fileName)) { - LogInfoError(cmStrCat("The header file ", this->MessagePath(fileName), - " does not exist.")); + + std::string name = entryName.asString(); + std::string flags = entryFlags.asString(); + std::string build = entryBuild.asString(); + if (testEntry(flags.size() == 2, "Invalid flags string size")) { return false; } - SourceFileHandleT sourceHandle = std::make_shared<SourceFileT>(fileName); + cmFileTime fileTime; + if (!fileTime.Load(name)) { + return info.LogError(cmStrCat( + "The header file ", this->MessagePath(name), " does not exist.")); + } + + SourceFileHandleT sourceHandle = std::make_shared<SourceFileT>(name); sourceHandle->FileTime = fileTime; sourceHandle->IsHeader = true; - sourceHandle->Moc = (fileFlags[0] == 'M'); - sourceHandle->Uic = (fileFlags[1] == 'U'); - + sourceHandle->Moc = (flags[0] == 'M'); + sourceHandle->Uic = (flags[1] == 'U'); if (sourceHandle->Moc && MocConst().Enabled) { - sourceHandle->BuildPath = std::move(builds[ii]); - if (sourceHandle->BuildPath.empty()) { - return LogInfoError("Header file build path is empty"); + if (build.empty()) { + return info.LogError( + cmStrCat("Header file ", ii, " build path is empty")); } + sourceHandle->BuildPath = std::move(build); } - BaseEval().Headers.emplace(std::move(fileName), std::move(sourceHandle)); + BaseEval().Headers.emplace(std::move(name), std::move(sourceHandle)); } } - // Sources + // -- Sources { - cm::string_view const keyFiles = "AM_SOURCES"; - cm::string_view const keyFlags = "AM_SOURCES_FLAGS"; - std::vector<std::string> files = InfoGetList(keyFiles); - std::vector<std::string> flags = InfoGetList(keyFlags); - if (!MatchSizes(keyFiles, keyFlags, files.size(), flags.size())) { - return false; - } + Json::Value const& val = info.GetValue("SOURCES"); + if (!val.isArray()) { + return info.LogError("SOURCES JSON value is not an array."); + } + Json::ArrayIndex const arraySize = val.size(); + for (Json::ArrayIndex ii = 0; ii != arraySize; ++ii) { + // Test entry closure + auto testEntry = [&info, ii](bool test, cm::string_view msg) -> bool { + if (!test) { + info.LogError(cmStrCat("SOURCES entry ", ii, ": ", msg)); + } + return !test; + }; - // Process file lists - for (std::size_t ii = 0; ii != files.size(); ++ii) { - std::string& fileName(files[ii]); - std::string const& fileFlags(flags[ii]); - if (fileFlags.size() != 2) { - LogInfoError(cmStrCat("Invalid flags string size ", fileFlags.size(), - "in ", keyFlags)); + Json::Value const& entry = val[ii]; + if (testEntry(entry.isArray(), "JSON value is not an array.") || + testEntry(entry.size() == 2, "JSON array size invalid.")) { return false; } - cmFileTime fileTime; - if (!fileTime.Load(fileName)) { - LogInfoError(cmStrCat("The source file ", this->MessagePath(fileName), - " does not exist.")); + + Json::Value const& entryName = entry[0u]; + Json::Value const& entryFlags = entry[1u]; + if (testEntry(entryName.isString(), + "JSON value for name is not a string.") || + testEntry(entryFlags.isString(), + "JSON value for flags is not a string.")) { return false; } - SourceFileHandleT sourceHandle = std::make_shared<SourceFileT>(fileName); + std::string name = entryName.asString(); + std::string flags = entryFlags.asString(); + if (testEntry(flags.size() == 2, "Invalid flags string size")) { + return false; + } + + cmFileTime fileTime; + if (!fileTime.Load(name)) { + return info.LogError(cmStrCat( + "The source file ", this->MessagePath(name), " does not exist.")); + } + + SourceFileHandleT sourceHandle = std::make_shared<SourceFileT>(name); sourceHandle->FileTime = fileTime; sourceHandle->IsHeader = false; - sourceHandle->Moc = (fileFlags[0] == 'M'); - sourceHandle->Uic = (fileFlags[1] == 'U'); - BaseEval().Sources.emplace(std::move(fileName), std::move(sourceHandle)); + sourceHandle->Moc = (flags[0] == 'M'); + sourceHandle->Uic = (flags[1] == 'U'); + BaseEval().Sources.emplace(std::move(name), std::move(sourceHandle)); } } - // Init derived information - // ------------------------ - + // -- Init derived information // Moc variables if (MocConst().Enabled) { - // Mocs compilation file - MocConst_.CompFileAbs = AbsoluteBuildPath("mocs_compilation.cpp"); - - // Moc predefs file - if (!MocConst_.PredefsCmd.empty()) { - std::string pathRel; - if (BaseConst_.MultiConfig) { - pathRel = cmStrCat("moc_predefs_", InfoConfig(), ".h"); - } else { - pathRel = "moc_predefs.h"; - } - MocConst_.PredefsFileAbs = AbsoluteBuildPath(pathRel); - } - // Compose moc includes list { // Compute framework paths @@ -1921,7 +2415,7 @@ bool cmQtAutoMocUic::Init(cmMakefile* makefile) } template <class JOBTYPE> -void cmQtAutoMocUic::CreateParseJobs(SourceFileMapT const& sourceMap) +void cmQtAutoMocUicT::CreateParseJobs(SourceFileMapT const& sourceMap) { cmFileTime const parseCacheTime = BaseEval().ParseCacheTime; ParseCacheT& parseCache = BaseEval().ParseCache; @@ -1938,13 +2432,13 @@ void cmQtAutoMocUic::CreateParseJobs(SourceFileMapT const& sourceMap) } /** Concurrently callable implementation of cmSystemTools::CollapseFullPath */ -std::string cmQtAutoMocUic::CollapseFullPathTS(std::string const& path) const +std::string cmQtAutoMocUicT::CollapseFullPathTS(std::string const& path) const { std::lock_guard<std::mutex> guard(CMakeLibMutex_); return cmSystemTools::CollapseFullPath(path, ProjectDirs().CurrentSource); } -void cmQtAutoMocUic::InitJobs() +void cmQtAutoMocUicT::InitJobs() { // Add moc_predefs.h job if (MocConst().Enabled && !MocConst().PredefsCmd.empty()) { @@ -1971,7 +2465,7 @@ void cmQtAutoMocUic::InitJobs() } } -bool cmQtAutoMocUic::Process() +bool cmQtAutoMocUicT::Process() { SettingsFileRead(); ParseCacheRead(); @@ -1994,7 +2488,7 @@ bool cmQtAutoMocUic::Process() return true; } -void cmQtAutoMocUic::SettingsFileRead() +void cmQtAutoMocUicT::SettingsFileRead() { // Compose current settings strings { @@ -2031,11 +2525,11 @@ void cmQtAutoMocUic::SettingsFileRead() if (UicConst().Enabled) { cryptoHash.Initialize(); cha(UicConst().Executable); - std::for_each(UicConst().TargetOptions.begin(), - UicConst().TargetOptions.end(), cha); - for (const auto& item : UicConst().Options) { + std::for_each(UicConst().Options.begin(), UicConst().Options.end(), cha); + for (const auto& item : UicConst().UiFiles) { cha(item.first); - std::for_each(item.second.begin(), item.second.end(), cha); + auto const& opts = item.second.Options; + std::for_each(opts.begin(), opts.end(), cha); } SettingsStringUic_ = cryptoHash.FinalizeHex(); } @@ -2073,14 +2567,14 @@ void cmQtAutoMocUic::SettingsFileRead() } } -bool cmQtAutoMocUic::SettingsFileWrite() +bool cmQtAutoMocUicT::SettingsFileWrite() { // Only write if any setting changed if (MocConst().SettingsChanged || UicConst().SettingsChanged) { if (Log().Verbose()) { Log().Info( GenT::GEN, - cmStrCat("Writing settings file ", MessagePath(SettingsFile_))); + cmStrCat("Writing the settings file ", MessagePath(SettingsFile_))); } // Compose settings file content std::string content; @@ -2108,7 +2602,7 @@ bool cmQtAutoMocUic::SettingsFileWrite() return true; } -void cmQtAutoMocUic::ParseCacheRead() +void cmQtAutoMocUicT::ParseCacheRead() { cm::string_view reason; // Don't read the cache if it is invalid @@ -2134,7 +2628,7 @@ void cmQtAutoMocUic::ParseCacheRead() } } -bool cmQtAutoMocUic::ParseCacheWrite() +bool cmQtAutoMocUicT::ParseCacheWrite() { if (BaseEval().ParseCacheChanged) { if (Log().Verbose()) { @@ -2153,7 +2647,7 @@ bool cmQtAutoMocUic::ParseCacheWrite() return true; } -bool cmQtAutoMocUic::CreateDirectories() +bool cmQtAutoMocUicT::CreateDirectories() { // Create AUTOGEN include directory if (!cmSystemTools::MakeDirectory(BaseConst().AutogenIncludeDir)) { @@ -2166,7 +2660,7 @@ bool cmQtAutoMocUic::CreateDirectories() return true; } -void cmQtAutoMocUic::Abort(bool error) +void cmQtAutoMocUicT::Abort(bool error) { if (error) { JobError_.store(true); @@ -2174,14 +2668,21 @@ void cmQtAutoMocUic::Abort(bool error) WorkerPool_.Abort(); } -std::string cmQtAutoMocUic::AbsoluteBuildPath( +std::string cmQtAutoMocUicT::AbsoluteBuildPath( cm::string_view relativePath) const { return cmStrCat(BaseConst().AutogenBuildDir, '/', relativePath); } -std::string cmQtAutoMocUic::AbsoluteIncludePath( +std::string cmQtAutoMocUicT::AbsoluteIncludePath( cm::string_view relativePath) const { return cmStrCat(BaseConst().AutogenIncludeDir, '/', relativePath); } + +} // End of unnamed namespace + +bool cmQtAutoMocUic(cm::string_view infoFile, cm::string_view config) +{ + return cmQtAutoMocUicT().Run(infoFile, config); +} diff --git a/Source/cmQtAutoMocUic.h b/Source/cmQtAutoMocUic.h index 15b66ca..ffcc2db 100644 --- a/Source/cmQtAutoMocUic.h +++ b/Source/cmQtAutoMocUic.h @@ -5,561 +5,12 @@ #include "cmConfigure.h" // IWYU pragma: keep -#include "cmFileTime.h" -#include "cmQtAutoGen.h" -#include "cmQtAutoGenerator.h" -#include "cmWorkerPool.h" -#include "cmsys/RegularExpression.hxx" - #include <cm/string_view> -#include <atomic> -#include <cstddef> -#include <map> -#include <memory> -#include <mutex> -#include <set> -#include <string> -#include <unordered_map> -#include <unordered_set> -#include <utility> -#include <vector> - -class cmMakefile; - -/** \class cmQtAutoMocUic - * \brief AUTOMOC and AUTOUIC generator +/** + * Process AUTOMOC and AUTOUIC + * @return true on success */ -class cmQtAutoMocUic : public cmQtAutoGenerator -{ -public: - cmQtAutoMocUic(); - ~cmQtAutoMocUic() override; - - cmQtAutoMocUic(cmQtAutoMocUic const&) = delete; - cmQtAutoMocUic& operator=(cmQtAutoMocUic const&) = delete; - -public: - // -- Types - - /** Search key plus regular expression pair. */ - struct KeyExpT - { - KeyExpT() = default; - - KeyExpT(const char* key, const char* exp) - : Key(key) - , Exp(exp) - { - } - - KeyExpT(std::string key, std::string const& exp) - : Key(std::move(key)) - , Exp(exp) - { - } - - std::string Key; - cmsys::RegularExpression Exp; - }; - - /** Include string with sub parts. */ - struct IncludeKeyT - { - IncludeKeyT(std::string const& key, std::size_t basePrefixLength); - - std::string Key; // Full include string - std::string Dir; // Include directory - std::string Base; // Base part of the include file name - }; - - /** Source file parsing cache. */ - class ParseCacheT - { - public: - // -- Types - /** - * Entry of the file parsing cache - */ - struct FileT - { - void Clear(); - - struct MocT - { - std::string Macro; - struct IncludeT - { - std::vector<IncludeKeyT> Underscore; - std::vector<IncludeKeyT> Dot; - } Include; - std::vector<std::string> Depends; - } Moc; - - struct UicT - { - std::vector<IncludeKeyT> Include; - std::vector<std::string> Depends; - } Uic; - }; - using FileHandleT = std::shared_ptr<FileT>; - using GetOrInsertT = std::pair<FileHandleT, bool>; - - public: - ParseCacheT(); - ~ParseCacheT(); - - void Clear(); - - bool ReadFromFile(std::string const& fileName); - bool WriteToFile(std::string const& fileName); - - //! Might return an invalid handle - FileHandleT Get(std::string const& fileName) const; - //! Always returns a valid handle - GetOrInsertT GetOrInsert(std::string const& fileName); - - private: - std::unordered_map<std::string, FileHandleT> Map_; - }; - - /** Source file data. */ - class SourceFileT - { - public: - SourceFileT(std::string fileName) - : FileName(std::move(fileName)) - { - } - - public: - std::string FileName; - cmFileTime FileTime; - ParseCacheT::FileHandleT ParseData; - std::string BuildPath; - bool IsHeader = false; - bool Moc = false; - bool Uic = false; - }; - using SourceFileHandleT = std::shared_ptr<SourceFileT>; - using SourceFileMapT = std::map<std::string, SourceFileHandleT>; - - /** Meta compiler file mapping information. */ - struct MappingT - { - SourceFileHandleT SourceFile; - std::string OutputFile; - std::string IncludeString; - std::vector<SourceFileHandleT> IncluderFiles; - }; - using MappingHandleT = std::shared_ptr<MappingT>; - using MappingMapT = std::map<std::string, MappingHandleT>; - - /** Common settings. */ - class BaseSettingsT - { - public: - // -- Constructors - BaseSettingsT(); - ~BaseSettingsT(); - - BaseSettingsT(BaseSettingsT const&) = delete; - BaseSettingsT& operator=(BaseSettingsT const&) = delete; - - // -- Attributes - // - Config - bool MultiConfig = false; - unsigned int QtVersionMajor = 4; - // - Directories - std::string AutogenBuildDir; - std::string AutogenIncludeDir; - // - Files - std::string CMakeExecutable; - cmFileTime CMakeExecutableTime; - std::string ParseCacheFile; - std::vector<std::string> HeaderExtensions; - }; - - /** Shared common variables. */ - class BaseEvalT - { - public: - // -- Parse Cache - bool ParseCacheChanged = false; - cmFileTime ParseCacheTime; - ParseCacheT ParseCache; - - // -- Sources - SourceFileMapT Headers; - SourceFileMapT Sources; - }; - - /** Moc settings. */ - class MocSettingsT - { - public: - // -- Constructors - MocSettingsT(); - ~MocSettingsT(); - - MocSettingsT(MocSettingsT const&) = delete; - MocSettingsT& operator=(MocSettingsT const&) = delete; - - // -- Const methods - bool skipped(std::string const& fileName) const; - std::string MacrosString() const; - - // -- Attributes - bool Enabled = false; - bool SettingsChanged = false; - bool RelaxedMode = false; - bool PathPrefix = false; - cmFileTime ExecutableTime; - std::string Executable; - std::string CompFileAbs; - std::string PredefsFileAbs; - std::unordered_set<std::string> SkipList; - std::vector<std::string> IncludePaths; - std::vector<std::string> Definitions; - std::vector<std::string> OptionsIncludes; - std::vector<std::string> OptionsDefinitions; - std::vector<std::string> OptionsExtra; - std::vector<std::string> PredefsCmd; - std::vector<KeyExpT> DependFilters; - std::vector<KeyExpT> MacroFilters; - cmsys::RegularExpression RegExpInclude; - }; - - /** Moc shared variables. */ - class MocEvalT - { - public: - // -- predefines file - cmFileTime PredefsTime; - // -- Mappings - MappingMapT HeaderMappings; - MappingMapT SourceMappings; - MappingMapT Includes; - // -- Discovered files - SourceFileMapT HeadersDiscovered; - // -- Output directories - std::unordered_set<std::string> OutputDirs; - // -- Mocs compilation - bool CompUpdated = false; - std::vector<std::string> CompFiles; - }; - - /** Uic settings. */ - class UicSettingsT - { - public: - UicSettingsT(); - ~UicSettingsT(); - - UicSettingsT(UicSettingsT const&) = delete; - UicSettingsT& operator=(UicSettingsT const&) = delete; - - // -- Const methods - bool skipped(std::string const& fileName) const; - - // -- Attributes - bool Enabled = false; - bool SettingsChanged = false; - cmFileTime ExecutableTime; - std::string Executable; - std::unordered_set<std::string> SkipList; - std::vector<std::string> TargetOptions; - std::map<std::string, std::vector<std::string>> Options; - std::vector<std::string> SearchPaths; - cmsys::RegularExpression RegExpInclude; - }; - - /** Uic shared variables. */ - class UicEvalT - { - public: - // -- Discovered files - SourceFileMapT UiFiles; - // -- Mappings - MappingMapT Includes; - // -- Output directories - std::unordered_set<std::string> OutputDirs; - }; - - /** Abstract job class for concurrent job processing. */ - class JobT : public cmWorkerPool::JobT - { - protected: - /** Protected default constructor. */ - JobT(bool fence = false) - : cmWorkerPool::JobT(fence) - { - } - - //! Get the generator. Only valid during Process() call! - cmQtAutoMocUic* Gen() const - { - return static_cast<cmQtAutoMocUic*>(UserData()); - }; - - // -- Accessors. Only valid during Process() call! - Logger const& Log() const { return Gen()->Log(); } - BaseSettingsT const& BaseConst() const { return Gen()->BaseConst(); } - BaseEvalT& BaseEval() const { return Gen()->BaseEval(); } - MocSettingsT const& MocConst() const { return Gen()->MocConst(); } - MocEvalT& MocEval() const { return Gen()->MocEval(); } - UicSettingsT const& UicConst() const { return Gen()->UicConst(); } - UicEvalT& UicEval() const { return Gen()->UicEval(); } - - // -- Logging - std::string MessagePath(cm::string_view path) const - { - return Gen()->MessagePath(path); - } - // - Error logging with automatic abort - void LogError(GenT genType, cm::string_view message) const; - void LogCommandError(GenT genType, cm::string_view message, - std::vector<std::string> const& command, - std::string const& output) const; - - /** @brief Run an external process. Use only during Process() call! */ - bool RunProcess(GenT genType, cmWorkerPool::ProcessResultT& result, - std::vector<std::string> const& command, - std::string* infoMessage = nullptr); - }; - - /** Fence job utility class. */ - class JobFenceT : public JobT - { - public: - JobFenceT() - : JobT(true) - { - } - void Process() override{}; - }; - - /** Generate moc_predefs.h. */ - class JobMocPredefsT : public JobFenceT - { - void Process() override; - bool Update(std::string* reason) const; - }; - - /** File parse job base class. */ - class JobParseT : public JobT - { - public: - JobParseT(SourceFileHandleT fileHandle) - : FileHandle(std::move(fileHandle)) - { - } - - protected: - bool ReadFile(); - void CreateKeys(std::vector<IncludeKeyT>& container, - std::set<std::string> const& source, - std::size_t basePrefixLength); - void MocMacro(); - void MocDependecies(); - void MocIncludes(); - void UicIncludes(); - - protected: - SourceFileHandleT FileHandle; - std::string Content; - }; - - /** Header file parse job. */ - class JobParseHeaderT : public JobParseT - { - public: - using JobParseT::JobParseT; - void Process() override; - }; - - /** Source file parse job. */ - class JobParseSourceT : public JobParseT - { - public: - using JobParseT::JobParseT; - void Process() override; - }; - - /** Evaluate cached file parse data - moc. */ - class JobEvalCacheT : public JobT - { - protected: - std::string MessageSearchLocations() const; - std::vector<std::string> SearchLocations; - }; - - /** Evaluate cached file parse data - moc. */ - class JobEvalCacheMocT : public JobEvalCacheT - { - void Process() override; - bool EvalHeader(SourceFileHandleT source); - bool EvalSource(SourceFileHandleT const& source); - bool FindIncludedHeader(SourceFileHandleT& headerHandle, - cm::string_view includerDir, - cm::string_view includeBase); - bool RegisterIncluded(std::string const& includeString, - SourceFileHandleT includerFileHandle, - SourceFileHandleT sourceFileHandle) const; - void RegisterMapping(MappingHandleT mappingHandle) const; - std::string MessageHeader(cm::string_view headerBase) const; - }; - - /** Evaluate cached file parse data - uic. */ - class JobEvalCacheUicT : public JobEvalCacheT - { - void Process() override; - bool EvalFile(SourceFileHandleT const& sourceFileHandle); - bool FindIncludedUi(cm::string_view sourceDirPrefix, - cm::string_view includePrefix); - bool RegisterMapping(std::string const& includeString, - SourceFileHandleT includerFileHandle); - - std::string UiName; - SourceFileHandleT UiFileHandle; - }; - - /** Evaluate cached file parse data - finish */ - class JobEvalCacheFinishT : public JobFenceT - { - void Process() override; - }; - - /** Dependency probing base job. */ - class JobProbeDepsT : public JobT - { - }; - - /** Probes file dependencies and generates moc compile jobs. */ - class JobProbeDepsMocT : public JobProbeDepsT - { - void Process() override; - bool Generate(MappingHandleT const& mapping, bool compFile) const; - bool Probe(MappingT const& mapping, std::string* reason) const; - std::pair<std::string, cmFileTime> FindDependency( - std::string const& sourceDir, std::string const& includeString) const; - }; - - /** Probes file dependencies and generates uic compile jobs. */ - class JobProbeDepsUicT : public JobProbeDepsT - { - void Process() override; - bool Probe(MappingT const& mapping, std::string* reason) const; - }; - - /** Dependency probing finish job. */ - class JobProbeDepsFinishT : public JobFenceT - { - void Process() override; - }; - - /** Meta compiler base job. */ - class JobCompileT : public JobT - { - public: - JobCompileT(MappingHandleT uicMapping, std::unique_ptr<std::string> reason) - : Mapping(std::move(uicMapping)) - , Reason(std::move(reason)) - { - } - - protected: - MappingHandleT Mapping; - std::unique_ptr<std::string> Reason; - }; - - /** moc compiles a file. */ - class JobCompileMocT : public JobCompileT - { - public: - using JobCompileT::JobCompileT; - void Process() override; - }; - - /** uic compiles a file. */ - class JobCompileUicT : public JobCompileT - { - public: - using JobCompileT::JobCompileT; - void Process() override; - }; - - /** Generate mocs_compilation.cpp. */ - class JobMocsCompilationT : public JobFenceT - { - private: - void Process() override; - }; - - /** @brief The last job. */ - class JobFinishT : public JobFenceT - { - private: - void Process() override; - }; - - // -- Const settings interface - BaseSettingsT const& BaseConst() const { return this->BaseConst_; } - BaseEvalT& BaseEval() { return this->BaseEval_; } - MocSettingsT const& MocConst() const { return this->MocConst_; } - MocEvalT& MocEval() { return this->MocEval_; } - UicSettingsT const& UicConst() const { return this->UicConst_; } - UicEvalT& UicEval() { return this->UicEval_; } - - // -- Parallel job processing interface - cmWorkerPool& WorkerPool() { return WorkerPool_; } - void AbortError() { Abort(true); } - void AbortSuccess() { Abort(false); } - - // -- Utility - std::string AbsoluteBuildPath(cm::string_view relativePath) const; - std::string AbsoluteIncludePath(cm::string_view relativePath) const; - template <class JOBTYPE> - void CreateParseJobs(SourceFileMapT const& sourceMap); - std::string CollapseFullPathTS(std::string const& path) const; - -private: - // -- Utility accessors - Logger const& Log() const { return Logger_; } - // -- Abstract processing interface - bool Init(cmMakefile* makefile) override; - void InitJobs(); - bool Process() override; - // -- Settings file - void SettingsFileRead(); - bool SettingsFileWrite(); - // -- Parse cache - void ParseCacheRead(); - bool ParseCacheWrite(); - // -- Thread processing - void Abort(bool error); - // -- Generation - bool CreateDirectories(); - -private: - // -- Utility - Logger Logger_; - // -- Settings - BaseSettingsT BaseConst_; - BaseEvalT BaseEval_; - MocSettingsT MocConst_; - MocEvalT MocEval_; - UicSettingsT UicConst_; - UicEvalT UicEval_; - // -- Settings file - std::string SettingsFile_; - std::string SettingsStringMoc_; - std::string SettingsStringUic_; - // -- Worker thread pool - std::atomic<bool> JobError_ = ATOMIC_VAR_INIT(false); - cmWorkerPool WorkerPool_; - // -- Concurrent processing - mutable std::mutex CMakeLibMutex_; -}; +bool cmQtAutoMocUic(cm::string_view infoFile, cm::string_view config); #endif diff --git a/Source/cmQtAutoRcc.cxx b/Source/cmQtAutoRcc.cxx index 1bf8ca4..4a3ecfa 100644 --- a/Source/cmQtAutoRcc.cxx +++ b/Source/cmQtAutoRcc.cxx @@ -5,127 +5,131 @@ #include "cmAlgorithms.h" #include "cmCryptoHash.h" #include "cmDuration.h" +#include "cmFileLock.h" #include "cmFileLockResult.h" -#include "cmMakefile.h" +#include "cmFileTime.h" #include "cmProcessOutput.h" #include "cmQtAutoGen.h" +#include "cmQtAutoGenerator.h" #include "cmStringAlgorithms.h" #include "cmSystemTools.h" -#include <cm/string_view> - #include <algorithm> +#include <string> +#include <vector> -cmQtAutoRcc::cmQtAutoRcc() = default; -cmQtAutoRcc::~cmQtAutoRcc() = default; +namespace { -bool cmQtAutoRcc::Init(cmMakefile* makefile) +/** \class cmQtAutoRccT + * \brief AUTORCC generator + */ +class cmQtAutoRccT : public cmQtAutoGenerator { - // -- Utility lambdas - auto InfoGet = [makefile](cm::string_view key) { - return makefile->GetSafeDefinition(std::string(key)); - }; - auto InfoGetList = - [makefile](cm::string_view key) -> std::vector<std::string> { - return cmExpandedList(makefile->GetSafeDefinition(std::string(key))); - }; - auto InfoGetConfig = [makefile, this](cm::string_view key) -> std::string { - if (const char* valueConf = - makefile->GetDefinition(cmStrCat(key, '_', InfoConfig()))) { - return std::string(valueConf); - } - return makefile->GetSafeDefinition(std::string(key)); - }; - auto InfoGetConfigList = - [&InfoGetConfig](cm::string_view key) -> std::vector<std::string> { - return cmExpandedList(InfoGetConfig(key)); - }; - auto LogInfoError = [this](cm::string_view msg) -> bool { - this->Log().Error( - GenT::RCC, cmStrCat("In ", MessagePath(this->InfoFile()), ":\n", msg)); - return false; - }; - - // -- Read info file - if (!makefile->ReadListFile(InfoFile())) { - return LogInfoError("File processing failed."); - } - - // - Configurations - Logger_.RaiseVerbosity(InfoGet("ARCC_VERBOSITY")); - MultiConfig_ = makefile->IsOn("ARCC_MULTI_CONFIG"); - - // - Directories - ProjectDirsRef().Source = InfoGet("ARCC_CMAKE_SOURCE_DIR"); - ProjectDirsRef().Binary = InfoGet("ARCC_CMAKE_BINARY_DIR"); - AutogenBuildDir_ = InfoGet("ARCC_BUILD_DIR"); - if (AutogenBuildDir_.empty()) { - return LogInfoError("Build directory empty."); - } - - IncludeDir_ = InfoGetConfig("ARCC_INCLUDE_DIR"); - if (IncludeDir_.empty()) { - return LogInfoError("Include directory empty."); - } +public: + cmQtAutoRccT(); + ~cmQtAutoRccT() override; + + cmQtAutoRccT(cmQtAutoRccT const&) = delete; + cmQtAutoRccT& operator=(cmQtAutoRccT const&) = delete; + +private: + // -- Utility + bool IsMultiConfig() const { return MultiConfig_; } + std::string MultiConfigOutput() const; + + // -- Abstract processing interface + bool InitFromInfo(InfoT const& info) override; + bool Process() override; + // -- Settings file + bool SettingsFileRead(); + bool SettingsFileWrite(); + // -- Tests + bool TestQrcRccFiles(bool& generate); + bool TestResources(bool& generate); + bool TestInfoFile(); + // -- Generation + bool GenerateRcc(); + bool GenerateWrapper(); + +private: + // -- Config settings + bool MultiConfig_ = false; + // -- Directories + std::string AutogenBuildDir_; + std::string IncludeDir_; + // -- Qt environment + std::string RccExecutable_; + cmFileTime RccExecutableTime_; + std::vector<std::string> RccListOptions_; + // -- Job + std::string LockFile_; + cmFileLock LockFileLock_; + std::string QrcFile_; + std::string QrcFileName_; + std::string QrcFileDir_; + cmFileTime QrcFileTime_; + std::string RccPathChecksum_; + std::string RccFileName_; + std::string RccFileOutput_; + std::string RccFilePublic_; + cmFileTime RccFileTime_; + std::string Reason; + std::vector<std::string> Options_; + std::vector<std::string> Inputs_; + // -- Settings file + std::string SettingsFile_; + std::string SettingsString_; + bool SettingsChanged_ = false; + bool BuildFileChanged_ = false; +}; + +cmQtAutoRccT::cmQtAutoRccT() + : cmQtAutoGenerator(GenT::RCC) +{ +} +cmQtAutoRccT::~cmQtAutoRccT() = default; - // - Rcc executable - RccExecutable_ = InfoGet("ARCC_RCC_EXECUTABLE"); - if (!RccExecutableTime_.Load(RccExecutable_)) { - return LogInfoError(cmStrCat( - "The rcc executable ", MessagePath(RccExecutable_), " does not exist.")); +bool cmQtAutoRccT::InitFromInfo(InfoT const& info) +{ + // -- Required settings + if (!info.GetBool("MULTI_CONFIG", MultiConfig_, true) || + !info.GetString("BUILD_DIR", AutogenBuildDir_, true) || + !info.GetStringConfig("INCLUDE_DIR", IncludeDir_, true) || + !info.GetString("RCC_EXECUTABLE", RccExecutable_, true) || + !info.GetArray("RCC_LIST_OPTIONS", RccListOptions_, false) || + !info.GetString("LOCK_FILE", LockFile_, true) || + !info.GetStringConfig("SETTINGS_FILE", SettingsFile_, true) || + !info.GetString("SOURCE", QrcFile_, true) || + !info.GetString("OUTPUT_CHECKSUM", RccPathChecksum_, true) || + !info.GetString("OUTPUT_NAME", RccFileName_, true) || + !info.GetArray("OPTIONS", Options_, false) || + !info.GetArray("INPUTS", Inputs_, false)) { + return false; } - RccListOptions_ = InfoGetList("ARCC_RCC_LIST_OPTIONS"); - // - Job - LockFile_ = InfoGet("ARCC_LOCK_FILE"); - QrcFile_ = InfoGet("ARCC_SOURCE"); + // -- Derive information QrcFileName_ = cmSystemTools::GetFilenameName(QrcFile_); QrcFileDir_ = cmSystemTools::GetFilenamePath(QrcFile_); - RccPathChecksum_ = InfoGet("ARCC_OUTPUT_CHECKSUM"); - RccFileName_ = InfoGet("ARCC_OUTPUT_NAME"); - Options_ = InfoGetConfigList("ARCC_OPTIONS"); - Inputs_ = InfoGetList("ARCC_INPUTS"); - - // - Settings file - SettingsFile_ = InfoGetConfig("ARCC_SETTINGS_FILE"); - - // - Validity checks - if (LockFile_.empty()) { - return LogInfoError("Lock file name missing."); - } - if (SettingsFile_.empty()) { - return LogInfoError("Settings file name missing."); - } - if (AutogenBuildDir_.empty()) { - return LogInfoError("Autogen build directory missing."); - } - if (RccExecutable_.empty()) { - return LogInfoError("rcc executable missing."); - } - if (QrcFile_.empty()) { - return LogInfoError("rcc input file missing."); - } - if (RccFileName_.empty()) { - return LogInfoError("rcc output file missing."); - } - - // Init derived information - // ------------------------ - RccFilePublic_ = cmStrCat(AutogenBuildDir_, '/', RccPathChecksum_, '/', RccFileName_); - // Compute rcc output file name + // rcc output file name if (IsMultiConfig()) { RccFileOutput_ = cmStrCat(IncludeDir_, '/', MultiConfigOutput()); } else { RccFileOutput_ = RccFilePublic_; } + // -- Checks + if (!RccExecutableTime_.Load(RccExecutable_)) { + return info.LogError(cmStrCat( + "The rcc executable ", MessagePath(RccExecutable_), " does not exist.")); + } + return true; } -bool cmQtAutoRcc::Process() +bool cmQtAutoRccT::Process() { if (!SettingsFileRead()) { return false; @@ -158,13 +162,13 @@ bool cmQtAutoRcc::Process() return SettingsFileWrite(); } -std::string cmQtAutoRcc::MultiConfigOutput() const +std::string cmQtAutoRccT::MultiConfigOutput() const { return cmStrCat(RccPathChecksum_, '/', AppendFilenameSuffix(RccFileName_, "_CMAKE_")); } -bool cmQtAutoRcc::SettingsFileRead() +bool cmQtAutoRccT::SettingsFileRead() { // Compose current settings strings { @@ -242,7 +246,7 @@ bool cmQtAutoRcc::SettingsFileRead() return true; } -bool cmQtAutoRcc::SettingsFileWrite() +bool cmQtAutoRccT::SettingsFileWrite() { // Only write if any setting changed if (SettingsChanged_) { @@ -269,7 +273,7 @@ bool cmQtAutoRcc::SettingsFileWrite() } /// Do basic checks if rcc generation is required -bool cmQtAutoRcc::TestQrcRccFiles(bool& generate) +bool cmQtAutoRccT::TestQrcRccFiles(bool& generate) { // Test if the rcc input file exists if (!QrcFileTime_.Load(QrcFile_)) { @@ -326,7 +330,7 @@ bool cmQtAutoRcc::TestQrcRccFiles(bool& generate) return true; } -bool cmQtAutoRcc::TestResources(bool& generate) +bool cmQtAutoRccT::TestResources(bool& generate) { // Read resource files list if (Inputs_.empty()) { @@ -365,7 +369,7 @@ bool cmQtAutoRcc::TestResources(bool& generate) return true; } -bool cmQtAutoRcc::TestInfoFile() +bool cmQtAutoRccT::TestInfoFile() { // Test if the rcc output file is older than the info file if (RccFileTime_.Older(InfoFileTime())) { @@ -388,7 +392,7 @@ bool cmQtAutoRcc::TestInfoFile() return true; } -bool cmQtAutoRcc::GenerateRcc() +bool cmQtAutoRccT::GenerateRcc() { // Make parent directory if (!MakeParentDirectory(RccFileOutput_)) { @@ -440,7 +444,7 @@ bool cmQtAutoRcc::GenerateRcc() return true; } -bool cmQtAutoRcc::GenerateWrapper() +bool cmQtAutoRccT::GenerateWrapper() { // Generate a wrapper source file on demand if (IsMultiConfig()) { @@ -490,3 +494,10 @@ bool cmQtAutoRcc::GenerateWrapper() } return true; } + +} // End of unnamed namespace + +bool cmQtAutoRcc(cm::string_view infoFile, cm::string_view config) +{ + return cmQtAutoRccT().Run(infoFile, config); +} diff --git a/Source/cmQtAutoRcc.h b/Source/cmQtAutoRcc.h index 636a667..a74b33a 100644 --- a/Source/cmQtAutoRcc.h +++ b/Source/cmQtAutoRcc.h @@ -5,77 +5,12 @@ #include "cmConfigure.h" // IWYU pragma: keep -#include "cmFileLock.h" -#include "cmFileTime.h" -#include "cmQtAutoGenerator.h" +#include <cm/string_view> -#include <string> -#include <vector> - -class cmMakefile; - -// @brief AUTORCC generator -class cmQtAutoRcc : public cmQtAutoGenerator -{ -public: - cmQtAutoRcc(); - ~cmQtAutoRcc() override; - - cmQtAutoRcc(cmQtAutoRcc const&) = delete; - cmQtAutoRcc& operator=(cmQtAutoRcc const&) = delete; - -private: - // -- Utility - Logger const& Log() const { return Logger_; } - bool IsMultiConfig() const { return MultiConfig_; } - std::string MultiConfigOutput() const; - - // -- Abstract processing interface - bool Init(cmMakefile* makefile) override; - bool Process() override; - // -- Settings file - bool SettingsFileRead(); - bool SettingsFileWrite(); - // -- Tests - bool TestQrcRccFiles(bool& generate); - bool TestResources(bool& generate); - bool TestInfoFile(); - // -- Generation - bool GenerateRcc(); - bool GenerateWrapper(); - -private: - // -- Logging - Logger Logger_; - // -- Config settings - bool MultiConfig_ = false; - // -- Directories - std::string AutogenBuildDir_; - std::string IncludeDir_; - // -- Qt environment - std::string RccExecutable_; - cmFileTime RccExecutableTime_; - std::vector<std::string> RccListOptions_; - // -- Job - std::string LockFile_; - cmFileLock LockFileLock_; - std::string QrcFile_; - std::string QrcFileName_; - std::string QrcFileDir_; - cmFileTime QrcFileTime_; - std::string RccPathChecksum_; - std::string RccFileName_; - std::string RccFileOutput_; - std::string RccFilePublic_; - cmFileTime RccFileTime_; - std::string Reason; - std::vector<std::string> Options_; - std::vector<std::string> Inputs_; - // -- Settings file - std::string SettingsFile_; - std::string SettingsString_; - bool SettingsChanged_ = false; - bool BuildFileChanged_ = false; -}; +/** + * Process AUTORCC + * @return true on success + */ +bool cmQtAutoRcc(cm::string_view infoFile, cm::string_view config); #endif diff --git a/Source/cmSetTargetPropertiesCommand.cxx b/Source/cmSetTargetPropertiesCommand.cxx index b4360e4..8d917db 100644 --- a/Source/cmSetTargetPropertiesCommand.cxx +++ b/Source/cmSetTargetPropertiesCommand.cxx @@ -5,31 +5,32 @@ #include <iterator> #include "cmAlgorithms.h" +#include "cmExecutionStatus.h" #include "cmMakefile.h" #include "cmStringAlgorithms.h" #include "cmTarget.h" -class cmExecutionStatus; +static bool SetOneTarget(const std::string& tname, + std::vector<std::string>& propertyPairs, + cmMakefile* mf); -// cmSetTargetPropertiesCommand -bool cmSetTargetPropertiesCommand::InitialPass( - std::vector<std::string> const& args, cmExecutionStatus&) +bool cmSetTargetPropertiesCommand(std::vector<std::string> const& args, + cmExecutionStatus& status) { if (args.size() < 2) { - this->SetError("called with incorrect number of arguments"); + status.SetError("called with incorrect number of arguments"); return false; } // first collect up the list of files std::vector<std::string> propertyPairs; int numFiles = 0; - std::vector<std::string>::const_iterator j; - for (j = args.begin(); j != args.end(); ++j) { + for (auto j = args.begin(); j != args.end(); ++j) { if (*j == "PROPERTIES") { // now loop through the rest of the arguments, new style ++j; if (std::distance(j, args.end()) % 2 != 0) { - this->SetError("called with incorrect number of arguments."); + status.SetError("called with incorrect number of arguments."); return false; } cmAppend(propertyPairs, j, args.end()); @@ -38,33 +39,32 @@ bool cmSetTargetPropertiesCommand::InitialPass( numFiles++; } if (propertyPairs.empty()) { - this->SetError("called with illegal arguments, maybe missing " - "a PROPERTIES specifier?"); + status.SetError("called with illegal arguments, maybe missing " + "a PROPERTIES specifier?"); return false; } + cmMakefile& mf = status.GetMakefile(); + // now loop over all the targets - int i; - for (i = 0; i < numFiles; ++i) { - if (this->Makefile->IsAlias(args[i])) { - this->SetError("can not be used on an ALIAS target."); + for (int i = 0; i < numFiles; ++i) { + if (mf.IsAlias(args[i])) { + status.SetError("can not be used on an ALIAS target."); return false; } - bool ret = cmSetTargetPropertiesCommand::SetOneTarget( - args[i], propertyPairs, this->Makefile); + bool ret = SetOneTarget(args[i], propertyPairs, &mf); if (!ret) { - std::string message = - cmStrCat("Can not find target to add properties to: ", args[i]); - this->SetError(message); + status.SetError( + cmStrCat("Can not find target to add properties to: ", args[i])); return false; } } return true; } -bool cmSetTargetPropertiesCommand::SetOneTarget( - const std::string& tname, std::vector<std::string>& propertyPairs, - cmMakefile* mf) +static bool SetOneTarget(const std::string& tname, + std::vector<std::string>& propertyPairs, + cmMakefile* mf) { if (cmTarget* target = mf->FindTargetToUse(tname)) { // now loop through all the props and set them diff --git a/Source/cmSetTargetPropertiesCommand.h b/Source/cmSetTargetPropertiesCommand.h index e77b752..9d40c74 100644 --- a/Source/cmSetTargetPropertiesCommand.h +++ b/Source/cmSetTargetPropertiesCommand.h @@ -8,34 +8,9 @@ #include <string> #include <vector> -#include <cm/memory> - -#include "cmCommand.h" - class cmExecutionStatus; -class cmMakefile; - -class cmSetTargetPropertiesCommand : public cmCommand -{ -public: - std::unique_ptr<cmCommand> Clone() override - { - return cm::make_unique<cmSetTargetPropertiesCommand>(); - } - - /** - * This is called when the command is first encountered in - * the input file. - */ - bool InitialPass(std::vector<std::string> const& args, - cmExecutionStatus& status) override; - /** - * Used by this command and cmSetPropertiesCommand - */ - static bool SetOneTarget(const std::string& tname, - std::vector<std::string>& propertyPairs, - cmMakefile* mf); -}; +bool cmSetTargetPropertiesCommand(std::vector<std::string> const& args, + cmExecutionStatus& status); #endif diff --git a/Source/cmSourceFile.cxx b/Source/cmSourceFile.cxx index 3344217..2a345eb 100644 --- a/Source/cmSourceFile.cxx +++ b/Source/cmSourceFile.cxx @@ -6,6 +6,7 @@ #include <utility> #include "cmGlobalGenerator.h" +#include "cmListFileCache.h" #include "cmMakefile.h" #include "cmMessageType.h" #include "cmProperty.h" @@ -28,6 +29,11 @@ std::string const& cmSourceFile::GetExtension() const const std::string cmSourceFile::propLANGUAGE = "LANGUAGE"; const std::string cmSourceFile::propLOCATION = "LOCATION"; const std::string cmSourceFile::propGENERATED = "GENERATED"; +const std::string cmSourceFile::propCOMPILE_DEFINITIONS = + "COMPILE_DEFINITIONS"; +const std::string cmSourceFile::propCOMPILE_OPTIONS = "COMPILE_OPTIONS"; +const std::string cmSourceFile::propINCLUDE_DIRECTORIES = + "INCLUDE_DIRECTORIES"; void cmSourceFile::SetObjectLibrary(std::string const& objlib) { @@ -226,7 +232,27 @@ bool cmSourceFile::Matches(cmSourceFileLocation const& loc) void cmSourceFile::SetProperty(const std::string& prop, const char* value) { - this->Properties.SetProperty(prop, value); + if (prop == propINCLUDE_DIRECTORIES) { + this->IncludeDirectories.clear(); + if (value) { + cmListFileBacktrace lfbt = this->Location.GetMakefile()->GetBacktrace(); + this->IncludeDirectories.emplace_back(value, lfbt); + } + } else if (prop == propCOMPILE_OPTIONS) { + this->CompileOptions.clear(); + if (value) { + cmListFileBacktrace lfbt = this->Location.GetMakefile()->GetBacktrace(); + this->CompileOptions.emplace_back(value, lfbt); + } + } else if (prop == propCOMPILE_DEFINITIONS) { + this->CompileDefinitions.clear(); + if (value) { + cmListFileBacktrace lfbt = this->Location.GetMakefile()->GetBacktrace(); + this->CompileDefinitions.emplace_back(value, lfbt); + } + } else { + this->Properties.SetProperty(prop, value); + } // Update IsGenerated flag if (prop == propGENERATED) { @@ -237,7 +263,24 @@ void cmSourceFile::SetProperty(const std::string& prop, const char* value) void cmSourceFile::AppendProperty(const std::string& prop, const char* value, bool asString) { - this->Properties.AppendProperty(prop, value, asString); + if (prop == propINCLUDE_DIRECTORIES) { + if (value && *value) { + cmListFileBacktrace lfbt = this->Location.GetMakefile()->GetBacktrace(); + this->IncludeDirectories.emplace_back(value, lfbt); + } + } else if (prop == propCOMPILE_OPTIONS) { + if (value && *value) { + cmListFileBacktrace lfbt = this->Location.GetMakefile()->GetBacktrace(); + this->CompileOptions.emplace_back(value, lfbt); + } + } else if (prop == propCOMPILE_DEFINITIONS) { + if (value && *value) { + cmListFileBacktrace lfbt = this->Location.GetMakefile()->GetBacktrace(); + this->CompileDefinitions.emplace_back(value, lfbt); + } + } else { + this->Properties.AppendProperty(prop, value, asString); + } // Update IsGenerated flag if (prop == propGENERATED) { @@ -287,6 +330,37 @@ const char* cmSourceFile::GetProperty(const std::string& prop) const return this->FullPath.c_str(); } + // Check for the properties with backtraces. + if (prop == propINCLUDE_DIRECTORIES) { + if (this->IncludeDirectories.empty()) { + return nullptr; + } + + static std::string output; + output = cmJoin(this->IncludeDirectories, ";"); + return output.c_str(); + } + + if (prop == propCOMPILE_OPTIONS) { + if (this->CompileOptions.empty()) { + return nullptr; + } + + static std::string output; + output = cmJoin(this->CompileOptions, ";"); + return output.c_str(); + } + + if (prop == propCOMPILE_DEFINITIONS) { + if (this->CompileDefinitions.empty()) { + return nullptr; + } + + static std::string output; + output = cmJoin(this->CompileDefinitions, ";"); + return output.c_str(); + } + const char* retVal = this->Properties.GetPropertyValue(prop); if (!retVal) { cmMakefile const* mf = this->Location.GetMakefile(); @@ -314,6 +388,13 @@ bool cmSourceFile::GetPropertyAsBool(const std::string& prop) const return cmIsOn(this->GetProperty(prop)); } +void cmSourceFile::SetProperties(cmPropertyMap properties) +{ + this->Properties = std::move(properties); + + this->IsGenerated = this->GetPropertyAsBool(propGENERATED); +} + cmCustomCommand* cmSourceFile::GetCustomCommand() const { return this->CustomCommand.get(); diff --git a/Source/cmSourceFile.h b/Source/cmSourceFile.h index ff465c7..6ef4167 100644 --- a/Source/cmSourceFile.h +++ b/Source/cmSourceFile.h @@ -6,6 +6,7 @@ #include "cmConfigure.h" // IWYU pragma: keep #include "cmCustomCommand.h" +#include "cmListFileCache.h" #include "cmPropertyMap.h" #include "cmSourceFileLocation.h" #include "cmSourceFileLocationKind.h" @@ -57,6 +58,21 @@ public: /// @return Equivalent to GetPropertyAsBool("GENERATED") bool GetIsGenerated() const { return this->IsGenerated; } + const std::vector<BT<std::string>>& GetCompileOptions() const + { + return this->CompileOptions; + } + + const std::vector<BT<std::string>>& GetCompileDefinitions() const + { + return this->CompileDefinitions; + } + + const std::vector<BT<std::string>>& GetIncludeDirectories() const + { + return this->IncludeDirectories; + } + /** * Resolves the full path to the file. Attempts to locate the file on disk * and finalizes its location. @@ -94,8 +110,9 @@ public: void AddDepend(const std::string& d) { this->Depends.push_back(d); } // Get the properties - cmPropertyMap& GetProperties() { return this->Properties; } const cmPropertyMap& GetProperties() const { return this->Properties; } + // Set the properties + void SetProperties(cmPropertyMap properties); /** * Check whether the given source file location could refer to this @@ -115,6 +132,9 @@ private: std::string FullPath; std::string ObjectLibrary; std::vector<std::string> Depends; + std::vector<BT<std::string>> CompileOptions; + std::vector<BT<std::string>> CompileDefinitions; + std::vector<BT<std::string>> IncludeDirectories; bool FindFullPathFailed = false; bool IsGenerated = false; @@ -125,6 +145,9 @@ private: static const std::string propLANGUAGE; static const std::string propLOCATION; static const std::string propGENERATED; + static const std::string propCOMPILE_DEFINITIONS; + static const std::string propCOMPILE_OPTIONS; + static const std::string propINCLUDE_DIRECTORIES; }; // TODO: Factor out into platform information modules. diff --git a/Source/cmSourceGroupCommand.cxx b/Source/cmSourceGroupCommand.cxx index 3be7fec..3a13e57 100644 --- a/Source/cmSourceGroupCommand.cxx +++ b/Source/cmSourceGroupCommand.cxx @@ -3,16 +3,22 @@ #include "cmSourceGroupCommand.h" #include <cstddef> +#include <map> #include <set> #include <utility> #include "cmAlgorithms.h" +#include "cmExecutionStatus.h" #include "cmMakefile.h" #include "cmSourceGroup.h" #include "cmStringAlgorithms.h" #include "cmSystemTools.h" namespace { + +using ParsedArguments = std::map<std::string, std::vector<std::string>>; +using ExpectedOptions = std::vector<std::string>; + const std::string kTreeOptionName = "TREE"; const std::string kPrefixOptionName = "PREFIX"; const std::string kFilesOptionName = "FILES"; @@ -117,13 +123,8 @@ bool addFilesToItsSourceGroups(const std::string& root, return true; } -} -class cmExecutionStatus; - -// cmSourceGroupCommand -cmSourceGroupCommand::ExpectedOptions -cmSourceGroupCommand::getExpectedOptions() const +ExpectedOptions getExpectedOptions() { ExpectedOptions options; @@ -135,15 +136,14 @@ cmSourceGroupCommand::getExpectedOptions() const return options; } -bool cmSourceGroupCommand::isExpectedOption( - const std::string& argument, const ExpectedOptions& expectedOptions) +bool isExpectedOption(const std::string& argument, + const ExpectedOptions& expectedOptions) { return cmContains(expectedOptions, argument); } -void cmSourceGroupCommand::parseArguments( - const std::vector<std::string>& args, - cmSourceGroupCommand::ParsedArguments& parsedArguments) +void parseArguments(const std::vector<std::string>& args, + ParsedArguments& parsedArguments) { const ExpectedOptions expectedOptions = getExpectedOptions(); size_t i = 0; @@ -172,21 +172,35 @@ void cmSourceGroupCommand::parseArguments( } } -bool cmSourceGroupCommand::InitialPass(std::vector<std::string> const& args, - cmExecutionStatus&) +} // namespace + +static bool checkArgumentsPreconditions(const ParsedArguments& parsedArguments, + std::string& errorMsg); + +static bool processTree(cmMakefile& mf, ParsedArguments& parsedArguments, + std::string& errorMsg); + +static bool checkSingleParameterArgumentPreconditions( + const std::string& argument, const ParsedArguments& parsedArguments, + std::string& errorMsg); + +bool cmSourceGroupCommand(std::vector<std::string> const& args, + cmExecutionStatus& status) { if (args.empty()) { - this->SetError("called with incorrect number of arguments"); + status.SetError("called with incorrect number of arguments"); return false; } + cmMakefile& mf = status.GetMakefile(); + // If only two arguments are given, the pre-1.8 version of the // command is being invoked. if (args.size() == 2 && args[1] != "FILES") { - cmSourceGroup* sg = this->Makefile->GetOrCreateSourceGroup(args[0]); + cmSourceGroup* sg = mf.GetOrCreateSourceGroup(args[0]); if (!sg) { - this->SetError("Could not create or find source group"); + status.SetError("Could not create or find source group"); return false; } @@ -204,21 +218,21 @@ bool cmSourceGroupCommand::InitialPass(std::vector<std::string> const& args, } if (parsedArguments.find(kTreeOptionName) != parsedArguments.end()) { - if (!processTree(parsedArguments, errorMsg)) { - this->SetError(errorMsg); + if (!processTree(mf, parsedArguments, errorMsg)) { + status.SetError(errorMsg); return false; } } else { if (parsedArguments.find(kSourceGroupOptionName) == parsedArguments.end()) { - this->SetError("Missing source group name."); + status.SetError("Missing source group name."); return false; } - cmSourceGroup* sg = this->Makefile->GetOrCreateSourceGroup(args[0]); + cmSourceGroup* sg = mf.GetOrCreateSourceGroup(args[0]); if (!sg) { - this->SetError("Could not create or find source group"); + status.SetError("Could not create or find source group"); return false; } @@ -234,8 +248,7 @@ bool cmSourceGroupCommand::InitialPass(std::vector<std::string> const& args, for (auto const& filesArg : filesArguments) { std::string src = filesArg; if (!cmSystemTools::FileIsFullPath(src)) { - src = - cmStrCat(this->Makefile->GetCurrentSourceDirectory(), '/', filesArg); + src = cmStrCat(mf.GetCurrentSourceDirectory(), '/', filesArg); } src = cmSystemTools::CollapseFullPath(src); sg->AddGroupFile(src); @@ -245,8 +258,8 @@ bool cmSourceGroupCommand::InitialPass(std::vector<std::string> const& args, return true; } -bool cmSourceGroupCommand::checkArgumentsPreconditions( - const ParsedArguments& parsedArguments, std::string& errorMsg) const +static bool checkArgumentsPreconditions(const ParsedArguments& parsedArguments, + std::string& errorMsg) { return checkSingleParameterArgumentPreconditions( kPrefixOptionName, parsedArguments, errorMsg) && @@ -256,8 +269,8 @@ bool cmSourceGroupCommand::checkArgumentsPreconditions( parsedArguments, errorMsg); } -bool cmSourceGroupCommand::processTree(ParsedArguments& parsedArguments, - std::string& errorMsg) +static bool processTree(cmMakefile& mf, ParsedArguments& parsedArguments, + std::string& errorMsg) { const std::string root = cmSystemTools::CollapseFullPath(parsedArguments[kTreeOptionName].front()); @@ -265,9 +278,8 @@ bool cmSourceGroupCommand::processTree(ParsedArguments& parsedArguments, ? "" : parsedArguments[kPrefixOptionName].front(); - const std::vector<std::string> filesVector = - prepareFilesPathsForTree(parsedArguments[kFilesOptionName], - this->Makefile->GetCurrentSourceDirectory()); + const std::vector<std::string> filesVector = prepareFilesPathsForTree( + parsedArguments[kFilesOptionName], mf.GetCurrentSourceDirectory()); if (!rootIsPrefix(root, filesVector, errorMsg)) { return false; @@ -276,13 +288,13 @@ bool cmSourceGroupCommand::processTree(ParsedArguments& parsedArguments, std::set<std::string> sourceGroupPaths = getSourceGroupFilesPaths(root, filesVector); - return addFilesToItsSourceGroups(root, sourceGroupPaths, prefix, - *(this->Makefile), errorMsg); + return addFilesToItsSourceGroups(root, sourceGroupPaths, prefix, mf, + errorMsg); } -bool cmSourceGroupCommand::checkSingleParameterArgumentPreconditions( +static bool checkSingleParameterArgumentPreconditions( const std::string& argument, const ParsedArguments& parsedArguments, - std::string& errorMsg) const + std::string& errorMsg) { auto foundArgument = parsedArguments.find(argument); if (foundArgument != parsedArguments.end()) { diff --git a/Source/cmSourceGroupCommand.h b/Source/cmSourceGroupCommand.h index 6273d92..ad39701 100644 --- a/Source/cmSourceGroupCommand.h +++ b/Source/cmSourceGroupCommand.h @@ -5,59 +5,12 @@ #include "cmConfigure.h" // IWYU pragma: keep -#include <map> #include <string> #include <vector> -#include <cm/memory> - -#include "cmCommand.h" - class cmExecutionStatus; -/** \class cmSourceGroupCommand - * \brief Adds a cmSourceGroup to the cmMakefile. - * - * cmSourceGroupCommand is used to define cmSourceGroups which split up - * source files in to named, organized groups in the generated makefiles. - */ -class cmSourceGroupCommand : public cmCommand -{ -public: - /** - * This is a virtual constructor for the command. - */ - std::unique_ptr<cmCommand> Clone() override - { - return cm::make_unique<cmSourceGroupCommand>(); - } - - /** - * This is called when the command is first encountered in - * the CMakeLists.txt file. - */ - bool InitialPass(std::vector<std::string> const& args, - cmExecutionStatus& status) override; - -private: - using ParsedArguments = std::map<std::string, std::vector<std::string>>; - using ExpectedOptions = std::vector<std::string>; - - ExpectedOptions getExpectedOptions() const; - - bool isExpectedOption(const std::string& argument, - const ExpectedOptions& expectedOptions); - - void parseArguments(const std::vector<std::string>& args, - cmSourceGroupCommand::ParsedArguments& parsedArguments); - - bool processTree(ParsedArguments& parsedArguments, std::string& errorMsg); - - bool checkArgumentsPreconditions(const ParsedArguments& parsedArguments, - std::string& errorMsg) const; - bool checkSingleParameterArgumentPreconditions( - const std::string& argument, const ParsedArguments& parsedArguments, - std::string& errorMsg) const; -}; +bool cmSourceGroupCommand(std::vector<std::string> const& args, + cmExecutionStatus& status); #endif diff --git a/Source/cmState.cxx b/Source/cmState.cxx index 93ad2d7..6de312c 100644 --- a/Source/cmState.cxx +++ b/Source/cmState.cxx @@ -503,7 +503,7 @@ void cmState::AddScriptedCommand(std::string const& name, Command command) std::string sName = cmSystemTools::LowerCase(name); // if the command already exists, give a new name to the old command. - if (Command oldCmd = this->GetCommand(sName)) { + if (Command oldCmd = this->GetCommandByExactName(sName)) { this->ScriptedCommands["_" + sName] = oldCmd; } diff --git a/Source/cmTarget.cxx b/Source/cmTarget.cxx index 1b88db6..62ead60 100644 --- a/Source/cmTarget.cxx +++ b/Source/cmTarget.cxx @@ -332,6 +332,9 @@ cmTarget::cmTarget(std::string const& name, cmStateEnums::TargetType type, initProp("C_STANDARD"); initProp("C_STANDARD_REQUIRED"); initProp("C_EXTENSIONS"); + initProp("OBJC_STANDARD"); + initProp("OBJC_STANDARD_REQUIRED"); + initProp("OBJC_EXTENSIONS"); initProp("CXX_CLANG_TIDY"); initProp("CXX_COMPILER_LAUNCHER"); initProp("CXX_CPPLINT"); @@ -340,6 +343,9 @@ cmTarget::cmTarget(std::string const& name, cmStateEnums::TargetType type, initProp("CXX_STANDARD"); initProp("CXX_STANDARD_REQUIRED"); initProp("CXX_EXTENSIONS"); + initProp("OBJCXX_STANDARD"); + initProp("OBJCXX_STANDARD_REQUIRED"); + initProp("OBJCXX_EXTENSIONS"); initProp("CUDA_STANDARD"); initProp("CUDA_STANDARD_REQUIRED"); initProp("CUDA_EXTENSIONS"); @@ -452,6 +458,8 @@ cmTarget::cmTarget(std::string const& name, cmStateEnums::TargetType type, this->GetType() != cmStateEnums::UTILITY) { initProp("C_VISIBILITY_PRESET"); initProp("CXX_VISIBILITY_PRESET"); + initProp("OBJC_VISIBILITY_PRESET"); + initProp("OBJCXX_VISIBILITY_PRESET"); initProp("CUDA_VISIBILITY_PRESET"); initProp("VISIBILITY_INLINES_HIDDEN"); } @@ -604,6 +612,11 @@ void cmTarget::AddPreBuildCommand(cmCustomCommand const& cmd) impl->PreBuildCommands.push_back(cmd); } +void cmTarget::AddPreBuildCommand(cmCustomCommand&& cmd) +{ + impl->PreBuildCommands.push_back(std::move(cmd)); +} + std::vector<cmCustomCommand> const& cmTarget::GetPreLinkCommands() const { return impl->PreLinkCommands; @@ -614,6 +627,11 @@ void cmTarget::AddPreLinkCommand(cmCustomCommand const& cmd) impl->PreLinkCommands.push_back(cmd); } +void cmTarget::AddPreLinkCommand(cmCustomCommand&& cmd) +{ + impl->PreLinkCommands.push_back(std::move(cmd)); +} + std::vector<cmCustomCommand> const& cmTarget::GetPostBuildCommands() const { return impl->PostBuildCommands; @@ -624,6 +642,11 @@ void cmTarget::AddPostBuildCommand(cmCustomCommand const& cmd) impl->PostBuildCommands.push_back(cmd); } +void cmTarget::AddPostBuildCommand(cmCustomCommand&& cmd) +{ + impl->PostBuildCommands.push_back(std::move(cmd)); +} + void cmTarget::AddTracedSources(std::vector<std::string> const& srcs) { if (!srcs.empty()) { diff --git a/Source/cmTarget.h b/Source/cmTarget.h index f4726d3..65a1ce3 100644 --- a/Source/cmTarget.h +++ b/Source/cmTarget.h @@ -43,13 +43,6 @@ public: VisibilityImportedGlobally }; - enum CustomCommandType - { - PRE_BUILD, - PRE_LINK, - POST_BUILD - }; - cmTarget(std::string const& name, cmStateEnums::TargetType type, Visibility vis, cmMakefile* mf); @@ -91,14 +84,17 @@ public: //! Get the list of the PRE_BUILD custom commands for this target std::vector<cmCustomCommand> const& GetPreBuildCommands() const; void AddPreBuildCommand(cmCustomCommand const& cmd); + void AddPreBuildCommand(cmCustomCommand&& cmd); //! Get the list of the PRE_LINK custom commands for this target std::vector<cmCustomCommand> const& GetPreLinkCommands() const; void AddPreLinkCommand(cmCustomCommand const& cmd); + void AddPreLinkCommand(cmCustomCommand&& cmd); //! Get the list of the POST_BUILD custom commands for this target std::vector<cmCustomCommand> const& GetPostBuildCommands() const; void AddPostBuildCommand(cmCustomCommand const& cmd); + void AddPostBuildCommand(cmCustomCommand&& cmd); //! Add sources to the target. void AddSources(std::vector<std::string> const& srcs); diff --git a/Source/cmTargetCompileDefinitionsCommand.cxx b/Source/cmTargetCompileDefinitionsCommand.cxx index 94e249f..edee167 100644 --- a/Source/cmTargetCompileDefinitionsCommand.cxx +++ b/Source/cmTargetCompileDefinitionsCommand.cxx @@ -6,43 +6,53 @@ #include "cmMessageType.h" #include "cmStringAlgorithms.h" #include "cmTarget.h" +#include "cmTargetPropCommandBase.h" -class cmExecutionStatus; +namespace { -bool cmTargetCompileDefinitionsCommand::InitialPass( - std::vector<std::string> const& args, cmExecutionStatus&) +class TargetCompileDefinitionsImpl : public cmTargetPropCommandBase { - return this->HandleArguments(args, "COMPILE_DEFINITIONS"); -} +public: + using cmTargetPropCommandBase::cmTargetPropCommandBase; -void cmTargetCompileDefinitionsCommand::HandleMissingTarget( - const std::string& name) -{ - this->Makefile->IssueMessage( - MessageType::FATAL_ERROR, - cmStrCat("Cannot specify compile definitions for target \"", name, - "\" which is not built by this project.")); -} +private: + void HandleMissingTarget(const std::string& name) override + { + this->Makefile->IssueMessage( + MessageType::FATAL_ERROR, + cmStrCat("Cannot specify compile definitions for target \"", name, + "\" which is not built by this project.")); + } -std::string cmTargetCompileDefinitionsCommand::Join( - const std::vector<std::string>& content) -{ - std::string defs; - std::string sep; - for (std::string const& it : content) { - if (cmHasLiteralPrefix(it, "-D")) { - defs += sep + it.substr(2); - } else { - defs += sep + it; + bool HandleDirectContent(cmTarget* tgt, + const std::vector<std::string>& content, + bool /*prepend*/, bool /*system*/) override + { + tgt->AppendProperty("COMPILE_DEFINITIONS", this->Join(content).c_str()); + return true; // Successfully handled. + } + + std::string Join(const std::vector<std::string>& content) override + { + std::string defs; + std::string sep; + for (std::string const& it : content) { + if (cmHasLiteralPrefix(it, "-D")) { + defs += sep + it.substr(2); + } else { + defs += sep + it; + } + sep = ";"; } - sep = ";"; + return defs; } - return defs; -} +}; + +} // namespace -bool cmTargetCompileDefinitionsCommand::HandleDirectContent( - cmTarget* tgt, const std::vector<std::string>& content, bool, bool) +bool cmTargetCompileDefinitionsCommand(std::vector<std::string> const& args, + cmExecutionStatus& status) { - tgt->AppendProperty("COMPILE_DEFINITIONS", this->Join(content).c_str()); - return true; // Successfully handled. + return TargetCompileDefinitionsImpl(status).HandleArguments( + args, "COMPILE_DEFINITIONS"); } diff --git a/Source/cmTargetCompileDefinitionsCommand.h b/Source/cmTargetCompileDefinitionsCommand.h index f85dc0a..05ff092 100644 --- a/Source/cmTargetCompileDefinitionsCommand.h +++ b/Source/cmTargetCompileDefinitionsCommand.h @@ -8,39 +8,9 @@ #include <string> #include <vector> -#include <cm/memory> - -#include "cmCommand.h" -#include "cmTargetPropCommandBase.h" - class cmExecutionStatus; -class cmTarget; - -class cmTargetCompileDefinitionsCommand : public cmTargetPropCommandBase -{ -public: - /** - * This is a virtual constructor for the command. - */ - std::unique_ptr<cmCommand> Clone() override - { - return cm::make_unique<cmTargetCompileDefinitionsCommand>(); - } - - /** - * This is called when the command is first encountered in - * the CMakeLists.txt file. - */ - bool InitialPass(std::vector<std::string> const& args, - cmExecutionStatus& status) override; - -private: - void HandleMissingTarget(const std::string& name) override; - bool HandleDirectContent(cmTarget* tgt, - const std::vector<std::string>& content, - bool prepend, bool system) override; - std::string Join(const std::vector<std::string>& content) override; -}; +bool cmTargetCompileDefinitionsCommand(std::vector<std::string> const& args, + cmExecutionStatus& status); #endif diff --git a/Source/cmTargetCompileFeaturesCommand.cxx b/Source/cmTargetCompileFeaturesCommand.cxx index a22b94b..06be4f0 100644 --- a/Source/cmTargetCompileFeaturesCommand.cxx +++ b/Source/cmTargetCompileFeaturesCommand.cxx @@ -5,40 +5,51 @@ #include "cmMakefile.h" #include "cmMessageType.h" #include "cmStringAlgorithms.h" +#include "cmTargetPropCommandBase.h" -class cmExecutionStatus; class cmTarget; -bool cmTargetCompileFeaturesCommand::InitialPass( - std::vector<std::string> const& args, cmExecutionStatus&) -{ - return this->HandleArguments(args, "COMPILE_FEATURES", NO_FLAGS); -} +namespace { -void cmTargetCompileFeaturesCommand::HandleMissingTarget( - const std::string& name) +class TargetCompileFeaturesImpl : public cmTargetPropCommandBase { - this->Makefile->IssueMessage( - MessageType::FATAL_ERROR, - cmStrCat("Cannot specify compile features for target \"", name, - "\" which is not built by this project.")); -} +public: + using cmTargetPropCommandBase::cmTargetPropCommandBase; -std::string cmTargetCompileFeaturesCommand::Join( - const std::vector<std::string>& content) -{ - return cmJoin(content, ";"); -} +private: + void HandleMissingTarget(const std::string& name) override + { + this->Makefile->IssueMessage( + MessageType::FATAL_ERROR, + cmStrCat("Cannot specify compile features for target \"", name, + "\" which is not built by this project.")); + } -bool cmTargetCompileFeaturesCommand::HandleDirectContent( - cmTarget* tgt, const std::vector<std::string>& content, bool, bool) -{ - for (std::string const& it : content) { - std::string error; - if (!this->Makefile->AddRequiredTargetFeature(tgt, it, &error)) { - this->SetError(error); - return false; // Not (successfully) handled. + bool HandleDirectContent(cmTarget* tgt, + const std::vector<std::string>& content, + bool /*prepend*/, bool /*system*/) override + { + for (std::string const& it : content) { + std::string error; + if (!this->Makefile->AddRequiredTargetFeature(tgt, it, &error)) { + this->SetError(error); + return false; // Not (successfully) handled. + } } + return true; // Successfully handled. + } + + std::string Join(const std::vector<std::string>& content) override + { + return cmJoin(content, ";"); } - return true; // Successfully handled. +}; + +} // namespace + +bool cmTargetCompileFeaturesCommand(std::vector<std::string> const& args, + cmExecutionStatus& status) +{ + return TargetCompileFeaturesImpl(status).HandleArguments(args, + "COMPILE_FEATURES"); } diff --git a/Source/cmTargetCompileFeaturesCommand.h b/Source/cmTargetCompileFeaturesCommand.h index 39597ca..db0c04b 100644 --- a/Source/cmTargetCompileFeaturesCommand.h +++ b/Source/cmTargetCompileFeaturesCommand.h @@ -8,31 +8,9 @@ #include <string> #include <vector> -#include <cm/memory> - -#include "cmCommand.h" -#include "cmTargetPropCommandBase.h" - class cmExecutionStatus; -class cmTarget; - -class cmTargetCompileFeaturesCommand : public cmTargetPropCommandBase -{ - std::unique_ptr<cmCommand> Clone() override - { - return cm::make_unique<cmTargetCompileFeaturesCommand>(); - } - - bool InitialPass(std::vector<std::string> const& args, - cmExecutionStatus& status) override; - -private: - void HandleMissingTarget(const std::string& name) override; - bool HandleDirectContent(cmTarget* tgt, - const std::vector<std::string>& content, - bool prepend, bool system) override; - std::string Join(const std::vector<std::string>& content) override; -}; +bool cmTargetCompileFeaturesCommand(std::vector<std::string> const& args, + cmExecutionStatus& status); #endif diff --git a/Source/cmTargetCompileOptionsCommand.cxx b/Source/cmTargetCompileOptionsCommand.cxx index ccc215a..e39b726 100644 --- a/Source/cmTargetCompileOptionsCommand.cxx +++ b/Source/cmTargetCompileOptionsCommand.cxx @@ -7,34 +7,44 @@ #include "cmMessageType.h" #include "cmStringAlgorithms.h" #include "cmTarget.h" +#include "cmTargetPropCommandBase.h" -class cmExecutionStatus; +namespace { -bool cmTargetCompileOptionsCommand::InitialPass( - std::vector<std::string> const& args, cmExecutionStatus&) +class TargetCompileOptionsImpl : public cmTargetPropCommandBase { - return this->HandleArguments(args, "COMPILE_OPTIONS", PROCESS_BEFORE); -} +public: + using cmTargetPropCommandBase::cmTargetPropCommandBase; -void cmTargetCompileOptionsCommand::HandleMissingTarget( - const std::string& name) -{ - this->Makefile->IssueMessage( - MessageType::FATAL_ERROR, - cmStrCat("Cannot specify compile options for target \"", name, - "\" which is not built by this project.")); -} +private: + void HandleMissingTarget(const std::string& name) override + { + this->Makefile->IssueMessage( + MessageType::FATAL_ERROR, + cmStrCat("Cannot specify compile options for target \"", name, + "\" which is not built by this project.")); + } -std::string cmTargetCompileOptionsCommand::Join( - const std::vector<std::string>& content) -{ - return cmJoin(content, ";"); -} + bool HandleDirectContent(cmTarget* tgt, + const std::vector<std::string>& content, + bool /*prepend*/, bool /*system*/) override + { + cmListFileBacktrace lfbt = this->Makefile->GetBacktrace(); + tgt->InsertCompileOption(this->Join(content), lfbt); + return true; // Successfully handled. + } + + std::string Join(const std::vector<std::string>& content) override + { + return cmJoin(content, ";"); + } +}; + +} // namespace -bool cmTargetCompileOptionsCommand::HandleDirectContent( - cmTarget* tgt, const std::vector<std::string>& content, bool, bool) +bool cmTargetCompileOptionsCommand(std::vector<std::string> const& args, + cmExecutionStatus& status) { - cmListFileBacktrace lfbt = this->Makefile->GetBacktrace(); - tgt->InsertCompileOption(this->Join(content), lfbt); - return true; // Successfully handled. + return TargetCompileOptionsImpl(status).HandleArguments( + args, "COMPILE_OPTIONS", TargetCompileOptionsImpl::PROCESS_BEFORE); } diff --git a/Source/cmTargetCompileOptionsCommand.h b/Source/cmTargetCompileOptionsCommand.h index b328ba2..3ab1a89 100644 --- a/Source/cmTargetCompileOptionsCommand.h +++ b/Source/cmTargetCompileOptionsCommand.h @@ -8,39 +8,9 @@ #include <string> #include <vector> -#include <cm/memory> - -#include "cmCommand.h" -#include "cmTargetPropCommandBase.h" - class cmExecutionStatus; -class cmTarget; - -class cmTargetCompileOptionsCommand : public cmTargetPropCommandBase -{ -public: - /** - * This is a virtual constructor for the command. - */ - std::unique_ptr<cmCommand> Clone() override - { - return cm::make_unique<cmTargetCompileOptionsCommand>(); - } - - /** - * This is called when the command is first encountered in - * the CMakeLists.txt file. - */ - bool InitialPass(std::vector<std::string> const& args, - cmExecutionStatus& status) override; - -private: - void HandleMissingTarget(const std::string& name) override; - bool HandleDirectContent(cmTarget* tgt, - const std::vector<std::string>& content, - bool prepend, bool system) override; - std::string Join(const std::vector<std::string>& content) override; -}; +bool cmTargetCompileOptionsCommand(std::vector<std::string> const& args, + cmExecutionStatus& status); #endif diff --git a/Source/cmTargetIncludeDirectoriesCommand.cxx b/Source/cmTargetIncludeDirectoriesCommand.cxx index 7801ee8..95b69f3 100644 --- a/Source/cmTargetIncludeDirectoriesCommand.cxx +++ b/Source/cmTargetIncludeDirectoriesCommand.cxx @@ -11,26 +11,36 @@ #include "cmStringAlgorithms.h" #include "cmSystemTools.h" #include "cmTarget.h" +#include "cmTargetPropCommandBase.h" -class cmExecutionStatus; +namespace { -bool cmTargetIncludeDirectoriesCommand::InitialPass( - std::vector<std::string> const& args, cmExecutionStatus&) +class TargetIncludeDirectoriesImpl : public cmTargetPropCommandBase { - return this->HandleArguments(args, "INCLUDE_DIRECTORIES", - ArgumentFlags(PROCESS_BEFORE | PROCESS_SYSTEM)); -} +public: + using cmTargetPropCommandBase::cmTargetPropCommandBase; -void cmTargetIncludeDirectoriesCommand::HandleMissingTarget( - const std::string& name) -{ - this->Makefile->IssueMessage( - MessageType::FATAL_ERROR, - cmStrCat("Cannot specify include directories for target \"", name, - "\" which is not built by this project.")); -} +private: + void HandleMissingTarget(const std::string& name) override + { + this->Makefile->IssueMessage( + MessageType::FATAL_ERROR, + cmStrCat("Cannot specify include directories for target \"", name, + "\" which is not built by this project.")); + } + + bool HandleDirectContent(cmTarget* tgt, + const std::vector<std::string>& content, + bool prepend, bool system) override; + + void HandleInterfaceContent(cmTarget* tgt, + const std::vector<std::string>& content, + bool prepend, bool system) override; -std::string cmTargetIncludeDirectoriesCommand::Join( + std::string Join(const std::vector<std::string>& content) override; +}; + +std::string TargetIncludeDirectoriesImpl::Join( const std::vector<std::string>& content) { std::string dirs; @@ -48,7 +58,7 @@ std::string cmTargetIncludeDirectoriesCommand::Join( return dirs; } -bool cmTargetIncludeDirectoriesCommand::HandleDirectContent( +bool TargetIncludeDirectoriesImpl::HandleDirectContent( cmTarget* tgt, const std::vector<std::string>& content, bool prepend, bool system) { @@ -70,16 +80,27 @@ bool cmTargetIncludeDirectoriesCommand::HandleDirectContent( return true; // Successfully handled. } -void cmTargetIncludeDirectoriesCommand::HandleInterfaceContent( +void TargetIncludeDirectoriesImpl::HandleInterfaceContent( cmTarget* tgt, const std::vector<std::string>& content, bool prepend, bool system) { cmTargetPropCommandBase::HandleInterfaceContent(tgt, content, prepend, system); - if (system) { std::string joined = this->Join(content); tgt->AppendProperty("INTERFACE_SYSTEM_INCLUDE_DIRECTORIES", joined.c_str()); } } + +} // namespace + +bool cmTargetIncludeDirectoriesCommand(std::vector<std::string> const& args, + cmExecutionStatus& status) +{ + return TargetIncludeDirectoriesImpl(status).HandleArguments( + args, "INCLUDE_DIRECTORIES", + TargetIncludeDirectoriesImpl::ArgumentFlags( + TargetIncludeDirectoriesImpl::PROCESS_BEFORE | + TargetIncludeDirectoriesImpl::PROCESS_SYSTEM)); +} diff --git a/Source/cmTargetIncludeDirectoriesCommand.h b/Source/cmTargetIncludeDirectoriesCommand.h index f6481db..9958f41 100644 --- a/Source/cmTargetIncludeDirectoriesCommand.h +++ b/Source/cmTargetIncludeDirectoriesCommand.h @@ -8,43 +8,9 @@ #include <string> #include <vector> -#include <cm/memory> - -#include "cmCommand.h" -#include "cmTargetPropCommandBase.h" - class cmExecutionStatus; -class cmTarget; - -class cmTargetIncludeDirectoriesCommand : public cmTargetPropCommandBase -{ -public: - /** - * This is a virtual constructor for the command. - */ - std::unique_ptr<cmCommand> Clone() override - { - return cm::make_unique<cmTargetIncludeDirectoriesCommand>(); - } - - /** - * This is called when the command is first encountered in - * the CMakeLists.txt file. - */ - bool InitialPass(std::vector<std::string> const& args, - cmExecutionStatus& status) override; - -private: - void HandleMissingTarget(const std::string& name) override; - - bool HandleDirectContent(cmTarget* tgt, - const std::vector<std::string>& content, - bool prepend, bool system) override; - void HandleInterfaceContent(cmTarget* tgt, - const std::vector<std::string>& content, - bool prepend, bool system) override; - std::string Join(const std::vector<std::string>& content) override; -}; +bool cmTargetIncludeDirectoriesCommand(std::vector<std::string> const& args, + cmExecutionStatus& status); #endif diff --git a/Source/cmTargetLinkDirectoriesCommand.cxx b/Source/cmTargetLinkDirectoriesCommand.cxx index c2ef6c1..0c68d60 100644 --- a/Source/cmTargetLinkDirectoriesCommand.cxx +++ b/Source/cmTargetLinkDirectoriesCommand.cxx @@ -9,25 +9,37 @@ #include "cmStringAlgorithms.h" #include "cmSystemTools.h" #include "cmTarget.h" +#include "cmTargetPropCommandBase.h" -class cmExecutionStatus; +namespace { -bool cmTargetLinkDirectoriesCommand::InitialPass( - std::vector<std::string> const& args, cmExecutionStatus&) +class TargetLinkDirectoriesImpl : public cmTargetPropCommandBase { - return this->HandleArguments(args, "LINK_DIRECTORIES", PROCESS_BEFORE); -} +public: + using cmTargetPropCommandBase::cmTargetPropCommandBase; -void cmTargetLinkDirectoriesCommand::HandleMissingTarget( - const std::string& name) -{ - this->Makefile->IssueMessage( - MessageType::FATAL_ERROR, - cmStrCat("Cannot specify link directories for target \"", name, - "\" which is not built by this project.")); -} +private: + void HandleMissingTarget(const std::string& name) override + { + this->Makefile->IssueMessage( + MessageType::FATAL_ERROR, + cmStrCat("Cannot specify link directories for target \"", name, + "\" which is not built by this project.")); + } + + std::string Join(const std::vector<std::string>& content) override; -std::string cmTargetLinkDirectoriesCommand::Join( + bool HandleDirectContent(cmTarget* tgt, + const std::vector<std::string>& content, + bool prepend, bool /*system*/) override + { + cmListFileBacktrace lfbt = this->Makefile->GetBacktrace(); + tgt->InsertLinkDirectory(this->Join(content), lfbt, prepend); + return true; // Successfully handled. + } +}; + +std::string TargetLinkDirectoriesImpl::Join( const std::vector<std::string>& content) { std::vector<std::string> directories; @@ -48,12 +60,11 @@ std::string cmTargetLinkDirectoriesCommand::Join( return cmJoin(directories, ";"); } -bool cmTargetLinkDirectoriesCommand::HandleDirectContent( - cmTarget* tgt, const std::vector<std::string>& content, bool prepend, bool) -{ - cmListFileBacktrace lfbt = this->Makefile->GetBacktrace(); +} // namespace - tgt->InsertLinkDirectory(this->Join(content), lfbt, prepend); - - return true; // Successfully handled. +bool cmTargetLinkDirectoriesCommand(std::vector<std::string> const& args, + cmExecutionStatus& status) +{ + return TargetLinkDirectoriesImpl(status).HandleArguments( + args, "LINK_DIRECTORIES", TargetLinkDirectoriesImpl::PROCESS_BEFORE); } diff --git a/Source/cmTargetLinkDirectoriesCommand.h b/Source/cmTargetLinkDirectoriesCommand.h index a651d73..3724d6c 100644 --- a/Source/cmTargetLinkDirectoriesCommand.h +++ b/Source/cmTargetLinkDirectoriesCommand.h @@ -8,39 +8,9 @@ #include <string> #include <vector> -#include <cm/memory> - -#include "cmCommand.h" -#include "cmTargetPropCommandBase.h" - class cmExecutionStatus; -class cmTarget; - -class cmTargetLinkDirectoriesCommand : public cmTargetPropCommandBase -{ -public: - /** - * This is a virtual constructor for the command. - */ - std::unique_ptr<cmCommand> Clone() override - { - return cm::make_unique<cmTargetLinkDirectoriesCommand>(); - } - - /** - * This is called when the command is first encountered in - * the CMakeLists.txt file. - */ - bool InitialPass(std::vector<std::string> const& args, - cmExecutionStatus& status) override; - -private: - void HandleMissingTarget(const std::string& name) override; - std::string Join(const std::vector<std::string>& content) override; - bool HandleDirectContent(cmTarget* tgt, - const std::vector<std::string>& content, - bool prepend, bool system) override; -}; +bool cmTargetLinkDirectoriesCommand(std::vector<std::string> const& args, + cmExecutionStatus& status); #endif diff --git a/Source/cmTargetLinkLibrariesCommand.cxx b/Source/cmTargetLinkLibrariesCommand.cxx index b432599..0d2383a 100644 --- a/Source/cmTargetLinkLibrariesCommand.cxx +++ b/Source/cmTargetLinkLibrariesCommand.cxx @@ -5,6 +5,7 @@ #include <cstring> #include <sstream> +#include "cmExecutionStatus.h" #include "cmGeneratorExpression.h" #include "cmGlobalGenerator.h" #include "cmMakefile.h" @@ -15,44 +16,64 @@ #include "cmStringAlgorithms.h" #include "cmSystemTools.h" #include "cmTarget.h" +#include "cmTargetLinkLibraryType.h" #include "cmake.h" -class cmExecutionStatus; +namespace { -const char* cmTargetLinkLibrariesCommand::LinkLibraryTypeNames[3] = { - "general", "debug", "optimized" +enum ProcessingState +{ + ProcessingLinkLibraries, + ProcessingPlainLinkInterface, + ProcessingKeywordLinkInterface, + ProcessingPlainPublicInterface, + ProcessingKeywordPublicInterface, + ProcessingPlainPrivateInterface, + ProcessingKeywordPrivateInterface }; -// cmTargetLinkLibrariesCommand -bool cmTargetLinkLibrariesCommand::InitialPass( - std::vector<std::string> const& args, cmExecutionStatus&) +const char* LinkLibraryTypeNames[3] = { "general", "debug", "optimized" }; + +} // namespace + +static void LinkLibraryTypeSpecifierWarning(cmMakefile& mf, int left, + int right); + +static bool HandleLibrary(cmMakefile& mf, cmTarget* target, + ProcessingState currentProcessingState, + const std::string& lib, cmTargetLinkLibraryType llt); + +bool cmTargetLinkLibrariesCommand(std::vector<std::string> const& args, + cmExecutionStatus& status) { // Must have at least one argument. if (args.empty()) { - this->SetError("called with incorrect number of arguments"); + status.SetError("called with incorrect number of arguments"); return false; } + + cmMakefile& mf = status.GetMakefile(); + // Alias targets cannot be on the LHS of this command. - if (this->Makefile->IsAlias(args[0])) { - this->SetError("can not be used on an ALIAS target."); + if (mf.IsAlias(args[0])) { + status.SetError("can not be used on an ALIAS target."); return false; } // Lookup the target for which libraries are specified. - this->Target = - this->Makefile->GetCMakeInstance()->GetGlobalGenerator()->FindTarget( - args[0]); - if (!this->Target) { + cmTarget* target = + mf.GetCMakeInstance()->GetGlobalGenerator()->FindTarget(args[0]); + if (!target) { const std::vector<cmTarget*>& importedTargets = - this->Makefile->GetOwnedImportedTargets(); + mf.GetOwnedImportedTargets(); for (cmTarget* importedTarget : importedTargets) { if (importedTarget->GetName() == args[0]) { - this->Target = importedTarget; + target = importedTarget; break; } } } - if (!this->Target) { + if (!target) { MessageType t = MessageType::FATAL_ERROR; // fail by default std::ostringstream e; e << "Cannot specify link libraries for target \"" << args[0] << "\" " @@ -60,7 +81,7 @@ bool cmTargetLinkLibrariesCommand::InitialPass( // The bad target is the only argument. Check how policy CMP0016 is set, // and accept, warn or fail respectively: if (args.size() < 2) { - switch (this->Makefile->GetPolicyStatus(cmPolicies::CMP0016)) { + switch (mf.GetPolicyStatus(cmPolicies::CMP0016)) { case cmPolicies::WARN: t = MessageType::AUTHOR_WARNING; // Print the warning. @@ -84,10 +105,10 @@ bool cmTargetLinkLibrariesCommand::InitialPass( // Now actually print the message. switch (t) { case MessageType::AUTHOR_WARNING: - this->Makefile->IssueMessage(MessageType::AUTHOR_WARNING, e.str()); + mf.IssueMessage(MessageType::AUTHOR_WARNING, e.str()); break; case MessageType::FATAL_ERROR: - this->Makefile->IssueMessage(MessageType::FATAL_ERROR, e.str()); + mf.IssueMessage(MessageType::FATAL_ERROR, e.str()); cmSystemTools::SetFatalErrorOccured(); break; default: @@ -97,11 +118,11 @@ bool cmTargetLinkLibrariesCommand::InitialPass( } // Having a UTILITY library on the LHS is a bug. - if (this->Target->GetType() == cmStateEnums::UTILITY) { + if (target->GetType() == cmStateEnums::UTILITY) { std::ostringstream e; const char* modal = nullptr; MessageType messageType = MessageType::AUTHOR_WARNING; - switch (this->Makefile->GetPolicyStatus(cmPolicies::CMP0039)) { + switch (mf.GetPolicyStatus(cmPolicies::CMP0039)) { case cmPolicies::WARN: e << cmPolicies::GetPolicyWarning(cmPolicies::CMP0039) << "\n"; modal = "should"; @@ -114,9 +135,9 @@ bool cmTargetLinkLibrariesCommand::InitialPass( messageType = MessageType::FATAL_ERROR; } if (modal) { - e << "Utility target \"" << this->Target->GetName() << "\" " << modal + e << "Utility target \"" << target->GetName() << "\" " << modal << " not be used as the target of a target_link_libraries call."; - this->Makefile->IssueMessage(messageType, e.str()); + mf.IssueMessage(messageType, e.str()); if (messageType == MessageType::FATAL_ERROR) { return false; } @@ -134,15 +155,15 @@ bool cmTargetLinkLibrariesCommand::InitialPass( // Start with primary linking and switch to link interface // specification if the keyword is encountered as the first argument. - this->CurrentProcessingState = ProcessingLinkLibraries; + ProcessingState currentProcessingState = ProcessingLinkLibraries; // Add libraries, note that there is an optional prefix // of debug and optimized that can be used. for (unsigned int i = 1; i < args.size(); ++i) { if (args[i] == "LINK_INTERFACE_LIBRARIES") { - this->CurrentProcessingState = ProcessingPlainLinkInterface; + currentProcessingState = ProcessingPlainLinkInterface; if (i != 1) { - this->Makefile->IssueMessage( + mf.IssueMessage( MessageType::FATAL_ERROR, "The LINK_INTERFACE_LIBRARIES option must appear as the second " "argument, just after the target name."); @@ -150,84 +171,83 @@ bool cmTargetLinkLibrariesCommand::InitialPass( } } else if (args[i] == "INTERFACE") { if (i != 1 && - this->CurrentProcessingState != ProcessingKeywordPrivateInterface && - this->CurrentProcessingState != ProcessingKeywordPublicInterface && - this->CurrentProcessingState != ProcessingKeywordLinkInterface) { - this->Makefile->IssueMessage( + currentProcessingState != ProcessingKeywordPrivateInterface && + currentProcessingState != ProcessingKeywordPublicInterface && + currentProcessingState != ProcessingKeywordLinkInterface) { + mf.IssueMessage( MessageType::FATAL_ERROR, "The INTERFACE, PUBLIC or PRIVATE option must appear as the second " "argument, just after the target name."); return true; } - this->CurrentProcessingState = ProcessingKeywordLinkInterface; + currentProcessingState = ProcessingKeywordLinkInterface; } else if (args[i] == "LINK_PUBLIC") { if (i != 1 && - this->CurrentProcessingState != ProcessingPlainPrivateInterface && - this->CurrentProcessingState != ProcessingPlainPublicInterface) { - this->Makefile->IssueMessage( + currentProcessingState != ProcessingPlainPrivateInterface && + currentProcessingState != ProcessingPlainPublicInterface) { + mf.IssueMessage( MessageType::FATAL_ERROR, "The LINK_PUBLIC or LINK_PRIVATE option must appear as the second " "argument, just after the target name."); return true; } - this->CurrentProcessingState = ProcessingPlainPublicInterface; + currentProcessingState = ProcessingPlainPublicInterface; } else if (args[i] == "PUBLIC") { if (i != 1 && - this->CurrentProcessingState != ProcessingKeywordPrivateInterface && - this->CurrentProcessingState != ProcessingKeywordPublicInterface && - this->CurrentProcessingState != ProcessingKeywordLinkInterface) { - this->Makefile->IssueMessage( + currentProcessingState != ProcessingKeywordPrivateInterface && + currentProcessingState != ProcessingKeywordPublicInterface && + currentProcessingState != ProcessingKeywordLinkInterface) { + mf.IssueMessage( MessageType::FATAL_ERROR, "The INTERFACE, PUBLIC or PRIVATE option must appear as the second " "argument, just after the target name."); return true; } - this->CurrentProcessingState = ProcessingKeywordPublicInterface; + currentProcessingState = ProcessingKeywordPublicInterface; } else if (args[i] == "LINK_PRIVATE") { - if (i != 1 && - this->CurrentProcessingState != ProcessingPlainPublicInterface && - this->CurrentProcessingState != ProcessingPlainPrivateInterface) { - this->Makefile->IssueMessage( + if (i != 1 && currentProcessingState != ProcessingPlainPublicInterface && + currentProcessingState != ProcessingPlainPrivateInterface) { + mf.IssueMessage( MessageType::FATAL_ERROR, "The LINK_PUBLIC or LINK_PRIVATE option must appear as the second " "argument, just after the target name."); return true; } - this->CurrentProcessingState = ProcessingPlainPrivateInterface; + currentProcessingState = ProcessingPlainPrivateInterface; } else if (args[i] == "PRIVATE") { if (i != 1 && - this->CurrentProcessingState != ProcessingKeywordPrivateInterface && - this->CurrentProcessingState != ProcessingKeywordPublicInterface && - this->CurrentProcessingState != ProcessingKeywordLinkInterface) { - this->Makefile->IssueMessage( + currentProcessingState != ProcessingKeywordPrivateInterface && + currentProcessingState != ProcessingKeywordPublicInterface && + currentProcessingState != ProcessingKeywordLinkInterface) { + mf.IssueMessage( MessageType::FATAL_ERROR, "The INTERFACE, PUBLIC or PRIVATE option must appear as the second " "argument, just after the target name."); return true; } - this->CurrentProcessingState = ProcessingKeywordPrivateInterface; + currentProcessingState = ProcessingKeywordPrivateInterface; } else if (args[i] == "debug") { if (haveLLT) { - this->LinkLibraryTypeSpecifierWarning(llt, DEBUG_LibraryType); + LinkLibraryTypeSpecifierWarning(mf, llt, DEBUG_LibraryType); } llt = DEBUG_LibraryType; haveLLT = true; } else if (args[i] == "optimized") { if (haveLLT) { - this->LinkLibraryTypeSpecifierWarning(llt, OPTIMIZED_LibraryType); + LinkLibraryTypeSpecifierWarning(mf, llt, OPTIMIZED_LibraryType); } llt = OPTIMIZED_LibraryType; haveLLT = true; } else if (args[i] == "general") { if (haveLLT) { - this->LinkLibraryTypeSpecifierWarning(llt, GENERAL_LibraryType); + LinkLibraryTypeSpecifierWarning(mf, llt, GENERAL_LibraryType); } llt = GENERAL_LibraryType; haveLLT = true; } else if (haveLLT) { // The link type was specified by the previous argument. haveLLT = false; - if (!this->HandleLibrary(args[i], llt)) { + if (!HandleLibrary(mf, target, currentProcessingState, args[i], llt)) { return false; } } else { @@ -239,7 +259,7 @@ bool cmTargetLinkLibrariesCommand::InitialPass( // with old versions of CMake and new) llt = GENERAL_LibraryType; std::string linkType = cmStrCat(args[0], "_LINK_TYPE"); - const char* linkTypeString = this->Makefile->GetDefinition(linkType); + const char* linkTypeString = mf.GetDefinition(linkType); if (linkTypeString) { if (strcmp(linkTypeString, "debug") == 0) { llt = DEBUG_LibraryType; @@ -248,7 +268,7 @@ bool cmTargetLinkLibrariesCommand::InitialPass( llt = OPTIMIZED_LibraryType; } } - if (!this->HandleLibrary(args[i], llt)) { + if (!HandleLibrary(mf, target, currentProcessingState, args[i], llt)) { return false; } } @@ -256,16 +276,14 @@ bool cmTargetLinkLibrariesCommand::InitialPass( // Make sure the last argument was not a library type specifier. if (haveLLT) { - this->Makefile->IssueMessage( - MessageType::FATAL_ERROR, - cmStrCat("The \"", - cmTargetLinkLibrariesCommand::LinkLibraryTypeNames[llt], - "\" argument must be followed by a library.")); + mf.IssueMessage(MessageType::FATAL_ERROR, + cmStrCat("The \"", LinkLibraryTypeNames[llt], + "\" argument must be followed by a library.")); cmSystemTools::SetFatalErrorOccured(); } const cmPolicies::PolicyStatus policy22Status = - this->Target->GetPolicyStatusCMP0022(); + target->GetPolicyStatusCMP0022(); // If any of the LINK_ options were given, make sure the // LINK_INTERFACE_LIBRARIES target property exists. @@ -274,41 +292,40 @@ bool cmTargetLinkLibrariesCommand::InitialPass( // result in an empty link interface. if ((policy22Status == cmPolicies::OLD || policy22Status == cmPolicies::WARN) && - this->CurrentProcessingState != ProcessingLinkLibraries && - !this->Target->GetProperty("LINK_INTERFACE_LIBRARIES")) { - this->Target->SetProperty("LINK_INTERFACE_LIBRARIES", ""); + currentProcessingState != ProcessingLinkLibraries && + !target->GetProperty("LINK_INTERFACE_LIBRARIES")) { + target->SetProperty("LINK_INTERFACE_LIBRARIES", ""); } return true; } -void cmTargetLinkLibrariesCommand::LinkLibraryTypeSpecifierWarning(int left, - int right) +static void LinkLibraryTypeSpecifierWarning(cmMakefile& mf, int left, + int right) { - this->Makefile->IssueMessage( + mf.IssueMessage( MessageType::AUTHOR_WARNING, cmStrCat( - "Link library type specifier \"", - cmTargetLinkLibrariesCommand::LinkLibraryTypeNames[left], - "\" is followed by specifier \"", - cmTargetLinkLibrariesCommand::LinkLibraryTypeNames[right], + "Link library type specifier \"", LinkLibraryTypeNames[left], + "\" is followed by specifier \"", LinkLibraryTypeNames[right], "\" instead of a library name. The first specifier will be ignored.")); } -bool cmTargetLinkLibrariesCommand::HandleLibrary(const std::string& lib, - cmTargetLinkLibraryType llt) +static bool HandleLibrary(cmMakefile& mf, cmTarget* target, + ProcessingState currentProcessingState, + const std::string& lib, cmTargetLinkLibraryType llt) { - if (this->Target->GetType() == cmStateEnums::INTERFACE_LIBRARY && - this->CurrentProcessingState != ProcessingKeywordLinkInterface) { - this->Makefile->IssueMessage( + if (target->GetType() == cmStateEnums::INTERFACE_LIBRARY && + currentProcessingState != ProcessingKeywordLinkInterface) { + mf.IssueMessage( MessageType::FATAL_ERROR, "INTERFACE library can only be used with the INTERFACE keyword of " "target_link_libraries"); return false; } - if (this->Target->IsImported() && - this->CurrentProcessingState != ProcessingKeywordLinkInterface) { - this->Makefile->IssueMessage( + if (target->IsImported() && + currentProcessingState != ProcessingKeywordLinkInterface) { + mf.IssueMessage( MessageType::FATAL_ERROR, "IMPORTED library can only be used with the INTERFACE keyword of " "target_link_libraries"); @@ -316,19 +333,18 @@ bool cmTargetLinkLibrariesCommand::HandleLibrary(const std::string& lib, } cmTarget::TLLSignature sig = - (this->CurrentProcessingState == ProcessingPlainPrivateInterface || - this->CurrentProcessingState == ProcessingPlainPublicInterface || - this->CurrentProcessingState == ProcessingKeywordPrivateInterface || - this->CurrentProcessingState == ProcessingKeywordPublicInterface || - this->CurrentProcessingState == ProcessingKeywordLinkInterface) + (currentProcessingState == ProcessingPlainPrivateInterface || + currentProcessingState == ProcessingPlainPublicInterface || + currentProcessingState == ProcessingKeywordPrivateInterface || + currentProcessingState == ProcessingKeywordPublicInterface || + currentProcessingState == ProcessingKeywordLinkInterface) ? cmTarget::KeywordTLLSignature : cmTarget::PlainTLLSignature; - if (!this->Target->PushTLLCommandTrace( - sig, this->Makefile->GetExecutionContext())) { + if (!target->PushTLLCommandTrace(sig, mf.GetExecutionContext())) { std::ostringstream e; const char* modal = nullptr; MessageType messageType = MessageType::AUTHOR_WARNING; - switch (this->Makefile->GetPolicyStatus(cmPolicies::CMP0023)) { + switch (mf.GetPolicyStatus(cmPolicies::CMP0023)) { case cmPolicies::WARN: e << cmPolicies::GetPolicyWarning(cmPolicies::CMP0023) << "\n"; modal = "should"; @@ -349,14 +365,14 @@ bool cmTargetLinkLibrariesCommand::HandleLibrary(const std::string& lib, e << "The " << existingSig << " signature for target_link_libraries has " "already been used with the target \"" - << this->Target->GetName() + << target->GetName() << "\". All uses of target_link_libraries with a target " << modal << " be either all-keyword or all-plain.\n"; - this->Target->GetTllSignatureTraces(e, - sig == cmTarget::KeywordTLLSignature - ? cmTarget::PlainTLLSignature - : cmTarget::KeywordTLLSignature); - this->Makefile->IssueMessage(messageType, e.str()); + target->GetTllSignatureTraces(e, + sig == cmTarget::KeywordTLLSignature + ? cmTarget::PlainTLLSignature + : cmTarget::KeywordTLLSignature); + mf.IssueMessage(messageType, e.str()); if (messageType == MessageType::FATAL_ERROR) { return false; } @@ -366,9 +382,9 @@ bool cmTargetLinkLibrariesCommand::HandleLibrary(const std::string& lib, bool warnRemoteInterface = false; bool rejectRemoteLinking = false; bool encodeRemoteReference = false; - if (this->Makefile != this->Target->GetMakefile()) { + if (&mf != target->GetMakefile()) { // The LHS target was created in another directory. - switch (this->Makefile->GetPolicyStatus(cmPolicies::CMP0079)) { + switch (mf.GetPolicyStatus(cmPolicies::CMP0079)) { case cmPolicies::WARN: warnRemoteInterface = true; CM_FALLTHROUGH; @@ -389,7 +405,7 @@ bool cmTargetLinkLibrariesCommand::HandleLibrary(const std::string& lib, // same directory as the target was created. Add a suffix to // the name to tell ResolveLinkItem to look up the name in the // caller's directory. - cmDirectoryId const dirId = this->Makefile->GetDirectoryId(); + cmDirectoryId const dirId = mf.GetDirectoryId(); libRef = lib + CMAKE_DIRECTORY_ID_SEP + dirId.String; } else { // This is an absolute path or a library name added by a caller @@ -401,21 +417,21 @@ bool cmTargetLinkLibrariesCommand::HandleLibrary(const std::string& lib, // Handle normal case where the command was called with another keyword than // INTERFACE / LINK_INTERFACE_LIBRARIES or none at all. (The "LINK_LIBRARIES" // property of the target on the LHS shall be populated.) - if (this->CurrentProcessingState != ProcessingKeywordLinkInterface && - this->CurrentProcessingState != ProcessingPlainLinkInterface) { + if (currentProcessingState != ProcessingKeywordLinkInterface && + currentProcessingState != ProcessingPlainLinkInterface) { if (rejectRemoteLinking) { - this->Makefile->IssueMessage( + mf.IssueMessage( MessageType::FATAL_ERROR, cmStrCat("Attempt to add link library \"", lib, "\" to target \"", - this->Target->GetName(), + target->GetName(), "\" which is not built in this " "directory.\nThis is allowed only when policy CMP0079 " "is set to NEW.")); return false; } - cmTarget* tgt = this->Makefile->GetGlobalGenerator()->FindTarget(lib); + cmTarget* tgt = mf.GetGlobalGenerator()->FindTarget(lib); if (tgt && (tgt->GetType() != cmStateEnums::STATIC_LIBRARY) && (tgt->GetType() != cmStateEnums::SHARED_LIBRARY) && @@ -423,7 +439,7 @@ bool cmTargetLinkLibrariesCommand::HandleLibrary(const std::string& lib, (tgt->GetType() != cmStateEnums::OBJECT_LIBRARY) && (tgt->GetType() != cmStateEnums::INTERFACE_LIBRARY) && !tgt->IsExecutableWithExports()) { - this->Makefile->IssueMessage( + mf.IssueMessage( MessageType::FATAL_ERROR, cmStrCat( "Target \"", lib, "\" of type ", @@ -433,15 +449,15 @@ bool cmTargetLinkLibrariesCommand::HandleLibrary(const std::string& lib, "executables with the ENABLE_EXPORTS property set.")); } - this->Target->AddLinkLibrary(*this->Makefile, lib, libRef, llt); + target->AddLinkLibrary(mf, lib, libRef, llt); } if (warnRemoteInterface) { - this->Makefile->IssueMessage( + mf.IssueMessage( MessageType::AUTHOR_WARNING, cmStrCat( cmPolicies::GetPolicyWarning(cmPolicies::CMP0079), "\nTarget\n ", - this->Target->GetName(), + target->GetName(), "\nis not created in this " "directory. For compatibility with older versions of CMake, link " "library\n ", @@ -454,18 +470,17 @@ bool cmTargetLinkLibrariesCommand::HandleLibrary(const std::string& lib, // LINK_PRIVATE and stop its processing. (The "INTERFACE_LINK_LIBRARIES" // property of the target on the LHS shall only be populated if it is a // STATIC library.) - if (this->CurrentProcessingState == ProcessingKeywordPrivateInterface || - this->CurrentProcessingState == ProcessingPlainPrivateInterface) { - if (this->Target->GetType() == cmStateEnums::STATIC_LIBRARY || - this->Target->GetType() == cmStateEnums::OBJECT_LIBRARY) { + if (currentProcessingState == ProcessingKeywordPrivateInterface || + currentProcessingState == ProcessingPlainPrivateInterface) { + if (target->GetType() == cmStateEnums::STATIC_LIBRARY || + target->GetType() == cmStateEnums::OBJECT_LIBRARY) { std::string configLib = - this->Target->GetDebugGeneratorExpressions(libRef, llt); + target->GetDebugGeneratorExpressions(libRef, llt); if (cmGeneratorExpression::IsValidTargetName(lib) || cmGeneratorExpression::Find(lib) != std::string::npos) { configLib = "$<LINK_ONLY:" + configLib + ">"; } - this->Target->AppendProperty("INTERFACE_LINK_LIBRARIES", - configLib.c_str()); + target->AppendProperty("INTERFACE_LINK_LIBRARIES", configLib.c_str()); } return true; } @@ -473,23 +488,23 @@ bool cmTargetLinkLibrariesCommand::HandleLibrary(const std::string& lib, // Handle general case where the command was called with another keyword than // PRIVATE / LINK_PRIVATE or none at all. (The "INTERFACE_LINK_LIBRARIES" // property of the target on the LHS shall be populated.) - this->Target->AppendProperty( + target->AppendProperty( "INTERFACE_LINK_LIBRARIES", - this->Target->GetDebugGeneratorExpressions(libRef, llt).c_str()); + target->GetDebugGeneratorExpressions(libRef, llt).c_str()); // Stop processing if called without any keyword. - if (this->CurrentProcessingState == ProcessingLinkLibraries) { + if (currentProcessingState == ProcessingLinkLibraries) { return true; } // Stop processing if policy CMP0022 is set to NEW. const cmPolicies::PolicyStatus policy22Status = - this->Target->GetPolicyStatusCMP0022(); + target->GetPolicyStatusCMP0022(); if (policy22Status != cmPolicies::OLD && policy22Status != cmPolicies::WARN) { return true; } // Stop processing if called with an INTERFACE library on the LHS. - if (this->Target->GetType() == cmStateEnums::INTERFACE_LIBRARY) { + if (target->GetType() == cmStateEnums::INTERFACE_LIBRARY) { return true; } @@ -499,7 +514,7 @@ bool cmTargetLinkLibrariesCommand::HandleLibrary(const std::string& lib, { // Get the list of configurations considered to be DEBUG. std::vector<std::string> debugConfigs = - this->Makefile->GetCMakeInstance()->GetDebugConfigs(); + mf.GetCMakeInstance()->GetDebugConfigs(); std::string prop; // Include this library in the link interface for the target. @@ -507,19 +522,19 @@ bool cmTargetLinkLibrariesCommand::HandleLibrary(const std::string& lib, // Put in the DEBUG configuration interfaces. for (std::string const& dc : debugConfigs) { prop = cmStrCat("LINK_INTERFACE_LIBRARIES_", dc); - this->Target->AppendProperty(prop, libRef.c_str()); + target->AppendProperty(prop, libRef.c_str()); } } if (llt == OPTIMIZED_LibraryType || llt == GENERAL_LibraryType) { // Put in the non-DEBUG configuration interfaces. - this->Target->AppendProperty("LINK_INTERFACE_LIBRARIES", libRef.c_str()); + target->AppendProperty("LINK_INTERFACE_LIBRARIES", libRef.c_str()); // Make sure the DEBUG configuration interfaces exist so that the // general one will not be used as a fall-back. for (std::string const& dc : debugConfigs) { prop = cmStrCat("LINK_INTERFACE_LIBRARIES_", dc); - if (!this->Target->GetProperty(prop)) { - this->Target->SetProperty(prop, ""); + if (!target->GetProperty(prop)) { + target->SetProperty(prop, ""); } } } diff --git a/Source/cmTargetLinkLibrariesCommand.h b/Source/cmTargetLinkLibrariesCommand.h index caf2cf4..4b2deab 100644 --- a/Source/cmTargetLinkLibrariesCommand.h +++ b/Source/cmTargetLinkLibrariesCommand.h @@ -8,61 +8,9 @@ #include <string> #include <vector> -#include <cm/memory> - -#include "cmCommand.h" -#include "cmTargetLinkLibraryType.h" - class cmExecutionStatus; -class cmTarget; - -/** \class cmTargetLinkLibrariesCommand - * \brief Specify a list of libraries to link into executables. - * - * cmTargetLinkLibrariesCommand is used to specify a list of libraries to link - * into executable(s) or shared objects. The names of the libraries - * should be those defined by the LIBRARY(library) command(s). - * - * Additionally, it allows to propagate usage-requirements (including link - * libraries) from one target into another. - */ -class cmTargetLinkLibrariesCommand : public cmCommand -{ -public: - /** - * This is a virtual constructor for the command. - */ - std::unique_ptr<cmCommand> Clone() override - { - return cm::make_unique<cmTargetLinkLibrariesCommand>(); - } - - /** - * This is called when the command is first encountered in - * the CMakeLists.txt file. - */ - bool InitialPass(std::vector<std::string> const& args, - cmExecutionStatus& status) override; - -private: - void LinkLibraryTypeSpecifierWarning(int left, int right); - static const char* LinkLibraryTypeNames[3]; - - cmTarget* Target = nullptr; - enum ProcessingState - { - ProcessingLinkLibraries, - ProcessingPlainLinkInterface, - ProcessingKeywordLinkInterface, - ProcessingPlainPublicInterface, - ProcessingKeywordPublicInterface, - ProcessingPlainPrivateInterface, - ProcessingKeywordPrivateInterface - }; - - ProcessingState CurrentProcessingState = ProcessingLinkLibraries; - bool HandleLibrary(const std::string& lib, cmTargetLinkLibraryType llt); -}; +bool cmTargetLinkLibrariesCommand(std::vector<std::string> const& args, + cmExecutionStatus& status); #endif diff --git a/Source/cmTargetLinkOptionsCommand.cxx b/Source/cmTargetLinkOptionsCommand.cxx index dbd7bfe..df9416f 100644 --- a/Source/cmTargetLinkOptionsCommand.cxx +++ b/Source/cmTargetLinkOptionsCommand.cxx @@ -7,33 +7,44 @@ #include "cmMessageType.h" #include "cmStringAlgorithms.h" #include "cmTarget.h" +#include "cmTargetPropCommandBase.h" -class cmExecutionStatus; +namespace { -bool cmTargetLinkOptionsCommand::InitialPass( - std::vector<std::string> const& args, cmExecutionStatus&) +class TargetLinkOptionsImpl : public cmTargetPropCommandBase { - return this->HandleArguments(args, "LINK_OPTIONS", PROCESS_BEFORE); -} +public: + using cmTargetPropCommandBase::cmTargetPropCommandBase; -void cmTargetLinkOptionsCommand::HandleMissingTarget(const std::string& name) -{ - this->Makefile->IssueMessage( - MessageType::FATAL_ERROR, - cmStrCat("Cannot specify link options for target \"", name, - "\" which is not built by this project.")); -} +private: + void HandleMissingTarget(const std::string& name) override + { + this->Makefile->IssueMessage( + MessageType::FATAL_ERROR, + cmStrCat("Cannot specify link options for target \"", name, + "\" which is not built by this project.")); + } -std::string cmTargetLinkOptionsCommand::Join( - const std::vector<std::string>& content) -{ - return cmJoin(content, ";"); -} + bool HandleDirectContent(cmTarget* tgt, + const std::vector<std::string>& content, + bool prepend, bool /*system*/) override + { + cmListFileBacktrace lfbt = this->Makefile->GetBacktrace(); + tgt->InsertLinkOption(this->Join(content), lfbt, prepend); + return true; // Successfully handled. + } + + std::string Join(const std::vector<std::string>& content) override + { + return cmJoin(content, ";"); + } +}; + +} // namespace -bool cmTargetLinkOptionsCommand::HandleDirectContent( - cmTarget* tgt, const std::vector<std::string>& content, bool prepend, bool) +bool cmTargetLinkOptionsCommand(std::vector<std::string> const& args, + cmExecutionStatus& status) { - cmListFileBacktrace lfbt = this->Makefile->GetBacktrace(); - tgt->InsertLinkOption(this->Join(content), lfbt, prepend); - return true; // Successfully handled. + return TargetLinkOptionsImpl(status).HandleArguments( + args, "LINK_OPTIONS", TargetLinkOptionsImpl::PROCESS_BEFORE); } diff --git a/Source/cmTargetLinkOptionsCommand.h b/Source/cmTargetLinkOptionsCommand.h index 918a8d7..13fb40c 100644 --- a/Source/cmTargetLinkOptionsCommand.h +++ b/Source/cmTargetLinkOptionsCommand.h @@ -8,39 +8,9 @@ #include <string> #include <vector> -#include <cm/memory> - -#include "cmCommand.h" -#include "cmTargetPropCommandBase.h" - class cmExecutionStatus; -class cmTarget; - -class cmTargetLinkOptionsCommand : public cmTargetPropCommandBase -{ -public: - /** - * This is a virtual constructor for the command. - */ - std::unique_ptr<cmCommand> Clone() override - { - return cm::make_unique<cmTargetLinkOptionsCommand>(); - } - - /** - * This is called when the command is first encountered in - * the CMakeLists.txt file. - */ - bool InitialPass(std::vector<std::string> const& args, - cmExecutionStatus& status) override; - -private: - void HandleMissingTarget(const std::string& name) override; - bool HandleDirectContent(cmTarget* tgt, - const std::vector<std::string>& content, - bool prepend, bool system) override; - std::string Join(const std::vector<std::string>& content) override; -}; +bool cmTargetLinkOptionsCommand(std::vector<std::string> const& args, + cmExecutionStatus& status); #endif diff --git a/Source/cmTargetPrecompileHeadersCommand.cxx b/Source/cmTargetPrecompileHeadersCommand.cxx index 97f1bea..887d973 100644 --- a/Source/cmTargetPrecompileHeadersCommand.cxx +++ b/Source/cmTargetPrecompileHeadersCommand.cxx @@ -2,35 +2,86 @@ file Copyright.txt or https://cmake.org/licensing for details. */ #include "cmTargetPrecompileHeadersCommand.h" +#include "cmGeneratorExpression.h" #include "cmMakefile.h" #include "cmMessageType.h" #include "cmStringAlgorithms.h" +#include "cmSystemTools.h" #include "cmTarget.h" +#include "cmTargetPropCommandBase.h" -bool cmTargetPrecompileHeadersCommand::InitialPass( - std::vector<std::string> const& args, cmExecutionStatus&) -{ - return this->HandleArguments(args, "PRECOMPILE_HEADERS", PROCESS_REUSE_FROM); -} +#include <utility> + +namespace { -void cmTargetPrecompileHeadersCommand::HandleMissingTarget( - const std::string& name) +std::vector<std::string> ConvertToAbsoluteContent( + const std::vector<std::string>& content, std::string const& baseDir) { - const std::string e = - cmStrCat("Cannot specify precompile headers for target \"", name, - "\" which is not built by this project."); - this->Makefile->IssueMessage(MessageType::FATAL_ERROR, e); + std::vector<std::string> absoluteContent; + absoluteContent.reserve(content.size()); + for (std::string const& src : content) { + std::string absoluteSrc; + // Use '<foo.h>' and '"foo.h"' includes and absolute paths as-is. + // Interpret relative paths with respect to the source directory. + // If the path starts in a generator expression, assume it is absolute. + if (cmHasLiteralPrefix(src, "<") || cmHasLiteralPrefix(src, "\"") || + cmSystemTools::FileIsFullPath(src) || + cmGeneratorExpression::Find(src) == 0) { + absoluteSrc = src; + } else { + absoluteSrc = cmStrCat(baseDir, '/', src); + } + absoluteContent.emplace_back(std::move(absoluteSrc)); + } + return absoluteContent; } -std::string cmTargetPrecompileHeadersCommand::Join( - const std::vector<std::string>& content) +class TargetPrecompileHeadersImpl : public cmTargetPropCommandBase { - return cmJoin(content, ";"); -} +public: + using cmTargetPropCommandBase::cmTargetPropCommandBase; + +private: + bool HandleDirectContent(cmTarget* tgt, + const std::vector<std::string>& content, + bool /*prepend*/, bool /*system*/) override + { + std::string const& base = this->Makefile->GetCurrentSourceDirectory(); + tgt->AppendProperty( + "PRECOMPILE_HEADERS", + this->Join(ConvertToAbsoluteContent(content, base)).c_str()); + return true; + } + + void HandleInterfaceContent(cmTarget* tgt, + const std::vector<std::string>& content, + bool prepend, bool system) override + { + std::string const& base = this->Makefile->GetCurrentSourceDirectory(); + cmTargetPropCommandBase::HandleInterfaceContent( + tgt, ConvertToAbsoluteContent(content, base), prepend, system); + } + + void HandleMissingTarget(const std::string& name) override + { + this->Makefile->IssueMessage( + MessageType::FATAL_ERROR, + cmStrCat("Cannot specify precompile headers for target \"", name, + "\" which is not built by this project.")); + } + + std::string Join(const std::vector<std::string>& content) override + { + return cmJoin(content, ";"); + } +}; + +} // namespace -bool cmTargetPrecompileHeadersCommand::HandleDirectContent( - cmTarget* tgt, const std::vector<std::string>& content, bool, bool) +bool cmTargetPrecompileHeadersCommand(std::vector<std::string> const& args, + cmExecutionStatus& status) { - tgt->AppendProperty("PRECOMPILE_HEADERS", this->Join(content).c_str()); - return true; + return TargetPrecompileHeadersImpl(status).HandleArguments( + args, "PRECOMPILE_HEADERS", + TargetPrecompileHeadersImpl::PROCESS_REUSE_FROM); } diff --git a/Source/cmTargetPrecompileHeadersCommand.h b/Source/cmTargetPrecompileHeadersCommand.h index 7e4558e..8b0ac97 100644 --- a/Source/cmTargetPrecompileHeadersCommand.h +++ b/Source/cmTargetPrecompileHeadersCommand.h @@ -8,34 +8,9 @@ #include <string> #include <vector> -#include <cm/memory> - -#include "cmCommand.h" - -#include "cmTargetPropCommandBase.h" - class cmExecutionStatus; -class cmTarget; - -class cmTargetPrecompileHeadersCommand : public cmTargetPropCommandBase -{ -public: - std::unique_ptr<cmCommand> Clone() override - { - return cm::make_unique<cmTargetPrecompileHeadersCommand>(); - } - - bool InitialPass(std::vector<std::string> const& args, - cmExecutionStatus& status) override; - -private: - void HandleMissingTarget(const std::string& name) override; - - bool HandleDirectContent(cmTarget* tgt, - const std::vector<std::string>& content, - bool prepend, bool system) override; - std::string Join(const std::vector<std::string>& content) override; -}; +bool cmTargetPrecompileHeadersCommand(std::vector<std::string> const& args, + cmExecutionStatus& status); #endif diff --git a/Source/cmTargetPropCommandBase.cxx b/Source/cmTargetPropCommandBase.cxx index 4bc3125..bbc1e16 100644 --- a/Source/cmTargetPropCommandBase.cxx +++ b/Source/cmTargetPropCommandBase.cxx @@ -2,12 +2,24 @@ file Copyright.txt or https://cmake.org/licensing for details. */ #include "cmTargetPropCommandBase.h" +#include "cmExecutionStatus.h" #include "cmGlobalGenerator.h" #include "cmMakefile.h" #include "cmStateTypes.h" #include "cmTarget.h" #include "cmake.h" +cmTargetPropCommandBase::cmTargetPropCommandBase(cmExecutionStatus& status) + : Makefile(&status.GetMakefile()) + , Status(status) +{ +} + +void cmTargetPropCommandBase::SetError(std::string const& e) +{ + this->Status.SetError(e); +} + bool cmTargetPropCommandBase::HandleArguments( std::vector<std::string> const& args, const std::string& prop, ArgumentFlags flags) diff --git a/Source/cmTargetPropCommandBase.h b/Source/cmTargetPropCommandBase.h index b244417..601ad01 100644 --- a/Source/cmTargetPropCommandBase.h +++ b/Source/cmTargetPropCommandBase.h @@ -8,13 +8,18 @@ #include <string> #include <vector> -#include "cmCommand.h" - +class cmExecutionStatus; +class cmMakefile; class cmTarget; -class cmTargetPropCommandBase : public cmCommand +class cmTargetPropCommandBase { public: + cmTargetPropCommandBase(cmExecutionStatus& status); + virtual ~cmTargetPropCommandBase() = default; + + void SetError(std::string const& e); + enum ArgumentFlags { NO_FLAGS = 0x0, @@ -30,6 +35,7 @@ public: protected: std::string Property; cmTarget* Target = nullptr; + cmMakefile* Makefile; virtual void HandleInterfaceContent(cmTarget* tgt, const std::vector<std::string>& content, @@ -49,6 +55,8 @@ private: bool PopulateTargetProperies(const std::string& scope, const std::vector<std::string>& content, bool prepend, bool system); + + cmExecutionStatus& Status; }; #endif diff --git a/Source/cmTargetSourcesCommand.cxx b/Source/cmTargetSourcesCommand.cxx index 7c9d03c..c2e0b28 100644 --- a/Source/cmTargetSourcesCommand.cxx +++ b/Source/cmTargetSourcesCommand.cxx @@ -11,47 +11,54 @@ #include "cmStringAlgorithms.h" #include "cmSystemTools.h" #include "cmTarget.h" +#include "cmTargetPropCommandBase.h" -class cmExecutionStatus; +namespace { -bool cmTargetSourcesCommand::InitialPass(std::vector<std::string> const& args, - cmExecutionStatus&) +class TargetSourcesImpl : public cmTargetPropCommandBase { - return this->HandleArguments(args, "SOURCES"); -} +public: + using cmTargetPropCommandBase::cmTargetPropCommandBase; -void cmTargetSourcesCommand::HandleInterfaceContent( - cmTarget* tgt, const std::vector<std::string>& content, bool prepend, - bool system) -{ - cmTargetPropCommandBase::HandleInterfaceContent( - tgt, ConvertToAbsoluteContent(tgt, content, true), prepend, system); -} +protected: + void HandleInterfaceContent(cmTarget* tgt, + const std::vector<std::string>& content, + bool prepend, bool system) override + { + cmTargetPropCommandBase::HandleInterfaceContent( + tgt, ConvertToAbsoluteContent(tgt, content, true), prepend, system); + } -void cmTargetSourcesCommand::HandleMissingTarget(const std::string& name) -{ - this->Makefile->IssueMessage( - MessageType::FATAL_ERROR, - cmStrCat("Cannot specify sources for target \"", name, - "\" which is not built by this project.")); -} +private: + void HandleMissingTarget(const std::string& name) override + { + this->Makefile->IssueMessage( + MessageType::FATAL_ERROR, + cmStrCat("Cannot specify sources for target \"", name, + "\" which is not built by this project.")); + } -std::string cmTargetSourcesCommand::Join( - const std::vector<std::string>& content) -{ - return cmJoin(content, ";"); -} + bool HandleDirectContent(cmTarget* tgt, + const std::vector<std::string>& content, + bool /*prepend*/, bool /*system*/) override + { + tgt->AppendProperty( + "SOURCES", + this->Join(ConvertToAbsoluteContent(tgt, content, false)).c_str()); + return true; // Successfully handled. + } -bool cmTargetSourcesCommand::HandleDirectContent( - cmTarget* tgt, const std::vector<std::string>& content, bool, bool) -{ - tgt->AppendProperty( - "SOURCES", - this->Join(ConvertToAbsoluteContent(tgt, content, false)).c_str()); - return true; // Successfully handled. -} + std::string Join(const std::vector<std::string>& content) override + { + return cmJoin(content, ";"); + } + + std::vector<std::string> ConvertToAbsoluteContent( + cmTarget* tgt, const std::vector<std::string>& content, + bool isInterfaceContent); +}; -std::vector<std::string> cmTargetSourcesCommand::ConvertToAbsoluteContent( +std::vector<std::string> TargetSourcesImpl::ConvertToAbsoluteContent( cmTarget* tgt, const std::vector<std::string>& content, bool isInterfaceContent) { @@ -120,3 +127,11 @@ std::vector<std::string> cmTargetSourcesCommand::ConvertToAbsoluteContent( return useAbsoluteContent ? absoluteContent : content; } + +} // namespace + +bool cmTargetSourcesCommand(std::vector<std::string> const& args, + cmExecutionStatus& status) +{ + return TargetSourcesImpl(status).HandleArguments(args, "SOURCES"); +} diff --git a/Source/cmTargetSourcesCommand.h b/Source/cmTargetSourcesCommand.h index 1cff8c3..5eecf34 100644 --- a/Source/cmTargetSourcesCommand.h +++ b/Source/cmTargetSourcesCommand.h @@ -8,49 +8,9 @@ #include <string> #include <vector> -#include <cm/memory> - -#include "cmCommand.h" -#include "cmTargetPropCommandBase.h" - class cmExecutionStatus; -class cmTarget; - -class cmTargetSourcesCommand : public cmTargetPropCommandBase -{ -public: - /** - * This is a virtual constructor for the command. - */ - std::unique_ptr<cmCommand> Clone() override - { - return cm::make_unique<cmTargetSourcesCommand>(); - } - - /** - * This is called when the command is first encountered in - * the CMakeLists.txt file. - */ - bool InitialPass(std::vector<std::string> const& args, - cmExecutionStatus& status) override; - -protected: - void HandleInterfaceContent(cmTarget* tgt, - const std::vector<std::string>& content, - bool prepend, bool system) override; - -private: - void HandleMissingTarget(const std::string& name) override; - - bool HandleDirectContent(cmTarget* tgt, - const std::vector<std::string>& content, - bool prepend, bool system) override; - - std::string Join(const std::vector<std::string>& content) override; - std::vector<std::string> ConvertToAbsoluteContent( - cmTarget* tgt, const std::vector<std::string>& content, - bool isInterfaceContent); -}; +bool cmTargetSourcesCommand(std::vector<std::string> const& args, + cmExecutionStatus& status); #endif diff --git a/Source/cmVisualStudio10TargetGenerator.cxx b/Source/cmVisualStudio10TargetGenerator.cxx index 4b83595..176e790 100644 --- a/Source/cmVisualStudio10TargetGenerator.cxx +++ b/Source/cmVisualStudio10TargetGenerator.cxx @@ -1139,10 +1139,8 @@ void cmVisualStudio10TargetGenerator::WriteProjectConfigurationValues(Elem& e0) std::string configType; if (const char* vsConfigurationType = this->GeneratorTarget->GetProperty("VS_CONFIGURATION_TYPE")) { - cmGeneratorExpression ge; - std::unique_ptr<cmCompiledGeneratorExpression> cge = - ge.Parse(vsConfigurationType); - configType = cge->Evaluate(this->LocalGenerator, c); + configType = cmGeneratorExpression::Evaluate(vsConfigurationType, + this->LocalGenerator, c); } else { switch (this->GeneratorTarget->GetType()) { case cmStateEnums::SHARED_LIBRARY: @@ -1468,6 +1466,7 @@ void cmVisualStudio10TargetGenerator::WriteCustomRule( } // output files for custom command std::stringstream outputs; + bool symbolic = false; { const char* sep = ""; for (std::string const& o : ccg.GetOutputs()) { @@ -1475,6 +1474,12 @@ void cmVisualStudio10TargetGenerator::WriteCustomRule( ConvertToWindowsSlash(out); outputs << sep << out; sep = ";"; + if (!symbolic) { + if (cmSourceFile* sf = this->Makefile->GetSource( + o, cmSourceFileLocationKind::Known)) { + symbolic = sf->GetPropertyAsBool("SYMBOLIC"); + } + } } } if (this->ProjectType == csproj) { @@ -1484,7 +1489,7 @@ void cmVisualStudio10TargetGenerator::WriteCustomRule( outputs.str(), comment); } else { this->WriteCustomRuleCpp(*spe2, c, script, additional_inputs.str(), - outputs.str(), comment); + outputs.str(), comment, symbolic); } } } @@ -1492,7 +1497,7 @@ void cmVisualStudio10TargetGenerator::WriteCustomRule( void cmVisualStudio10TargetGenerator::WriteCustomRuleCpp( Elem& e2, std::string const& config, std::string const& script, std::string const& additional_inputs, std::string const& outputs, - std::string const& comment) + std::string const& comment, bool symbolic) { const std::string cond = this->CalcCondition(config); e2.WritePlatformConfigTag("Message", cond, comment); @@ -1504,6 +1509,13 @@ void cmVisualStudio10TargetGenerator::WriteCustomRuleCpp( // VS >= 11 let us turn off linking of custom command outputs. e2.WritePlatformConfigTag("LinkObjects", cond, "false"); } + if (symbolic && + this->LocalGenerator->GetVersion() >= + cmGlobalVisualStudioGenerator::VS16) { + // VS >= 16.4 warn if outputs are not created, but one of our + // outputs is marked SYMBOLIC and not expected to be created. + e2.WritePlatformConfigTag("VerifyInputsAndOutputsExist", cond, "false"); + } } void cmVisualStudio10TargetGenerator::WriteCustomRuleCSharp( @@ -2447,49 +2459,32 @@ void cmVisualStudio10TargetGenerator::WritePathAndIncrementalLinkOptions( if (ttype <= cmStateEnums::UTILITY) { if (const char* workingDir = this->GeneratorTarget->GetProperty( "VS_DEBUGGER_WORKING_DIRECTORY")) { - cmGeneratorExpression ge; - std::unique_ptr<cmCompiledGeneratorExpression> cge = - ge.Parse(workingDir); - std::string genWorkingDir = - cge->Evaluate(this->LocalGenerator, config); - + std::string genWorkingDir = cmGeneratorExpression::Evaluate( + workingDir, this->LocalGenerator, config); e1.WritePlatformConfigTag("LocalDebuggerWorkingDirectory", cond, genWorkingDir); } if (const char* environment = this->GeneratorTarget->GetProperty("VS_DEBUGGER_ENVIRONMENT")) { - cmGeneratorExpression ge; - std::unique_ptr<cmCompiledGeneratorExpression> cge = - ge.Parse(environment); - std::string genEnvironment = - cge->Evaluate(this->LocalGenerator, config); - + std::string genEnvironment = cmGeneratorExpression::Evaluate( + environment, this->LocalGenerator, config); e1.WritePlatformConfigTag("LocalDebuggerEnvironment", cond, genEnvironment); } if (const char* debuggerCommand = this->GeneratorTarget->GetProperty("VS_DEBUGGER_COMMAND")) { - - cmGeneratorExpression ge; - std::unique_ptr<cmCompiledGeneratorExpression> cge = - ge.Parse(debuggerCommand); - std::string genDebuggerCommand = - cge->Evaluate(this->LocalGenerator, config); - + std::string genDebuggerCommand = cmGeneratorExpression::Evaluate( + debuggerCommand, this->LocalGenerator, config); e1.WritePlatformConfigTag("LocalDebuggerCommand", cond, genDebuggerCommand); } if (const char* commandArguments = this->GeneratorTarget->GetProperty( "VS_DEBUGGER_COMMAND_ARGUMENTS")) { - cmGeneratorExpression ge; - std::unique_ptr<cmCompiledGeneratorExpression> cge = - ge.Parse(commandArguments); - std::string genCommandArguments = - cge->Evaluate(this->LocalGenerator, config); - + std::string genCommandArguments = cmGeneratorExpression::Evaluate( + commandArguments, this->LocalGenerator, config); e1.WritePlatformConfigTag("LocalDebuggerCommandArguments", cond, genCommandArguments); } @@ -3479,22 +3474,16 @@ void cmVisualStudio10TargetGenerator::WriteAntBuildOptions( if (const char* nativeLibDirectoriesExpression = this->GeneratorTarget->GetProperty("ANDROID_NATIVE_LIB_DIRECTORIES")) { - cmGeneratorExpression ge; - std::unique_ptr<cmCompiledGeneratorExpression> cge = - ge.Parse(nativeLibDirectoriesExpression); - std::string nativeLibDirs = - cge->Evaluate(this->LocalGenerator, configName); + std::string nativeLibDirs = cmGeneratorExpression::Evaluate( + nativeLibDirectoriesExpression, this->LocalGenerator, configName); e2.Element("NativeLibDirectories", nativeLibDirs); } if (const char* nativeLibDependenciesExpression = this->GeneratorTarget->GetProperty( "ANDROID_NATIVE_LIB_DEPENDENCIES")) { - cmGeneratorExpression ge; - std::unique_ptr<cmCompiledGeneratorExpression> cge = - ge.Parse(nativeLibDependenciesExpression); - std::string nativeLibDeps = - cge->Evaluate(this->LocalGenerator, configName); + std::string nativeLibDeps = cmGeneratorExpression::Evaluate( + nativeLibDependenciesExpression, this->LocalGenerator, configName); e2.Element("NativeLibDependencies", nativeLibDeps); } @@ -3505,11 +3494,8 @@ void cmVisualStudio10TargetGenerator::WriteAntBuildOptions( if (const char* jarDirectoriesExpression = this->GeneratorTarget->GetProperty("ANDROID_JAR_DIRECTORIES")) { - cmGeneratorExpression ge; - std::unique_ptr<cmCompiledGeneratorExpression> cge = - ge.Parse(jarDirectoriesExpression); - std::string jarDirectories = - cge->Evaluate(this->LocalGenerator, configName); + std::string jarDirectories = cmGeneratorExpression::Evaluate( + jarDirectoriesExpression, this->LocalGenerator, configName); e2.Element("JarDirectories", jarDirectories); } @@ -4092,32 +4078,8 @@ void cmVisualStudio10TargetGenerator::WriteProjectReferences(Elem& e0) e2.Element("Name", name); this->WriteDotNetReferenceCustomTags(e2, name); - // If the dependency target is not managed (compiled with /clr or - // C# target) and not a WinRT component we cannot reference it and - // have to set 'ReferenceOutputAssembly' to false. - auto referenceNotManaged = - dt->GetManagedType("") < cmGeneratorTarget::ManagedType::Mixed; - // Workaround to check for manually set /clr flags. - if (referenceNotManaged) { - if (const auto* flags = dt->GetProperty("COMPILE_OPTIONS")) { - std::string flagsStr = flags; - if (flagsStr.find("clr") != std::string::npos) { - // There is a warning already issued when building the flags. - referenceNotManaged = false; - } - } - } - // Workaround for static library C# targets - if (referenceNotManaged && dt->GetType() == cmStateEnums::STATIC_LIBRARY) { - referenceNotManaged = !dt->IsCSharpOnly(); - } - - // Referencing WinRT components is okay. - if (referenceNotManaged) { - referenceNotManaged = !dt->GetPropertyAsBool("VS_WINRT_COMPONENT"); - } - - if (referenceNotManaged) { + // Don't reference targets that don't produce any output. + if (dt->GetManagedType("") == cmGeneratorTarget::ManagedType::Undefined) { e2.Element("ReferenceOutputAssembly", "false"); e2.Element("CopyToOutputDirectory", "Never"); } diff --git a/Source/cmVisualStudio10TargetGenerator.h b/Source/cmVisualStudio10TargetGenerator.h index 4dd92eb..a18a33d 100644 --- a/Source/cmVisualStudio10TargetGenerator.h +++ b/Source/cmVisualStudio10TargetGenerator.h @@ -142,7 +142,7 @@ private: std::string const& script, std::string const& additional_inputs, std::string const& outputs, - std::string const& comment); + std::string const& comment, bool symbolic); void WriteCustomRuleCSharp(Elem& e0, std::string const& config, std::string const& commandName, std::string const& script, diff --git a/Source/cmake.cxx b/Source/cmake.cxx index 96d903e..b96eedb 100644 --- a/Source/cmake.cxx +++ b/Source/cmake.cxx @@ -1751,7 +1751,7 @@ int cmake::Run(const std::vector<std::string>& args, bool noconfigure) cmGlobalVisualStudioGenerator* gg = static_cast<cmGlobalVisualStudioGenerator*>(this->GlobalGenerator); gg->CallVisualStudioMacro(cmGlobalVisualStudioGenerator::MacroStop, - this->VSSolutionFile.c_str()); + this->VSSolutionFile); } #endif return ret; diff --git a/Source/cmcmd.cxx b/Source/cmcmd.cxx index 211fd4c..7bb3dce 100644 --- a/Source/cmcmd.cxx +++ b/Source/cmcmd.cxx @@ -27,6 +27,7 @@ #if !defined(CMAKE_BOOTSTRAP) && defined(_WIN32) # include "bindexplib.h" +# include "cmFileTime.h" # include "cmsys/ConsoleBuf.hxx" #endif @@ -48,6 +49,8 @@ #include <sstream> #include <utility> +#include <cm/string_view> + class cmConnection; int cmcmd_cmake_ninja_depends(std::vector<std::string>::const_iterator argBeg, @@ -1057,19 +1060,15 @@ int cmcmd::ExecuteCMakeCommand(std::vector<std::string> const& args) #ifndef CMAKE_BOOTSTRAP if ((args[1] == "cmake_autogen") && (args.size() >= 4)) { - cmQtAutoMocUic autoGen; - std::string const& infoDir = args[2]; - std::string const& config = args[3]; - return autoGen.Run(infoDir, config) ? 0 : 1; + cm::string_view const infoFile = args[2]; + cm::string_view const config = args[3]; + return cmQtAutoMocUic(infoFile, config) ? 0 : 1; } if ((args[1] == "cmake_autorcc") && (args.size() >= 3)) { - cmQtAutoRcc autoGen; - std::string const& infoFile = args[2]; - std::string config; - if (args.size() > 3) { - config = args[3]; - } - return autoGen.Run(infoFile, config) ? 0 : 1; + cm::string_view const infoFile = args[2]; + cm::string_view const config = + (args.size() > 3) ? cm::string_view(args[3]) : cm::string_view(); + return cmQtAutoRcc(infoFile, config) ? 0 : 1; } #endif diff --git a/Tests/CMakeLists.txt b/Tests/CMakeLists.txt index 78ae7aa..789fc0e 100644 --- a/Tests/CMakeLists.txt +++ b/Tests/CMakeLists.txt @@ -2079,6 +2079,9 @@ ${CMake_SOURCE_DIR}/Utilities/Release/push.bash --dir dev -- '${CMake_BUILD_NIGH --build-config $<CONFIGURATION> --build-options -DCMAKE_SYSTEM_NAME=${systemName} -DCMAKE_SYSTEM_VERSION=${systemVersion} + --test-command + ${CMAKE_CMAKE_COMMAND} -DAPP_PACKAGE_DIR="${CMake_BINARY_DIR}/Tests/VSWinStorePhone/${name}" + -P "${CMake_SOURCE_DIR}/Tests/VSWinStorePhone/VerifyAppPackage.cmake" ) list(APPEND TEST_BUILD_DIRS "${CMake_BINARY_DIR}/Tests/VSWinStorePhone/${name}") endmacro() @@ -2325,6 +2328,79 @@ ${CMake_SOURCE_DIR}/Utilities/Release/push.bash --dir dev -- '${CMake_BUILD_NIGH list(APPEND TEST_BUILD_DIRS "${CMake_BINARY_DIR}/Tests/CFBundleTest") ADD_TEST_MACRO(ObjC++ ObjC++) + + add_test(Objective-C.simple-build-test ${CMAKE_CTEST_COMMAND} + --build-and-test + "${CMake_SOURCE_DIR}/Tests/Objective-C/simple-build-test" + "${CMake_BINARY_DIR}/Tests/Objective-C/simple-build-test" + --build-two-config + ${build_generator_args} + --build-project simple-build-test + --build-options ${build_options} + --test-command simple-build-test + ) + list(APPEND TEST_BUILD_DIRS "${CMake_BINARY_DIR}/Tests/Objective-C/simple-build-test") + + add_test(Objective-C.c-file-extension-test ${CMAKE_CTEST_COMMAND} + --build-and-test + "${CMake_SOURCE_DIR}/Tests/Objective-C/c-file-extension-test" + "${CMake_BINARY_DIR}/Tests/Objective-C/c-file-extension-test" + --build-two-config + ${build_generator_args} + --build-project c-file-extension-test + --build-options ${build_options} + --test-command c-file-extension-test + ) + list(APPEND TEST_BUILD_DIRS "${CMake_BINARY_DIR}/Tests/Objective-C/c-file-extension-test") + + add_test(Objective-C.objc-file-extension-test ${CMAKE_CTEST_COMMAND} + --build-and-test + "${CMake_SOURCE_DIR}/Tests/Objective-C/objc-file-extension-test" + "${CMake_BINARY_DIR}/Tests/Objective-C/objc-file-extension-test" + --build-two-config + ${build_generator_args} + --build-project objc-file-extension-test + --build-options ${build_options} + --test-command objc-file-extension-test + ) + list(APPEND TEST_BUILD_DIRS "${CMAKE_BINARY_DIR}/Tests/Objective-C/objc-file-extension-test") + + add_test(Objective-CXX.simple-build-test ${CMAKE_CTEST_COMMAND} + --build-and-test + "${CMake_SOURCE_DIR}/Tests/Objective-C++/simple-build-test" + "${CMake_BINARY_DIR}/Tests/Objective-C++/simple-build-test" + --build-two-config + ${build_generator_args} + --build-project simple-build-test + --build-options ${build_options} + --test-command simple-build-test + ) + list(APPEND TEST_BUILD_DIRS "${CMake_BINARY_DIR}/Tests/Objective-C++/simple-build-test") + + add_test(Objective-CXX.cxx-file-extension-test ${CMAKE_CTEST_COMMAND} + --build-and-test + "${CMake_SOURCE_DIR}/Tests/Objective-C++/cxx-file-extension-test" + "${CMake_BINARY_DIR}/Tests/Objective-C++/cxx-file-extension-test" + --build-two-config + ${build_generator_args} + --build-project cxx-file-extension-test + --build-options ${build_options} + --test-command cxx-file-extension-test + ) + list(APPEND TEST_BUILD_DIRS "${CMake_BINARY_DIR}/Tests/Objective-C++/cxx-file-extension-test") + + add_test(Objective-CXX.objcxx-file-extension-test ${CMAKE_CTEST_COMMAND} + --build-and-test + "${CMake_SOURCE_DIR}/Tests/Objective-C++/objcxx-file-extension-test" + "${CMake_BINARY_DIR}/Tests/Objective-C++/objcxx-file-extension-test" + --build-two-config + ${build_generator_args} + --build-project objcxx-file-extension-test + --build-options ${build_options} + --test-command objcxx-file-extension-test + ) + list(APPEND TEST_BUILD_DIRS "${CMAKE_BINARY_DIR}/Tests/Objective-C++/objcxx-file-extension-test") + endif () endif () diff --git a/Tests/CMakeOnly/CMakeLists.txt b/Tests/CMakeOnly/CMakeLists.txt index 19f3f79..03babd2 100644 --- a/Tests/CMakeOnly/CMakeLists.txt +++ b/Tests/CMakeOnly/CMakeLists.txt @@ -30,6 +30,17 @@ add_CMakeOnly_test(CheckStructHasMember) add_CMakeOnly_test(CompilerIdC) add_CMakeOnly_test(CompilerIdCXX) + +if(CMAKE_OBJC_COMPILER) + add_CMakeOnly_test(CompilerIdOBJC) + add_CMakeOnly_test(CheckOBJCCompilerFlag) +endif() + +if(CMAKE_OBJCXX_COMPILER) + add_CMakeOnly_test(CompilerIdOBJCXX) + add_CMakeOnly_test(CheckOBJCXXCompilerFlag) +endif() + if(CMAKE_Fortran_COMPILER) add_CMakeOnly_test(CompilerIdFortran) endif() diff --git a/Tests/CMakeOnly/CheckLanguage/CMakeLists.txt b/Tests/CMakeOnly/CheckLanguage/CMakeLists.txt index ca4becb..90aa921 100644 --- a/Tests/CMakeOnly/CheckLanguage/CMakeLists.txt +++ b/Tests/CMakeOnly/CheckLanguage/CMakeLists.txt @@ -5,10 +5,20 @@ include(CheckLanguage) set(langs ) set(expect_C 1) set(expect_CXX 1) + +if(APPLE) + set(expect_OBJC 1) + set(expect_OBJCXX 1) +endif() unset(expect_Fortran) set(expect_NoSuchLanguage 0) -foreach(lang C CXX Fortran CUDA NoSuchLanguage) +set(LANGUAGES C CXX Fortran CUDA NoSuchLanguage) +if(APPLE) + list(APPEND LANGUAGES OBJC OBJCXX) +endif() + +foreach(lang ${LANGUAGES}) check_language(${lang}) if(NOT DEFINED CMAKE_${lang}_COMPILER) message(FATAL_ERROR "check_language(${lang}) did not set result") diff --git a/Tests/CMakeOnly/CheckOBJCCompilerFlag/CMakeLists.txt b/Tests/CMakeOnly/CheckOBJCCompilerFlag/CMakeLists.txt new file mode 100644 index 0000000..a9a96ee --- /dev/null +++ b/Tests/CMakeOnly/CheckOBJCCompilerFlag/CMakeLists.txt @@ -0,0 +1,17 @@ +cmake_minimum_required(VERSION 2.8.12) + +project(CheckOBJCCompilerFlag) + +include(CheckOBJCCompilerFlag) + +if(CMAKE_COMPILER_IS_GNUOBJC) + set(COMPILER_FLAG -fobjc-direct-dispatch) +else() + set(COMPILER_FLAG -fobjc-gc) +endif() + +CHECK_OBJC_COMPILER_FLAGS(${COMPILER_FLAG} HAS_COMPILER_FLAG) + +if(NOT HAS_COMPILER_FLAG) + message(SEND_ERROR "Test fail: HAS_COMPILER_FLAG: ${COMPILER_FLAG}") +endif diff --git a/Tests/CMakeOnly/CheckOBJCXXCompilerFlag/CMakeLists.txt b/Tests/CMakeOnly/CheckOBJCXXCompilerFlag/CMakeLists.txt new file mode 100644 index 0000000..f83b738 --- /dev/null +++ b/Tests/CMakeOnly/CheckOBJCXXCompilerFlag/CMakeLists.txt @@ -0,0 +1,17 @@ +cmake_minimum_required(VERSION 2.8.12) + +project(CheckOBJCXXCompilerFlag) + +include(CheckOBJCXXCompilerFlag) + +if(CMAKE_COMPILER_IS_GNUOBJCXX) + set(COMPILER_FLAG -fobjc-direct-dispatch) +else() + set(COMPILER_FLAG -fobjc-gc) +endif() + +CHECK_OBJCXX_COMPILER_FLAGS(${COMPILER_FLAG} HAS_COMPILER_FLAG) + +if(NOT HAS_COMPILER_FLAG) + message(SEND_ERROR "Test fail: HAS_COMPILER_FLAG: ${COMPILER_FLAG}") +endif() diff --git a/Tests/CMakeOnly/CompilerIdOBJC/CMakeLists.txt b/Tests/CMakeOnly/CompilerIdOBJC/CMakeLists.txt new file mode 100644 index 0000000..8f13787 --- /dev/null +++ b/Tests/CMakeOnly/CompilerIdOBJC/CMakeLists.txt @@ -0,0 +1,14 @@ +cmake_minimum_required(VERSION 2.8.12) +project(CompilerIdOBJC OBJC) + +foreach(v + CMAKE_OBJC_COMPILER + CMAKE_OBJC_COMPILER_ID + CMAKE_OBJC_COMPILER_VERSION + ) + if(${v}) + message(STATUS "${v}=[${${v}}]") + else() + message(SEND_ERROR "${v} not set!") + endif() +endforeach() diff --git a/Tests/CMakeOnly/CompilerIdOBJCXX/CMakeLists.txt b/Tests/CMakeOnly/CompilerIdOBJCXX/CMakeLists.txt new file mode 100644 index 0000000..8f41db0 --- /dev/null +++ b/Tests/CMakeOnly/CompilerIdOBJCXX/CMakeLists.txt @@ -0,0 +1,14 @@ +cmake_minimum_required(VERSION 2.8.12) +project(CompilerIdOBJCXX OBJCXX) + +foreach(v + CMAKE_OBJCXX_COMPILER + CMAKE_OBJCXX_COMPILER_ID + CMAKE_OBJCXX_COMPILER_VERSION + ) + if(${v}) + message(STATUS "${v}=[${${v}}]") + else() + message(SEND_ERROR "${v} not set!") + endif() +endforeach() diff --git a/Tests/CPackComponentsDEB/MyLibCPackConfig-components-description1.cmake.in b/Tests/CPackComponentsDEB/MyLibCPackConfig-components-description1.cmake.in index 74d816c..67b108b 100644 --- a/Tests/CPackComponentsDEB/MyLibCPackConfig-components-description1.cmake.in +++ b/Tests/CPackComponentsDEB/MyLibCPackConfig-components-description1.cmake.in @@ -15,8 +15,9 @@ set(CPACK_COMPONENTS_IGNORE_GROUPS 1) #set(CPACK_COMPONENTS_ALL_IN_ONE_PACKAGE 1) # overriding previous descriptions -set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "main description") -set(CPACK_COMPONENT_APPLICATIONS_DESCRIPTION "applications_description") -set(CPACK_COMPONENT_HEADERS_DESCRIPTION "headers_description") +set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "main description") # This become a summary line (the first one) of all descriptions +set(CPACK_COMPONENT_APPLICATIONS_DESCRIPTION "applications_description") +set(CPACK_COMPONENT_HEADERS_DESCRIPTION "headers_description") # libraries does not have any description and should inherit from CPACK_PACKAGE_DESCRIPTION_SUMMARY +# plus content of the `CPACK_PACKAGE_DESCRIPTION_FILE`. unset(CPACK_COMPONENT_LIBRARIES_DESCRIPTION) diff --git a/Tests/CPackComponentsDEB/MyLibCPackConfig-components-description2.cmake.in b/Tests/CPackComponentsDEB/MyLibCPackConfig-components-description2.cmake.in index cda79bc..d877325 100644 --- a/Tests/CPackComponentsDEB/MyLibCPackConfig-components-description2.cmake.in +++ b/Tests/CPackComponentsDEB/MyLibCPackConfig-components-description2.cmake.in @@ -15,12 +15,12 @@ set(CPACK_COMPONENTS_IGNORE_GROUPS 1) #set(CPACK_COMPONENTS_ALL_IN_ONE_PACKAGE 1) # overriding previous descriptions -set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "main description 2") +set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "main description 2") -# Components do not have any description +# Components do not have any description. +# So, content of `CPACK_PACKAGE_DESCRIPTION_FILE` gonna used +# after summary line. unset(CPACK_COMPONENT_APPLICATIONS_DESCRIPTION) unset(CPACK_COMPONENT_HEADERS_DESCRIPTION) -unset(CPACK_COMPONENT_LIBRARIES_DESCRIPTION) - -set(CPACK_COMPONENT_LIBRARIES_DESCRIPTION "library description") +set(CPACK_COMPONENT_LIBRARIES_DESCRIPTION "library description") diff --git a/Tests/CPackComponentsDEB/RunCPackVerifyResult-components-description1.cmake b/Tests/CPackComponentsDEB/RunCPackVerifyResult-components-description1.cmake index f74137c..86a74b2 100644 --- a/Tests/CPackComponentsDEB/RunCPackVerifyResult-components-description1.cmake +++ b/Tests/CPackComponentsDEB/RunCPackVerifyResult-components-description1.cmake @@ -48,26 +48,26 @@ if(DPKGDEB_EXECUTABLE) DPKGDEB_OUTPUT "${dpkg_output}" METAENTRY "Package:") - dpkgdeb_return_specific_metaentry(dpkg_description - DPKGDEB_OUTPUT "${dpkg_output}" - METAENTRY "Description:") + get_package_description("${dpkg_output}" dpkg_description) message(STATUS "package='${dpkg_package_name}', description='${dpkg_description}'") if(dpkg_package_name STREQUAL "mylib-applications") - if(NOT "${dpkg_description}" STREQUAL "applications_description") + set(expected_description "main description\n applications_description") + if(NOT dpkg_description STREQUAL expected_description) set(dpkgdeb_output_errors_all ${dpkgdeb_output_errors_all} - "dpkg-deb: ${_f}: Incorrect description for package ${dpkg_package_name}: ${dpkg_description} != applications_description") + "dpkg-deb: ${_f}: Incorrect description for package ${dpkg_package_name}: `${dpkg_description}` != `${expected_description}`") endif() elseif(dpkg_package_name STREQUAL "mylib-headers") - if(NOT dpkg_description STREQUAL "headers_description") + set(expected_description "main description\n headers_description") + if(NOT dpkg_description STREQUAL expected_description) set(dpkgdeb_output_errors_all ${dpkgdeb_output_errors_all} - "dpkg-deb: ${_f}: Incorrect description for package ${dpkg_package_name}: ${dpkg_description} != headers_description") + "dpkg-deb: ${_f}: Incorrect description for package ${dpkg_package_name}: `${dpkg_description}` != `${expected_description}`") endif() elseif(dpkg_package_name STREQUAL "mylib-libraries") - if(NOT dpkg_description STREQUAL "main description") + if(NOT dpkg_description MATCHES "main description\n.*") set(dpkgdeb_output_errors_all ${dpkgdeb_output_errors_all} - "dpkg-deb: ${_f}: Incorrect description for package ${dpkg_package_name}: ${dpkg_description} != 'main description'") + "dpkg-deb: ${_f}: Incorrect description for package ${dpkg_package_name}: `${dpkg_description}` =~ `main description.*`") endif() else() set(dpkgdeb_output_errors_all ${dpkgdeb_output_errors_all} diff --git a/Tests/CPackComponentsDEB/RunCPackVerifyResult-components-description2.cmake b/Tests/CPackComponentsDEB/RunCPackVerifyResult-components-description2.cmake index 241dda5..d53c73d 100644 --- a/Tests/CPackComponentsDEB/RunCPackVerifyResult-components-description2.cmake +++ b/Tests/CPackComponentsDEB/RunCPackVerifyResult-components-description2.cmake @@ -48,26 +48,20 @@ if(DPKGDEB_EXECUTABLE) DPKGDEB_OUTPUT "${dpkg_output}" METAENTRY "Package:") - dpkgdeb_return_specific_metaentry(dpkg_description - DPKGDEB_OUTPUT "${dpkg_output}" - METAENTRY "Description:") + get_package_description("${dpkg_output}" dpkg_description) message(STATUS "package='${dpkg_package_name}', description='${dpkg_description}'") - if(dpkg_package_name STREQUAL "mylib-applications") - if(NOT dpkg_description STREQUAL "main description 2") - set(dpkgdeb_output_errors_all ${dpkgdeb_output_errors_all} - "dpkg-deb: ${_f}: Incorrect description for package ${dpkg_package_name}: ${dpkg_description} != applications_description") - endif() - elseif(dpkg_package_name STREQUAL "mylib-headers") - if(NOT dpkg_description STREQUAL "main description 2") + if(dpkg_package_name STREQUAL "mylib-applications" OR dpkg_package_name STREQUAL "mylib-headers") + if(NOT dpkg_description MATCHES "main description 2\n.*") set(dpkgdeb_output_errors_all ${dpkgdeb_output_errors_all} - "dpkg-deb: ${_f}: Incorrect description for package ${dpkg_package_name}: ${dpkg_description} != headers_description") + "dpkg-deb: ${_f}: Incorrect description for package ${dpkg_package_name}: `${dpkg_description}` =~ `main description 2`") endif() elseif(dpkg_package_name STREQUAL "mylib-libraries") - if(NOT dpkg_description STREQUAL "library description") + set(expected_description "main description 2\n library description") + if(NOT dpkg_description STREQUAL expected_description) set(dpkgdeb_output_errors_all ${dpkgdeb_output_errors_all} - "dpkg-deb: ${_f}: Incorrect description for package ${dpkg_package_name}: ${dpkg_description} != 'main description'") + "dpkg-deb: ${_f}: Incorrect description for package ${dpkg_package_name}: `${dpkg_description}` != `${expected_description}`") endif() else() set(dpkgdeb_output_errors_all ${dpkgdeb_output_errors_all} diff --git a/Tests/CPackComponentsDEB/RunCPackVerifyResult.cmake b/Tests/CPackComponentsDEB/RunCPackVerifyResult.cmake index 2093e7e..b172da2 100644 --- a/Tests/CPackComponentsDEB/RunCPackVerifyResult.cmake +++ b/Tests/CPackComponentsDEB/RunCPackVerifyResult.cmake @@ -200,4 +200,29 @@ function(dpkgdeb_return_specific_metaentry output) endif() endfunction() +function(get_package_description DPKG_OUTPUT RESULT_VAR) + string(UUID uuid NAMESPACE 00000000-0000-0000-0000-000000000000 TYPE SHA1) + string(REPLACE ";" "${uuid}" DPKG_OUTPUT "${DPKG_OUTPUT}") + string(REPLACE "\n" ";" DPKG_OUTPUT "${DPKG_OUTPUT}") + + unset(_actual_description) + set(_parse_description FALSE) + foreach(_line IN LISTS DPKG_OUTPUT) + if(_line MATCHES " Description:.*") + set(_parse_description TRUE) + string(REPLACE " Description: " "" _line "${_line}") + list(APPEND _actual_description "${_line}") + elseif(_parse_description) + if(_line MATCHES " [A-Z][A-Za-z\-]+: .*") + set(_parse_description FALSE) + else() + list(APPEND _actual_description "${_line}") + endif() + endif() + endforeach() + list(JOIN _actual_description "\n" _actual_description) + + set(${RESULT_VAR} "${_actual_description}" PARENT_SCOPE) +endfunction() + cmake_policy(POP) diff --git a/Tests/ExportImport/Export/Interface/CMakeLists.txt b/Tests/ExportImport/Export/Interface/CMakeLists.txt index 22a4ef6..43b7217 100644 --- a/Tests/ExportImport/Export/Interface/CMakeLists.txt +++ b/Tests/ExportImport/Export/Interface/CMakeLists.txt @@ -6,6 +6,12 @@ set_property(TARGET headeronly PROPERTY INTERFACE_INCLUDE_DIRECTORIES ) set_property(TARGET headeronly PROPERTY INTERFACE_COMPILE_DEFINITIONS "HEADERONLY_DEFINE") +add_library(pch_iface INTERFACE) +target_precompile_headers(pch_iface INTERFACE + "$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/pch/pch.h>" + "$<INSTALL_INTERFACE:$<INSTALL_PREFIX>/include/pch/pch.h>" + ) + include(GenerateExportHeader) add_library(sharedlib SHARED sharedlib.cpp) generate_export_header(sharedlib) @@ -45,7 +51,7 @@ set_property(TARGET cmakeonly PROPERTY custom_property CustomPropertyValue) set_property(TARGET cmakeonly PROPERTY EXPORT_PROPERTIES custom_property) install(TARGETS headeronly sharediface use_auto_type use_c_restrict source_target - cmakeonly + pch_iface cmakeonly EXPORT expInterface ) install(TARGETS sharedlib @@ -61,6 +67,10 @@ install(FILES DESTINATION include/headeronly ) install(FILES + pch/pch.h + DESTINATION include/pch +) +install(FILES sharedlib/sharedlib.h "${CMAKE_CURRENT_BINARY_DIR}/sharedlib_export.h" DESTINATION include/sharedlib diff --git a/Tests/ExportImport/Export/Interface/pch/pch.h b/Tests/ExportImport/Export/Interface/pch/pch.h new file mode 100644 index 0000000..bc50727 --- /dev/null +++ b/Tests/ExportImport/Export/Interface/pch/pch.h @@ -0,0 +1 @@ +#define PCH_PCH_H_INCLUDED diff --git a/Tests/ExportImport/Import/Interface/CMakeLists.txt b/Tests/ExportImport/Import/Interface/CMakeLists.txt index a07a5b3..ef666b1 100644 --- a/Tests/ExportImport/Import/Interface/CMakeLists.txt +++ b/Tests/ExportImport/Import/Interface/CMakeLists.txt @@ -98,6 +98,17 @@ set_property(TARGET exp::sharediface APPEND PROPERTY INTERFACE_LINK_LIBRARIES de add_executable(interfacetest_exp interfacetest.cpp) target_link_libraries(interfacetest_exp exp::sharediface) +if(NOT CMAKE_OSX_ARCHITECTURES MATCHES "[;$]" OR CMAKE_GENERATOR STREQUAL "Xcode") + add_executable(pch_iface_test_bld pch_iface_test.cpp) + target_link_libraries(pch_iface_test_bld bld::pch_iface) + add_executable(pch_iface_test_exp pch_iface_test.cpp) + target_link_libraries(pch_iface_test_exp exp::pch_iface) + if(CMAKE_CXX_COMPILE_OPTIONS_USE_PCH) + target_compile_definitions(pch_iface_test_bld PRIVATE EXPECT_PCH) + target_compile_definitions(pch_iface_test_exp PRIVATE EXPECT_PCH) + endif() +endif() + do_try_compile(exp) foreach(ns exp bld) diff --git a/Tests/ExportImport/Import/Interface/pch_iface_test.cpp b/Tests/ExportImport/Import/Interface/pch_iface_test.cpp new file mode 100644 index 0000000..a460d0d --- /dev/null +++ b/Tests/ExportImport/Import/Interface/pch_iface_test.cpp @@ -0,0 +1,16 @@ +#ifdef EXPECT_PCH +// Verify that pch/pch.h was included via '-include ...' or equivalent. +# ifndef PCH_PCH_H_INCLUDED +# error "Expected PCH_PCH_H_INCLUDED." +# endif +#elif defined(__PGIC__) || defined(__ibmxl__) +// No PCH expected but these compilers define macros below. +#elif defined(__GNUC__) || defined(__clang__) || defined(_INTEL_COMPILER) || \ + defined(_MSC_VER) +# error "Expected EXPECT_PCH for this compiler." +#endif + +int main() +{ + return 0; +} diff --git a/Tests/FortranModules/Library/a.f90 b/Tests/FortranModules/Library/a.f90 index 3031c07..c180cc0 100644 --- a/Tests/FortranModules/Library/a.f90 +++ b/Tests/FortranModules/Library/a.f90 @@ -1,3 +1,6 @@ MODULE libraryModuleA USE libraryModuleB +CONTAINS + SUBROUTINE libA + END SUBROUTINE END MODULE diff --git a/Tests/FortranModules/Library/b.f90 b/Tests/FortranModules/Library/b.f90 index ae1edcb..f550996 100644 --- a/Tests/FortranModules/Library/b.f90 +++ b/Tests/FortranModules/Library/b.f90 @@ -1,2 +1,5 @@ MODULE libraryModuleB +CONTAINS + SUBROUTINE libB + END SUBROUTINE END MODULE diff --git a/Tests/FortranModules/Subdir/subdir.f90 b/Tests/FortranModules/Subdir/subdir.f90 index 68955f6..5288b06 100644 --- a/Tests/FortranModules/Subdir/subdir.f90 +++ b/Tests/FortranModules/Subdir/subdir.f90 @@ -1,2 +1,5 @@ MODULE subdirModuleA +CONTAINS + SUBROUTINE subdirLibA + END SUBROUTINE END MODULE diff --git a/Tests/Objective-C++/cxx-file-extension-test/CMakeLists.txt b/Tests/Objective-C++/cxx-file-extension-test/CMakeLists.txt new file mode 100644 index 0000000..0b33875 --- /dev/null +++ b/Tests/Objective-C++/cxx-file-extension-test/CMakeLists.txt @@ -0,0 +1,5 @@ +cmake_minimum_required(VERSION 3.15) + +project(cxx-file-extension-test CXX) + +add_executable(cxx-file-extension-test main.mm) diff --git a/Tests/Objective-C++/cxx-file-extension-test/main.mm b/Tests/Objective-C++/cxx-file-extension-test/main.mm new file mode 100644 index 0000000..1c159a9 --- /dev/null +++ b/Tests/Objective-C++/cxx-file-extension-test/main.mm @@ -0,0 +1,8 @@ +#ifndef __OBJC__ +# error "Compiler cannot compile Objective-C" +#endif + +int main() +{ + return 0; +} diff --git a/Tests/Objective-C++/objcxx-file-extension-test/CMakeLists.txt b/Tests/Objective-C++/objcxx-file-extension-test/CMakeLists.txt new file mode 100644 index 0000000..eda7bba --- /dev/null +++ b/Tests/Objective-C++/objcxx-file-extension-test/CMakeLists.txt @@ -0,0 +1,6 @@ +cmake_minimum_required(VERSION 3.15) + +project(objcxx-file-extension-test OBJCXX CXX) + +add_executable(objcxx-file-extension-test main.mm) +target_link_libraries(objcxx-file-extension-test "-framework Foundation") diff --git a/Tests/Objective-C++/objcxx-file-extension-test/main.mm b/Tests/Objective-C++/objcxx-file-extension-test/main.mm new file mode 100644 index 0000000..d4aa1bb --- /dev/null +++ b/Tests/Objective-C++/objcxx-file-extension-test/main.mm @@ -0,0 +1,14 @@ +#ifndef __OBJC__ +# error "Compiler is not an Objective-C compiler." +#endif + +#import <Foundation/Foundation.h> +#include <iostream> + +int main() +{ + NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; + std::cout << "Hello World" << std::endl; + [pool release]; + return 0; +} diff --git a/Tests/Objective-C++/simple-build-test/CMakeLists.txt b/Tests/Objective-C++/simple-build-test/CMakeLists.txt new file mode 100644 index 0000000..cf27683 --- /dev/null +++ b/Tests/Objective-C++/simple-build-test/CMakeLists.txt @@ -0,0 +1,11 @@ +cmake_minimum_required(VERSION 3.15) + +set(CMAKE_MACOSX_RPATH OFF) + +project(simple-build-test OBJCXX) + +add_library(foo SHARED foo.mm) +target_link_libraries(foo "-framework Foundation") + +add_executable(simple-build-test main.mm) +target_link_libraries(simple-build-test "-framework Foundation" foo) diff --git a/Tests/Objective-C++/simple-build-test/foo.h b/Tests/Objective-C++/simple-build-test/foo.h new file mode 100644 index 0000000..b3fb084 --- /dev/null +++ b/Tests/Objective-C++/simple-build-test/foo.h @@ -0,0 +1,9 @@ +#import <Foundation/Foundation.h> + +@interface Foo : NSObject { + NSNumber* age; +} + +@property (nonatomic, retain) NSNumber* age; + +@end diff --git a/Tests/Objective-C++/simple-build-test/foo.mm b/Tests/Objective-C++/simple-build-test/foo.mm new file mode 100644 index 0000000..2d452a8 --- /dev/null +++ b/Tests/Objective-C++/simple-build-test/foo.mm @@ -0,0 +1,7 @@ +#import "foo.h" + +@implementation Foo + +@synthesize age; + +@end diff --git a/Tests/Objective-C++/simple-build-test/main.mm b/Tests/Objective-C++/simple-build-test/main.mm new file mode 100644 index 0000000..7c85551 --- /dev/null +++ b/Tests/Objective-C++/simple-build-test/main.mm @@ -0,0 +1,14 @@ +#import <Foundation/Foundation.h> +#import "foo.h" +#include <iostream> + +int main(int argc, char **argv) +{ + NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; + Foo *theFoo = [[Foo alloc] init]; + theFoo.age = [NSNumber numberWithInt:argc]; + NSLog(@"%d\n",[theFoo.age intValue]); + std::cout << [theFoo.age intValue] << std::endl; + [pool release]; + return 0; +} diff --git a/Tests/Objective-C/c-file-extension-test/CMakeLists.txt b/Tests/Objective-C/c-file-extension-test/CMakeLists.txt new file mode 100644 index 0000000..e091448 --- /dev/null +++ b/Tests/Objective-C/c-file-extension-test/CMakeLists.txt @@ -0,0 +1,5 @@ +cmake_minimum_required(VERSION 3.15) + +project(c-file-extension-test C) + +add_executable(c-file-extension-test main.m) diff --git a/Tests/Objective-C/c-file-extension-test/main.m b/Tests/Objective-C/c-file-extension-test/main.m new file mode 100644 index 0000000..1c159a9 --- /dev/null +++ b/Tests/Objective-C/c-file-extension-test/main.m @@ -0,0 +1,8 @@ +#ifndef __OBJC__ +# error "Compiler cannot compile Objective-C" +#endif + +int main() +{ + return 0; +} diff --git a/Tests/Objective-C/objc-file-extension-test/CMakeLists.txt b/Tests/Objective-C/objc-file-extension-test/CMakeLists.txt new file mode 100644 index 0000000..27e88be --- /dev/null +++ b/Tests/Objective-C/objc-file-extension-test/CMakeLists.txt @@ -0,0 +1,6 @@ +cmake_minimum_required(VERSION 3.15) + +project(objc-file-extension-test OBJC CXX) + +add_executable(objc-file-extension-test main.m) +target_link_libraries(objc-file-extension-test "-framework Foundation") diff --git a/Tests/Objective-C/objc-file-extension-test/main.m b/Tests/Objective-C/objc-file-extension-test/main.m new file mode 100644 index 0000000..2ec3917 --- /dev/null +++ b/Tests/Objective-C/objc-file-extension-test/main.m @@ -0,0 +1,12 @@ +#ifndef __OBJC__ +# error "Compiler is not an Objective-C compiler." +#endif + +#import <Foundation/Foundation.h> + +int main() +{ + NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; + [pool release]; + return 0; +} diff --git a/Tests/Objective-C/simple-build-test/CMakeLists.txt b/Tests/Objective-C/simple-build-test/CMakeLists.txt new file mode 100644 index 0000000..5ab46ac --- /dev/null +++ b/Tests/Objective-C/simple-build-test/CMakeLists.txt @@ -0,0 +1,11 @@ +cmake_minimum_required(VERSION 3.15) + +set(CMAKE_MACOSX_RPATH OFF) + +project(simple-build-test OBJC) + +add_library(foo SHARED foo.m) +target_link_libraries(foo "-framework Foundation") + +add_executable(simple-build-test main.m) +target_link_libraries(simple-build-test "-framework Foundation" foo) diff --git a/Tests/Objective-C/simple-build-test/foo.h b/Tests/Objective-C/simple-build-test/foo.h new file mode 100644 index 0000000..b3fb084 --- /dev/null +++ b/Tests/Objective-C/simple-build-test/foo.h @@ -0,0 +1,9 @@ +#import <Foundation/Foundation.h> + +@interface Foo : NSObject { + NSNumber* age; +} + +@property (nonatomic, retain) NSNumber* age; + +@end diff --git a/Tests/Objective-C/simple-build-test/foo.m b/Tests/Objective-C/simple-build-test/foo.m new file mode 100644 index 0000000..2d452a8 --- /dev/null +++ b/Tests/Objective-C/simple-build-test/foo.m @@ -0,0 +1,7 @@ +#import "foo.h" + +@implementation Foo + +@synthesize age; + +@end diff --git a/Tests/Objective-C/simple-build-test/main.m b/Tests/Objective-C/simple-build-test/main.m new file mode 100644 index 0000000..970d554 --- /dev/null +++ b/Tests/Objective-C/simple-build-test/main.m @@ -0,0 +1,12 @@ +#import <Foundation/Foundation.h> +#import "foo.h" + +int main(int argc, char **argv) +{ + NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; + Foo *theFoo = [[Foo alloc] init]; + theFoo.age = [NSNumber numberWithInt:argc]; + NSLog(@"%d\n",[theFoo.age intValue]); + [pool release]; + return 0; +} diff --git a/Tests/RunCMake/CMakeLists.txt b/Tests/RunCMake/CMakeLists.txt index c663484..026baa7 100644 --- a/Tests/RunCMake/CMakeLists.txt +++ b/Tests/RunCMake/CMakeLists.txt @@ -167,7 +167,7 @@ if(UNIX AND "${CMAKE_GENERATOR}" MATCHES "Unix Makefiles|Ninja") add_RunCMake_test(CompilerChange) endif() add_RunCMake_test(CompilerNotFound) -add_RunCMake_test(Configure) +add_RunCMake_test(Configure -DMSVC_IDE=${MSVC_IDE}) add_RunCMake_test(DisallowedCommands) add_RunCMake_test(ExternalData) add_RunCMake_test(FeatureSummary) @@ -510,6 +510,7 @@ set(cpack_tests DEB.TIMESTAMPS DEB.MD5SUMS DEB.DEB_PACKAGE_VERSION_BACK_COMPATIBILITY + DEB.DEB_DESCRIPTION RPM.CUSTOM_BINARY_SPEC_FILE RPM.CUSTOM_NAMES diff --git a/Tests/RunCMake/CPack/RunCMakeTest.cmake b/Tests/RunCMake/CPack/RunCMakeTest.cmake index 8e98961..b0b7a99 100644 --- a/Tests/RunCMake/CPack/RunCMakeTest.cmake +++ b/Tests/RunCMake/CPack/RunCMakeTest.cmake @@ -39,3 +39,10 @@ run_cpack_test_subtests(EXTERNAL "none;good;good_multi;bad_major;bad_minor;inval if(RunCMake_GENERATOR MATCHES "Visual Studio|Xcode") run_cpack_test(CPACK_INSTALL_CMAKE_CONFIGURATIONS "ZIP" false "MONOLITHIC") endif() +run_cpack_test_subtests( + DEB_DESCRIPTION + "CPACK_DEBIAN_PACKAGE_DESCRIPTION;CPACK_PACKAGE_DESCRIPTION;CPACK_PACKAGE_DESCRIPTION_FILE" + "DEB.DEB_DESCRIPTION" + false + "MONOLITHIC;COMPONENT" +) diff --git a/Tests/RunCMake/CPack/tests/DEB_DESCRIPTION/ExpectedFiles.cmake b/Tests/RunCMake/CPack/tests/DEB_DESCRIPTION/ExpectedFiles.cmake new file mode 100644 index 0000000..39f18a3 --- /dev/null +++ b/Tests/RunCMake/CPack/tests/DEB_DESCRIPTION/ExpectedFiles.cmake @@ -0,0 +1,16 @@ +set(EXPECTED_FILES_COUNT_MONOLITHIC "1") +set(EXPECTED_FILES_COUNT_COMPONENT "2") +set(EXPECTED_FILES_COUNT "${EXPECTED_FILES_COUNT_${PACKAGING_TYPE}}") + +if(PACKAGING_TYPE STREQUAL "COMPONENT") + set(EXPECTED_FILE_1 "deb_description-0.1.1-*-satu.deb") + set(EXPECTED_FILE_2 "deb_description-0.1.1-*-dua.deb") + set(EXPECTED_FILE_CONTENT_1_LIST "/satu;/satu/CMakeLists.txt") + set(EXPECTED_FILE_CONTENT_2_LIST "/dua;/dua/CMakeLists.txt") + +elseif(PACKAGING_TYPE STREQUAL "MONOLITHIC") + set(EXPECTED_FILE_CONTENT_1_LIST "/dua;/dua/CMakeLists.txt;/satu;/satu/CMakeLists.txt") + +endif() + +# kate: indent-width 2; diff --git a/Tests/RunCMake/CPack/tests/DEB_DESCRIPTION/VerifyResult.cmake b/Tests/RunCMake/CPack/tests/DEB_DESCRIPTION/VerifyResult.cmake new file mode 100644 index 0000000..e9ac13a --- /dev/null +++ b/Tests/RunCMake/CPack/tests/DEB_DESCRIPTION/VerifyResult.cmake @@ -0,0 +1,65 @@ +function(checkPackageDescription FILE EXPECTED_DESCRIPTION) + getPackageInfo("${FILE}" "_file_info") + string(UUID uuid NAMESPACE 00000000-0000-0000-0000-000000000000 TYPE SHA1) + string(REPLACE ";" "${uuid}" _file_info "${_file_info}") + string(REPLACE ";" "${uuid}" EXPECTED_DESCRIPTION "${EXPECTED_DESCRIPTION}") + string(REPLACE "\n" ";" _file_info "${_file_info}") + + set(_actual_description) + set(_parse_description FALSE) + foreach(_line IN LISTS _file_info) + if(_line MATCHES " Description:.*") + set(_parse_description TRUE) + list(APPEND _actual_description "${_line}") + elseif(_parse_description) + if(_line MATCHES " [A-Z][A-Za-z\-]+: .*") + set(_parse_description FALSE) + else() + list(APPEND _actual_description "${_line}") + endif() + endif() + endforeach() + list(JOIN _actual_description "\n" _actual_description) + + if(NOT _actual_description STREQUAL EXPECTED_DESCRIPTION) + set(_error "---[BEGIN Expected description]---\n${EXPECTED_DESCRIPTION}---[END Expected description]---\n") + string(APPEND _error "---[BEGIN Actual description]---\n${_actual_description}---[END Actual description]---\n") + string(REPLACE "${uuid}" ";" _error "${_error}") + message(FATAL_ERROR "${_error}") + endif() +endfunction() + +# ALERT The output of `dpkg -I *.deb` indented by one space +set(_expected_description [[ Description: This is the summary line + This is the Debian package multiline description. + . + It must be formatted properly! Otherwise, the result `*.deb` + package become broken and cant be installed! + . + It may contains `;` characters (even like this `;;;;`). Example: + . + - one; + - two; + - three; + . + ... and they are properly handled by the automatic description formatter! + . + See also: https://www.debian.org/doc/debian-policy/ch-controlfields.html#description]]) + +# ATTENTION The code in `cmCPackGenerator.cxx` to read `CPACK_PACKAGE_DESCRIPTION_FILE` +# has a BUG: it appends the `\n` character to every line of the +# input, even if there was no EOL (e.g. at the last line of the file). +# That is WHY for this sub-test the one more pre-formatted "empty" +# line required! +# NOTE For component based installers content of the file gonna read by +# `CPackDeb` module and the `file(READ...)` command so no the mentioned +# workaround required! +if(RunCMake_SUBTEST_SUFFIX STREQUAL "CPACK_PACKAGE_DESCRIPTION_FILE" AND PACKAGING_TYPE STREQUAL "MONOLITHIC") + string(APPEND _expected_description "\n ." ) +endif() + +foreach(_file_no RANGE 1 ${EXPECTED_FILES_COUNT}) + checkPackageDescription("${FOUND_FILE_${_file_no}}" "${_expected_description}") +endforeach() + +# kate: indent-width 2; diff --git a/Tests/RunCMake/CPack/tests/DEB_DESCRIPTION/test.cmake b/Tests/RunCMake/CPack/tests/DEB_DESCRIPTION/test.cmake new file mode 100644 index 0000000..ce3f651 --- /dev/null +++ b/Tests/RunCMake/CPack/tests/DEB_DESCRIPTION/test.cmake @@ -0,0 +1,45 @@ +install(FILES CMakeLists.txt DESTINATION satu COMPONENT satu) +install(FILES CMakeLists.txt DESTINATION dua COMPONENT dua) + +set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "This is the summary line") +set(_description [[This is the Debian package multiline description. + +It must be formatted properly! Otherwise, the result `*.deb` +package become broken and cant be installed! + +It may contains `;` characters (even like this `;;;;`). Example: + + - one; + - two; + - three; + +... and they are properly handled by the automatic description formatter! + +See also: https://www.debian.org/doc/debian-policy/ch-controlfields.html#description]]) + +if(RunCMake_SUBTEST_SUFFIX STREQUAL "CPACK_DEBIAN_PACKAGE_DESCRIPTION") + if(PACKAGING_TYPE STREQUAL "COMPONENT") + set(CPACK_DEBIAN_SATU_DESCRIPTION "${_description}") + set(CPACK_DEBIAN_DUA_DESCRIPTION "${_description}") + else() + set(CPACK_DEBIAN_PACKAGE_DESCRIPTION "${_description}") + endif() + +elseif(RunCMake_SUBTEST_SUFFIX STREQUAL "CPACK_PACKAGE_DESCRIPTION") + # NOTE Documented fallback variable + if(PACKAGING_TYPE STREQUAL "COMPONENT") + set(CPACK_COMPONENT_SATU_DESCRIPTION "${_description}") + set(CPACK_COMPONENT_DUA_DESCRIPTION "${_description}") + else() + set(CPACK_PACKAGE_DESCRIPTION "${_description}") + endif() + +elseif(RunCMake_SUBTEST_SUFFIX STREQUAL "CPACK_PACKAGE_DESCRIPTION_FILE") + # NOTE Getting the description from the file + set(_file "${CMAKE_CURRENT_BINARY_DIR}/description.txt") + file(WRITE "${_file}" "${_description}") + set(CPACK_PACKAGE_DESCRIPTION_FILE "${_file}") + +endif() + +# kate: indent-width 2; diff --git a/Tests/RunCMake/CPack/tests/PER_COMPONENT_FIELDS/VerifyResult.cmake b/Tests/RunCMake/CPack/tests/PER_COMPONENT_FIELDS/VerifyResult.cmake index b4bdb61..c47b40e 100644 --- a/Tests/RunCMake/CPack/tests/PER_COMPONENT_FIELDS/VerifyResult.cmake +++ b/Tests/RunCMake/CPack/tests/PER_COMPONENT_FIELDS/VerifyResult.cmake @@ -8,6 +8,9 @@ endfunction() if(GENERATOR_TYPE STREQUAL "DEB") set(name_ "Package") set(group_ "Section") + # NOTE For a Debian package the first line of the `Description` + # field is generated by CMake and gonna be ignored + set(ignore_rest_cond_ ".*\n") elseif(GENERATOR_TYPE STREQUAL "RPM") set(name_ "Name") set(group_ "Group") @@ -33,6 +36,6 @@ if(GENERATOR_TYPE STREQUAL "RPM") endif() # check package description -checkPackageInfo_("description" "${FOUND_FILE_1}" ".*Description${whitespaces_}:${whitespaces_}Description for pkg_1") -checkPackageInfo_("description" "${FOUND_FILE_2}" ".*Description${whitespaces_}:${whitespaces_}Description for pkg_2") -checkPackageInfo_("description" "${FOUND_FILE_3}" ".*Description${whitespaces_}:${whitespaces_}Description for pkg_3") +checkPackageInfo_("description" "${FOUND_FILE_1}" ".*Description${whitespaces_}:${ignore_rest_cond_}${whitespaces_}Description for pkg_1") +checkPackageInfo_("description" "${FOUND_FILE_2}" ".*Description${whitespaces_}:${ignore_rest_cond_}${whitespaces_}Description for pkg_2") +checkPackageInfo_("description" "${FOUND_FILE_3}" ".*Description${whitespaces_}:${ignore_rest_cond_}${whitespaces_}Description for pkg_3") diff --git a/Tests/RunCMake/Configure/RerunCMake-build3-result.txt b/Tests/RunCMake/Configure/RerunCMake-build3-result.txt new file mode 100644 index 0000000..d197c91 --- /dev/null +++ b/Tests/RunCMake/Configure/RerunCMake-build3-result.txt @@ -0,0 +1 @@ +[^0] diff --git a/Tests/RunCMake/Configure/RerunCMake-build3-stdout.txt b/Tests/RunCMake/Configure/RerunCMake-build3-stdout.txt new file mode 100644 index 0000000..dde2ea6 --- /dev/null +++ b/Tests/RunCMake/Configure/RerunCMake-build3-stdout.txt @@ -0,0 +1 @@ +Rerun error 3 diff --git a/Tests/RunCMake/Configure/RerunCMake-build4-result.txt b/Tests/RunCMake/Configure/RerunCMake-build4-result.txt new file mode 100644 index 0000000..d197c91 --- /dev/null +++ b/Tests/RunCMake/Configure/RerunCMake-build4-result.txt @@ -0,0 +1 @@ +[^0] diff --git a/Tests/RunCMake/Configure/RerunCMake-build4-stdout.txt b/Tests/RunCMake/Configure/RerunCMake-build4-stdout.txt new file mode 100644 index 0000000..b8727e1 --- /dev/null +++ b/Tests/RunCMake/Configure/RerunCMake-build4-stdout.txt @@ -0,0 +1 @@ +Rerun error 4 diff --git a/Tests/RunCMake/Configure/RerunCMake.cmake b/Tests/RunCMake/Configure/RerunCMake.cmake index 5a561bf..c0b0798 100644 --- a/Tests/RunCMake/Configure/RerunCMake.cmake +++ b/Tests/RunCMake/Configure/RerunCMake.cmake @@ -9,3 +9,9 @@ set_property(DIRECTORY APPEND PROPERTY CMAKE_CONFIGURE_DEPENDS ${depend}) file(READ ${depend} content) file(WRITE ${output} "${content}") set_property(DIRECTORY APPEND PROPERTY CMAKE_CONFIGURE_DEPENDS RerunCMake.txt) + +set(error ${CMAKE_CURRENT_BINARY_DIR}/CustomCMakeError.txt) +if(EXISTS ${error}) + file(READ ${error} content) + message(FATAL_ERROR "Rerun error ${content}") +endif() diff --git a/Tests/RunCMake/Configure/RunCMakeTest.cmake b/Tests/RunCMake/Configure/RunCMakeTest.cmake index 4a135be..76d843c 100644 --- a/Tests/RunCMake/Configure/RunCMakeTest.cmake +++ b/Tests/RunCMake/Configure/RunCMakeTest.cmake @@ -14,6 +14,7 @@ set(input "${RunCMake_TEST_BINARY_DIR}/CustomCMakeInput.txt") set(stamp "${RunCMake_TEST_BINARY_DIR}/CustomCMakeStamp.txt") set(depend "${RunCMake_TEST_BINARY_DIR}/CustomCMakeDepend.txt") set(output "${RunCMake_TEST_BINARY_DIR}/CustomCMakeOutput.txt") +set(error "${RunCMake_TEST_BINARY_DIR}/CustomCMakeError.txt") file(WRITE "${input}" "1") file(WRITE "${depend}" "1") run_cmake(RerunCMake) @@ -22,6 +23,22 @@ file(WRITE "${input}" "2") run_cmake_command(RerunCMake-build1 ${CMAKE_COMMAND} --build .) file(WRITE "${depend}" "2") run_cmake_command(RerunCMake-build2 ${CMAKE_COMMAND} --build .) +execute_process(COMMAND ${CMAKE_COMMAND} -E sleep 1) # handle 1s resolution +file(WRITE "${depend}" "3") +file(WRITE "${error}" "3") +set(RunCMake_TEST_OUTPUT_MERGE 1) +run_cmake_command(RerunCMake-build3 ${CMAKE_COMMAND} --build .) +if(MSVC_IDE) + # Make sure that for Visual Studio the error occurs from within the build + # system. + file(REMOVE "${RunCMake_TEST_BINARY_DIR}/CMakeFiles/generate.stamp.list") + file(WRITE "${error}" "4") + # With Visual Studio the error must be on stdout, otherwise the error was not + # emitted by ZERO_CHECK. + set(RunCMake_TEST_OUTPUT_MERGE 0) + run_cmake_command(RerunCMake-build4 ${CMAKE_COMMAND} --build .) +endif() +unset(RunCMake_TEST_OUTPUT_MERGE) unset(RunCMake_TEST_BINARY_DIR) unset(RunCMake_TEST_NO_CLEAN) diff --git a/Tests/RunCMake/FPHSA/CustomMessageConfig.cmake b/Tests/RunCMake/FPHSA/CustomMessageConfig.cmake new file mode 100644 index 0000000..e25db1a --- /dev/null +++ b/Tests/RunCMake/FPHSA/CustomMessageConfig.cmake @@ -0,0 +1 @@ +# pseudo config module diff --git a/Tests/RunCMake/FPHSA/CustomMessageConfigVersion.cmake b/Tests/RunCMake/FPHSA/CustomMessageConfigVersion.cmake new file mode 100644 index 0000000..b7c9e18 --- /dev/null +++ b/Tests/RunCMake/FPHSA/CustomMessageConfigVersion.cmake @@ -0,0 +1,4 @@ +# pseudo find_module + +set (PACKAGE_VERSION 2) +set (PACKAGE_VERSION_UNSUITABLE TRUE) diff --git a/Tests/RunCMake/FPHSA/FindCustomMessage.cmake b/Tests/RunCMake/FPHSA/FindCustomMessage.cmake new file mode 100644 index 0000000..4d67db8 --- /dev/null +++ b/Tests/RunCMake/FPHSA/FindCustomMessage.cmake @@ -0,0 +1,17 @@ +# pseudo find_module + +if (REASON_FAILURE_MESSAGE) + list (PREPEND REASON_FAILURE_MESSAGE "REASON_FAILURE_MESSAGE") +endif() + +include(FindPackageHandleStandardArgs) + +if (CONFIG_MODE) + find_package (CustomMessage QUIET CONFIG HINTS "${CMAKE_MODULE_PATH}") + find_package_handle_standard_args(CustomMessage CONFIG_MODE + ${REASON_FAILURE_MESSAGE}) +else() + find_package_handle_standard_args(CustomMessage REQUIRED_VARS FOOBAR + VERSION_VAR CustomMessage_VERSION + ${REASON_FAILURE_MESSAGE}) +endif() diff --git a/Tests/RunCMake/FPHSA/RunCMakeTest.cmake b/Tests/RunCMake/FPHSA/RunCMakeTest.cmake index dd73cd4..f3e6c3e 100644 --- a/Tests/RunCMake/FPHSA/RunCMakeTest.cmake +++ b/Tests/RunCMake/FPHSA/RunCMakeTest.cmake @@ -39,3 +39,10 @@ unset(RunCMake_DEFAULT_stderr) # check if searching for a version 0 works list(APPEND RunCMake_TEST_OPTIONS "-DCMAKE_MODULE_PATH=${CMAKE_CURRENT_LIST_DIR}" "-DPseudo_VERSION=0") run_cmake(exact_0_matching) + +# check custom error message +set(RunCMake_TEST_OPTIONS "-DCMAKE_MODULE_PATH=${CMAKE_CURRENT_LIST_DIR}" "-DCustomMessage_VERSION=1.2.3.4") +run_cmake(custom_message_1) +set(RunCMake_TEST_OPTIONS "-DCMAKE_MODULE_PATH=${CMAKE_CURRENT_LIST_DIR}" "-DCONFIG_MODE=TRUE") +run_cmake(custom_message_2) +run_cmake(custom_message_3) diff --git a/Tests/RunCMake/FPHSA/custom_message_1-result.txt b/Tests/RunCMake/FPHSA/custom_message_1-result.txt new file mode 100644 index 0000000..d00491f --- /dev/null +++ b/Tests/RunCMake/FPHSA/custom_message_1-result.txt @@ -0,0 +1 @@ +1 diff --git a/Tests/RunCMake/FPHSA/custom_message_1-stderr.txt b/Tests/RunCMake/FPHSA/custom_message_1-stderr.txt new file mode 100644 index 0000000..992fe39 --- /dev/null +++ b/Tests/RunCMake/FPHSA/custom_message_1-stderr.txt @@ -0,0 +1,7 @@ +^CMake Error at .+/Modules/FindPackageHandleStandardArgs.cmake:[0-9]+ \(message\): + Could NOT find CustomMessage \(missing: FOOBAR\) \(found suitable version + "1\.2\.3\.4", minimum required is "1\.2"\) + + Reason given by package: Reason Failure + +Call Stack \(most recent call first\): diff --git a/Tests/RunCMake/FPHSA/custom_message_1.cmake b/Tests/RunCMake/FPHSA/custom_message_1.cmake new file mode 100644 index 0000000..330de50 --- /dev/null +++ b/Tests/RunCMake/FPHSA/custom_message_1.cmake @@ -0,0 +1,4 @@ + +set (REASON_FAILURE_MESSAGE "Reason Failure") + +find_package(CustomMessage 1.2 REQUIRED) diff --git a/Tests/RunCMake/FPHSA/custom_message_2-result.txt b/Tests/RunCMake/FPHSA/custom_message_2-result.txt new file mode 100644 index 0000000..d00491f --- /dev/null +++ b/Tests/RunCMake/FPHSA/custom_message_2-result.txt @@ -0,0 +1 @@ +1 diff --git a/Tests/RunCMake/FPHSA/custom_message_2-stderr.txt b/Tests/RunCMake/FPHSA/custom_message_2-stderr.txt new file mode 100644 index 0000000..4940752 --- /dev/null +++ b/Tests/RunCMake/FPHSA/custom_message_2-stderr.txt @@ -0,0 +1,8 @@ +^CMake Error at .+/Modules/FindPackageHandleStandardArgs.cmake:[0-9]+ \(message\): + Could NOT find CustomMessage \(Required is at least version "1\.2"\), checked + the following files: + + .+/Tests/RunCMake/FPHSA/CustomMessageConfig.cmake \(version 2\) + Reason given by package: Not Found Message + +Call Stack \(most recent call first\): diff --git a/Tests/RunCMake/FPHSA/custom_message_2.cmake b/Tests/RunCMake/FPHSA/custom_message_2.cmake new file mode 100644 index 0000000..a3f2d96 --- /dev/null +++ b/Tests/RunCMake/FPHSA/custom_message_2.cmake @@ -0,0 +1,5 @@ + +unset (REASON_FAILURE_MESSAGE) +set (CustomMessage_NOT_FOUND_MESSAGE "Not Found Message") + +find_package(CustomMessage 1.2 REQUIRED) diff --git a/Tests/RunCMake/FPHSA/custom_message_3-result.txt b/Tests/RunCMake/FPHSA/custom_message_3-result.txt new file mode 100644 index 0000000..d00491f --- /dev/null +++ b/Tests/RunCMake/FPHSA/custom_message_3-result.txt @@ -0,0 +1 @@ +1 diff --git a/Tests/RunCMake/FPHSA/custom_message_3-stderr.txt b/Tests/RunCMake/FPHSA/custom_message_3-stderr.txt new file mode 100644 index 0000000..dc55843 --- /dev/null +++ b/Tests/RunCMake/FPHSA/custom_message_3-stderr.txt @@ -0,0 +1,9 @@ +^CMake Error at .+/Modules/FindPackageHandleStandardArgs.cmake:[0-9]+ \(message\): + Could NOT find CustomMessage \(Required is at least version "1\.2"\), checked + the following files: + + .+/Tests/RunCMake/FPHSA/CustomMessageConfig.cmake \(version 2\) + Reason given by package: Not Found Message + Reason Failure + +Call Stack \(most recent call first\): diff --git a/Tests/RunCMake/FPHSA/custom_message_3.cmake b/Tests/RunCMake/FPHSA/custom_message_3.cmake new file mode 100644 index 0000000..203e012 --- /dev/null +++ b/Tests/RunCMake/FPHSA/custom_message_3.cmake @@ -0,0 +1,5 @@ + +set (REASON_FAILURE_MESSAGE "Reason Failure") +set (CustomMessage_NOT_FOUND_MESSAGE "Not Found Message") + +find_package(CustomMessage 1.2 REQUIRED) diff --git a/Tests/RunCMake/FileAPI/codemodel-v2-check.py b/Tests/RunCMake/FileAPI/codemodel-v2-check.py index 52934f2..66c559d 100644 --- a/Tests/RunCMake/FileAPI/codemodel-v2-check.py +++ b/Tests/RunCMake/FileAPI/codemodel-v2-check.py @@ -4838,7 +4838,20 @@ def gen_check_targets(c, g, inSource): { "path": "^.*/Tests/RunCMake/FileAPI/FileAPIExternalBuild$", "isSystem": None, - "backtrace": None, + "backtrace": [ + { + "file": "^.*/Tests/RunCMake/FileAPIExternalSource/CMakeLists\\.txt$", + "line": 10, + "command": "set_property", + "hasParent": True, + }, + { + "file": "^.*/Tests/RunCMake/FileAPIExternalSource/CMakeLists\\.txt$", + "line": None, + "command": None, + "hasParent": False, + }, + ], }, { "path": "^.*/Tests/RunCMake/FileAPIExternalSource$", @@ -4862,11 +4875,37 @@ def gen_check_targets(c, g, inSource): "defines": [ { "define": "EMPTY_C=1", - "backtrace": None, + "backtrace": [ + { + "file": "^.*/Tests/RunCMake/FileAPIExternalSource/CMakeLists\\.txt$", + "line": 9, + "command": "set_property", + "hasParent": True, + }, + { + "file": "^.*/Tests/RunCMake/FileAPIExternalSource/CMakeLists\\.txt$", + "line": None, + "command": None, + "hasParent": False, + }, + ], }, { "define": "SRC_DUMMY", - "backtrace": None, + "backtrace": [ + { + "file": "^.*/Tests/RunCMake/FileAPIExternalSource/CMakeLists\\.txt$", + "line": 9, + "command": "set_property", + "hasParent": True, + }, + { + "file": "^.*/Tests/RunCMake/FileAPIExternalSource/CMakeLists\\.txt$", + "line": None, + "command": None, + "hasParent": False, + }, + ], }, { "define": "GENERATED_EXE=1", @@ -4903,7 +4942,25 @@ def gen_check_targets(c, g, inSource): ], }, ], - "compileCommandFragments": None, + "compileCommandFragments": [ + { + "fragment" : "SRC_COMPILE_OPTIONS_DUMMY", + "backtrace": [ + { + "file": "^.*/Tests/RunCMake/FileAPIExternalSource/CMakeLists\\.txt$", + "line": 13, + "command": "set_source_files_properties", + "hasParent": True, + }, + { + "file" : "^.*/Tests/RunCMake/FileAPIExternalSource/CMakeLists\\.txt$", + "line": None, + "command": None, + "hasParent": False, + }, + ], + } + ], }, { "language": "CXX", diff --git a/Tests/RunCMake/FileAPIExternalSource/CMakeLists.txt b/Tests/RunCMake/FileAPIExternalSource/CMakeLists.txt index f5670a7..b3ca660 100644 --- a/Tests/RunCMake/FileAPIExternalSource/CMakeLists.txt +++ b/Tests/RunCMake/FileAPIExternalSource/CMakeLists.txt @@ -10,3 +10,4 @@ set_property(SOURCE empty.c PROPERTY COMPILE_DEFINITIONS EMPTY_C=1 SRC_DUMMY) set_property(SOURCE empty.c PROPERTY INCLUDE_DIRECTORIES "${CMAKE_CURRENT_BINARY_DIR}") target_include_directories(generated_exe SYSTEM PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}") target_compile_definitions(generated_exe PRIVATE GENERATED_EXE=1 -DTGT_DUMMY) +set_source_files_properties(empty.c PROPERTIES COMPILE_OPTIONS SRC_COMPILE_OPTIONS_DUMMY) diff --git a/Tests/RunCMake/PrecompileHeaders/DisabledPch.cmake b/Tests/RunCMake/PrecompileHeaders/DisabledPch.cmake index ee47980..59ee14b 100644 --- a/Tests/RunCMake/PrecompileHeaders/DisabledPch.cmake +++ b/Tests/RunCMake/PrecompileHeaders/DisabledPch.cmake @@ -4,7 +4,7 @@ project(DisabledPch C) add_library(foo foo.c) target_include_directories(foo PUBLIC include) target_precompile_headers(foo PUBLIC - foo.h + include/foo.h <stdio.h> \"string.h\" ) diff --git a/Tests/RunCMake/PrecompileHeaders/PchInterface-check.cmake b/Tests/RunCMake/PrecompileHeaders/PchInterface-check.cmake index cbd6ede..caeb22b 100644 --- a/Tests/RunCMake/PrecompileHeaders/PchInterface-check.cmake +++ b/Tests/RunCMake/PrecompileHeaders/PchInterface-check.cmake @@ -18,19 +18,14 @@ endif() file(STRINGS ${foo_pch_header} foo_pch_header_strings) -if (NOT "#include \"foo.h\"" IN_LIST foo_pch_header_strings OR - NOT "#include <stdio.h>" IN_LIST foo_pch_header_strings OR - NOT "#include \"string.h\"" IN_LIST foo_pch_header_strings) - set(RunCMake_TEST_FAILED "Generated foo pch header ${foo_pch_header} has bad content") +if (NOT foo_pch_header_strings MATCHES ";#include \"[^\"]*PrecompileHeaders/include/foo.h\";#include \"foo2.h\";#include <stdio.h>;#include \"string.h\"(;|$)") + set(RunCMake_TEST_FAILED "Generated foo pch header\n ${foo_pch_header}\nhas bad content:\n ${foo_pch_header_strings}") return() endif() file(STRINGS ${foobar_pch_header} foobar_pch_header_strings) -if (NOT "#include \"foo.h\"" IN_LIST foobar_pch_header_strings OR - NOT "#include <stdio.h>" IN_LIST foobar_pch_header_strings OR - NOT "#include \"string.h\"" IN_LIST foobar_pch_header_strings OR - NOT "#include \"bar.h\"" IN_LIST foobar_pch_header_strings) - set(RunCMake_TEST_FAILED "Generated foobar pch header ${foobar_pch_header} has bad content") +if (NOT foobar_pch_header_strings MATCHES ";#include \"[^\"]*PrecompileHeaders/include/foo.h\";#include \"foo2.h\";#include <stdio.h>;#include \"string.h\";#include \"[^\"]*PrecompileHeaders/include/bar.h\"(;|$)") + set(RunCMake_TEST_FAILED "Generated foobar pch header\n ${foobar_pch_header}\nhas bad content:\n ${foobar_pch_header_strings}") return() endif() diff --git a/Tests/RunCMake/PrecompileHeaders/PchInterface.cmake b/Tests/RunCMake/PrecompileHeaders/PchInterface.cmake index 9041b09..a1e0792 100644 --- a/Tests/RunCMake/PrecompileHeaders/PchInterface.cmake +++ b/Tests/RunCMake/PrecompileHeaders/PchInterface.cmake @@ -4,14 +4,15 @@ project(PchInterface C) add_library(foo foo.c) target_include_directories(foo PUBLIC include) target_precompile_headers(foo PUBLIC - foo.h + include/foo.h + \"foo2.h\" <stdio.h> \"string.h\" ) add_library(bar INTERFACE) target_include_directories(bar INTERFACE include) -target_precompile_headers(bar INTERFACE bar.h) +target_precompile_headers(bar INTERFACE include/bar.h) add_executable(foobar foobar.c) target_link_libraries(foobar foo bar) diff --git a/Tests/RunCMake/PrecompileHeaders/foo.c b/Tests/RunCMake/PrecompileHeaders/foo.c index 974a248..85ea230 100644 --- a/Tests/RunCMake/PrecompileHeaders/foo.c +++ b/Tests/RunCMake/PrecompileHeaders/foo.c @@ -1,6 +1,12 @@ #include "foo.h" +#include "foo2.h" int foo() { return 0; } + +int foo2() +{ + return 0; +} diff --git a/Tests/RunCMake/PrecompileHeaders/foobar.c b/Tests/RunCMake/PrecompileHeaders/foobar.c index 6dbf8ce..7a135ea 100644 --- a/Tests/RunCMake/PrecompileHeaders/foobar.c +++ b/Tests/RunCMake/PrecompileHeaders/foobar.c @@ -1,7 +1,8 @@ #include "bar.h" #include "foo.h" +#include "foo2.h" int main() { - return foo() + bar(); + return foo() + foo2() + bar(); } diff --git a/Tests/RunCMake/PrecompileHeaders/include/foo2.h b/Tests/RunCMake/PrecompileHeaders/include/foo2.h new file mode 100644 index 0000000..4bf9c81 --- /dev/null +++ b/Tests/RunCMake/PrecompileHeaders/include/foo2.h @@ -0,0 +1,6 @@ +#ifndef foo2_h +#define foo2_h + +int foo2(void); + +#endif diff --git a/Tests/RunCMake/RunCMake.cmake b/Tests/RunCMake/RunCMake.cmake index 568bdf8..da4d1e5 100644 --- a/Tests/RunCMake/RunCMake.cmake +++ b/Tests/RunCMake/RunCMake.cmake @@ -92,9 +92,12 @@ function(run_cmake test) set(maybe_input_file "") endif() if(RunCMake_TEST_COMMAND) + if(NOT RunCMake_TEST_COMMAND_WORKING_DIRECTORY) + set(RunCMake_TEST_COMMAND_WORKING_DIRECTORY "${RunCMake_TEST_BINARY_DIR}") + endif() execute_process( COMMAND ${RunCMake_TEST_COMMAND} - WORKING_DIRECTORY "${RunCMake_TEST_BINARY_DIR}" + WORKING_DIRECTORY "${RunCMake_TEST_COMMAND_WORKING_DIRECTORY}" OUTPUT_VARIABLE actual_stdout ERROR_VARIABLE ${actual_stderr_var} RESULT_VARIABLE actual_result diff --git a/Tests/RunCMake/add_custom_command/AppendNotOutput-stderr.txt b/Tests/RunCMake/add_custom_command/AppendNotOutput-stderr.txt index cd542d8..b7ee23a 100644 --- a/Tests/RunCMake/add_custom_command/AppendNotOutput-stderr.txt +++ b/Tests/RunCMake/add_custom_command/AppendNotOutput-stderr.txt @@ -1,7 +1,7 @@ CMake Error at AppendNotOutput.cmake:1 \(add_custom_command\): add_custom_command given APPEND option with output - .*RunCMake/add_custom_command/AppendNotOutput-build/out.* + .*RunCMake/add_custom_command/AppendNotOutput-build/out which is not already a custom command output. Call Stack \(most recent call first\): diff --git a/Tests/RunCMake/add_custom_command/BadByproduct-result.txt b/Tests/RunCMake/add_custom_command/BadByproduct-result.txt new file mode 100644 index 0000000..d00491f --- /dev/null +++ b/Tests/RunCMake/add_custom_command/BadByproduct-result.txt @@ -0,0 +1 @@ +1 diff --git a/Tests/RunCMake/add_custom_command/BadByproduct-stderr.txt b/Tests/RunCMake/add_custom_command/BadByproduct-stderr.txt new file mode 100644 index 0000000..086e397 --- /dev/null +++ b/Tests/RunCMake/add_custom_command/BadByproduct-stderr.txt @@ -0,0 +1,36 @@ +CMake Error at BadByproduct.cmake:2 \(add_custom_command\): + add_custom_command called with BYPRODUCTS containing a "#". This character + is not allowed. +Call Stack \(most recent call first\): + CMakeLists.txt:3 \(include\) + + +CMake Error at BadByproduct.cmake:3 \(add_custom_command\): + add_custom_command called with BYPRODUCTS containing a "<". This character + is not allowed. +Call Stack \(most recent call first\): + CMakeLists.txt:3 \(include\) + + +CMake Error at BadByproduct.cmake:4 \(add_custom_command\): + add_custom_command called with BYPRODUCTS containing a ">". This character + is not allowed. +Call Stack \(most recent call first\): + CMakeLists.txt:3 \(include\) + + +CMake Error at BadByproduct.cmake:5 \(add_custom_command\): + add_custom_command called with BYPRODUCTS containing a "<". This character + is not allowed. +Call Stack \(most recent call first\): + CMakeLists.txt:3 \(include\) + + +CMake Error at BadByproduct.cmake:6 \(add_custom_command\): + add_custom_command attempted to have a file + + .*RunCMake/add_custom_command/f + + in a source directory as an output of custom command. +Call Stack \(most recent call first\): + CMakeLists.txt:3 \(include\) diff --git a/Tests/RunCMake/add_custom_command/BadByproduct.cmake b/Tests/RunCMake/add_custom_command/BadByproduct.cmake new file mode 100644 index 0000000..91bca52 --- /dev/null +++ b/Tests/RunCMake/add_custom_command/BadByproduct.cmake @@ -0,0 +1,6 @@ +set(CMAKE_DISABLE_SOURCE_CHANGES ON) +add_custom_command(OUTPUT a BYPRODUCTS "a#") +add_custom_command(OUTPUT b BYPRODUCTS "a<") +add_custom_command(OUTPUT c BYPRODUCTS "a>") +add_custom_command(OUTPUT d BYPRODUCTS "$<CONFIG>/#") +add_custom_command(OUTPUT e BYPRODUCTS ${CMAKE_CURRENT_SOURCE_DIR}/f) diff --git a/Tests/RunCMake/add_custom_command/BadOutput-result.txt b/Tests/RunCMake/add_custom_command/BadOutput-result.txt new file mode 100644 index 0000000..d00491f --- /dev/null +++ b/Tests/RunCMake/add_custom_command/BadOutput-result.txt @@ -0,0 +1 @@ +1 diff --git a/Tests/RunCMake/add_custom_command/BadOutput-stderr.txt b/Tests/RunCMake/add_custom_command/BadOutput-stderr.txt new file mode 100644 index 0000000..731e58d --- /dev/null +++ b/Tests/RunCMake/add_custom_command/BadOutput-stderr.txt @@ -0,0 +1,36 @@ +CMake Error at BadOutput.cmake:2 \(add_custom_command\): + add_custom_command called with OUTPUT containing a "#". This character is + not allowed. +Call Stack \(most recent call first\): + CMakeLists.txt:3 \(include\) + + +CMake Error at BadOutput.cmake:3 \(add_custom_command\): + add_custom_command called with OUTPUT containing a "<". This character is + not allowed. +Call Stack \(most recent call first\): + CMakeLists.txt:3 \(include\) + + +CMake Error at BadOutput.cmake:4 \(add_custom_command\): + add_custom_command called with OUTPUT containing a ">". This character is + not allowed. +Call Stack \(most recent call first\): + CMakeLists.txt:3 \(include\) + + +CMake Error at BadOutput.cmake:5 \(add_custom_command\): + add_custom_command called with OUTPUT containing a "<". This character is + not allowed. +Call Stack \(most recent call first\): + CMakeLists.txt:3 \(include\) + + +CMake Error at BadOutput.cmake:6 \(add_custom_command\): + add_custom_command attempted to have a file + + .*RunCMake/add_custom_command/e + + in a source directory as an output of custom command. +Call Stack \(most recent call first\): + CMakeLists.txt:3 \(include\) diff --git a/Tests/RunCMake/add_custom_command/BadOutput.cmake b/Tests/RunCMake/add_custom_command/BadOutput.cmake new file mode 100644 index 0000000..6875fe9 --- /dev/null +++ b/Tests/RunCMake/add_custom_command/BadOutput.cmake @@ -0,0 +1,6 @@ +set(CMAKE_DISABLE_SOURCE_CHANGES ON) +add_custom_command(OUTPUT "a#" COMMAND a) +add_custom_command(OUTPUT "a<" COMMAND b) +add_custom_command(OUTPUT "a>" COMMAND c) +add_custom_command(OUTPUT "$<CONFIG>/#" COMMAND d) +add_custom_command(OUTPUT ${CMAKE_CURRENT_SOURCE_DIR}/e COMMAND f) diff --git a/Tests/RunCMake/add_custom_command/RunCMakeTest.cmake b/Tests/RunCMake/add_custom_command/RunCMakeTest.cmake index 270df2f..96642fa 100644 --- a/Tests/RunCMake/add_custom_command/RunCMakeTest.cmake +++ b/Tests/RunCMake/add_custom_command/RunCMakeTest.cmake @@ -4,6 +4,8 @@ run_cmake(AppendLiteralQuotes) run_cmake(AppendNoOutput) run_cmake(AppendNotOutput) run_cmake(BadArgument) +run_cmake(BadByproduct) +run_cmake(BadOutput) run_cmake(GeneratedProperty) run_cmake(LiteralQuotes) run_cmake(NoArguments) diff --git a/Tests/RunCMake/add_custom_target/BadByproduct-result.txt b/Tests/RunCMake/add_custom_target/BadByproduct-result.txt new file mode 100644 index 0000000..d00491f --- /dev/null +++ b/Tests/RunCMake/add_custom_target/BadByproduct-result.txt @@ -0,0 +1 @@ +1 diff --git a/Tests/RunCMake/add_custom_target/BadByproduct-stderr.txt b/Tests/RunCMake/add_custom_target/BadByproduct-stderr.txt new file mode 100644 index 0000000..0f58550 --- /dev/null +++ b/Tests/RunCMake/add_custom_target/BadByproduct-stderr.txt @@ -0,0 +1,36 @@ +CMake Error at BadByproduct.cmake:2 \(add_custom_target\): + add_custom_target called with BYPRODUCTS containing a "#". This character + is not allowed. +Call Stack \(most recent call first\): + CMakeLists.txt:3 \(include\) + + +CMake Error at BadByproduct.cmake:3 \(add_custom_target\): + add_custom_target called with BYPRODUCTS containing a "<". This character + is not allowed. +Call Stack \(most recent call first\): + CMakeLists.txt:3 \(include\) + + +CMake Error at BadByproduct.cmake:4 \(add_custom_target\): + add_custom_target called with BYPRODUCTS containing a ">". This character + is not allowed. +Call Stack \(most recent call first\): + CMakeLists.txt:3 \(include\) + + +CMake Error at BadByproduct.cmake:5 \(add_custom_target\): + add_custom_target called with BYPRODUCTS containing a "<". This character + is not allowed. +Call Stack \(most recent call first\): + CMakeLists.txt:3 \(include\) + + +CMake Error at BadByproduct.cmake:6 \(add_custom_target\): + add_custom_target attempted to have a file + + .*RunCMake/add_custom_target/j + + in a source directory as an output of custom command. +Call Stack \(most recent call first\): + CMakeLists.txt:3 \(include\) diff --git a/Tests/RunCMake/add_custom_target/BadByproduct.cmake b/Tests/RunCMake/add_custom_target/BadByproduct.cmake new file mode 100644 index 0000000..963d641 --- /dev/null +++ b/Tests/RunCMake/add_custom_target/BadByproduct.cmake @@ -0,0 +1,6 @@ +set(CMAKE_DISABLE_SOURCE_CHANGES ON) +add_custom_target(a BYPRODUCTS "a#" COMMAND b) +add_custom_target(c BYPRODUCTS "a<" COMMAND d) +add_custom_target(e BYPRODUCTS "a>" COMMAND f) +add_custom_target(g BYPRODUCTS "$<CONFIG>/#" COMMAND h) +add_custom_target(i BYPRODUCTS ${CMAKE_CURRENT_SOURCE_DIR}/j COMMAND k) diff --git a/Tests/RunCMake/add_custom_target/RunCMakeTest.cmake b/Tests/RunCMake/add_custom_target/RunCMakeTest.cmake index 49c7d3e..f5d5dd2 100644 --- a/Tests/RunCMake/add_custom_target/RunCMakeTest.cmake +++ b/Tests/RunCMake/add_custom_target/RunCMakeTest.cmake @@ -1,11 +1,12 @@ include(RunCMake) -run_cmake(CommandExpandsEmpty) -run_cmake(GeneratedProperty) -run_cmake(NoArguments) +run_cmake(BadByproduct) run_cmake(BadTargetName) run_cmake(ByproductsNoCommand) +run_cmake(CommandExpandsEmpty) +run_cmake(GeneratedProperty) run_cmake(LiteralQuotes) +run_cmake(NoArguments) run_cmake(UsesTerminalNoCommand) function(run_TargetOrder) diff --git a/Tests/RunCMake/add_subdirectory/ExcludeFromAll-build-sub-stderr.txt b/Tests/RunCMake/add_subdirectory/ExcludeFromAll-build-sub-stderr.txt new file mode 100644 index 0000000..8d98f9d --- /dev/null +++ b/Tests/RunCMake/add_subdirectory/ExcludeFromAll-build-sub-stderr.txt @@ -0,0 +1 @@ +.* diff --git a/Tests/RunCMake/add_subdirectory/ExcludeFromAll.cmake b/Tests/RunCMake/add_subdirectory/ExcludeFromAll.cmake index f686005..ff676a6 100644 --- a/Tests/RunCMake/add_subdirectory/ExcludeFromAll.cmake +++ b/Tests/RunCMake/add_subdirectory/ExcludeFromAll.cmake @@ -4,3 +4,11 @@ add_subdirectory(ExcludeFromAll EXCLUDE_FROM_ALL) add_executable(main main.cpp) target_link_libraries(main PRIVATE foo) + +file(GENERATE OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/check-$<LOWER_CASE:$<CONFIG>>.cmake CONTENT " +set(main_exe \"$<TARGET_FILE:main>\") +set(foo_lib \"$<TARGET_FILE:foo>\") +set(bar_lib \"$<TARGET_FILE:bar>\") +set(zot_lib \"$<TARGET_FILE:zot>\") +set(subinc_lib \"$<TARGET_FILE:subinc>\") +") diff --git a/Tests/RunCMake/add_subdirectory/ExcludeFromAll/CMakeLists.txt b/Tests/RunCMake/add_subdirectory/ExcludeFromAll/CMakeLists.txt index 9e6462b..790da54 100644 --- a/Tests/RunCMake/add_subdirectory/ExcludeFromAll/CMakeLists.txt +++ b/Tests/RunCMake/add_subdirectory/ExcludeFromAll/CMakeLists.txt @@ -1,20 +1,11 @@ -add_library(bar STATIC bar.cpp) +project(ExcludeFromAllSub NONE) -add_library(foo STATIC foo.cpp) - -add_library(baz STATIC foo.cpp) -set_target_properties(baz PROPERTIES EXCLUDE_FROM_ALL OFF) - -file(GENERATE - OUTPUT "${CMAKE_BINARY_DIR}/main.txt" - CONTENT "$<TARGET_FILE_NAME:main>") +add_library(bar STATIC EXCLUDE_FROM_ALL bar.cpp) -file(GENERATE - OUTPUT "${CMAKE_BINARY_DIR}/bar.txt" - CONTENT "$<TARGET_FILE_NAME:bar>") - -file(GENERATE - OUTPUT "${CMAKE_BINARY_DIR}/baz.txt" - CONTENT "$<TARGET_FILE_NAME:baz>") +add_library(zot STATIC zot.cpp) +add_library(foo STATIC foo.cpp) target_include_directories(foo PUBLIC .) + +add_library(subinc STATIC subinc.cpp) +set_target_properties(subinc PROPERTIES EXCLUDE_FROM_ALL OFF) diff --git a/Tests/RunCMake/add_subdirectory/ExcludeFromAll/check-sub.cmake b/Tests/RunCMake/add_subdirectory/ExcludeFromAll/check-sub.cmake new file mode 100644 index 0000000..297ad1e --- /dev/null +++ b/Tests/RunCMake/add_subdirectory/ExcludeFromAll/check-sub.cmake @@ -0,0 +1,32 @@ +if(EXISTS ${RunCMake_TEST_BINARY_DIR}/check-debug.cmake) + include(${RunCMake_TEST_BINARY_DIR}/check-debug.cmake) + if(RunCMake_TEST_FAILED) + return() + endif() + + foreach(file + "${foo_lib}" + "${subinc_lib}" + "${zot_lib}" + ) + if(NOT EXISTS "${file}") + set(RunCMake_TEST_FAILED + "Artifact should exist but is missing:\n ${file}") + return() + endif() + endforeach() + foreach(file + "${main_exe}" + "${bar_lib}" + ) + if(EXISTS "${file}") + set(RunCMake_TEST_FAILED + "Artifact should be missing but exists:\n ${file}") + return() + endif() + endforeach() +else() + set(RunCMake_TEST_FAILED " + '${RunCMake_TEST_BINARY_DIR}/check-debug.cmake' missing +") +endif() diff --git a/Tests/RunCMake/add_subdirectory/ExcludeFromAll/check.cmake b/Tests/RunCMake/add_subdirectory/ExcludeFromAll/check.cmake index 14ec482..433c032 100644 --- a/Tests/RunCMake/add_subdirectory/ExcludeFromAll/check.cmake +++ b/Tests/RunCMake/add_subdirectory/ExcludeFromAll/check.cmake @@ -1,44 +1,35 @@ -# Use globbing to check if exes / libs were built because determining -# exactly where these files will live inside a CMake -P script is -# pretty challenging. - -file(READ "${RunCMake_TEST_BINARY_DIR}/main.txt" main_exe) -file(READ "${RunCMake_TEST_BINARY_DIR}/bar.txt" bar_lib) -file(READ "${RunCMake_TEST_BINARY_DIR}/baz.txt" baz_lib) - -set(found_main FALSE) -file(GLOB_RECURSE files - LIST_DIRECTORIES FALSE - RELATIVE "${RunCMake_TEST_BINARY_DIR}" - "${RunCMake_TEST_BINARY_DIR}/*") -foreach (file IN LISTS files) - if (file MATCHES "${main_exe}") - set(found_main TRUE) +if(EXISTS ${RunCMake_TEST_BINARY_DIR}/check-debug.cmake) + include(${RunCMake_TEST_BINARY_DIR}/check-debug.cmake) + if(RunCMake_TEST_FAILED) + return() endif() -endforeach() -if (NOT found_main) - set(RunCMake_TEST_FAILED "'main' missing from ${RunCMake_TEST_BINARY_DIR}") -endif() -set(found_bar FALSE) -set(found_baz FALSE) -file(GLOB_RECURSE files - LIST_DIRECTORIES FALSE - RELATIVE "${RunCMake_TEST_BINARY_DIR}/ExcludeFromAll" - "${RunCMake_TEST_BINARY_DIR}/ExcludeFromAll/*") -foreach (file IN LISTS files) - if (file MATCHES "${bar_lib}") - set(found_bar TRUE) - endif() - if (file MATCHES "${baz_lib}") - set(found_baz TRUE) - endif() -endforeach() -if (found_bar) - set(RunCMake_TEST_FAILED - "'bar' was not excluded from ${RunCMake_TEST_BINARY_DIR}/ExcludeFromAll") -endif() -if (NOT found_baz) - set(RunCMake_TEST_FAILED - "'baz' missing from ${RunCMake_TEST_BINARY_DIR}/ExcludeFromAll") + foreach(file + "${foo_lib}" + "${subinc_lib}" + "${main_exe}" + ) + if(EXISTS "${file}") + # Remove for next step of test. + file(REMOVE "${file}") + else() + set(RunCMake_TEST_FAILED + "Artifact should exist but is missing:\n ${file}") + return() + endif() + endforeach() + foreach(file + "${zot_lib}" + "${bar_lib}" + ) + if(EXISTS "${file}") + set(RunCMake_TEST_FAILED + "Artifact should be missing but exists:\n ${file}") + return() + endif() + endforeach() +else() + set(RunCMake_TEST_FAILED " + '${RunCMake_TEST_BINARY_DIR}/check-debug.cmake' missing +") endif() diff --git a/Tests/RunCMake/add_subdirectory/ExcludeFromAll/subinc.cpp b/Tests/RunCMake/add_subdirectory/ExcludeFromAll/subinc.cpp new file mode 100644 index 0000000..e9faacd --- /dev/null +++ b/Tests/RunCMake/add_subdirectory/ExcludeFromAll/subinc.cpp @@ -0,0 +1,4 @@ +int subinc() +{ + return 0; +} diff --git a/Tests/RunCMake/add_subdirectory/ExcludeFromAll/zot.cpp b/Tests/RunCMake/add_subdirectory/ExcludeFromAll/zot.cpp new file mode 100644 index 0000000..ba7e966 --- /dev/null +++ b/Tests/RunCMake/add_subdirectory/ExcludeFromAll/zot.cpp @@ -0,0 +1,4 @@ +int zot() +{ + return 0; +} diff --git a/Tests/RunCMake/add_subdirectory/RunCMakeTest.cmake b/Tests/RunCMake/add_subdirectory/RunCMakeTest.cmake index 781e483..951e03c 100644 --- a/Tests/RunCMake/add_subdirectory/RunCMakeTest.cmake +++ b/Tests/RunCMake/add_subdirectory/RunCMakeTest.cmake @@ -27,14 +27,34 @@ run_cmake_install(CMP0082-OLD -DCMP0082_VALUE=OLD) run_cmake_install(CMP0082-NEW -DCMP0082_VALUE=NEW) set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/ExcludeFromAll-build) -set(RunCMake_TEST_NO_CLEAN 1) - -file(REMOVE_RECURSE "${RunCMake_TEST_BINARY_DIR}") -file(MAKE_DIRECTORY "${RunCMake_TEST_BINARY_DIR}") - +if(NOT RunCMake_GENERATOR_IS_MULTI_CONFIG) + set(RunCMake_TEST_OPTIONS -DCMAKE_BUILD_TYPE=Debug) +endif() run_cmake(ExcludeFromAll) +set(RunCMake_TEST_NO_CLEAN 1) set(RunCMake-check-file ExcludeFromAll/check.cmake) -run_cmake_command(ExcludeFromAll-build ${CMAKE_COMMAND} --build .) - -unset(RunCMake_TEST_BINARY_DIR) +run_cmake_command(ExcludeFromAll-build ${CMAKE_COMMAND} --build . --config Debug) +if(RunCMake_GENERATOR STREQUAL "Ninja") + if(WIN32) + set(slash [[\]]) + else() + set(slash [[/]]) + endif() + set(RunCMake-check-file ExcludeFromAll/check-sub.cmake) + run_cmake_command(ExcludeFromAll-build-sub ${CMAKE_COMMAND} --build . --target "ExcludeFromAll${slash}all") +elseif(RunCMake_GENERATOR MATCHES "Make") + set(RunCMake-check-file ExcludeFromAll/check-sub.cmake) + set(RunCMake_TEST_COMMAND_WORKING_DIRECTORY ${RunCMake_BINARY_DIR}/ExcludeFromAll-build/ExcludeFromAll) + run_cmake_command(ExcludeFromAll-build-sub "${RunCMake_MAKE_PROGRAM}") +elseif(RunCMake_GENERATOR MATCHES "^Visual Studio [1-9][0-9]") + set(RunCMake-check-file ExcludeFromAll/check-sub.cmake) + run_cmake_command(ExcludeFromAll-build-sub ${CMAKE_COMMAND} --build ExcludeFromAll --config Debug) +elseif(RunCMake_GENERATOR STREQUAL "Xcode") + set(RunCMake-check-file ExcludeFromAll/check-sub.cmake) + set(RunCMake_TEST_COMMAND_WORKING_DIRECTORY ${RunCMake_BINARY_DIR}/ExcludeFromAll-build/ExcludeFromAll) + run_cmake_command(ExcludeFromAll-build-sub xcodebuild -configuration Debug) +endif() +unset(RunCMake-check-file) unset(RunCMake_TEST_NO_CLEAN) +unset(RunCMake_TEST_OPTIONS) +unset(RunCMake_TEST_BINARY_DIR) diff --git a/Tests/TryCompile/CMakeLists.txt b/Tests/TryCompile/CMakeLists.txt index 54e96a2..498e556 100644 --- a/Tests/TryCompile/CMakeLists.txt +++ b/Tests/TryCompile/CMakeLists.txt @@ -313,6 +313,60 @@ if(CMAKE_C_COMPILER_ID STREQUAL "GNU") TEST_ASSERT(C_STRICT_PROTOTYPES "CHECK_C_COMPILER_FLAG failed -Werror -Wstrict-prototypes") endif() +######################################################################### +# +# Test that the CHECK_OBJCC_SOURCE_COMPILES, CHECK_OBJCXX_SOURCE_COMPILES +# CHECK_OBJC_SOURCE_RUNS and CHECK_OBJCXX_SOURCE_RUNS macros work + +if (APPLE) + enable_language(OBJC) + enable_language(OBJCXX) + + include(CheckOBJCSourceCompiles) + include(CheckOBJCXXSourceCompiles) + include(CheckOBJCSourceRuns) + include(CheckOBJCXXSourceRuns) + + CHECK_OBJC_SOURCE_COMPILES("I don't build in Objective-C" OBJC_BUILD_SHOULD_FAIL) + CHECK_OBJC_SOURCE_COMPILES("int main() { return 0; }" SIMPLE_OBJC_BUILD_SHOULD_WORK) + + TEST_FAIL(OBJC_BUILD_SHOULD_FAIL "CHECK_OBJC_SOURCE_COMPILES() succeeded, but should have failed") + TEST_ASSERT(SIMPLE_OBJC_BUILD_SHOULD_WORK "CHECK_OBJC_SOURCE_COMPILES() failed, but should have succeeded") + + set(CMAKE_REQUIRED_LIBRARIES "-framework Foundation") + + CHECK_OBJC_SOURCE_COMPILES("#import <Foundation/Foundation.h>\nint main()\n{\nNSObject *foo;\nreturn 0;\n}\n" OBJC_BUILD_SHOULD_WORK) + CHECK_OBJC_SOURCE_RUNS("int main() { return 2; }" SIMPLE_OBJC_RUN_SHOULD_FAIL) + CHECK_OBJC_SOURCE_RUNS("int main() { return 0; }" SIMPLE_OBJC_RUN_SHOULD_WORK) + CHECK_OBJC_SOURCE_RUNS("#import <Foundation/Foundation.h>\nint main()\n{\nNSObject *foo;\nreturn 2;\n}\n" OBJC_RUN_SHOULD_FAIL) + CHECK_OBJC_SOURCE_RUNS("#import <Foundation/Foundation.h>\nint main()\n{\nNSObject *foo;\nreturn 0;\n}\n" OBJC_RUN_SHOULD_WORK) + + TEST_ASSERT(OBJC_BUILD_SHOULD_WORK "CHECK_OBJC_SOURCE_COMPILES() failed, but should have succeeded") + TEST_FAIL(SIMPLE_OBJC_RUN_SHOULD_FAIL "CHECK_OBJC_SOURC_RUNS() succeeds, but should have failed") + TEST_ASSERT(SIMPLE_OBJC_RUN_SHOULD_WORK "CHECK_OBJC_SOURCE_RUNS() failed, but should have succeeded") + TEST_FAIL(OBJC_RUN_SHOULD_FAIL "CHECK_OBJC_SOURCE_RUNS() succeeds, but should have failed") + TEST_ASSERT(OBJC_RUN_SHOULD_WORK "CHECK_OBJC_SOURCE_RUNS() failed, but should have succeeded") + + + CHECK_OBJCXX_SOURCE_COMPILES("I don't build in Objective-C++" OBJCXX_BUILD_SHOULD_FAIL) + CHECK_OBJCXX_SOURCE_COMPILES("int main() { return 0; }" SIMPLE_OBJCXX_BUILD_SHOULD_WORK) + + TEST_FAIL(OBJCXX_BUILD_SHOULD_FAIL "CHECK_OBJCXX_SOURCE_COMPILES() succeeded, but should have failed") + TEST_ASSERT(SIMPLE_OBJCXX_BUILD_SHOULD_WORK "CHECK_OBJCXX_SOURCE_COMPILES() failed, but should have succeeded") + + CHECK_OBJCXX_SOURCE_COMPILES("#import <Foundation/Foundation.h>\n#include <iostream>\nint main()\n{\nNSObject *foo;\nstd::cout << \"Hello\" << std::endl;\nreturn 0;\n}\n" OBJCXX_BUILD_SHOULD_WORK) + CHECK_OBJCXX_SOURCE_RUNS("int main() { return 2; }" SIMPLE_OBJCXX_RUN_SHOULD_FAIL) + CHECK_OBJCXX_SOURCE_RUNS("int main() { return 0; }" SIMPLE_OBJCXX_RUN_SHOULD_WORK) + CHECK_OBJCXX_SOURCE_RUNS("#import <Foundation/Foundation.h>\n#include <vector>\nint main()\n{\nNSObject *foo;\nstd::vector<int> bar;\nreturn 2;\n}\n" OBJCXX_RUN_SHOULD_FAIL) + CHECK_OBJCXX_SOURCE_RUNS("#import <Foundation/Foundation.h>\n#include <vector>\nint main()\n{\nNSObject *foo;\nstd::vector<int> bar;\nreturn 0;\n}\n" OBJCXX_RUN_SHOULD_WORK) + + TEST_ASSERT(OBJCXX_BUILD_SHOULD_WORK "CHECK_OBJCXX_SOURCE_COMPILES() failed, but should have succeeded") + TEST_FAIL(SIMPLE_OBJCXX_RUN_SHOULD_FAIL "CHECK_OBJCXX_SOURC_RUNS() succeeds, but should have failed") + TEST_ASSERT(SIMPLE_OBJCXX_RUN_SHOULD_WORK "CHECK_OBJCXX_SOURCE_RUNS() failed, but should have succeeded") + TEST_FAIL(OBJCXX_RUN_SHOULD_FAIL "CHECK_OBJCXX_SOURCE_RUNS() succeeds, but should have failed") + TEST_ASSERT(OBJCXX_RUN_SHOULD_WORK "CHECK_OBJCXX_SOURCE_RUNS() failed, but should have succeeded") +endif() + ####################################################################### # # also test that the check_prototype_definition macro works diff --git a/Tests/VSWinStorePhone/CMakeLists.txt b/Tests/VSWinStorePhone/CMakeLists.txt index efc7760..b8e157d 100644 --- a/Tests/VSWinStorePhone/CMakeLists.txt +++ b/Tests/VSWinStorePhone/CMakeLists.txt @@ -9,6 +9,7 @@ elseif(MSVC_VERSION GREATER 1600) endif() add_subdirectory(WinRT) +add_subdirectory(CxxDLL) set (APP_MANIFEST_NAME Package.appxmanifest) if("${CMAKE_SYSTEM_NAME}" STREQUAL "WindowsPhone") @@ -151,4 +152,4 @@ if("${SHORT_VERSION}" STREQUAL "10.0") set_property(TARGET ${EXE_NAME} PROPERTY VS_SDK_REFERENCES "Microsoft.UniversalCRT.Debug, Version=${CMAKE_VS_WINDOWS_TARGET_PLATFORM_VERSION}") endif() -target_link_libraries(${EXE_NAME} d3d11 JusticeLeagueWinRT) +target_link_libraries(${EXE_NAME} d3d11 JusticeLeagueWinRT CxxDll) diff --git a/Tests/VSWinStorePhone/CxxDLL/CMakeLists.txt b/Tests/VSWinStorePhone/CxxDLL/CMakeLists.txt new file mode 100644 index 0000000..6bd32a2 --- /dev/null +++ b/Tests/VSWinStorePhone/CxxDLL/CMakeLists.txt @@ -0,0 +1,3 @@ +project(CxxDll CXX) + +add_library(CxxDll SHARED cxxdll.cpp) diff --git a/Tests/VSWinStorePhone/CxxDLL/cxxdll.cpp b/Tests/VSWinStorePhone/CxxDLL/cxxdll.cpp new file mode 100644 index 0000000..d82a792 --- /dev/null +++ b/Tests/VSWinStorePhone/CxxDLL/cxxdll.cpp @@ -0,0 +1,7 @@ +#include "cxxdll.h" +#include <iostream> + +void CxxDllClass::SomeMethod() +{ + std::cout << "CxxDllClass::SomeMethod\n"; +} diff --git a/Tests/VSWinStorePhone/CxxDLL/cxxdll.h b/Tests/VSWinStorePhone/CxxDLL/cxxdll.h new file mode 100644 index 0000000..86edceb --- /dev/null +++ b/Tests/VSWinStorePhone/CxxDLL/cxxdll.h @@ -0,0 +1,5 @@ +class __declspec(dllexport) CxxDllClass +{ +public: + static void SomeMethod(); +}; diff --git a/Tests/VSWinStorePhone/VerifyAppPackage.cmake b/Tests/VSWinStorePhone/VerifyAppPackage.cmake new file mode 100644 index 0000000..f9440d7 --- /dev/null +++ b/Tests/VSWinStorePhone/VerifyAppPackage.cmake @@ -0,0 +1,34 @@ +set(APP_PKG_NAME Direct3DApp1) + +# List of files that are expected to be present in the generated app package +set(EXPECTED_APP_PKG_CONTENT + ${APP_PKG_NAME}.exe + CxxDll.dll + JusticeLeagueWinRT.winmd + JusticeLeagueWinRT.dll +) + +# Windows app package formats can be either msix, appx or xap +file(GLOB_RECURSE ALL_APP_PKG_FILES ${APP_PACKAGE_DIR} ${APP_PKG_NAME}*.msix ${APP_PKG_NAME}*.appx ${APP_PKG_NAME}*.xap) + +# There can be only one generated app package +list(LENGTH ALL_APP_PKG_FILES APP_PKG_COUNT) +if(NOT APP_PKG_COUNT EQUAL 1) + message(FATAL_ERROR "Expected 1 generated app package, but detected ${APP_PKG_COUNT}: ${ALL_APP_PKG_FILES}") +endif() + +execute_process(COMMAND ${CMAKE_COMMAND} -E tar tf ${ALL_APP_PKG_FILES} + OUTPUT_VARIABLE APP_PKG_CONTENT_OUTPUT + ERROR_VARIABLE error + RESULT_VARIABLE result) + +if(NOT result EQUAL 0) + message(FATAL_ERROR "Listing app package content failed with: ${error}") +endif() + +foreach(app_pkg_item ${EXPECTED_APP_PKG_CONTENT}) + string(FIND ${APP_PKG_CONTENT_OUTPUT} ${app_pkg_item} _found) + if(_found EQUAL -1) + message(FATAL_ERROR "Generated app package is missing an expected item: ${app_pkg_item}") + endif() +endforeach() diff --git a/Utilities/cmjsoncpp/src/lib_json/json_writer.cpp b/Utilities/cmjsoncpp/src/lib_json/json_writer.cpp index 6e6e57e..fc86505 100644 --- a/Utilities/cmjsoncpp/src/lib_json/json_writer.cpp +++ b/Utilities/cmjsoncpp/src/lib_json/json_writer.cpp @@ -30,7 +30,7 @@ #define isfinite finite #endif #elif defined(__hpux) -#if !defined(isfinite) +#if !defined(isfinite) && !defined(__GNUC__) #if defined(__ia64) && !defined(finite) #define isfinite(x) ((sizeof(x) == sizeof(float) ? \ _Isfinitef(x) : _IsFinite(x))) @@ -86,10 +86,11 @@ // HP-UX #if defined(__hpux) # if !defined(isfinite) -# if defined(__ia64) && !defined(finite) +# if defined(__ia64) && !defined(finite) && !defined(__GNUC__) # define isfinite(x) ((sizeof(x) == sizeof(float) ? \ _Isfinitef(x) : _Isfinite(x))) # else +# include <math.h> # define isfinite finite # endif # endif diff --git a/Utilities/cmlibuv/CMakeLists.txt b/Utilities/cmlibuv/CMakeLists.txt index 2e781f1..fe2ef75 100644 --- a/Utilities/cmlibuv/CMakeLists.txt +++ b/Utilities/cmlibuv/CMakeLists.txt @@ -300,6 +300,23 @@ if(CMAKE_SYSTEM_NAME STREQUAL "SunOS") ) endif() +if(CMAKE_SYSTEM_NAME STREQUAL "HP-UX") + list(APPEND uv_libraries + rt + ) + list(APPEND uv_headers + include/uv/posix.h + ) + list(APPEND uv_defines + _XOPEN_SOURCE_EXTENDED + ) + list(APPEND uv_sources + src/unix/hpux.c + src/unix/no-fsevents.c + src/unix/posix-poll.c + ) +endif() + include_directories( ${uv_includes} ${KWSYS_HEADER_ROOT} diff --git a/Utilities/cmlibuv/include/uv/unix.h b/Utilities/cmlibuv/include/uv/unix.h index 011abcf..4e26108 100644 --- a/Utilities/cmlibuv/include/uv/unix.h +++ b/Utilities/cmlibuv/include/uv/unix.h @@ -55,6 +55,8 @@ # include "aix.h" #elif defined(__sun) # include "sunos.h" +#elif defined(__hpux) +# include "posix.h" #elif defined(__APPLE__) # include "darwin.h" #elif defined(__DragonFly__) || \ diff --git a/Utilities/cmlibuv/src/unix/core.c b/Utilities/cmlibuv/src/unix/core.c index 93df7af..cf7dea0 100644 --- a/Utilities/cmlibuv/src/unix/core.c +++ b/Utilities/cmlibuv/src/unix/core.c @@ -587,7 +587,7 @@ int uv__nonblock_ioctl(int fd, int set) { } -#if !defined(__CYGWIN__) && !defined(__MSYS__) && !defined(__HAIKU__) +#if !defined(__hpux) && !defined(__CYGWIN__) && !defined(__MSYS__) && !defined(__HAIKU__) int uv__cloexec_ioctl(int fd, int set) { int r; diff --git a/Utilities/cmlibuv/src/unix/fs.c b/Utilities/cmlibuv/src/unix/fs.c index 3023b1e..5fb34f1b 100644 --- a/Utilities/cmlibuv/src/unix/fs.c +++ b/Utilities/cmlibuv/src/unix/fs.c @@ -234,7 +234,7 @@ static ssize_t uv__fs_futime(uv_fs_t* req) { #endif } -#if defined(__sun) && (_XOPEN_SOURCE < 600 || defined(CMAKE_BOOTSTRAP)) +#if (defined(__sun) || defined(__hpux)) && (_XOPEN_SOURCE < 600 || defined(CMAKE_BOOTSTRAP)) static char* uv__mkdtemp(char *template) { if (!mktemp(template) || mkdir(template, 0700)) diff --git a/Utilities/cmlibuv/src/unix/hpux.c b/Utilities/cmlibuv/src/unix/hpux.c new file mode 100644 index 0000000..4d3f628 --- /dev/null +++ b/Utilities/cmlibuv/src/unix/hpux.c @@ -0,0 +1,30 @@ +/* Copyright libuv project contributors. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +#include "uv.h" +#include "internal.h" + +#include <stdint.h> +#include <time.h> + +uint64_t uv__hrtime(uv_clocktype_t type) { + return (uint64_t) gethrtime(); +} diff --git a/Utilities/cmlibuv/src/unix/posix-hrtime.c b/Utilities/cmlibuv/src/unix/posix-hrtime.c index a264250..870b45c 100644 --- a/Utilities/cmlibuv/src/unix/posix-hrtime.c +++ b/Utilities/cmlibuv/src/unix/posix-hrtime.c @@ -43,6 +43,20 @@ uint64_t uv__hrtime(uv_clocktype_t type) { return mach_absolute_time() * info.numer / info.denom; } +#elif defined(__hpux) +/* Special case for CMake bootstrap: no CLOCK_MONOTONIC on HP-UX */ + +#ifndef CMAKE_BOOTSTRAP +#error "This code path meant only for use during CMake bootstrap." +#endif + +#include <stdint.h> +#include <time.h> + +uint64_t uv__hrtime(uv_clocktype_t type) { + return (uint64_t) gethrtime(); +} + #else #include <stdint.h> diff --git a/Utilities/cmlibuv/src/unix/thread.c b/Utilities/cmlibuv/src/unix/thread.c index cd0b7aa..0453221 100644 --- a/Utilities/cmlibuv/src/unix/thread.c +++ b/Utilities/cmlibuv/src/unix/thread.c @@ -700,7 +700,7 @@ int uv_cond_init(uv_cond_t* cond) { if (err) return UV__ERR(err); -#if !(defined(__ANDROID_API__) && __ANDROID_API__ < 21) +#if !(defined(__ANDROID_API__) && __ANDROID_API__ < 21) && !defined(__hpux) err = pthread_condattr_setclock(&attr, CLOCK_MONOTONIC); if (err) goto error2; diff --git a/Utilities/cmlibuv/src/unix/tty.c b/Utilities/cmlibuv/src/unix/tty.c index b8bc283..db479d6 100644 --- a/Utilities/cmlibuv/src/unix/tty.c +++ b/Utilities/cmlibuv/src/unix/tty.c @@ -200,7 +200,7 @@ skip: static void uv__tty_make_raw(struct termios* tio) { assert(tio != NULL); -#if defined __sun || defined __MVS__ +#if defined __sun || defined __MVS__ || defined __hpux /* * This implementation of cfmakeraw for Solaris and derivatives is taken from * http://www.perkin.org.uk/posts/solaris-portability-cfmakeraw.html. diff --git a/Utilities/std/cm/algorithm b/Utilities/std/cm/algorithm new file mode 100644 index 0000000..8ade99c --- /dev/null +++ b/Utilities/std/cm/algorithm @@ -0,0 +1,38 @@ +// -*-c++-*- +// vim: set ft=cpp: + +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing for details. */ +#ifndef cm_algorithm +#define cm_algorithm + +#include <algorithm> // IWYU pragma: export +#include <cassert> + +namespace cm { + +#if __cplusplus >= 201703L || defined(_MSVC_LANG) && _MSVC_LANG >= 201703L + +using std::clamp; + +#else + +template <typename T> +T const& clamp(T const& v, T const& lo, T const& hi) +{ + assert(!(hi < lo)); + return (v < lo) ? lo : (hi < v) ? hi : v; +} + +template <typename T, typename Comp> +T const& clamp(T const& v, T const& lo, T const& hi, Comp comp) +{ + assert(!comp(hi, lo)); + return comp(v, lo) ? lo : comp(hi, v) ? hi : v; +} + +#endif + +} // namespace cm + +#endif @@ -278,6 +278,7 @@ CMAKE_CXX_SOURCES="\ cmCMakePolicyCommand \ cmCPackPropertiesGenerator \ cmCacheManager \ + cmCheckCustomOutputs \ cmCommand \ cmCommandArgumentParserHelper \ cmCommands \ @@ -1069,6 +1070,10 @@ echo ' #error "On Solaris we need C99." #endif +#if defined(__hpux) && !(defined(__GNUC__) && ((__GNUC__ * 100) + __GNUC_MINOR__) >= 409) +#error "On HP-UX we need GCC 4.9 or higher." +#endif + #include <stdio.h> int main(int argc, char* argv[]) @@ -1137,6 +1142,10 @@ echo ' #error "SunPro <= 5.13 mode not supported due to bug in move semantics." #endif +#if defined(__hpux) && !(defined(__GNUC__) && ((__GNUC__ * 100) + __GNUC_MINOR__) >= 409) +#error "On HP-UX we need GCC 4.9 or higher." +#endif + #if __cplusplus > 201103L #include <iterator> int check_cxx14() @@ -1450,6 +1459,9 @@ else *Darwin*) uv_c_flags="${uv_c_flags} -D_DARWIN_USE_64_BIT_INODE=1 -D_DARWIN_UNLIMITED_SELECT=1" ;; + *HP-UX*) + uv_c_flags="${uv_c_flags} -D_XOPEN_SOURCE_EXTENDED" + ;; *Linux*) uv_c_flags="${uv_c_flags} -D_GNU_SOURCE" libs="${libs} -ldl -lrt" |