diff --git a/CompileFlags.cmake b/CompileFlags.cmake index 62e5233a80..cdf0a5b28c 100644 --- a/CompileFlags.cmake +++ b/CompileFlags.cmake @@ -113,6 +113,17 @@ foreach(lang IN ITEMS C CXX) endif() endforeach() +# CMake's own sources are UTF-8. The Embarcadero (EMBTClang) bcc64x compiler +# defaults the narrow execution code page to the host ANSI code page, unlike +# Clang and MSVC which preserve UTF-8, so non-ASCII narrow string literals such +# as the localized regexes in Source/CTest/cmCTestBuildHandler.cxx are corrupted. +# Select UTF-8 (code page 65001) when building CMake itself with EMBTClang. +foreach(lang IN ITEMS C CXX) + if(CMAKE_${lang}_COMPILER_ID STREQUAL "EMBTClang") + string(APPEND CMAKE_${lang}_FLAGS " -fborland-exec-code-page=65001") + endif() +endforeach() + # use the ansi CXX compile flag for building cmake if (CMAKE_ANSI_CXXFLAGS) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${CMAKE_ANSI_CXXFLAGS}") diff --git a/Help/manual/cmake-properties.7.rst b/Help/manual/cmake-properties.7.rst index bb7a52034c..69619271dd 100644 --- a/Help/manual/cmake-properties.7.rst +++ b/Help/manual/cmake-properties.7.rst @@ -253,6 +253,7 @@ Properties on Targets /prop_tgt/DOTNET_SDK /prop_tgt/DOTNET_TARGET_FRAMEWORK /prop_tgt/DOTNET_TARGET_FRAMEWORK_VERSION + /prop_tgt/EMBT_RUNTIME_LIBRARY /prop_tgt/EchoString /prop_tgt/ENABLE_EXPORTS /prop_tgt/EXCLUDE_FROM_ALL diff --git a/Help/manual/cmake-variables.7.rst b/Help/manual/cmake-variables.7.rst index c92d14fca2..391299ea5a 100644 --- a/Help/manual/cmake-variables.7.rst +++ b/Help/manual/cmake-variables.7.rst @@ -485,6 +485,7 @@ Variables that Control the Build /variable/CMAKE_DEPENDS_USE_COMPILER /variable/CMAKE_DISABLE_PRECOMPILE_HEADERS /variable/CMAKE_DLL_NAME_WITH_SOVERSION + /variable/CMAKE_EMBT_RUNTIME_LIBRARY /variable/CMAKE_EXECUTABLE_ENABLE_EXPORTS /variable/CMAKE_EXE_LINKER_FLAGS /variable/CMAKE_EXE_LINKER_FLAGS_CONFIG diff --git a/Help/prop_tgt/EMBT_RUNTIME_LIBRARY.rst b/Help/prop_tgt/EMBT_RUNTIME_LIBRARY.rst new file mode 100644 index 0000000000..ba42265b9a --- /dev/null +++ b/Help/prop_tgt/EMBT_RUNTIME_LIBRARY.rst @@ -0,0 +1,26 @@ +EMBT_RUNTIME_LIBRARY +-------------------- + +.. versionadded:: 4.6 + +Select the runtime library used by the Embarcadero (``bcc64x``) toolchain. + +The allowed values are: + +.. include:: include/EMBT_RUNTIME_LIBRARY-VALUES.rst + +Use :manual:`generator expressions ` to +support per-configuration specification. + +For example, the code: + +.. code-block:: cmake + + add_library(mylib SHARED mylib.cxx) + set_property(TARGET mylib PROPERTY EMBT_RUNTIME_LIBRARY "Dynamic") + +selects for the target ``mylib`` the dynamically-linked runtime library. + +This property is initialized by the :variable:`CMAKE_EMBT_RUNTIME_LIBRARY` +variable if it is set when a target is created. It has no effect on +toolchains other than ``EMBTClang``. diff --git a/Help/prop_tgt/include/EMBT_RUNTIME_LIBRARY-VALUES.rst b/Help/prop_tgt/include/EMBT_RUNTIME_LIBRARY-VALUES.rst new file mode 100644 index 0000000000..4a2a381840 --- /dev/null +++ b/Help/prop_tgt/include/EMBT_RUNTIME_LIBRARY-VALUES.rst @@ -0,0 +1,12 @@ +``Static`` + Compile without additional flags to use the statically-linked runtime + library. +``Dynamic`` + Compile with ``-tR`` and define ``USEPACKAGES`` to use the + dynamically-linked (shared) runtime library. + +The value is ignored on non-Embarcadero compilers but an unsupported value +will be rejected as an error when using the ``bcc64x`` toolchain. + +If this property is not set then no runtime library selection flag is added +explicitly by CMake, which is equivalent to ``Static``. diff --git a/Help/release/dev/embtclang-toolchain.rst b/Help/release/dev/embtclang-toolchain.rst new file mode 100644 index 0000000000..9398504b2a --- /dev/null +++ b/Help/release/dev/embtclang-toolchain.rst @@ -0,0 +1,9 @@ +embtclang-toolchain +------------------- + +* Embarcadero Clang-based compilers are now supported with + :variable:`compiler id _COMPILER_ID>` ``EMBTClang``. + C++ modules are not yet supported with this compiler. + +* The runtime library for the Embarcadero ``bcc64x`` toolchain may now be + selected with the :prop_tgt:`EMBT_RUNTIME_LIBRARY` target property. diff --git a/Help/variable/CMAKE_EMBT_RUNTIME_LIBRARY.rst b/Help/variable/CMAKE_EMBT_RUNTIME_LIBRARY.rst new file mode 100644 index 0000000000..074db53496 --- /dev/null +++ b/Help/variable/CMAKE_EMBT_RUNTIME_LIBRARY.rst @@ -0,0 +1,15 @@ +CMAKE_EMBT_RUNTIME_LIBRARY +-------------------------- + +.. versionadded:: 4.6 + +Select the runtime library used by the Embarcadero (``bcc64x``) toolchain. +This variable is used to initialize the :prop_tgt:`EMBT_RUNTIME_LIBRARY` +property on all targets as they are created. + +The allowed values are: + +.. include:: ../prop_tgt/include/EMBT_RUNTIME_LIBRARY-VALUES.rst + +If this variable is not set then the :prop_tgt:`EMBT_RUNTIME_LIBRARY` target +property is not set automatically. diff --git a/Help/variable/CMAKE_LANG_COMPILER_ID.rst b/Help/variable/CMAKE_LANG_COMPILER_ID.rst index 1efeef8b44..81c8ba2197 100644 --- a/Help/variable/CMAKE_LANG_COMPILER_ID.rst +++ b/Help/variable/CMAKE_LANG_COMPILER_ID.rst @@ -22,6 +22,7 @@ Value Name ``CrayClang`` Cray Clang-based Compiler ``Diab`` `Wind River Systems Diab Compiler`_ ``DPClang`` `Open-source DPC++ Compiler`_ +``EMBTClang`` `Embarcadero`_ Clang-based Compiler ``Embarcadero``, ``Borland`` `Embarcadero`_ ``Flang`` `Classic Flang Fortran Compiler`_ ``LLVMFlang`` `LLVM Flang Fortran Compiler`_ diff --git a/Modules/CMakeCompilerIdDetection.cmake b/Modules/CMakeCompilerIdDetection.cmake index b7eff21022..d8db25cb2c 100644 --- a/Modules/CMakeCompilerIdDetection.cmake +++ b/Modules/CMakeCompilerIdDetection.cmake @@ -47,6 +47,7 @@ function(compiler_id_detection outvar lang) IntelLLVM DPClang PathScale + EMBTClang Embarcadero Borland Watcom diff --git a/Modules/CMakeDetermineASMCompiler.cmake b/Modules/CMakeDetermineASMCompiler.cmake index 8922149ab4..b44d99b005 100644 --- a/Modules/CMakeDetermineASMCompiler.cmake +++ b/Modules/CMakeDetermineASMCompiler.cmake @@ -43,9 +43,9 @@ if(NOT CMAKE_ASM${ASM_DIALECT}_COMPILER) else() # List all default C and CXX compilers set(CMAKE_ASM_COMPILER_LIST - ${_CMAKE_TOOLCHAIN_PREFIX}cc ${_CMAKE_TOOLCHAIN_PREFIX}gcc xlc + ${_CMAKE_TOOLCHAIN_PREFIX}cc ${_CMAKE_TOOLCHAIN_PREFIX}gcc bcc64x xlc ${_CMAKE_ASM_MAYBE_CL} - CC ${_CMAKE_TOOLCHAIN_PREFIX}c++ ${_CMAKE_TOOLCHAIN_PREFIX}g++ xlC) + CC ${_CMAKE_TOOLCHAIN_PREFIX}c++ ${_CMAKE_TOOLCHAIN_PREFIX}g++ bcc64x xlC) unset(_CMAKE_ASM_MAYBE_CL) unset(_CMAKE_ASM_REGEX_CL) unset(_CMAKE_ASM_REGEX_MSVC) diff --git a/Modules/CMakeDetermineCCompiler.cmake b/Modules/CMakeDetermineCCompiler.cmake index 034832c0ff..28460a4cf9 100644 --- a/Modules/CMakeDetermineCCompiler.cmake +++ b/Modules/CMakeDetermineCCompiler.cmake @@ -58,7 +58,7 @@ else() # finally list compilers to try if(NOT CMAKE_C_COMPILER_INIT) - set(CMAKE_C_COMPILER_LIST ${_CMAKE_TOOLCHAIN_PREFIX}cc ${_CMAKE_TOOLCHAIN_PREFIX}gcc cl bcc xlc icx clang dpclang) + set(CMAKE_C_COMPILER_LIST ${_CMAKE_TOOLCHAIN_PREFIX}cc ${_CMAKE_TOOLCHAIN_PREFIX}gcc cl bcc64x bcc xlc icx clang dpclang) endif() _cmake_find_compiler(C) diff --git a/Modules/CMakeDetermineCXXCompiler.cmake b/Modules/CMakeDetermineCXXCompiler.cmake index e7b559c4b0..0cf742dd2e 100644 --- a/Modules/CMakeDetermineCXXCompiler.cmake +++ b/Modules/CMakeDetermineCXXCompiler.cmake @@ -57,7 +57,7 @@ else() # finally list compilers to try if(NOT CMAKE_CXX_COMPILER_INIT) - set(CMAKE_CXX_COMPILER_LIST CC ${_CMAKE_TOOLCHAIN_PREFIX}c++ ${_CMAKE_TOOLCHAIN_PREFIX}g++ aCC cl bcc xlC) + set(CMAKE_CXX_COMPILER_LIST CC ${_CMAKE_TOOLCHAIN_PREFIX}c++ ${_CMAKE_TOOLCHAIN_PREFIX}g++ aCC cl bcc64x bcc xlC) if(NOT CMAKE_HOST_WIN32) # FIXME(#24314): Add support for the GNU-like icpx compiler driver # on Windows, first introduced by Intel oneAPI 2023.0. diff --git a/Modules/CMakeDetermineCompilerId.cmake b/Modules/CMakeDetermineCompilerId.cmake index 1f7eec9360..e442325993 100644 --- a/Modules/CMakeDetermineCompilerId.cmake +++ b/Modules/CMakeDetermineCompilerId.cmake @@ -361,6 +361,7 @@ function(CMAKE_DETERMINE_COMPILER_ID lang flagvar src) elseif("x${CMAKE_${lang}_COMPILER_ID}" STREQUAL "xGNU" OR "x${CMAKE_${lang}_COMPILER_ID}" STREQUAL "xAdaptiveCpp" OR "x${CMAKE_${lang}_COMPILER_ID}" STREQUAL "xAppleClang" + OR "x${CMAKE_${lang}_COMPILER_ID}" STREQUAL "xEMBTClang" OR "x${CMAKE_${lang}_COMPILER_ID}" STREQUAL "xFujitsuClang" OR "x${CMAKE_${lang}_COMPILER_ID}" STREQUAL "xIBMClang" OR "x${CMAKE_${lang}_COMPILER_ID}" STREQUAL "xTIClang") @@ -375,7 +376,7 @@ function(CMAKE_DETERMINE_COMPILER_ID lang flagvar src) # for C++ and the GNU-frontend variant. set(CMAKE_${lang}_COMPILER_CLANG_RESOURCE_DIR "") if ("x${lang}" STREQUAL "xCXX" AND - CMAKE_${lang}_COMPILER_ID MATCHES "^(Clang|DPClang)$" AND + CMAKE_${lang}_COMPILER_ID MATCHES "^(Clang|DPClang|EMBTClang)$" AND "x${CMAKE_${lang}_COMPILER_FRONTEND_VARIANT}" STREQUAL "xGNU") execute_process( COMMAND "${CMAKE_${lang}_COMPILER}" diff --git a/Modules/Compiler/EMBTClang-ASM.cmake b/Modules/Compiler/EMBTClang-ASM.cmake new file mode 100644 index 0000000000..81528a9af6 --- /dev/null +++ b/Modules/Compiler/EMBTClang-ASM.cmake @@ -0,0 +1,4 @@ +include(Compiler/Clang-ASM) +include(Compiler/EMBTClang) + +string(APPEND CMAKE_ASM_FLAGS_INIT " -q") diff --git a/Modules/Compiler/EMBTClang-C-FeatureTests.cmake b/Modules/Compiler/EMBTClang-C-FeatureTests.cmake new file mode 100644 index 0000000000..ef79229d0d --- /dev/null +++ b/Modules/Compiler/EMBTClang-C-FeatureTests.cmake @@ -0,0 +1 @@ +include(Compiler/Clang-C-FeatureTests) diff --git a/Modules/Compiler/EMBTClang-C.cmake b/Modules/Compiler/EMBTClang-C.cmake new file mode 100644 index 0000000000..5e8cb1b450 --- /dev/null +++ b/Modules/Compiler/EMBTClang-C.cmake @@ -0,0 +1,4 @@ +include(Compiler/Clang-C) +include(Compiler/EMBTClang) + +string(APPEND CMAKE_C_FLAGS_INIT " -q") diff --git a/Modules/Compiler/EMBTClang-CXX-FeatureTests.cmake b/Modules/Compiler/EMBTClang-CXX-FeatureTests.cmake new file mode 100644 index 0000000000..e038e807bc --- /dev/null +++ b/Modules/Compiler/EMBTClang-CXX-FeatureTests.cmake @@ -0,0 +1 @@ +include(Compiler/Clang-CXX-FeatureTests) diff --git a/Modules/Compiler/EMBTClang-CXX.cmake b/Modules/Compiler/EMBTClang-CXX.cmake new file mode 100644 index 0000000000..3477718e85 --- /dev/null +++ b/Modules/Compiler/EMBTClang-CXX.cmake @@ -0,0 +1,19 @@ +# Keep a scanning rule set before enable_language() (CMake's own tests set one +# to fake scanning); drop only the one Compiler/Clang-CXX adds. +if(DEFINED CMAKE_CXX_SCANDEP_SOURCE) + set(__embt_scandep_source "${CMAKE_CXX_SCANDEP_SOURCE}") +endif() + +include(Compiler/Clang-CXX) +include(Compiler/EMBTClang) + +# The toolchain's clang-scan-deps does not apply the bcc64x driver's target, +# standard, and include paths, so C++ module scanning is not supported. +if(DEFINED __embt_scandep_source) + set(CMAKE_CXX_SCANDEP_SOURCE "${__embt_scandep_source}") + unset(__embt_scandep_source) +else() + unset(CMAKE_CXX_SCANDEP_SOURCE) +endif() + +string(APPEND CMAKE_CXX_FLAGS_INIT " -q") diff --git a/Modules/Compiler/EMBTClang-DetermineCompiler.cmake b/Modules/Compiler/EMBTClang-DetermineCompiler.cmake new file mode 100644 index 0000000000..5bc24d1c14 --- /dev/null +++ b/Modules/Compiler/EMBTClang-DetermineCompiler.cmake @@ -0,0 +1,4 @@ + +set(_compiler_id_pp_test "defined(__CODEGEARC__) && defined(__clang__) && defined(__MINGW64__)") + +include("${CMAKE_CURRENT_LIST_DIR}/Clang-DetermineCompilerInternal.cmake") diff --git a/Modules/Compiler/EMBTClang-FindBinUtils.cmake b/Modules/Compiler/EMBTClang-FindBinUtils.cmake new file mode 100644 index 0000000000..c24467c730 --- /dev/null +++ b/Modules/Compiler/EMBTClang-FindBinUtils.cmake @@ -0,0 +1,69 @@ +# Distributed under the OSI-approved BSD 3-Clause License. See accompanying +# file LICENSE.rst or https://cmake.org/licensing for details. + + +# Runs immediately after CMakeFindBinUtils during compiler determination. +# CMakeFindBinUtils does not match EMBTClang in its Clang/IntelLLVM/LLVMFlang +# regex, so it falls through to the generic branch and finds whatever "ar" +# happens to be on PATH (often a stray ar.exe from MSYS, MinGW, etc.). +# Override CMAKE_AR/CMAKE_RANLIB with the llvm-ar/llvm-ranlib that ship +# alongside bcc64x in the Embarcadero installation. + +if(NOT DEFINED _CMAKE_PROCESSING_LANGUAGE OR _CMAKE_PROCESSING_LANGUAGE STREQUAL "") + message(FATAL_ERROR "Internal error: _CMAKE_PROCESSING_LANGUAGE is not set") +endif() + +# The compiler path is fully resolved by this point in the determination +# phase, so get_filename_component yields a real directory. +get_filename_component(__embt_hint + "${CMAKE_${_CMAKE_PROCESSING_LANGUAGE}_COMPILER}" REALPATH) +get_filename_component(__embt_hint "${__embt_hint}" DIRECTORY) + +find_program(__embt_ar NAMES llvm-ar + HINTS "${__embt_hint}" + NO_CMAKE_PATH NO_CMAKE_ENVIRONMENT_PATH NO_DEFAULT_PATH) +if(__embt_ar) + set(CMAKE_AR "${__embt_ar}" CACHE FILEPATH "Archiver" FORCE) + set(CMAKE_${_CMAKE_PROCESSING_LANGUAGE}_COMPILER_AR "${__embt_ar}" + CACHE FILEPATH "LLVM archiver" FORCE) + mark_as_advanced(CMAKE_AR + CMAKE_${_CMAKE_PROCESSING_LANGUAGE}_COMPILER_AR) +endif() +unset(__embt_ar CACHE) + +find_program(__embt_ranlib NAMES llvm-ranlib + HINTS "${__embt_hint}" + NO_CMAKE_PATH NO_CMAKE_ENVIRONMENT_PATH NO_DEFAULT_PATH) +if(__embt_ranlib) + set(CMAKE_RANLIB "${__embt_ranlib}" CACHE FILEPATH "Archive index tool" FORCE) + set(CMAKE_${_CMAKE_PROCESSING_LANGUAGE}_COMPILER_RANLIB "${__embt_ranlib}" + CACHE FILEPATH "Generate index for LLVM archive" FORCE) + mark_as_advanced(CMAKE_RANLIB + CMAKE_${_CMAKE_PROCESSING_LANGUAGE}_COMPILER_RANLIB) +endif() +unset(__embt_ranlib CACHE) + +# Pin the linker and the remaining LLVM binutils that ship beside bcc64x, for the +# same reason as CMAKE_AR above: CMakeFindBinUtils leaves them unset (or, for a +# bare name, resolvable from a stray PATH entry). Linking itself goes through +# the bcc64x driver, so CMAKE_LINKER is informational (CheckLinkerFlag and the +# like consume it). +foreach(__embt_tool "LINKER=ld.lld" "NM=llvm-nm" "OBJDUMP=llvm-objdump" + "STRIP=llvm-strip" "DLLTOOL=llvm-dlltool") + string(REPLACE "=" ";" __embt_tool "${__embt_tool}") + list(GET __embt_tool 0 __embt_var) + list(GET __embt_tool 1 __embt_prog) + find_program(__embt_found NAMES ${__embt_prog} + HINTS "${__embt_hint}" + NO_CMAKE_PATH NO_CMAKE_ENVIRONMENT_PATH NO_DEFAULT_PATH) + if(__embt_found) + set(CMAKE_${__embt_var} "${__embt_found}" CACHE FILEPATH "${__embt_prog}" FORCE) + mark_as_advanced(CMAKE_${__embt_var}) + endif() + unset(__embt_found CACHE) +endforeach() +unset(__embt_var) +unset(__embt_prog) +unset(__embt_tool) + +unset(__embt_hint) diff --git a/Modules/Compiler/EMBTClang.cmake b/Modules/Compiler/EMBTClang.cmake new file mode 100644 index 0000000000..b671a33056 --- /dev/null +++ b/Modules/Compiler/EMBTClang.cmake @@ -0,0 +1,37 @@ +# Distributed under the OSI-approved BSD 3-Clause License. See accompanying +# file LICENSE.rst or https://cmake.org/licensing for details. + + +# This module is shared by multiple languages; use include blocker. +if(__COMPILER_EMBTCLANG) + return() +endif() +set(__COMPILER_EMBTCLANG 1) + +if("x${CMAKE_C_COMPILER_ID}" STREQUAL "xEMBTClang") + set(__embt_driver "${CMAKE_C_COMPILER}") +elseif("x${CMAKE_CXX_COMPILER_ID}" STREQUAL "xEMBTClang") + set(__embt_driver "${CMAKE_CXX_COMPILER}") +elseif("x${CMAKE_ASM_COMPILER_ID}" STREQUAL "xEMBTClang") + set(__embt_driver "${CMAKE_ASM_COMPILER}") +endif() +if(__embt_driver) + get_filename_component(EMBT_DRIVER_NAME "${__embt_driver}" NAME_WE) +endif() + +if(WIN32 AND EMBT_DRIVER_NAME AND NOT CMAKE_RC_COMPILER_INIT AND NOT CMAKE_GENERATOR_RC) + # Resolve the resource compiler that ships beside the driver to an absolute + # path, not a bare name that would be taken from PATH at build time. + get_filename_component(__embt_bindir "${__embt_driver}" DIRECTORY) + find_program(__embt_rc NAMES ${EMBT_DRIVER_NAME}-windres + HINTS "${__embt_bindir}" + NO_CMAKE_PATH NO_CMAKE_ENVIRONMENT_PATH NO_DEFAULT_PATH) + if(__embt_rc) + set(CMAKE_RC_COMPILER_INIT "${__embt_rc}") + else() + set(CMAKE_RC_COMPILER_INIT ${EMBT_DRIVER_NAME}-windres) + endif() + unset(__embt_rc CACHE) + unset(__embt_bindir) +endif() +unset(__embt_driver) diff --git a/Modules/Compiler/Embarcadero-DetermineCompiler.cmake b/Modules/Compiler/Embarcadero-DetermineCompiler.cmake index 8375624e9c..e4f18772ae 100644 --- a/Modules/Compiler/Embarcadero-DetermineCompiler.cmake +++ b/Modules/Compiler/Embarcadero-DetermineCompiler.cmake @@ -1,5 +1,5 @@ -set(_compiler_id_pp_test "defined(__BORLANDC__) && defined(__CODEGEARC_VERSION__)") +set(_compiler_id_pp_test "defined(__BORLANDC__) && defined(__CODEGEARC_VERSION__) && !defined(__MINGW64__)") set(_compiler_id_version_compute " # define @PREFIX@COMPILER_VERSION_MAJOR @MACRO_HEX@(__CODEGEARC_VERSION__>>24 & 0x00FF) diff --git a/Modules/Platform/Windows-EMBTClang-ASM.cmake b/Modules/Platform/Windows-EMBTClang-ASM.cmake new file mode 100644 index 0000000000..6d42f2712a --- /dev/null +++ b/Modules/Platform/Windows-EMBTClang-ASM.cmake @@ -0,0 +1,2 @@ +include(Platform/Windows-EMBTClang) +__windows_compiler_embtclang(ASM) diff --git a/Modules/Platform/Windows-EMBTClang-C.cmake b/Modules/Platform/Windows-EMBTClang-C.cmake new file mode 100644 index 0000000000..71fd8a07f3 --- /dev/null +++ b/Modules/Platform/Windows-EMBTClang-C.cmake @@ -0,0 +1,2 @@ +include(Platform/Windows-EMBTClang) +__windows_compiler_embtclang(C) diff --git a/Modules/Platform/Windows-EMBTClang-CXX.cmake b/Modules/Platform/Windows-EMBTClang-CXX.cmake new file mode 100644 index 0000000000..fd619dd8e4 --- /dev/null +++ b/Modules/Platform/Windows-EMBTClang-CXX.cmake @@ -0,0 +1,2 @@ +include(Platform/Windows-EMBTClang) +__windows_compiler_embtclang(CXX) diff --git a/Modules/Platform/Windows-EMBTClang.cmake b/Modules/Platform/Windows-EMBTClang.cmake new file mode 100644 index 0000000000..41d5d75c8f --- /dev/null +++ b/Modules/Platform/Windows-EMBTClang.cmake @@ -0,0 +1,140 @@ +# Distributed under the OSI-approved BSD 3-Clause License. See accompanying +# file LICENSE.rst or https://cmake.org/licensing for details. + + +# This module is shared by multiple languages; use include blocker. +if(__WINDOWS_EMBTCLANG) + return() +endif() +set(__WINDOWS_EMBTCLANG 1) + +include(Platform/Windows-Clang) + +# CMAKE_EMBT_RUNTIME_LIBRARY is forwarded into try_compile() by cmCoreTryCompile, +# so probes compile with the right runtime. The derived RTL link directory and +# RAD Studio include base are not, so forward them too: a probe for a project +# that adds -tJ to link the RAD Studio RTL, or that includes the RAD headers, +# then resolves rtl.lib/rtl.bpi/rtle.lib and from these paths. +list(APPEND CMAKE_TRY_COMPILE_PLATFORM_VARIABLES + CMAKE_EMBT_RTL_LIBRARY_DIR CMAKE_EMBT_INCLUDE_DIR) + +macro(__windows_compiler_embtclang lang) + __windows_compiler_clang(${lang}) + + # The GNU-driver Clang path gives Debug builds only "-g"; the RAD Studio + # headers select debug runtime behavior on _DEBUG, which "-g" does not + # define. Add it so a Debug build matches the debug runtime. + string(APPEND CMAKE_${lang}_FLAGS_DEBUG_INIT " -D_DEBUG") + + # Drive the WIN32_EXECUTABLE property through the bcc64x subsystem switches + # instead of the inherited MSVC-style linker options. These are link rules. + # At compile -tC only defines __CONSOLE__, which the RAD Studio headers + # branch on, and -tW defines nothing; but -tC is recorded in precompiled + # headers, so the compile side defines the macro instead. Keep the two + # separate: a CREATE_*_EXE link rule may carry link-only syntax (LINKER:) + # that is meaningless on a compile command. + set(CMAKE_${lang}_CREATE_WIN32_EXE "-tW") + set(CMAKE_${lang}_CREATE_CONSOLE_EXE "-tC") + set(CMAKE_${lang}_COMPILE_OPTIONS_EMBT_GUI "") + set(CMAKE_${lang}_COMPILE_OPTIONS_EMBT_CONSOLE -D__CONSOLE__=1) + + # The bcc64x toolchain uses MSVC-style naming (no "lib" prefix, ".lib" + # import/static, ".dll" shared), not the GNU/MinGW names inherited from + # Windows-Clang's non-MSVC path. + set(CMAKE_STATIC_LIBRARY_PREFIX "") + set(CMAKE_STATIC_LIBRARY_SUFFIX ".lib") + set(CMAKE_SHARED_LIBRARY_PREFIX "") + set(CMAKE_SHARED_LIBRARY_SUFFIX ".dll") + set(CMAKE_SHARED_MODULE_PREFIX "") + set(CMAKE_SHARED_MODULE_SUFFIX ".dll") + set(CMAKE_IMPORT_LIBRARY_PREFIX "") + set(CMAKE_IMPORT_LIBRARY_SUFFIX ".lib") + # Find RAD Studio-named libraries first, then the MinGW names the toolchain's + # own libraries use: static before import, as the driver links by default. + set(CMAKE_FIND_LIBRARY_PREFIXES "" "lib") + set(CMAKE_FIND_LIBRARY_SUFFIXES ".lib" ".a" ".dll.a") + + # Disable the GNU link-type switches (-Bstatic/-Bdynamic) inherited above. + # Static and import libraries share the ".lib" name, so they cannot select + # between them, and the driver sets the link type itself to match the + # runtime library, which a switch left at the end of the line would undo. + foreach(__embt_type IN ITEMS EXE SHARED_LIBRARY SHARED_MODULE) + set(CMAKE_${__embt_type}_LINK_STATIC_${lang}_FLAGS "") + set(CMAKE_${__embt_type}_LINK_DYNAMIC_${lang}_FLAGS "") + endforeach() + unset(__embt_type) + + set(CMAKE_${lang}_OUTPUT_EXTENSION ".o") + # Append (foo.c.o), do not replace (foo.o): replacing the source extension + # collides when a target has two sources sharing a stem (e.g. test.c + + # test.cxx), producing "multiple rules generate .o" + set(CMAKE_${lang}_OUTPUT_EXTENSION_REPLACE 0) + + # The default archive command (qc) does not write a symbol index. The + # ARCHIVE_FINISH step runs ranlib to add one, but llvm-ranlib may not be + # present. Use 's' so llvm-ar embeds the index in the same pass; lld + # requires the index to resolve symbols from a static archive. + set(CMAKE_${lang}_ARCHIVE_CREATE " -qcDs ") + set(CMAKE_${lang}_ARCHIVE_APPEND " -qDs ") + + # The EMBT_RUNTIME_LIBRARY property selects the runtime library the same way as + # MSVC_RUNTIME_LIBRARY / WATCOM_RUNTIME_LIBRARY: a default activates it and the + # property overrides. Static relies on the driver's -tC/-tW/-tD default, which + # links libc++ statically; Dynamic adds -tR for the shared libc++ and defines + # USEPACKAGES so the RAD Studio headers import RTL symbols. + set(CMAKE_EMBT_RUNTIME_LIBRARY_DEFAULT "Static") + set(CMAKE_${lang}_COMPILE_OPTIONS_EMBT_RUNTIME_LIBRARY_Static "") + set(CMAKE_${lang}_COMPILE_OPTIONS_EMBT_RUNTIME_LIBRARY_Dynamic -tR -DUSEPACKAGES) + + # A shared or module library is a plain DLL: -tD at link. At compile -tD + # only defines __DLL__, but it is recorded in precompiled headers, which + # then cannot be reused by other target kinds; define the macro instead. + set(CMAKE_SHARED_LIBRARY_CREATE_${lang}_FLAGS "-shared -tD") + set(CMAKE_SHARED_MODULE_CREATE_${lang}_FLAGS "-shared -tD") + set(CMAKE_${lang}_COMPILE_OPTIONS_EMBT_DLL -D__DLL__=1) + + # When a project adds -tJ to link against the RAD Studio RTL, the driver pulls + # the RTL import libraries (rtl.lib/rtl.bpi and rtle.lib) and startup object + # from the link search path. They sit next to the compiler at + # /../lib/win64x/. Put that directory on the standard link + # path so a user-added -tJ resolves them. Override with + # CMAKE_EMBT_RTL_LIBRARY_DIR. + if(NOT DEFINED CMAKE_EMBT_RTL_LIBRARY_DIR) + get_filename_component(__embt_bindir "${CMAKE_${lang}_COMPILER}" DIRECTORY) + # Use the debug RTL only for a Debug build -- the config that also defines + # _DEBUG, matching the debug runtime; use release otherwise, including when + # no build type is set. Keyed on CMAKE_BUILD_TYPE, so this assumes a + # single-config generator; multi-config would need a $ genex. + if(CMAKE_BUILD_TYPE MATCHES "^[Dd][Ee][Bb][Uu][Gg]$") + set(__embt_rtl_config debug) + else() + set(__embt_rtl_config release) + endif() + get_filename_component(CMAKE_EMBT_RTL_LIBRARY_DIR + "${__embt_bindir}/../lib/win64x/${__embt_rtl_config}" ABSOLUTE) + unset(__embt_bindir) + unset(__embt_rtl_config) + endif() + if(EXISTS "${CMAKE_EMBT_RTL_LIBRARY_DIR}") + list(APPEND CMAKE_${lang}_STANDARD_LINK_DIRECTORIES + "${CMAKE_EMBT_RTL_LIBRARY_DIR}") + endif() + + # The bcc64x driver does not put the RAD Studio C++ headers on the default + # include path (the IDE adds them). Do the same so and the + # framework headers resolve. They live at /../include/windows/. + # Override the base with CMAKE_EMBT_INCLUDE_DIR. + if(NOT DEFINED CMAKE_EMBT_INCLUDE_DIR) + get_filename_component(__embt_bindir "${CMAKE_${lang}_COMPILER}" DIRECTORY) + get_filename_component(CMAKE_EMBT_INCLUDE_DIR + "${__embt_bindir}/../include/windows" ABSOLUTE) + unset(__embt_bindir) + endif() + foreach(__embt_inc IN ITEMS sdk rtl vcl fmx) + if(EXISTS "${CMAKE_EMBT_INCLUDE_DIR}/${__embt_inc}") + list(APPEND CMAKE_${lang}_STANDARD_INCLUDE_DIRECTORIES + "${CMAKE_EMBT_INCLUDE_DIR}/${__embt_inc}") + endif() + endforeach() + unset(__embt_inc) +endmacro() diff --git a/Source/cmCoreTryCompile.cxx b/Source/cmCoreTryCompile.cxx index 6499b71086..c55df0a55f 100644 --- a/Source/cmCoreTryCompile.cxx +++ b/Source/cmCoreTryCompile.cxx @@ -1150,6 +1150,7 @@ cm::optional cmCoreTryCompile::TryCompileCode( vars.insert("CMAKE_SYCL_DEVICE_TARGETS"); vars.emplace("CMAKE_MSVC_RUNTIME_LIBRARY"_s); vars.emplace("CMAKE_WATCOM_RUNTIME_LIBRARY"_s); + vars.emplace("CMAKE_EMBT_RUNTIME_LIBRARY"_s); vars.emplace("CMAKE_MSVC_DEBUG_INFORMATION_FORMAT"_s); vars.emplace("CMAKE_MSVC_RUNTIME_CHECKS"_s); vars.emplace("CMAKE_CXX_COMPILER_CLANG_SCAN_DEPS"_s); diff --git a/Source/cmLocalGenerator.cxx b/Source/cmLocalGenerator.cxx index e0b8518d6b..6d4c7f8c59 100644 --- a/Source/cmLocalGenerator.cxx +++ b/Source/cmLocalGenerator.cxx @@ -2318,6 +2318,7 @@ void cmLocalGenerator::AddLanguageFlags(std::string& flags, (compilerId == "MSVC" || compilerSimulateId == "MSVC"); bool const compilerTargetsWatcomABI = (compilerId == "OpenWatcom" || compilerSimulateId == "OpenWatcom"); + bool const compilerTargetsEmbtABI = (compilerId == "EMBTClang"); if (lang == "Swift") { target->AddSwiftTargetFlags(flags); @@ -2419,6 +2420,62 @@ void cmLocalGenerator::AddLanguageFlags(std::string& flags, } } + // Add Embarcadero (bcc64x) runtime library flags. This is activated by the + // presence of a default selection whether or not it is overridden by a + // property, the same way as MSVC and Watcom above. + cmValue embtRuntimeLibraryDefault = + this->Makefile->GetDefinition("CMAKE_EMBT_RUNTIME_LIBRARY_DEFAULT"); + if (cmNonempty(embtRuntimeLibraryDefault)) { + cmValue embtRuntimeLibraryValue = + target->GetProperty("EMBT_RUNTIME_LIBRARY"); + if (!embtRuntimeLibraryValue) { + embtRuntimeLibraryValue = embtRuntimeLibraryDefault; + } + std::string const embtRuntimeLibrary = cmGeneratorExpression::Evaluate( + *embtRuntimeLibraryValue, this, config, target); + if (!embtRuntimeLibrary.empty()) { + if (cmValue embtRuntimeLibraryOptions = this->Makefile->GetDefinition( + cmStrCat("CMAKE_", lang, "_COMPILE_OPTIONS_EMBT_RUNTIME_LIBRARY_", + embtRuntimeLibrary))) { + this->AppendCompileOptions(flags, *embtRuntimeLibraryOptions); + } else if (compilerTargetsEmbtABI && + !cmSystemTools::GetErrorOccurredFlag()) { + // The compiler is EMBTClang so it needs a known runtime library. + this->IssueMessage( + MessageType::FATAL_ERROR, + cmStrCat("EMBT_RUNTIME_LIBRARY value '", embtRuntimeLibrary, + "' not known for this ", lang, " compiler.")); + } + } + } + + // Add Embarcadero (bcc64x) target-kind flags for shared and module libraries + // and the console/GUI subsystem, active only for the EMBTClang toolchain. + if (compilerId == "EMBTClang") { + // A shared or module library is a plain DLL: it is compiled with __DLL__ + // for the RAD Studio headers (the platform module links it with -tD). + if (target->GetType() == cm::TargetType::SHARED_LIBRARY || + target->GetType() == cm::TargetType::MODULE_LIBRARY) { + if (cmValue embtOptions = this->Makefile->GetDefinition( + cmStrCat("CMAKE_", lang, "_COMPILE_OPTIONS_EMBT_DLL"))) { + this->AppendCompileOptions(flags, *embtOptions); + } + } + // -tC/-tW set the subsystem at link, and a console executable is also + // compiled with __CONSOLE__, which the RAD Studio headers branch on. + // WIN32_EXECUTABLE drives the switch only at link, so apply the matching + // compile options to an executable too, via dedicated compile-option + // variables (not the CREATE_{WIN32,CONSOLE}_EXE link rules, which may + // carry link-only syntax). + if (target->GetType() == cm::TargetType::EXECUTABLE) { + if (cmValue subsystem = this->Makefile->GetDefinition( + cmStrCat("CMAKE_", lang, "_COMPILE_OPTIONS_EMBT_", + target->IsWin32Executable(config) ? "GUI" : "CONSOLE"))) { + this->AppendCompileOptions(flags, *subsystem); + } + } + } + // Add MSVC runtime checks flags. This is activated by the presence // of a default selection whether or not it is overridden by a property. cmValue msvcRuntimeChecksDefault = diff --git a/Source/cmTarget.cxx b/Source/cmTarget.cxx index f9d396bec9..4c1fc6e0cc 100644 --- a/Source/cmTarget.cxx +++ b/Source/cmTarget.cxx @@ -362,6 +362,8 @@ TargetProperty const StaticTargetProperties[] = { { "VS_USE_DEBUG_LIBRARIES"_s, IC::NonImportedTarget }, // ---- OpenWatcom { "WATCOM_RUNTIME_LIBRARY"_s, IC::CanCompileSources }, + // ---- Embarcadero + { "EMBT_RUNTIME_LIBRARY"_s, IC::CanCompileSources }, // ---- AIX { "AIX_SHARED_LIBRARY_ARCHIVE"_s, IC::SharedLibraryTarget }, // -- Language @@ -1997,6 +1999,8 @@ void cmTarget::CopyCxxModulesProperties(cmTarget const* tgt) "VS_PLATFORM_TOOLSET", // ---- OpenWatcom "WATCOM_RUNTIME_LIBRARY", + // ---- Embarcadero + "EMBT_RUNTIME_LIBRARY", // -- Language // ---- C++ "CXX_COMPILER_LAUNCHER", diff --git a/Tests/CMakeCommands/target_compile_options/CMakeLists.txt b/Tests/CMakeCommands/target_compile_options/CMakeLists.txt index 7721713ae2..abdcf89311 100644 --- a/Tests/CMakeCommands/target_compile_options/CMakeLists.txt +++ b/Tests/CMakeCommands/target_compile_options/CMakeLists.txt @@ -10,11 +10,11 @@ add_executable(target_compile_options "${CMAKE_CURRENT_SOURCE_DIR}/main.cpp" ) target_compile_options(target_compile_options - PRIVATE $<$:-DMY_PRIVATE_DEFINE> + PRIVATE $<$:-DMY_PRIVATE_DEFINE> PUBLIC $<$:-DMY_PUBLIC_DEFINE> - PUBLIC $<$:-DMY_MUTLI_COMP_PUBLIC_DEFINE> + PUBLIC $<$:-DMY_MUTLI_COMP_PUBLIC_DEFINE> INTERFACE $<$:-DMY_INTERFACE_DEFINE> - INTERFACE $<$:-DMY_MULTI_COMP_INTERFACE_DEFINE> + INTERFACE $<$:-DMY_MULTI_COMP_INTERFACE_DEFINE> ) if(CMAKE_CXX_COMPILER_ID MATCHES "GNU|LCC") @@ -50,7 +50,7 @@ if(CMAKE_GENERATOR MATCHES "Visual Studio") endif() target_compile_options(consumer - PRIVATE $<$:$> + PRIVATE $<$:$> ) if(CMAKE_CXX_COMPILER_ID MATCHES "GNU|LCC") diff --git a/Tests/CMakeLists.txt b/Tests/CMakeLists.txt index cc310fffc5..e6b434054a 100644 --- a/Tests/CMakeLists.txt +++ b/Tests/CMakeLists.txt @@ -2142,6 +2142,10 @@ if(BUILD_TESTING) ADD_TEST_MACRO(WatcomRuntimeLibrary) endif() + if(CMAKE_C_COMPILER_ID MATCHES "EMBTClang" AND WIN32) + ADD_TEST_MACRO(EMBTRuntimeLibrary) + endif() + ADD_TEST_MACRO(CheckCompilerRelatedVariables CheckCompilerRelatedVariables) if("${CMAKE_GENERATOR}" MATCHES "^Ninja$|Makefile|FASTBuild") diff --git a/Tests/EMBTRuntimeLibrary/CMakeLists.txt b/Tests/EMBTRuntimeLibrary/CMakeLists.txt new file mode 100644 index 0000000000..f2f7b7389e --- /dev/null +++ b/Tests/EMBTRuntimeLibrary/CMakeLists.txt @@ -0,0 +1,66 @@ +cmake_minimum_required(VERSION 3.28) +project(EMBTRuntimeLibrary C CXX) + +# Verify that an EMBT_RUNTIME_LIBRARY selection compiles and defines the expected +# macros. The selection is applied both as a directory variable (which +# initializes the target property and is forwarded into try_compile) and directly +# on a target, so both paths are exercised. The VERIFY_* switches arm the #error +# guards in verify.h, making a wrong property->flag mapping a build failure. +function(verify tag lang src) + cmake_parse_arguments(PARSE_ARGV 3 A "" "PROP;VALUE;TYPE" "DEFS") + if(NOT A_TYPE) + set(A_TYPE STATIC) + endif() + + # Local to this function scope, so it does not leak to sibling cases. + if(A_PROP) + set(CMAKE_${A_PROP} "${A_VALUE}") + endif() + + # try_compile wants -D-prefixed definitions; target_compile_definitions wants + # bare names. + set(try_defs "") + foreach(d IN LISTS A_DEFS) + list(APPEND try_defs "-D${d}") + endforeach() + + # try_compile only supports EXECUTABLE / STATIC_LIBRARY target types, so the + # shared-library case is covered by the real add_library() target below. + if(NOT A_TYPE STREQUAL "SHARED") + set(CMAKE_TRY_COMPILE_TARGET_TYPE "STATIC_LIBRARY") + try_compile(${tag}_${lang}_COMPILES + ${CMAKE_CURRENT_BINARY_DIR}/tc/${tag}-${lang} + ${CMAKE_CURRENT_SOURCE_DIR}/${src} + COMPILE_DEFINITIONS ${try_defs} + OUTPUT_VARIABLE out) + if(${tag}_${lang}_COMPILES) + message(STATUS "try_compile ${tag} (${lang}) worked") + else() + string(REPLACE "\n" "\n " out " ${out}") + message(SEND_ERROR "try_compile ${tag} (${lang}) failed:\n${out}") + endif() + endif() + + add_library(${tag}-${lang} ${A_TYPE} ${src}) + if(A_PROP) + set_property(TARGET ${tag}-${lang} PROPERTY ${A_PROP} "${A_VALUE}") + endif() + target_compile_definitions(${tag}-${lang} PRIVATE ${A_DEFS}) +endfunction() + +foreach(lang_src "C;verify.c" "CXX;verify.cxx") + list(GET lang_src 0 lang) + list(GET lang_src 1 src) + + # No selection: none of the macros are defined. + verify(default ${lang} ${src}) + + # Runtime library. + verify(static ${lang} ${src} PROP EMBT_RUNTIME_LIBRARY VALUE Static) + verify(dynamic ${lang} ${src} PROP EMBT_RUNTIME_LIBRARY VALUE Dynamic + DEFS VERIFY_RTLDLL VERIFY_USEPACKAGES) + + # A shared library is a plain DLL: linked with -tD and compiled with __DLL__. + # A trivial DLL links without the RAD Studio runtime. + verify(dll ${lang} ${src} TYPE SHARED DEFS VERIFY_DLL) +endforeach() diff --git a/Tests/EMBTRuntimeLibrary/verify.c b/Tests/EMBTRuntimeLibrary/verify.c new file mode 100644 index 0000000000..741bca6b89 --- /dev/null +++ b/Tests/EMBTRuntimeLibrary/verify.c @@ -0,0 +1 @@ +#include "verify.h" diff --git a/Tests/EMBTRuntimeLibrary/verify.cxx b/Tests/EMBTRuntimeLibrary/verify.cxx new file mode 100644 index 0000000000..741bca6b89 --- /dev/null +++ b/Tests/EMBTRuntimeLibrary/verify.cxx @@ -0,0 +1 @@ +#include "verify.h" diff --git a/Tests/EMBTRuntimeLibrary/verify.h b/Tests/EMBTRuntimeLibrary/verify.h new file mode 100644 index 0000000000..916502f88a --- /dev/null +++ b/Tests/EMBTRuntimeLibrary/verify.h @@ -0,0 +1,38 @@ +/* The EMBT_RUNTIME_LIBRARY property and the target type select bcc64x driver + flags. Each flag defines characteristic preprocessor macros; assert them + here so a wrong mapping fails the build. The VERIFY_* switches are passed + by the test for the selection under test. */ + +/* EMBT_RUNTIME_LIBRARY=Dynamic uses the dynamic RTL (-tR), which defines + _RTLDLL. */ +#ifdef VERIFY_RTLDLL +# ifndef _RTLDLL +# error "_RTLDLL not defined by the dynamic runtime selection" +# endif +#else +# ifdef _RTLDLL +# error "_RTLDLL defined without the dynamic runtime selection" +# endif +#endif + +/* The dynamic RTL also defines USEPACKAGES (added by CMake). */ +#ifdef VERIFY_USEPACKAGES +# ifndef USEPACKAGES +# error "USEPACKAGES not defined by the dynamic runtime selection" +# endif +#else +# ifdef USEPACKAGES +# error "USEPACKAGES defined without the dynamic runtime selection" +# endif +#endif + +/* A shared library defines __DLL__. */ +#ifdef VERIFY_DLL +# ifndef __DLL__ +# error "__DLL__ not defined for a shared library" +# endif +#else +# ifdef __DLL__ +# error "__DLL__ defined for a non-shared target" +# endif +#endif diff --git a/Tests/MSManifest/Subdir/CMakeLists.txt b/Tests/MSManifest/Subdir/CMakeLists.txt index 68c66fe209..575605ed7a 100644 --- a/Tests/MSManifest/Subdir/CMakeLists.txt +++ b/Tests/MSManifest/Subdir/CMakeLists.txt @@ -12,7 +12,8 @@ if(MSVC AND NOT MSVC_VERSION LESS 1400) -P ${CMAKE_CURRENT_SOURCE_DIR}/check.cmake) add_executable(MSManifestNone main.c) set_property(TARGET MSManifestNone PROPERTY LINK_FLAGS "/MANIFEST:NO") -elseif(WIN32 AND CMAKE_C_COMPILER_ID MATCHES "Clang") +elseif(WIN32 AND CMAKE_C_COMPILER_ID MATCHES "Clang" + AND CMAKE_C_SIMULATE_ID STREQUAL "MSVC") add_test(NAME MSManifest.Single COMMAND ${CMAKE_COMMAND} -Dexe=$ -P ${CMAKE_CURRENT_SOURCE_DIR}/check.cmake) diff --git a/Tests/MSManifest/Subdir2/CMakeLists.txt b/Tests/MSManifest/Subdir2/CMakeLists.txt index bbc70dcb1b..12e4dde4ca 100644 --- a/Tests/MSManifest/Subdir2/CMakeLists.txt +++ b/Tests/MSManifest/Subdir2/CMakeLists.txt @@ -6,7 +6,9 @@ add_executable(MSMultipleManifest main.c ${CMAKE_CURRENT_BINARY_DIR}/test_manifest2.manifest ${CMAKE_CURRENT_BINARY_DIR}/test_manifest3.manifest) -if((MSVC AND NOT MSVC_VERSION LESS 1400) OR (WIN32 AND CMAKE_C_COMPILER_ID MATCHES "Clang") ) +if((MSVC AND NOT MSVC_VERSION LESS 1400) + OR (WIN32 AND CMAKE_C_COMPILER_ID MATCHES "Clang" + AND CMAKE_C_SIMULATE_ID STREQUAL "MSVC")) add_test(NAME MSManifest.Multiple COMMAND ${CMAKE_COMMAND} -Dexe=$ -P ${CMAKE_CURRENT_SOURCE_DIR}/check.cmake) diff --git a/Tests/Module/WriteCompilerDetectionHeader/CMakeLists.txt b/Tests/Module/WriteCompilerDetectionHeader/CMakeLists.txt index cba9f95f14..c38ce57735 100644 --- a/Tests/Module/WriteCompilerDetectionHeader/CMakeLists.txt +++ b/Tests/Module/WriteCompilerDetectionHeader/CMakeLists.txt @@ -55,7 +55,7 @@ endmacro() # detailed features tables, not just meta-features if (CMAKE_C_COMPILE_FEATURES) - if (NOT CMAKE_C_COMPILER_ID MATCHES "^(LCC|Cray|CrayClang|PGI|NVHPC|XL|XLClang|IBMClang|IntelLLVM|DPClang|Fujitsu|FujitsuClang|OrangeC)$") + if (NOT CMAKE_C_COMPILER_ID MATCHES "^(LCC|Cray|CrayClang|PGI|NVHPC|XL|XLClang|IBMClang|IntelLLVM|DPClang|Fujitsu|FujitsuClang|OrangeC|EMBTClang)$") set(C_expected_features ${CMAKE_C_COMPILE_FEATURES}) list(FILTER C_expected_features EXCLUDE REGEX "^c_std_[0-9][0-9]") endif() @@ -98,7 +98,7 @@ if (C_expected_features) endif() if (CMAKE_CXX_COMPILE_FEATURES) - if (NOT CMAKE_CXX_COMPILER_ID MATCHES "^(LCC|Cray|CrayClang|PGI|NVHPC|XL|XLClang|IBMClang|IntelLLVM|DPClang|Fujitsu|FujitsuClang|OrangeC)$") + if (NOT CMAKE_CXX_COMPILER_ID MATCHES "^(LCC|Cray|CrayClang|PGI|NVHPC|XL|XLClang|IBMClang|IntelLLVM|DPClang|Fujitsu|FujitsuClang|OrangeC|EMBTClang)$") set(CXX_expected_features ${CMAKE_CXX_COMPILE_FEATURES}) list(FILTER CXX_expected_features EXCLUDE REGEX "^cxx_std_[0-9][0-9]") endif() diff --git a/Tests/RunCMake/CMakeLists.txt b/Tests/RunCMake/CMakeLists.txt index 56a7441a76..c83b275f6c 100644 --- a/Tests/RunCMake/CMakeLists.txt +++ b/Tests/RunCMake/CMakeLists.txt @@ -661,6 +661,7 @@ if(MSVC) add_RunCMake_test(MSVCRuntimeTypeInfo) add_RunCMake_test(MSVCDebugInformationFormat) endif() +add_RunCMake_test(EMBTClang) if(XCODE_VERSION) set(ObjectLibrary_ARGS -DXCODE_VERSION=${XCODE_VERSION}) endif() diff --git a/Tests/RunCMake/EMBTClang/CMakeLists.txt b/Tests/RunCMake/EMBTClang/CMakeLists.txt new file mode 100644 index 0000000000..3e470a29cb --- /dev/null +++ b/Tests/RunCMake/EMBTClang/CMakeLists.txt @@ -0,0 +1,3 @@ +cmake_minimum_required(VERSION 3.14) +project(${RunCMake_TEST} NONE) +include(${RunCMake_TEST}.cmake) diff --git a/Tests/RunCMake/EMBTClang/RunCMakeTest.cmake b/Tests/RunCMake/EMBTClang/RunCMakeTest.cmake new file mode 100644 index 0000000000..d74cfb377c --- /dev/null +++ b/Tests/RunCMake/EMBTClang/RunCMakeTest.cmake @@ -0,0 +1,32 @@ +include(RunCMake) + +# Registration and initialization of the EMBT_RUNTIME_LIBRARY target property is +# generic and does not require the Embarcadero toolchain, so it runs on any host. +run_cmake(embt-properties) + +# EMBT_RUNTIME_LIBRARY link selection: Dynamic links the shared libc++ +# (libc++-380.dll import), Static links it statically (no import). Builds real +# exes with bcc64x and checks imports; a no-op where bcc64x is absent. +run_cmake(runtime-library) + +# Compiler-id regression: bcc64x -> EMBTClang, older drivers -> Embarcadero. +# The case finds the drivers itself and is a no-op where none are installed. +run_cmake(compiler-id-drivers) + +# Binutils pinning: the linker and LLVM tools resolve to distinct absolute paths +# beside the compiler. Finds bcc64x itself; a no-op where it is absent. +run_cmake(binutils-pinned) + +# Subsystem macros: a console target compiles with __CONSOLE__ and a GUI target +# without it (compilation sees the macro, not just the link switch). +# Driver-gated. +run_cmake(subsystem-macros) + +# Precompiled header reuse across target kinds: a static library's header is +# reused by shared and module libraries and console and GUI executables. +# Driver-gated. +run_cmake(pch-reuse-kinds) + +# C++ module scanning is not supported: a C++20 target builds without being +# scanned, and C++ module sources are rejected. Driver-gated. +run_cmake(cxx-module-scanning) diff --git a/Tests/RunCMake/EMBTClang/binutils-pinned.cmake b/Tests/RunCMake/EMBTClang/binutils-pinned.cmake new file mode 100644 index 0000000000..20a8d68d08 --- /dev/null +++ b/Tests/RunCMake/EMBTClang/binutils-pinned.cmake @@ -0,0 +1,60 @@ +# Regression guard for C4: EMBTClang-FindBinUtils must pin the linker and the +# LLVM binutils to absolute paths beside the compiler, each a distinct tool. +# The distinctness check specifically catches the find_program cache-reuse trap +# -- dropping the per-iteration unset(... CACHE) collapses every tool onto the +# first (ld.lld). Gated on bcc64x being present; a no-op where it is absent. + +if(NOT WIN32) + return() +endif() + +set(_hints "") +if(DEFINED ENV{TP}) + list(APPEND _hints "$ENV{TP}/bin64") +endif() +find_program(EMBT_BCC64X NAMES bcc64x HINTS ${_hints}) +if(NOT EMBT_BCC64X) + message(STATUS "bcc64x not found on PATH or under $TP; binutils pinning check skipped.") + return() +endif() + +# Start from a clean nested build tree: compiler determination (and thus +# FindBinUtils) is cached, so a stale tree would not re-run the logic under test. +file(REMOVE_RECURSE "${CMAKE_CURRENT_BINARY_DIR}/build") +execute_process( + COMMAND ${CMAKE_COMMAND} + -S "${CMAKE_CURRENT_SOURCE_DIR}/binutils" + -B "${CMAKE_CURRENT_BINARY_DIR}/build" + -G "${CMAKE_GENERATOR}" + "-DCMAKE_MAKE_PROGRAM=${CMAKE_MAKE_PROGRAM}" + "-DCMAKE_C_COMPILER=${EMBT_BCC64X}" + -DCMAKE_C_COMPILER_WORKS=1 + OUTPUT_VARIABLE _out ERROR_VARIABLE _err) + +# FindBinUtils pins tools found under REALPATH(compiler dir), so canonicalize +# both sides before comparing -- the work trees are reachable through junctions, +# where a literal directory would not string-match the resolved one. +get_filename_component(_bindir "${EMBT_BCC64X}" REALPATH) +get_filename_component(_bindir "${_bindir}" DIRECTORY) +set(_seen "") +foreach(_tool LINKER NM OBJDUMP STRIP DLLTOOL) + if(NOT _out MATCHES "BINUTIL ${_tool}=\\[([^]]+)\\]") + message(SEND_ERROR "no CMAKE_${_tool} reported.\n${_out}${_err}") + continue() + endif() + set(_path "${CMAKE_MATCH_1}") + if(NOT IS_ABSOLUTE "${_path}" OR NOT EXISTS "${_path}") + message(SEND_ERROR "CMAKE_${_tool} is not an existing absolute path: [${_path}]") + endif() + get_filename_component(_dir "${_path}" REALPATH) + get_filename_component(_dir "${_dir}" DIRECTORY) + if(NOT _dir STREQUAL _bindir) + message(SEND_ERROR "CMAKE_${_tool} is not beside the compiler: [${_path}]") + endif() + if(_path IN_LIST _seen) + message(SEND_ERROR + "CMAKE_${_tool} collapsed onto an already-pinned tool: [${_path}] " + "(find_program cache-reuse regression)") + endif() + list(APPEND _seen "${_path}") +endforeach() diff --git a/Tests/RunCMake/EMBTClang/binutils/CMakeLists.txt b/Tests/RunCMake/EMBTClang/binutils/CMakeLists.txt new file mode 100644 index 0000000000..b5a8185151 --- /dev/null +++ b/Tests/RunCMake/EMBTClang/binutils/CMakeLists.txt @@ -0,0 +1,6 @@ +cmake_minimum_required(VERSION 3.14) +project(embt_binutils C) +# The parent test reads these back to assert each tool was pinned. +foreach(t LINKER NM OBJDUMP STRIP DLLTOOL) + message(STATUS "BINUTIL ${t}=[${CMAKE_${t}}]") +endforeach() diff --git a/Tests/RunCMake/EMBTClang/compiler-id-drivers.cmake b/Tests/RunCMake/EMBTClang/compiler-id-drivers.cmake new file mode 100644 index 0000000000..2e5fe4e4cc --- /dev/null +++ b/Tests/RunCMake/EMBTClang/compiler-id-drivers.cmake @@ -0,0 +1,56 @@ +# Regression guard for the Embarcadero compiler-id detection: bcc64x must +# identify as EMBTClang while the older Clang-based drivers (bcc32x, bcc64, +# bcc32c) stay Embarcadero. bcc64x is +# discriminated by __MINGW64__ (bcc64 also defines _WIN64, so _WIN64 would not +# tell them apart), and EMBTClang is tried before Embarcadero because bcc64x +# also defines __BORLANDC__/__CODEGEARC_VERSION__. +# +# Each driver's identity is checked by a nested configure, gated on the driver +# being present: real coverage where a RAD Studio tree is installed, a no-op +# elsewhere (upstream CI has none of these compilers). + +if(NOT WIN32) + return() +endif() + +set(_hints "") +if(DEFINED ENV{TP}) + list(APPEND _hints "$ENV{TP}/bin64" "$ENV{TP}/bin") +endif() + +# driver name -> expected CMAKE_C_COMPILER_ID +set(_expect + bcc64x EMBTClang + bcc32x Embarcadero + bcc64 Embarcadero + bcc32c Embarcadero) + +set(_found_any NO) +while(_expect) + list(POP_FRONT _expect _drv _want) + find_program(EMBT_DRIVER_${_drv} NAMES ${_drv} HINTS ${_hints}) + if(NOT EMBT_DRIVER_${_drv}) + continue() + endif() + set(_found_any YES) + execute_process( + COMMAND ${CMAKE_COMMAND} + -S "${CMAKE_CURRENT_SOURCE_DIR}/compiler-id" + -B "${CMAKE_CURRENT_BINARY_DIR}/id-${_drv}" + -G "${CMAKE_GENERATOR}" + "-DCMAKE_MAKE_PROGRAM=${CMAKE_MAKE_PROGRAM}" + "-DCMAKE_C_COMPILER=${EMBT_DRIVER_${_drv}}" + -DCMAKE_C_COMPILER_WORKS=1 + OUTPUT_VARIABLE _out ERROR_VARIABLE _err) + if(NOT _out MATCHES "DRIVER_COMPILER_ID=\\[${_want}\\]") + message(SEND_ERROR + "driver '${_drv}' should identify as '${_want}'.\n" + "----- configure output -----\n${_out}${_err}") + endif() +endwhile() + +if(NOT _found_any) + message(STATUS + "No Embarcadero drivers found on PATH or under \$TP; " + "compiler-id regression check skipped.") +endif() diff --git a/Tests/RunCMake/EMBTClang/compiler-id/CMakeLists.txt b/Tests/RunCMake/EMBTClang/compiler-id/CMakeLists.txt new file mode 100644 index 0000000000..5c223ea8c0 --- /dev/null +++ b/Tests/RunCMake/EMBTClang/compiler-id/CMakeLists.txt @@ -0,0 +1,4 @@ +cmake_minimum_required(VERSION 3.14) +project(embt_compiler_id C) +# The parent test reads this line back to assert the detected identity. +message(STATUS "DRIVER_COMPILER_ID=[${CMAKE_C_COMPILER_ID}]") diff --git a/Tests/RunCMake/EMBTClang/cxx-module-scanning.cmake b/Tests/RunCMake/EMBTClang/cxx-module-scanning.cmake new file mode 100644 index 0000000000..54e1a4fa8f --- /dev/null +++ b/Tests/RunCMake/EMBTClang/cxx-module-scanning.cmake @@ -0,0 +1,80 @@ +# C++ module scanning is not supported: a C++20 target that includes standard +# headers must build without being scanned, even when clang-scan-deps is +# present beside bcc64x, and a target with C++ module sources must be rejected +# at configure time. Driver-gated: a no-op where bcc64x is not installed. +if(NOT WIN32) + return() +endif() + +set(_hints "") +if(DEFINED ENV{TP}) + list(APPEND _hints "$ENV{TP}/bin64" "$ENV{TP}/bin") +endif() +find_program(EMBT_BCC64X NAMES bcc64x HINTS ${_hints}) +if(NOT EMBT_BCC64X) + message(STATUS "bcc64x not found; C++ module scanning check skipped.") + return() +endif() +get_filename_component(_bindir "${EMBT_BCC64X}" DIRECTORY) + +function(_scan_configure case) + set(_b "${CMAKE_CURRENT_BINARY_DIR}/scan-${case}") + file(REMOVE_RECURSE "${_b}") + execute_process( + COMMAND ${CMAKE_COMMAND} + -S "${CMAKE_CURRENT_SOURCE_DIR}/cxx-module-scanning" + -B "${_b}" + -G "${CMAKE_GENERATOR}" + "-DCMAKE_MAKE_PROGRAM=${CMAKE_MAKE_PROGRAM}" + "-DCMAKE_CXX_COMPILER=${EMBT_BCC64X}" + ${ARGN} + OUTPUT_VARIABLE _out ERROR_VARIABLE _err RESULT_VARIABLE _rc) + set(_rc "${_rc}" PARENT_SCOPE) + set(_log "${_out}${_err}" PARENT_SCOPE) + set(_b "${_b}" PARENT_SCOPE) +endfunction() + +# A C++20 target builds without scan steps, also when a scanner is named. +set(_cases plain) +if(EXISTS "${_bindir}/clang-scan-deps.exe") + list(APPEND _cases scanner) +endif() +foreach(_case IN LISTS _cases) + set(_opts "") + if(_case STREQUAL "scanner") + set(_opts + "-DCMAKE_CXX_COMPILER_CLANG_SCAN_DEPS=${_bindir}/clang-scan-deps.exe") + endif() + _scan_configure(${_case} ${_opts}) + if(NOT _rc EQUAL 0) + message(SEND_ERROR "configure (${_case}) failed:\n${_log}") + continue() + endif() + execute_process( + COMMAND ${CMAKE_COMMAND} --build "${_b}" + OUTPUT_VARIABLE _out ERROR_VARIABLE _err RESULT_VARIABLE _rc) + if(NOT _rc EQUAL 0) + message(SEND_ERROR "build (${_case}) failed:\n${_out}${_err}") + continue() + endif() + if(CMAKE_GENERATOR MATCHES "Ninja") + file(GLOB _ninja_files "${_b}/build*.ninja") + foreach(_f IN LISTS _ninja_files) + file(STRINGS "${_f}" _steps REGEX "^build [^:]*: CXX_SCAN__") + if(_steps) + message(SEND_ERROR "sources are scanned (${_case}):\n${_steps}") + endif() + endforeach() + endif() +endforeach() + +# A target with C++ module sources is rejected. +_scan_configure(module -DWITH_MODULE=ON) +set(_want "does not provide a way to discover the import graph dependencies") +string(REGEX REPLACE "[ \n]+" " " _flat "${_log}") +string(FIND "${_flat}" "${_want}" _pos) +if(_rc EQUAL 0 OR _pos EQUAL -1) + message(SEND_ERROR + "a CXX_MODULES target should be rejected with:\n ${_want}\n" + "Result: ${_rc}\n${_log}") +endif() diff --git a/Tests/RunCMake/EMBTClang/cxx-module-scanning/CMakeLists.txt b/Tests/RunCMake/EMBTClang/cxx-module-scanning/CMakeLists.txt new file mode 100644 index 0000000000..aaadac30ef --- /dev/null +++ b/Tests/RunCMake/EMBTClang/cxx-module-scanning/CMakeLists.txt @@ -0,0 +1,13 @@ +cmake_minimum_required(VERSION 3.28) +project(scantest CXX) + +# CMP0155 is NEW, so a C++20 target would be scanned for module dependencies +# if the toolchain provided a scanning rule. +add_library(scantest STATIC scantest.cpp) +target_compile_features(scantest PRIVATE cxx_std_20) + +if(WITH_MODULE) + add_library(modtest STATIC) + target_sources(modtest PUBLIC FILE_SET CXX_MODULES FILES modtest.cppm) + target_compile_features(modtest PUBLIC cxx_std_20) +endif() diff --git a/Tests/RunCMake/EMBTClang/cxx-module-scanning/modtest.cppm b/Tests/RunCMake/EMBTClang/cxx-module-scanning/modtest.cppm new file mode 100644 index 0000000000..5d8d3f4a02 --- /dev/null +++ b/Tests/RunCMake/EMBTClang/cxx-module-scanning/modtest.cppm @@ -0,0 +1,6 @@ +export module modtest; + +export int modtest() +{ + return 0; +} diff --git a/Tests/RunCMake/EMBTClang/cxx-module-scanning/scantest.cpp b/Tests/RunCMake/EMBTClang/cxx-module-scanning/scantest.cpp new file mode 100644 index 0000000000..3e7e36f6a6 --- /dev/null +++ b/Tests/RunCMake/EMBTClang/cxx-module-scanning/scantest.cpp @@ -0,0 +1,7 @@ +#include + +int scantest() +{ + std::cout << "scantest\n"; + return 0; +} diff --git a/Tests/RunCMake/EMBTClang/embt-properties.cmake b/Tests/RunCMake/EMBTClang/embt-properties.cmake new file mode 100644 index 0000000000..37cee91a49 --- /dev/null +++ b/Tests/RunCMake/EMBTClang/embt-properties.cmake @@ -0,0 +1,18 @@ +enable_language(C) + +function(expect_prop tgt prop want) + get_property(got TARGET ${tgt} PROPERTY ${prop}) + if(NOT "${got}" STREQUAL "${want}") + message(SEND_ERROR "${tgt} ${prop}: expected '${want}', got '${got}'") + endif() +endfunction() + +# A target created before the variable is set must not inherit it. +add_library(before empty.c) +expect_prop(before EMBT_RUNTIME_LIBRARY "") + +# The CMAKE_EMBT_RUNTIME_LIBRARY variable initializes the property at creation. +set(CMAKE_EMBT_RUNTIME_LIBRARY "Dynamic") + +add_library(after empty.c) +expect_prop(after EMBT_RUNTIME_LIBRARY "Dynamic") diff --git a/Tests/RunCMake/EMBTClang/empty.c b/Tests/RunCMake/EMBTClang/empty.c new file mode 100644 index 0000000000..f8ff78454e --- /dev/null +++ b/Tests/RunCMake/EMBTClang/empty.c @@ -0,0 +1,4 @@ +int embt_empty(void) +{ + return 0; +} diff --git a/Tests/RunCMake/EMBTClang/pch-reuse-kinds.cmake b/Tests/RunCMake/EMBTClang/pch-reuse-kinds.cmake new file mode 100644 index 0000000000..1968f0d7b5 --- /dev/null +++ b/Tests/RunCMake/EMBTClang/pch-reuse-kinds.cmake @@ -0,0 +1,45 @@ +# One precompiled header, built by a static library and reused by a shared +# library, a module library, a console executable and a GUI executable. bcc64x +# records some target-kind switches in a precompiled header and rejects a +# mismatch (E904), so they must not reach compilation; each target must still +# see its own __DLL__/__CONSOLE__ state. The sources #error on the wrong +# state, so a clean build is the assertion. Driver-gated; a no-op where bcc64x +# is absent. + +if(NOT WIN32) + return() +endif() + +set(_hints "") +if(DEFINED ENV{TP}) + list(APPEND _hints "$ENV{TP}/bin64") +endif() +find_program(EMBT_BCC64X NAMES bcc64x HINTS ${_hints}) +if(NOT EMBT_BCC64X) + message(STATUS + "bcc64x not found on PATH or under $TP; PCH reuse check skipped.") + return() +endif() + +set(_bin "${CMAKE_CURRENT_BINARY_DIR}/pch-reuse-kinds-build") +file(REMOVE_RECURSE "${_bin}") +execute_process( + COMMAND ${CMAKE_COMMAND} + -S "${CMAKE_CURRENT_SOURCE_DIR}/pch-reuse-kinds" + -B "${_bin}" + -G "${CMAKE_GENERATOR}" + "-DCMAKE_MAKE_PROGRAM=${CMAKE_MAKE_PROGRAM}" + "-DCMAKE_CXX_COMPILER=${EMBT_BCC64X}" + RESULT_VARIABLE _rc OUTPUT_VARIABLE _o ERROR_VARIABLE _e) +if(NOT _rc EQUAL 0) + message(SEND_ERROR "configure failed:\n${_o}${_e}") + return() +endif() +execute_process( + COMMAND ${CMAKE_COMMAND} --build "${_bin}" + RESULT_VARIABLE _rc OUTPUT_VARIABLE _o ERROR_VARIABLE _e) +if(NOT _rc EQUAL 0) + message(SEND_ERROR + "build failed -- a target could not reuse the precompiled header, or saw " + "the wrong __DLL__/__CONSOLE__ state:\n${_o}${_e}") +endif() diff --git a/Tests/RunCMake/EMBTClang/pch-reuse-kinds/CMakeLists.txt b/Tests/RunCMake/EMBTClang/pch-reuse-kinds/CMakeLists.txt new file mode 100644 index 0000000000..7302135bda --- /dev/null +++ b/Tests/RunCMake/EMBTClang/pch-reuse-kinds/CMakeLists.txt @@ -0,0 +1,13 @@ +cmake_minimum_required(VERSION 3.23) +project(embt_pch_reuse CXX) + +add_library(pch STATIC lib.cpp) +target_precompile_headers(pch PRIVATE pch.h) + +add_library(dll SHARED dll.cpp) +add_library(mod MODULE dll.cpp) +add_executable(console console.cpp) +add_executable(gui WIN32 gui.cpp) +foreach(t IN ITEMS dll mod console gui) + target_precompile_headers(${t} REUSE_FROM pch) +endforeach() diff --git a/Tests/RunCMake/EMBTClang/pch-reuse-kinds/console.cpp b/Tests/RunCMake/EMBTClang/pch-reuse-kinds/console.cpp new file mode 100644 index 0000000000..895b8d4a5c --- /dev/null +++ b/Tests/RunCMake/EMBTClang/pch-reuse-kinds/console.cpp @@ -0,0 +1,8 @@ +// A console executable must be compiled with __CONSOLE__ and without __DLL__. +#if !defined(__CONSOLE__) || defined(__DLL__) +# error "console target compiled with the wrong __CONSOLE__/__DLL__ state" +#endif +int main() +{ + return static_cast(std::string().size()); +} diff --git a/Tests/RunCMake/EMBTClang/pch-reuse-kinds/dll.cpp b/Tests/RunCMake/EMBTClang/pch-reuse-kinds/dll.cpp new file mode 100644 index 0000000000..a106c55014 --- /dev/null +++ b/Tests/RunCMake/EMBTClang/pch-reuse-kinds/dll.cpp @@ -0,0 +1,8 @@ +// A shared or module library must be compiled with __DLL__. +#ifndef __DLL__ +# error "DLL compiled without __DLL__" +#endif +__declspec(dllexport) int dll_value() +{ + return static_cast(std::string("dll").size()); +} diff --git a/Tests/RunCMake/EMBTClang/pch-reuse-kinds/gui.cpp b/Tests/RunCMake/EMBTClang/pch-reuse-kinds/gui.cpp new file mode 100644 index 0000000000..743dd8f0b3 --- /dev/null +++ b/Tests/RunCMake/EMBTClang/pch-reuse-kinds/gui.cpp @@ -0,0 +1,8 @@ +// A GUI (WIN32) executable must be compiled without __CONSOLE__ and __DLL__. +#if defined(__CONSOLE__) || defined(__DLL__) +# error "GUI target compiled with __CONSOLE__ or __DLL__" +#endif +extern "C" int __stdcall WinMain(void*, void*, char*, int) +{ + return static_cast(std::string().size()); +} diff --git a/Tests/RunCMake/EMBTClang/pch-reuse-kinds/lib.cpp b/Tests/RunCMake/EMBTClang/pch-reuse-kinds/lib.cpp new file mode 100644 index 0000000000..8c80ccbcb8 --- /dev/null +++ b/Tests/RunCMake/EMBTClang/pch-reuse-kinds/lib.cpp @@ -0,0 +1,8 @@ +// A static library is neither a DLL nor a console target. +#if defined(__DLL__) || defined(__CONSOLE__) +# error "static library compiled with __DLL__ or __CONSOLE__" +#endif +std::string lib_value() +{ + return "lib"; +} diff --git a/Tests/RunCMake/EMBTClang/pch-reuse-kinds/pch.h b/Tests/RunCMake/EMBTClang/pch-reuse-kinds/pch.h new file mode 100644 index 0000000000..d5a7322872 --- /dev/null +++ b/Tests/RunCMake/EMBTClang/pch-reuse-kinds/pch.h @@ -0,0 +1 @@ +#include diff --git a/Tests/RunCMake/EMBTClang/runtime-library.cmake b/Tests/RunCMake/EMBTClang/runtime-library.cmake new file mode 100644 index 0000000000..f1080754f1 --- /dev/null +++ b/Tests/RunCMake/EMBTClang/runtime-library.cmake @@ -0,0 +1,107 @@ +# EMBT_RUNTIME_LIBRARY must select the linked C++ runtime: Dynamic links the +# shared libc++ (an import of libc++-.dll), Static (and the default) link it +# statically (no such import). Build a real exe and module library for each +# value with bcc64x and inspect their imports -- a configure-only check cannot +# tell them apart, since the switches only change the link. The module library +# must also be compiled with __DLL__, seen as an export only defined under it. +# The helper project also carries a .rc source, so this guards against the +# runtime-library handling mishandling a non-C/CXX language. Driver-gated: a +# no-op where bcc64x is not installed. +if(NOT WIN32) + return() +endif() + +set(_hints "") +if(DEFINED ENV{TP}) + list(APPEND _hints "$ENV{TP}/bin64" "$ENV{TP}/bin") +endif() +find_program(EMBT_BCC64X NAMES bcc64x HINTS ${_hints}) +if(NOT EMBT_BCC64X) + message(STATUS "bcc64x not found; EMBT_RUNTIME_LIBRARY link check skipped.") + return() +endif() +get_filename_component(_bindir "${EMBT_BCC64X}" DIRECTORY) +find_program(EMBT_OBJDUMP NAMES llvm-objdump HINTS "${_bindir}") +if(NOT EMBT_OBJDUMP) + message(STATUS + "llvm-objdump not found; EMBT_RUNTIME_LIBRARY link check skipped.") + return() +endif() + +# "" exercises the default (Static); Static and Dynamic the explicit values. +foreach(_val "" Static Dynamic) + if(_val STREQUAL "") + set(_tag default) + else() + set(_tag "${_val}") + endif() + set(_b "${CMAKE_CURRENT_BINARY_DIR}/rtl-${_tag}") + execute_process( + COMMAND ${CMAKE_COMMAND} + -S "${CMAKE_CURRENT_SOURCE_DIR}/runtime-library" + -B "${_b}" + -G "${CMAKE_GENERATOR}" + "-DCMAKE_MAKE_PROGRAM=${CMAKE_MAKE_PROGRAM}" + "-DCMAKE_CXX_COMPILER=${EMBT_BCC64X}" + "-DRTL_VALUE=${_val}" + OUTPUT_VARIABLE _out ERROR_VARIABLE _err RESULT_VARIABLE _rc) + if(NOT _rc EQUAL 0) + message(SEND_ERROR "configure (${_tag}) failed:\n${_out}${_err}") + continue() + endif() + execute_process( + COMMAND ${CMAKE_COMMAND} --build "${_b}" + OUTPUT_VARIABLE _out ERROR_VARIABLE _err RESULT_VARIABLE _rc) + if(NOT _rc EQUAL 0) + message(SEND_ERROR "build (${_tag}) failed:\n${_out}${_err}") + continue() + endif() + foreach(_art IN ITEMS rtltest.exe rtlmod.dll) + execute_process( + COMMAND "${EMBT_OBJDUMP}" -p "${_b}/${_art}" + OUTPUT_VARIABLE _imports RESULT_VARIABLE _od_rc) + if(NOT _od_rc EQUAL 0) + message(SEND_ERROR + "llvm-objdump (${_tag}, ${_art}) failed:\n${_imports}") + continue() + endif() + if(_imports MATCHES "libc\\+\\+[-0-9]*\\.dll") + set(_dynamic YES) + else() + set(_dynamic NO) + endif() + if(_val STREQUAL "Dynamic" AND NOT _dynamic) + message(SEND_ERROR + "EMBT_RUNTIME_LIBRARY=Dynamic should link the shared libc++, but " + "${_art} has no libc++ import.") + elseif(NOT _val STREQUAL "Dynamic" AND _dynamic) + message(SEND_ERROR + "EMBT_RUNTIME_LIBRARY=${_tag} should link libc++ statically, but " + "${_art} imports the shared libc++.") + endif() + if(_art STREQUAL "rtlmod.dll" AND + NOT _imports MATCHES "rtlmod_has_dll_macro") + message(SEND_ERROR + "The module library (${_tag}) was not compiled with __DLL__.") + endif() + endforeach() +endforeach() + +# An unknown value is rejected when generating for the bcc64x toolchain. +execute_process( + COMMAND ${CMAKE_COMMAND} + -S "${CMAKE_CURRENT_SOURCE_DIR}/runtime-library" + -B "${CMAKE_CURRENT_BINARY_DIR}/rtl-Bogus" + -G "${CMAKE_GENERATOR}" + "-DCMAKE_MAKE_PROGRAM=${CMAKE_MAKE_PROGRAM}" + "-DCMAKE_CXX_COMPILER=${EMBT_BCC64X}" + "-DRTL_VALUE=Bogus" + OUTPUT_VARIABLE _out ERROR_VARIABLE _err RESULT_VARIABLE _rc) +set(_want + "EMBT_RUNTIME_LIBRARY value 'Bogus' not known for this CXX compiler.") +string(FIND "${_err}" "${_want}" _pos) +if(_rc EQUAL 0 OR _pos EQUAL -1) + message(SEND_ERROR + "EMBT_RUNTIME_LIBRARY=Bogus should be rejected with:\n ${_want}\n" + "Result: ${_rc}\n${_out}${_err}") +endif() diff --git a/Tests/RunCMake/EMBTClang/runtime-library/CMakeLists.txt b/Tests/RunCMake/EMBTClang/runtime-library/CMakeLists.txt new file mode 100644 index 0000000000..1abcbc66bb --- /dev/null +++ b/Tests/RunCMake/EMBTClang/runtime-library/CMakeLists.txt @@ -0,0 +1,18 @@ +cmake_minimum_required(VERSION 3.28) +project(rtltest CXX RC) + +# A console exe that uses libc++ (std::string), so the runtime-library choice is +# observable in its imports. It also carries a .rc resource -- which a RAD +# Studio WIN32 app almost always has -- to guard against the runtime-library +# handling mishandling a non-C/CXX language (the RC "compiler" has no runtime +# library). RTL_VALUE is passed as Static, Dynamic, or empty (the default). +add_executable(rtltest main.cpp app.rc) + +# A module library takes the same runtime-library choice, and must also be +# compiled with __DLL__ defined. +add_library(rtlmod MODULE module.cpp) + +if(NOT RTL_VALUE STREQUAL "") + set_property(TARGET rtltest rtlmod + PROPERTY EMBT_RUNTIME_LIBRARY "${RTL_VALUE}") +endif() diff --git a/Tests/RunCMake/EMBTClang/runtime-library/app.rc b/Tests/RunCMake/EMBTClang/runtime-library/app.rc new file mode 100644 index 0000000000..99b7ed7ca6 --- /dev/null +++ b/Tests/RunCMake/EMBTClang/runtime-library/app.rc @@ -0,0 +1,4 @@ +STRINGTABLE +BEGIN + 1 "EMBTClang runtime-library test" +END diff --git a/Tests/RunCMake/EMBTClang/runtime-library/main.cpp b/Tests/RunCMake/EMBTClang/runtime-library/main.cpp new file mode 100644 index 0000000000..c43f2435c5 --- /dev/null +++ b/Tests/RunCMake/EMBTClang/runtime-library/main.cpp @@ -0,0 +1,6 @@ +#include +int main() +{ + std::string s = "x"; + return static_cast(s.size()); +} diff --git a/Tests/RunCMake/EMBTClang/runtime-library/module.cpp b/Tests/RunCMake/EMBTClang/runtime-library/module.cpp new file mode 100644 index 0000000000..a7656c2d73 --- /dev/null +++ b/Tests/RunCMake/EMBTClang/runtime-library/module.cpp @@ -0,0 +1,14 @@ +#include + +extern "C" __declspec(dllexport) int rtlmod_size() +{ + std::string s = "x"; + return static_cast(s.size()); +} + +#ifdef __DLL__ +extern "C" __declspec(dllexport) int rtlmod_has_dll_macro() +{ + return 1; +} +#endif diff --git a/Tests/RunCMake/EMBTClang/subsystem-macros.cmake b/Tests/RunCMake/EMBTClang/subsystem-macros.cmake new file mode 100644 index 0000000000..484a1dd2cd --- /dev/null +++ b/Tests/RunCMake/EMBTClang/subsystem-macros.cmake @@ -0,0 +1,57 @@ +# Regression guard for C3: a console target is compiled with __CONSOLE__, which +# -tC defines in the IDE build, not only linked with -tC, and a GUI target is +# not -- and compile uses the same per-config WIN32_EXECUTABLE determination as +# the link, so a genex-valued property cannot make the two disagree. The +# sources #error on the wrong __CONSOLE__ state, so a clean build is the +# assertion. Driver-gated; a no-op where bcc64x is absent. + +if(NOT WIN32) + return() +endif() + +set(_hints "") +if(DEFINED ENV{TP}) + list(APPEND _hints "$ENV{TP}/bin64") +endif() +find_program(EMBT_BCC64X NAMES bcc64x HINTS ${_hints}) +if(NOT EMBT_BCC64X) + message(STATUS "bcc64x not found on PATH or under $TP; __CONSOLE__ check skipped.") + return() +endif() + +function(_subsystem_build case config) + set(_bin "${CMAKE_CURRENT_BINARY_DIR}/${case}") + file(REMOVE_RECURSE "${_bin}") + set(_cfg_arg "") + if(config) + set(_cfg_arg "-DCMAKE_BUILD_TYPE=${config}") + endif() + execute_process( + COMMAND ${CMAKE_COMMAND} + -S "${CMAKE_CURRENT_SOURCE_DIR}/subsystem-macros" + -B "${_bin}" + -G "${CMAKE_GENERATOR}" + "-DCMAKE_MAKE_PROGRAM=${CMAKE_MAKE_PROGRAM}" + "-DCMAKE_CXX_COMPILER=${EMBT_BCC64X}" + "-DCASE=${case}" + ${_cfg_arg} + RESULT_VARIABLE _rc OUTPUT_VARIABLE _o ERROR_VARIABLE _e) + if(NOT _rc EQUAL 0) + message(SEND_ERROR "CASE ${case}: configure failed:\n${_o}${_e}") + return() + endif() + execute_process( + COMMAND ${CMAKE_COMMAND} --build "${_bin}" + RESULT_VARIABLE _rc OUTPUT_VARIABLE _o ERROR_VARIABLE _e) + if(NOT _rc EQUAL 0) + message(SEND_ERROR + "CASE ${case}: build failed -- a target compiled with the wrong " + "__CONSOLE__ state:\n${_o}${_e}") + endif() +endfunction() + +# Plain bool: console gets __CONSOLE__, GUI does not. +_subsystem_build(plain "") +# Genex WIN32_EXECUTABLE ($): GUI in a Release build, so it must +# not get __CONSOLE__ -- catches a non-genex read that would pick console. +_subsystem_build(genex "Release") diff --git a/Tests/RunCMake/EMBTClang/subsystem-macros/CMakeLists.txt b/Tests/RunCMake/EMBTClang/subsystem-macros/CMakeLists.txt new file mode 100644 index 0000000000..9e87f725f1 --- /dev/null +++ b/Tests/RunCMake/EMBTClang/subsystem-macros/CMakeLists.txt @@ -0,0 +1,14 @@ +cmake_minimum_required(VERSION 3.23) +project(embt_subsystem CXX) + +if(NOT DEFINED CASE OR CASE STREQUAL "plain") + add_executable(console console.cpp) # console (default subsystem) + add_executable(gui WIN32 gui.cpp) # GUI subsystem +elseif(CASE STREQUAL "genex") + # WIN32_EXECUTABLE as a per-config genex: GUI in Release, console otherwise. + # Built in Release, so it is GUI and must NOT see __CONSOLE__ (gui.cpp asserts + # that). A non-genex read of the property would pick console here and define + # __CONSOLE__, tripping the #error -- so this locks compile==link per config. + add_executable(genexgui gui.cpp) + set_target_properties(genexgui PROPERTIES WIN32_EXECUTABLE "$") +endif() diff --git a/Tests/RunCMake/EMBTClang/subsystem-macros/console.cpp b/Tests/RunCMake/EMBTClang/subsystem-macros/console.cpp new file mode 100644 index 0000000000..f6b2b02c2f --- /dev/null +++ b/Tests/RunCMake/EMBTClang/subsystem-macros/console.cpp @@ -0,0 +1,8 @@ +// A console target must be compiled with __CONSOLE__. +#ifndef __CONSOLE__ +# error "console target compiled without __CONSOLE__" +#endif +int main() +{ + return 0; +} diff --git a/Tests/RunCMake/EMBTClang/subsystem-macros/gui.cpp b/Tests/RunCMake/EMBTClang/subsystem-macros/gui.cpp new file mode 100644 index 0000000000..658877c3df --- /dev/null +++ b/Tests/RunCMake/EMBTClang/subsystem-macros/gui.cpp @@ -0,0 +1,8 @@ +// A GUI (WIN32) target must NOT be compiled with __CONSOLE__. +#ifdef __CONSOLE__ +# error "GUI target compiled with __CONSOLE__" +#endif +extern "C" int __stdcall WinMain(void*, void*, char*, int) +{ + return 0; +} diff --git a/Tests/RunCMake/FileAPI/codemodel-v2-check.py b/Tests/RunCMake/FileAPI/codemodel-v2-check.py index 3d5388472a..008ae025e6 100644 --- a/Tests/RunCMake/FileAPI/codemodel-v2-check.py +++ b/Tests/RunCMake/FileAPI/codemodel-v2-check.py @@ -1133,7 +1133,7 @@ def gen_check_build_system_targets(c, g, inSource): e["compileGroups"] = apple_exe_framework["compileGroups"] e["link"] = apple_exe_framework["link"] - if args.cxx_compiler_id in ['Clang', 'AppleClang', 'LCC', 'GNU', 'Intel', 'IntelLLVM', 'DPClang', 'MSVC', 'Embarcadero', 'CrayClang', 'IBMClang'] and g["name"] != "Xcode": + if args.cxx_compiler_id in ['Clang', 'AppleClang', 'LCC', 'GNU', 'Intel', 'IntelLLVM', 'DPClang', 'MSVC', 'Embarcadero', 'CrayClang', 'EMBTClang', 'IBMClang'] and g["name"] != "Xcode": for e in expected: if e["name"] == "cxx_exe": if matches(g["name"], "^(Visual Studio |Ninja Multi-Config)"): diff --git a/Tests/RunCMake/property_init/CompileSources.cmake b/Tests/RunCMake/property_init/CompileSources.cmake index 75aff5265f..7ca9f2a49f 100644 --- a/Tests/RunCMake/property_init/CompileSources.cmake +++ b/Tests/RunCMake/property_init/CompileSources.cmake @@ -30,6 +30,8 @@ set(properties "VS_JUST_MY_CODE_DEBUGGING" "ON" "" ### OpenWatcom "WATCOM_RUNTIME_LIBRARY" "MultiThreaded" "" + ### Embarcadero + "EMBT_RUNTIME_LIBRARY" "Dynamic" "" ## Language ### CUDA "CUDA_SEPARABLE_COMPILATION" "ON" "" diff --git a/Tests/SetLang/CMakeLists.txt b/Tests/SetLang/CMakeLists.txt index edfb61c23d..0d82d42d9d 100644 --- a/Tests/SetLang/CMakeLists.txt +++ b/Tests/SetLang/CMakeLists.txt @@ -6,6 +6,10 @@ endif() project(SetLang) # force this to be verbose so I can debug a dashboard entry set(CMAKE_VERBOSE_MAKEFILE 1) +if(CMAKE_C_COMPILER_ID MATCHES "(GNU|LCC|Clang|MSVC|Borland|Embarcadero|Intel|TI|XL)") + set(have_explicit_language 1) + cmake_policy(SET CMP0119 NEW) +endif() add_library(foo foo.c) add_executable(SetLang bar.c) set_source_files_properties(foo.c bar.c PROPERTIES LANGUAGE CXX) @@ -23,8 +27,7 @@ if(CMAKE_GENERATOR MATCHES "^Visual Studio" AND "x${CMAKE_C_COMPILER_ID}" STREQU set_property(TARGET stay PROPERTY COMPILE_OPTIONS "-TP") endif() -if((CMAKE_C_COMPILER_ID MATCHES "(GNU|LCC|Clang|MSVC|Borland|Embarcadero|Intel|TI|XL)")) - cmake_policy(SET CMP0119 NEW) +if(have_explicit_language) add_library(zoom zoom.zzz) set_source_files_properties(zoom.zzz PROPERTIES LANGUAGE CXX) target_link_libraries(SetLang zoom)