diff options
Diffstat (limited to 'rcc')
30 files changed, 8144 insertions, 0 deletions
diff --git a/rcc/CMakeLists.txt b/rcc/CMakeLists.txt new file mode 100644 index 0000000..1fb86a0 --- /dev/null +++ b/rcc/CMakeLists.txt @@ -0,0 +1,75 @@ +# SPDX-License-Identifier: GPL-3.0-or-later + +cmake_minimum_required(VERSION 3.25) +include("${CMAKE_CURRENT_LIST_DIR}/../cmake/Modules/CMakePolicy.cmake" NO_POLICY_SCOPE) +include("${CMAKE_CURRENT_LIST_DIR}/../cmake/Modules/RCCVersion.cmake") + +project(RCC VERSION "${RCC_VERSION}" LANGUAGES C CXX) + +if(CMAKE_SOURCE_DIR STREQUAL CMAKE_BINARY_DIR) + message(FATAL_ERROR "RCC requires an out-of-source build.") +endif() + +set(RCC_SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}") +set(RCC_MAIN_SRC_DIR "${RCC_SOURCE_DIR}") +set(RCC_BINARY_DIR "${CMAKE_CURRENT_BINARY_DIR}") +set(RCC_CMAKE_DIR "${RCC_SOURCE_DIR}/cmake/modules") +set(RCC_INCLUDE_DIRS "${RCC_SOURCE_DIR}/include;${RCC_BINARY_DIR}/include") +set(RCC_ENABLE_PROJECTS "" CACHE STRING "Additional projects to build: cfront") +set(CMAKE_EXPORT_COMPILE_COMMANDS ON CACHE BOOL "Export the compilation database") +file(MAKE_DIRECTORY "${RCC_BINARY_DIR}/include/rcc/Config") + +list(PREPEND CMAKE_MODULE_PATH "${RCC_CMAKE_DIR}") +include(GNUInstallDirs) +include(HandleRCCOptions) +include(AddRCC) +rcc_configure_options() +rcc_configure_output_directories() +set(RCC_LIBRARY_DIR "${RCC_LIBRARY_OUTPUT_INTDIR}") + +set(RCC_TARGET_ARCH "${CMAKE_SYSTEM_PROCESSOR}" CACHE STRING + "Target architecture used for RCC configuration") +set(RCC_TARGETS_TO_BUILD "Native" CACHE STRING + "Target architectures built by RCC") +option(RCC_ENABLE_THREADS "Use threads if available." ON) +set(RCC_DEFAULT_TARGET_TRIPLE "" CACHE STRING + "Target triple RCC will generate code for by default") +include("${RCC_SOURCE_DIR}/cmake/config-ix.cmake") +if(RCC_DEFAULT_TARGET_TRIPLE STREQUAL "") + set(RCC_DEFAULT_TARGET_TRIPLE "${RCC_HOST_TRIPLE}" CACHE STRING + "Target triple RCC will generate code for by default" FORCE) +endif() + +set(RCC_MAIN_INCLUDE_DIR "${RCC_SOURCE_DIR}/include") +set(RCC_INCLUDE_DIR "${RCC_BINARY_DIR}/include") +set(PACKAGE_VERSION "${RCC_VERSION}") +set(RCC_BUILD_RCC_DYLIB OFF) +set(RCC_BUILD_SHARED_LIBS "${BUILD_SHARED_LIBS}") + +configure_file( + "${RCC_MAIN_INCLUDE_DIR}/rcc/Config/config.h.cmake" + "${RCC_INCLUDE_DIR}/rcc/Config/config.h") +configure_file( + "${RCC_MAIN_INCLUDE_DIR}/rcc/Config/rcc-config.h.cmake" + "${RCC_INCLUDE_DIR}/rcc/Config/rcc-config.h") + +install(DIRECTORY include/rcc DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}" + FILES_MATCHING PATTERN "*.h" PATTERN "*.inc" PATTERN "*.def") +install(DIRECTORY "${RCC_BINARY_DIR}/include/rcc/Config" DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}/rcc" + FILES_MATCHING PATTERN "*.h" PATTERN "*.inc" PATTERN "*.def" + PATTERN "config.h" EXCLUDE) + +set(RCC_LIBBASE_SOURCE_DIR "${RCC_SOURCE_DIR}/../libbase") +set(RCC_LIBBASE_BINARY_DIR "${RCC_BINARY_DIR}/libbase") +add_subdirectory("${RCC_LIBBASE_SOURCE_DIR}" "${RCC_LIBBASE_BINARY_DIR}") +add_subdirectory(tools) +foreach(rccProject IN LISTS RCC_ENABLE_PROJECTS) + if(NOT rccProject STREQUAL "cfront") + message(FATAL_ERROR "Unknown RCC project: ${rccProject}. Available: cfront") + endif() +endforeach() +if("cfront" IN_LIST RCC_ENABLE_PROJECTS) + add_subdirectory("${RCC_SOURCE_DIR}/../cfront" "${CMAKE_BINARY_DIR}/cfront") +endif() + +add_subdirectory(cmake/modules) diff --git a/rcc/CMakePresets.json b/rcc/CMakePresets.json new file mode 100644 index 0000000..4e32d8b --- /dev/null +++ b/rcc/CMakePresets.json @@ -0,0 +1,70 @@ +{ + "version": 6, + "cmakeMinimumRequired": { "major": 3, "minor": 25, "patch": 0 }, + "configurePresets": [ + { + "name": "ninja", + "hidden": true, + "generator": "Ninja", + "binaryDir": "${sourceDir}/../build/${presetName}", + "cacheVariables": { "CMAKE_EXPORT_COMPILE_COMMANDS": true } + }, + { + "name": "debug", + "hidden": true, + "cacheVariables": { + "CMAKE_BUILD_TYPE": "Debug", + "RCC_ENABLE_ASSERTIONS": true + } + }, + { + "name": "release", + "hidden": true, + "cacheVariables": { + "CMAKE_BUILD_TYPE": "Release", + "RCC_ENABLE_ASSERTIONS": false + } + }, + { + "name": "assertions", + "hidden": true, + "cacheVariables": { "RCC_ENABLE_ASSERTIONS": true } + }, + { + "name": "asan-ubsan", + "hidden": true, + "cacheVariables": { "RCC_USE_SANITIZER": "Address;Undefined" } + }, + { + "name": "with-cfront", + "hidden": true, + "cacheVariables": { "RCC_ENABLE_PROJECTS": "cfront" } + }, + { + "name": "dev", + "displayName": "RCC debug build", + "inherits": ["ninja", "debug"] + }, + { + "name": "dev-cfront", + "displayName": "RCC and CFront debug build", + "inherits": ["with-cfront", "dev"] + }, + { + "name": "release-assertions", + "displayName": "RCC release build with assertions", + "inherits": ["ninja", "assertions", "release"] + }, + { + "name": "sanitize", + "displayName": "RCC debug build with ASan and UBSan", + "inherits": ["asan-ubsan", "dev"] + } + ], + "buildPresets": [ + { "name": "dev", "configurePreset": "dev" }, + { "name": "dev-cfront", "configurePreset": "dev-cfront" }, + { "name": "release-assertions", "configurePreset": "release-assertions" }, + { "name": "sanitize", "configurePreset": "sanitize" } + ] +} diff --git a/rcc/cmake/config-ix.cmake b/rcc/cmake/config-ix.cmake new file mode 100644 index 0000000..b7468db --- /dev/null +++ b/rcc/cmake/config-ix.cmake @@ -0,0 +1,811 @@ +include(CheckIncludeFile) +include(CheckLibraryExists) +include(CheckSymbolExists) +include(CheckCXXSymbolExists) +include(CheckFunctionExists) +include(CheckStructHasMember) +include(CheckCCompilerFlag) +include(CheckCSourceCompiles) +include(CMakePushCheckState) + +include(CheckCompilerVersion) +include(CheckProblematicConfigurations) +include(HandleRCCStdlib) + +if (ANDROID OR CYGWIN OR CMAKE_SYSTEM_NAME MATCHES "AIX|DragonFly|FreeBSD|Haiku|Linux|NetBSD|OpenBSD|SunOS") + set(HAVE_MACH_MACH_H 0) + set(HAVE_MALLOC_MALLOC_H 0) + set(HAVE_PTHREAD_H 1) + set(HAVE_SYS_MMAN_H 1) + set(HAVE_SYSEXITS_H 1) + set(HAVE_UNISTD_H 1) + set(HAVE_SYS_IOCTL_H 1) +elseif (APPLE) + set(HAVE_MACH_MACH_H 1) + set(HAVE_MALLOC_MALLOC_H 1) + set(HAVE_PTHREAD_H 1) + set(HAVE_SYS_MMAN_H 1) + set(HAVE_SYSEXITS_H 1) + set(HAVE_UNISTD_H 1) + set(HAVE_SYS_IOCTL_H 1) +elseif (WIN32) + set(HAVE_MACH_MACH_H 0) + set(HAVE_MALLOC_MALLOC_H 0) + set(HAVE_PTHREAD_H 0) + set(HAVE_SYS_MMAN_H 0) + set(HAVE_SYSEXITS_H 0) + set(HAVE_UNISTD_H 0) + set(HAVE_SYS_IOCTL_H 0) +elseif (ZOS) + # Confirmed in + # https://github.com/rcc/rcc-project/pull/104706#issuecomment-2297109613 + set(HAVE_MACH_MACH_H 0) + set(HAVE_MALLOC_MALLOC_H 0) + set(HAVE_PTHREAD_H 1) + set(HAVE_SYS_MMAN_H 1) + set(HAVE_SYSEXITS_H 0) + set(HAVE_UNISTD_H 1) + set(HAVE_SYS_IOCTL_H 1) +else() + # Other platforms that we don't promise support for. + check_include_file(mach/mach.h HAVE_MACH_MACH_H) + check_include_file(malloc/malloc.h HAVE_MALLOC_MALLOC_H) + check_include_file(pthread.h HAVE_PTHREAD_H) + check_include_file(sys/mman.h HAVE_SYS_MMAN_H) + check_include_file(sysexits.h HAVE_SYSEXITS_H) + check_include_file(unistd.h HAVE_UNISTD_H) + check_include_file(sys/ioctl.h HAVE_SYS_IOCTL_H) +endif() + +if( UNIX AND NOT (APPLE OR BEOS OR HAIKU) ) + # Used by check_symbol_exists: + list(APPEND CMAKE_REQUIRED_LIBRARIES "m") +endif() +# x86_64 FreeBSD 9.2 requires libcxxrt to be specified explicitly. +if( CMAKE_SYSTEM MATCHES "FreeBSD-9.2-RELEASE" AND + CMAKE_SIZEOF_VOID_P EQUAL 8 ) + list(APPEND CMAKE_REQUIRED_LIBRARIES "cxxrt") +endif() + +# Do checks with _XOPEN_SOURCE and large-file API on AIX, because we will build +# with those too. +if (UNIX AND "${CMAKE_SYSTEM_NAME}" MATCHES "AIX") + list(APPEND CMAKE_REQUIRED_DEFINITIONS "-D_XOPEN_SOURCE=700") + list(APPEND CMAKE_REQUIRED_DEFINITIONS "-D_LARGE_FILE_API") +endif() + +# Do checks with _FILE_OFFSET_BITS=64 on Solaris, because we will build +# with those too. +if (UNIX AND "${CMAKE_SYSTEM_NAME}" MATCHES "SunOS") + list(APPEND CMAKE_REQUIRED_DEFINITIONS "-D_FILE_OFFSET_BITS=64") +endif() + +# Newer POSIX functions aren't available without the appropriate defines. +# Usually those are set by the use of -std=gnuXX, but one can also use the +# newer functions with -std=c(++)XX, i.e. without the GNU language extensions. +# Keep this at the top to make sure we don't add _GNU_SOURCE dependent checks +# before adding it. +check_symbol_exists(__GLIBC__ stdio.h RCC_USING_GLIBC) +if(RCC_USING_GLIBC OR CYGWIN) + add_compile_definitions(_GNU_SOURCE) + list(APPEND CMAKE_REQUIRED_DEFINITIONS "-D_GNU_SOURCE") +endif() + +# enable 64bit off_t on 32bit systems using glibc +if(RCC_USING_GLIBC AND CMAKE_SIZEOF_VOID_P EQUAL 4) + add_compile_definitions(_FILE_OFFSET_BITS=64) + list(APPEND CMAKE_REQUIRED_DEFINITIONS "-D_FILE_OFFSET_BITS=64") +endif() + +# include checks +check_c_source_compiles(" + #if __has_attribute(used) + #define RCC_ATTRIBUTE_USED __attribute__((__used__)) + #else + #define RCC_ATTRIBUTE_USED + #endif + RCC_ATTRIBUTE_USED void *foo() { + return __builtin_thread_pointer(); + } + int main(void) { return 0; }" + HAVE_BUILTIN_THREAD_POINTER) + +if(APPLE) + check_c_source_compiles(" + static const char *__crashreporter_info__ = 0; + asm(\".desc ___crashreporter_info__, 0x10\"); + int main(void) { return 0; }" + HAVE_CRASHREPORTER_INFO) +endif() + +# library checks +if(NOT WIN32) + check_library_exists(pthread pthread_create "" HAVE_LIBPTHREAD) + if (HAVE_LIBPTHREAD) + check_library_exists(pthread pthread_rwlock_init "" HAVE_PTHREAD_RWLOCK_INIT) + else() + # this could be Android + check_library_exists(c pthread_create "" PTHREAD_IN_LIBC) + if (PTHREAD_IN_LIBC) + check_library_exists(c pthread_rwlock_init "" HAVE_PTHREAD_RWLOCK_INIT) + endif() + endif() +endif() + +if(HAVE_LIBPTHREAD) + # We want to find pthreads library and at the moment we do want to + # have it reported as '-l<lib>' instead of '-pthread'. + # TODO: switch to -pthread once the rest of the build system can deal with it. + set(CMAKE_THREAD_PREFER_PTHREAD TRUE) + set(THREADS_HAVE_PTHREAD_ARG Off) + find_package(Threads REQUIRED) + set(RCC_PTHREAD_LIB ${CMAKE_THREAD_LIBS_INIT}) + if(RCC_PTHREAD_LIB) + list(APPEND CMAKE_REQUIRED_LIBRARIES ${RCC_PTHREAD_LIB}) + endif() +endif() + +# Keep the dlopen and rt library checks after FindThreads so that +# CMAKE_REQUIRED_LIBRARIES includes pthread. glibc versions before 2.34 may +# need pthread to satisfy librt dependencies. +if(NOT WIN32) + check_library_exists(dl dlopen "" HAVE_LIBDL) + check_library_exists(rt shm_open "" HAVE_LIBRT) +endif() + +# Check for libpfm. +include(FindLibpfm) + +if(RCC_ENABLE_ZLIB) + if(RCC_ENABLE_ZLIB STREQUAL FORCE_ON) + find_package(ZLIB REQUIRED) + elseif(NOT RCC_USE_SANITIZER MATCHES "Memory.*") + find_package(ZLIB) + endif() + if(ZLIB_FOUND) + # Check if zlib we found is usable; for example, we may have found a 32-bit + # library on a 64-bit system which would result in a link-time failure. + cmake_push_check_state() + list(APPEND CMAKE_REQUIRED_LIBRARIES ZLIB::ZLIB) + check_symbol_exists(compress2 zlib.h HAVE_ZLIB) + cmake_pop_check_state() + if(RCC_ENABLE_ZLIB STREQUAL FORCE_ON AND NOT HAVE_ZLIB) + message(FATAL_ERROR "Failed to configure zlib") + endif() + endif() + set(RCC_ENABLE_ZLIB "${HAVE_ZLIB}") +else() + set(RCC_ENABLE_ZLIB 0) +endif() + +set(zstd_FOUND 0) +if(RCC_ENABLE_ZSTD) + if(RCC_ENABLE_ZSTD STREQUAL FORCE_ON) + find_package(zstd REQUIRED) + if(NOT zstd_FOUND) + message(FATAL_ERROR "Failed to configure zstd, but RCC_ENABLE_ZSTD is FORCE_ON") + endif() + elseif(NOT RCC_USE_SANITIZER MATCHES "Memory.*") + find_package(zstd QUIET) + endif() + + # If RCC_USE_STATIC_ZSTD is specified, make sure we enable zstd only if static + # libraries are found. + if(RCC_USE_STATIC_ZSTD AND NOT TARGET zstd::libzstd_static) + # Fail if RCC_ENABLE_ZSTD is FORCE_ON. + if(RCC_ENABLE_ZSTD STREQUAL FORCE_ON) + message(FATAL_ERROR "Failed to find static zstd libraries, but RCC_USE_STATIC_ZSTD=ON and RCC_ENABLE_ZSTD=FORCE_ON.") + endif() + set(RCC_ENABLE_ZSTD OFF) + else() + set(RCC_ENABLE_ZSTD ${zstd_FOUND}) + endif() +endif() + +if(RCC_ENABLE_LZMA) + if(RCC_ENABLE_LZMA STREQUAL FORCE_ON) + find_package(LibLZMA REQUIRED) + elseif(NOT RCC_USE_SANITIZER MATCHES "Memory.*") + find_package(LibLZMA) + endif() + if(LIBLZMA_FOUND) + # Check that the liblzma we found is usable. FindLibLZMA locates lzma.h and + # link-probes the library, but never compiles the header, so it reports + # success for an installation whose header cannot be included. + cmake_push_check_state() + list(APPEND CMAKE_REQUIRED_LIBRARIES LibLZMA::LibLZMA) + check_symbol_exists(lzma_stream_buffer_decode lzma.h HAVE_LZMA) + cmake_pop_check_state() + if(RCC_ENABLE_LZMA STREQUAL FORCE_ON AND NOT HAVE_LZMA) + message(FATAL_ERROR "Failed to configure lzma") + endif() + endif() + set(RCC_ENABLE_LZMA "${HAVE_LZMA}") +else() + set(RCC_ENABLE_LZMA 0) +endif() + +if(RCC_ENABLE_LIBXML2) + if(RCC_ENABLE_LIBXML2 STREQUAL FORCE_ON) + find_package(LibXml2 REQUIRED) + elseif(NOT RCC_USE_SANITIZER MATCHES "Memory.*") + find_package(LibXml2) + endif() + if(LibXml2_FOUND) + # Check if libxml2 we found is usable; for example, we may have found a 32-bit + # library on a 64-bit system which would result in a link-time failure. + cmake_push_check_state() + list(APPEND CMAKE_REQUIRED_LIBRARIES LibXml2::LibXml2) + check_cxx_symbol_exists(xmlReadMemory libxml/xmlreader.h HAVE_LIBXML2) + cmake_pop_check_state() + if(RCC_ENABLE_LIBXML2 STREQUAL FORCE_ON AND NOT HAVE_LIBXML2) + message(FATAL_ERROR "Failed to configure libxml2") + endif() + + if(RCC_USE_STATIC_LIBXML2) + if(NOT TARGET LibXml2::LibXml2Static) + message(FATAL_ERROR "Failed to find static libxml2 library, but RCC_USE_STATIC_LIBXML2=ON") + endif() + cmake_push_check_state() + list(APPEND CMAKE_REQUIRED_INCLUDES ${LIBXML2_INCLUDE_DIR}) + list(APPEND CMAKE_REQUIRED_LIBRARIES ${LIBXML2_STATIC_LIBRARY} ${LIBXML2_STATIC_DEPS}) + list(APPEND CMAKE_REQUIRED_DEFINITIONS ${LIBXML2_DEFINITIONS}) + check_cxx_symbol_exists(xmlReadMemory libxml/xmlreader.h HAVE_LIBXML2_STATIC) + cmake_pop_check_state() + if(NOT HAVE_LIBXML2_STATIC) + message(FATAL_ERROR "Failed to configure static libxml2, but RCC_USE_STATIC_LIBXML2=ON") + endif() + endif() + endif() + set(RCC_ENABLE_LIBXML2 "${HAVE_LIBXML2}") +endif() + +if(RCC_ENABLE_CURL) + if(RCC_ENABLE_CURL STREQUAL FORCE_ON) + find_package(CURL REQUIRED) + else() + find_package(CURL) + endif() + if(CURL_FOUND) + # Check if curl we found is usable; for example, we may have found a 32-bit + # library on a 64-bit system which would result in a link-time failure. + cmake_push_check_state() + list(APPEND CMAKE_REQUIRED_LIBRARIES CURL::libcurl) + check_symbol_exists(curl_easy_init curl/curl.h HAVE_CURL) + cmake_pop_check_state() + if(RCC_ENABLE_CURL STREQUAL FORCE_ON AND NOT HAVE_CURL) + message(FATAL_ERROR "Failed to configure curl") + endif() + endif() + set(RCC_ENABLE_CURL "${HAVE_CURL}") +endif() + +if(RCC_ENABLE_HTTPLIB) + if(RCC_ENABLE_HTTPLIB STREQUAL FORCE_ON) + find_package(httplib REQUIRED) + else() + find_package(httplib) + endif() + if(HTTPLIB_FOUND) + # Check if the "httplib" we found is usable; for example there may be another + # library with the same name. + cmake_push_check_state() + list(APPEND CMAKE_REQUIRED_LIBRARIES ${HTTPLIB_LIBRARY}) + check_cxx_symbol_exists(CPPHTTPLIB_HTTPLIB_H ${HTTPLIB_HEADER_PATH} HAVE_HTTPLIB) + cmake_pop_check_state() + if(RCC_ENABLE_HTTPLIB STREQUAL FORCE_ON AND NOT HAVE_HTTPLIB) + message(FATAL_ERROR "Failed to configure cpp-httplib") + endif() + endif() + set(RCC_ENABLE_HTTPLIB "${HAVE_HTTPLIB}") +endif() + +# Don't look for these libraries if we're using MSan, since uninstrumented third +# party code may call MSan interceptors like strlen, leading to false positives. +if(NOT RCC_USE_SANITIZER MATCHES "Memory.*") + # Don't look for these libraries on Windows. + if (NOT WIN32) + # Skip libedit if using ASan as it contains memory leaks. + if (RCC_ENABLE_LIBEDIT AND NOT RCC_USE_SANITIZER MATCHES ".*Address.*") + if(RCC_ENABLE_LIBEDIT STREQUAL FORCE_ON) + find_package(LibEdit REQUIRED) + else() + find_package(LibEdit) + endif() + set(HAVE_LIBEDIT "${LibEdit_FOUND}") + else() + set(HAVE_LIBEDIT 0) + endif() + else() + set(HAVE_LIBEDIT 0) + endif() +else() + set(HAVE_LIBEDIT 0) +endif() + +if(RCC_HAS_LOGF128) + include(CheckCXXSymbolExists) + check_cxx_symbol_exists(logf128 math.h HAS_LOGF128) + + if(RCC_HAS_LOGF128 STREQUAL FORCE_ON AND NOT HAS_LOGF128) + message(FATAL_ERROR "Failed to configure logf128") + endif() + + set(RCC_HAS_LOGF128 "${HAS_LOGF128}") +endif() + +if (RCC_ENABLE_ICU STREQUAL FORCE_ON AND RCC_ENABLE_ICONV STREQUAL FORCE_ON) + message(FATAL_ERROR "RCC_ENABLE_ICU and RCC_ENABLE_ICONV should not both be FORCE_ON") +endif() + +# Check for ICU. Only allow an optional, dynamic link for ICU so we don't impact RCC's licensing. +if(RCC_ENABLE_ICU AND NOT(RCC_ENABLE_ICONV STREQUAL FORCE_ON)) + set(LIBRARY_SUFFIXES ${CMAKE_FIND_LIBRARY_SUFFIXES}) + set(CMAKE_FIND_LIBRARY_SUFFIXES "${CMAKE_SHARED_LIBRARY_SUFFIX}") + set(HAVE_WINDOWS_ICU OFF) + if(RCC_ENABLE_WINDOWS_ICU AND WIN32 AND CMAKE_SYSTEM_VERSION VERSION_GREATER_EQUAL "10.0.18362") + message(STATUS "Use Windows vendored ICU") + set(HAVE_WINDOWS_ICU ON) + endif() + if(NOT HAVE_WINDOWS_ICU) + if (RCC_ENABLE_ICU STREQUAL FORCE_ON) + find_package(ICU REQUIRED COMPONENTS uc i18n) + if (NOT ICU_FOUND) + message(FATAL_ERROR "Failed to configure ICU, but RCC_ENABLE_ICU is FORCE_ON") + endif() + else() + find_package(ICU COMPONENTS uc i18n) + endif() + set(HAVE_ICU ${ICU_FOUND}) + else() + set(HAVE_ICU ON) + endif() + set(CMAKE_FIND_LIBRARY_SUFFIXES ${LIBRARY_SUFFIXES}) +endif() + +# Check only for builtin iconv to avoid licensing issues. +if(RCC_ENABLE_ICONV AND NOT HAVE_ICU) + if (RCC_ENABLE_ICONV STREQUAL FORCE_ON) + find_package(Iconv REQUIRED) + if (NOT Iconv_FOUND OR NOT Iconv_IS_BUILT_IN) + message(FATAL_ERROR "Failed to configure iconv, but RCC_ENABLE_ICONV is FORCE_ON") + endif() + else() + find_package(Iconv) + endif() + if(Iconv_FOUND AND Iconv_IS_BUILT_IN) + set(HAVE_ICONV 1) + endif() +endif() + +# function checks +check_symbol_exists(arc4random "stdlib.h" HAVE_DECL_ARC4RANDOM) +find_package(Backtrace) +set(HAVE_BACKTRACE ${Backtrace_FOUND}) +set(BACKTRACE_HEADER ${Backtrace_HEADER}) + +# Prevent check_symbol_exists from using API that is not supported for a given +# deployment target. +check_c_compiler_flag("-Werror=unguarded-availability-new" "C_SUPPORTS_WERROR_UNGUARDED_AVAILABILITY_NEW") +if(C_SUPPORTS_WERROR_UNGUARDED_AVAILABILITY_NEW) + set(CMAKE_REQUIRED_FLAGS "${CMAKE_REQUIRED_FLAGS} -Werror=unguarded-availability-new") +endif() + +# Determine whether we can register EH tables. +check_symbol_exists(__register_frame "${CMAKE_CURRENT_LIST_DIR}/unwind.h" HAVE_REGISTER_FRAME) +check_symbol_exists(__deregister_frame "${CMAKE_CURRENT_LIST_DIR}/unwind.h" HAVE_DEREGISTER_FRAME) +check_symbol_exists(__unw_add_dynamic_fde "${CMAKE_CURRENT_LIST_DIR}/unwind.h" HAVE_UNW_ADD_DYNAMIC_FDE) + +check_symbol_exists(_Unwind_Backtrace "unwind.h" HAVE__UNWIND_BACKTRACE) +check_symbol_exists(getpagesize unistd.h HAVE_GETPAGESIZE) +check_symbol_exists(sysconf unistd.h HAVE_SYSCONF) +check_symbol_exists(getrusage sys/resource.h HAVE_GETRUSAGE) +check_symbol_exists(isatty unistd.h HAVE_ISATTY) +check_symbol_exists(futimens sys/stat.h HAVE_FUTIMENS) +check_symbol_exists(futimes sys/time.h HAVE_FUTIMES) +check_symbol_exists(getauxval sys/auxv.h HAVE_GETAUXVAL) +# AddressSanitizer conflicts with lib/Support/Unix/Signals.inc +# Avoid sigaltstack on Apple platforms, where backtrace() cannot handle it +# (rdar://7089625) and _Unwind_Backtrace is unusable because it cannot unwind +# past the signal handler after an assertion failure (rdar://29866587). +if( NOT RCC_USE_SANITIZER MATCHES ".*Address.*" AND NOT APPLE ) + check_symbol_exists(sigaltstack signal.h HAVE_SIGALTSTACK) +endif() +check_symbol_exists(mallctl malloc_np.h HAVE_MALLCTL) +check_symbol_exists(mallinfo malloc.h HAVE_MALLINFO) +check_symbol_exists(mallinfo2 malloc.h HAVE_MALLINFO2) +check_symbol_exists(malloc_zone_statistics malloc/malloc.h + HAVE_MALLOC_ZONE_STATISTICS) +check_symbol_exists(posix_spawn spawn.h HAVE_POSIX_SPAWN) +check_symbol_exists(pread unistd.h HAVE_PREAD) +check_symbol_exists(sbrk unistd.h HAVE_SBRK) +check_symbol_exists(strerror_r string.h HAVE_STRERROR_R) +check_symbol_exists(strerror_s string.h HAVE_DECL_STRERROR_S) +check_symbol_exists(setenv stdlib.h HAVE_SETENV) +if(WIN32) + check_symbol_exists(_chsize_s io.h HAVE__CHSIZE_S) + + check_function_exists(_alloca HAVE__ALLOCA) + check_function_exists(__alloca HAVE___ALLOCA) + check_function_exists(__chkstk HAVE___CHKSTK) + check_function_exists(__chkstk_ms HAVE___CHKSTK_MS) + check_function_exists(___chkstk HAVE____CHKSTK) + check_function_exists(___chkstk_ms HAVE____CHKSTK_MS) + + check_function_exists(__ashldi3 HAVE___ASHLDI3) + check_function_exists(__ashrdi3 HAVE___ASHRDI3) + check_function_exists(__divdi3 HAVE___DIVDI3) + check_function_exists(__fixdfdi HAVE___FIXDFDI) + check_function_exists(__fixsfdi HAVE___FIXSFDI) + check_function_exists(__floatdidf HAVE___FLOATDIDF) + check_function_exists(__lshrdi3 HAVE___LSHRDI3) + check_function_exists(__moddi3 HAVE___MODDI3) + check_function_exists(__udivdi3 HAVE___UDIVDI3) + check_function_exists(__umoddi3 HAVE___UMODDI3) + + check_function_exists(__main HAVE___MAIN) + check_function_exists(__cmpdi2 HAVE___CMPDI2) +endif() + +CHECK_STRUCT_HAS_MEMBER("struct stat" st_mtimespec.tv_nsec + "sys/types.h;sys/stat.h" HAVE_STRUCT_STAT_ST_MTIMESPEC_TV_NSEC) +if (UNIX AND "${CMAKE_SYSTEM_NAME}" MATCHES "AIX") +# The st_mtim.tv_nsec member of a `stat` structure is not reliable on some AIX +# environments. + set(HAVE_STRUCT_STAT_ST_MTIM_TV_NSEC 0) +else() + CHECK_STRUCT_HAS_MEMBER("struct stat" st_mtim.tv_nsec + "sys/types.h;sys/stat.h" HAVE_STRUCT_STAT_ST_MTIM_TV_NSEC) +endif() + +if (NOT WIN32) + check_symbol_exists(pthread_getname_np pthread.h HAVE_PTHREAD_GETNAME_NP) + check_symbol_exists(pthread_setname_np pthread.h HAVE_PTHREAD_SETNAME_NP) + check_symbol_exists(pthread_get_name_np "pthread.h;pthread_np.h" HAVE_PTHREAD_GET_NAME_NP) + check_symbol_exists(pthread_set_name_np "pthread.h;pthread_np.h" HAVE_PTHREAD_SET_NAME_NP) + + if( HAVE_LIBDL ) + list(APPEND CMAKE_REQUIRED_LIBRARIES dl) + endif() + # Add the _XOPEN_SOURCE macro on z/OS, as certain test(s) use dlopen + if (ZOS) + list(APPEND CMAKE_REQUIRED_DEFINITIONS "-D_XOPEN_SOURCE=600") + endif() + check_symbol_exists(dlopen dlfcn.h HAVE_DLOPEN) + if (ZOS) + list(REMOVE_ITEM CMAKE_REQUIRED_DEFINITIONS "-D_XOPEN_SOURCE=600") + endif() + if( HAVE_LIBDL ) + list(REMOVE_ITEM CMAKE_REQUIRED_LIBRARIES dl) + endif() +endif() + +if(RCC_ENABLE_FFI) + set(FFI_REQUIRE_INCLUDE On) + if(RCC_ENABLE_FFI STREQUAL FORCE_ON) + find_package(FFI REQUIRED) + else() + find_package(FFI) + endif() + set(RCC_ENABLE_FFI "${FFI_FOUND}") +else() + unset(HAVE_FFI_FFI_H CACHE) + unset(HAVE_FFI_H CACHE) + unset(HAVE_FFI_CALL CACHE) +endif() + +check_symbol_exists(proc_pid_rusage "libproc.h" HAVE_PROC_PID_RUSAGE) + +# Define RCC_HAS_ATOMICS if gcc or MSVC atomic builtins are supported. +include(CheckAtomic) + +if( RCC_ENABLE_PIC ) + set(ENABLE_PIC 1) +else() + set(ENABLE_PIC 0) + set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -fno-pie") +endif() + +set(SUPPORTS_VARIADIC_MACROS_FLAG 0) +if (RCC_COMPILER_IS_GCC_COMPATIBLE) + set(SUPPORTS_VARIADIC_MACROS_FLAG 1) +endif() +if (CMAKE_CXX_COMPILER_ID MATCHES "Clang") + set(SUPPORTS_GNU_ZERO_VARIADIC_MACRO_ARGUMENTS_FLAG 1) +else() + set(SUPPORTS_GNU_ZERO_VARIADIC_MACRO_ARGUMENTS_FLAG 0) +endif() + +set(USE_NO_MAYBE_UNINITIALIZED 0) +set(USE_NO_UNINITIALIZED 0) + +# Disable gcc's potentially uninitialized use analysis as it presents lots of +# false positives. +if (CMAKE_COMPILER_IS_GNUCXX) + # Disable all -Wuninitialized warning for old GCC versions. + if (CMAKE_CXX_COMPILER_VERSION VERSION_LESS 15.0) + set(USE_NO_UNINITIALIZED 1) + else() + set(USE_NO_MAYBE_UNINITIALIZED 1) + endif() +endif() + +if(RCC_INCLUDE_TESTS) + include(GetErrcMessages) + get_errc_messages(RCC_LIT_ERRC_MESSAGES) +endif() + +# By default, we target the host, but this can be overridden at CMake +# invocation time. +include(GetHostTriple) +get_host_triple(RCC_INFERRED_HOST_TRIPLE) + +set(RCC_HOST_TRIPLE "${RCC_INFERRED_HOST_TRIPLE}" CACHE STRING + "Host on which RCC binaries will run") +message(STATUS "RCC host triple: ${RCC_HOST_TRIPLE}") + +# Determine the native architecture. +string(TOLOWER "${RCC_TARGET_ARCH}" RCC_NATIVE_ARCH) +if( RCC_NATIVE_ARCH STREQUAL "host" ) + string(REGEX MATCH "^[^-]*" RCC_NATIVE_ARCH ${RCC_HOST_TRIPLE}) +endif () + +if (RCC_NATIVE_ARCH MATCHES "i[2-6]86") + set(RCC_NATIVE_ARCH X86) +elseif (RCC_NATIVE_ARCH STREQUAL "x86") + set(RCC_NATIVE_ARCH X86) +elseif (RCC_NATIVE_ARCH STREQUAL "amd64") + set(RCC_NATIVE_ARCH X86) +elseif (RCC_NATIVE_ARCH STREQUAL "x86_64") + set(RCC_NATIVE_ARCH X86) +elseif (RCC_NATIVE_ARCH MATCHES "sparc") + set(RCC_NATIVE_ARCH Sparc) +elseif (RCC_NATIVE_ARCH MATCHES "powerpc") + set(RCC_NATIVE_ARCH PowerPC) +elseif (RCC_NATIVE_ARCH MATCHES "ppc64le") + set(RCC_NATIVE_ARCH PowerPC) +elseif (RCC_NATIVE_ARCH MATCHES "aarch64") + set(RCC_NATIVE_ARCH AArch64) +elseif (RCC_NATIVE_ARCH MATCHES "arm64") + set(RCC_NATIVE_ARCH AArch64) +elseif (RCC_NATIVE_ARCH MATCHES "arm") + set(RCC_NATIVE_ARCH ARM) +elseif (RCC_NATIVE_ARCH MATCHES "avr") + set(RCC_NATIVE_ARCH AVR) +elseif (RCC_NATIVE_ARCH MATCHES "mips") + set(RCC_NATIVE_ARCH Mips) +elseif (RCC_NATIVE_ARCH MATCHES "xcore") + set(RCC_NATIVE_ARCH XCore) +elseif (RCC_NATIVE_ARCH MATCHES "msp430") + set(RCC_NATIVE_ARCH MSP430) +elseif (RCC_NATIVE_ARCH MATCHES "hexagon") + set(RCC_NATIVE_ARCH Hexagon) +elseif (RCC_NATIVE_ARCH MATCHES "s390x") + set(RCC_NATIVE_ARCH SystemZ) +elseif (RCC_NATIVE_ARCH MATCHES "wasm32") + set(RCC_NATIVE_ARCH WebAssembly) +elseif (RCC_NATIVE_ARCH MATCHES "wasm64") + set(RCC_NATIVE_ARCH WebAssembly) +elseif (RCC_NATIVE_ARCH MATCHES "riscv32") + set(RCC_NATIVE_ARCH RISCV) +elseif (RCC_NATIVE_ARCH MATCHES "riscv64") + set(RCC_NATIVE_ARCH RISCV) +elseif (RCC_NATIVE_ARCH STREQUAL "m68k") + set(RCC_NATIVE_ARCH M68k) +elseif (RCC_NATIVE_ARCH MATCHES "loongarch") + set(RCC_NATIVE_ARCH LoongArch) +else () + message(FATAL_ERROR "Unknown architecture ${RCC_NATIVE_ARCH}") +endif () + +# If build targets includes "host" or "Native", then replace with native architecture. +foreach (NATIVE_KEYWORD host Native) + list(FIND RCC_TARGETS_TO_BUILD ${NATIVE_KEYWORD} idx) + if( NOT idx LESS 0 ) + list(REMOVE_AT RCC_TARGETS_TO_BUILD ${idx}) + list(APPEND RCC_TARGETS_TO_BUILD ${RCC_NATIVE_ARCH}) + list(REMOVE_DUPLICATES RCC_TARGETS_TO_BUILD) + endif() +endforeach() + +if (NOT ${RCC_NATIVE_ARCH} IN_LIST RCC_TARGETS_TO_BUILD) + message(STATUS + "Native target ${RCC_NATIVE_ARCH} is not selected; lli will not JIT code") +else () + message(STATUS "Native target architecture is ${RCC_NATIVE_ARCH}") + set(RCC_NATIVE_TARGET RCCInitialize${RCC_NATIVE_ARCH}Target) + set(RCC_NATIVE_TARGETINFO RCCInitialize${RCC_NATIVE_ARCH}TargetInfo) + set(RCC_NATIVE_TARGETMC RCCInitialize${RCC_NATIVE_ARCH}TargetMC) + set(RCC_NATIVE_ASMPRINTER RCCInitialize${RCC_NATIVE_ARCH}AsmPrinter) + + # We don't have an ASM parser for all architectures yet. + if (EXISTS ${PROJECT_SOURCE_DIR}/lib/Target/${RCC_NATIVE_ARCH}/AsmParser/CMakeLists.txt) + set(RCC_NATIVE_ASMPARSER RCCInitialize${RCC_NATIVE_ARCH}AsmParser) + endif () + + # We don't have an disassembler for all architectures yet. + if (EXISTS ${PROJECT_SOURCE_DIR}/lib/Target/${RCC_NATIVE_ARCH}/Disassembler/CMakeLists.txt) + set(RCC_NATIVE_DISASSEMBLER RCCInitialize${RCC_NATIVE_ARCH}Disassembler) + endif () +endif () + +if( MSVC ) + set(SHLIBEXT ".lib") + set(stricmp "_stricmp") + set(strdup "_strdup") + + # Allow setting clang-cl's /winsysroot flag. + set(RCC_WINSYSROOT "" CACHE STRING + "If set, argument to clang-cl's /winsysroot") + + find_package(DIASDK) + option(RCC_ENABLE_DIA_SDK "Use MSVC DIA SDK for debugging if available." + ${DIASDK_FOUND}) + + if(RCC_ENABLE_DIA_SDK AND NOT DIASDK_FOUND) + message(FATAL_ERROR "DIA SDK not found. If you have both VS 2012 and 2013 installed, you may need to uninstall the former and re-install the latter afterwards.") + endif() +else() + set(RCC_ENABLE_DIA_SDK 0) +endif( MSVC ) + +if( RCC_ENABLE_THREADS ) + # Check if threading primitives aren't supported on this platform + if( NOT HAVE_PTHREAD_H AND NOT WIN32 ) + set(RCC_ENABLE_THREADS 0) + endif() +endif() + +if( RCC_ENABLE_THREADS ) + message(STATUS "Threads enabled.") +else( RCC_ENABLE_THREADS ) + message(STATUS "Threads disabled.") +endif() + +find_program(GOLD_EXECUTABLE NAMES ${RCC_DEFAULT_TARGET_TRIPLE}-ld.gold ld.gold DOC "The gold linker") +find_program(LD_BFD_EXECUTABLE NAMES ${RCC_DEFAULT_TARGET_TRIPLE}-ld.bfd ld.bfd ${RCC_DEFAULT_TARGET_TRIPLE}-ld ld DOC "The bfd linker") +set(RCC_BINUTILS_INCDIR "" CACHE PATH + "PATH to binutils/include containing plugin-api.h for gold plugin.") + +if(CMAKE_GENERATOR MATCHES "Ninja") + execute_process(COMMAND ${CMAKE_MAKE_PROGRAM} --version + OUTPUT_VARIABLE NINJA_VERSION + OUTPUT_STRIP_TRAILING_WHITESPACE) + set(NINJA_VERSION ${NINJA_VERSION} CACHE STRING "Ninja version number" FORCE) + message(STATUS "Ninja version: ${NINJA_VERSION}") +endif() + +if(CMAKE_GENERATOR MATCHES "Ninja" AND + NOT "${NINJA_VERSION}" VERSION_LESS "1.9.0" AND + CMAKE_HOST_APPLE AND CMAKE_HOST_SYSTEM_VERSION VERSION_GREATER "15.6.0") + set(RCC_TOUCH_STATIC_LIBRARIES ON) +endif() + +if(CMAKE_HOST_APPLE AND APPLE) + if(NOT LD64_EXECUTABLE) + if(NOT CMAKE_XCRUN) + find_program(CMAKE_XCRUN NAMES xcrun) + endif() + + # First, check if there's ld-classic, which is ld64 in newer SDKs. + if(CMAKE_XCRUN) + execute_process(COMMAND ${CMAKE_XCRUN} -find ld-classic + OUTPUT_VARIABLE LD64_EXECUTABLE + ERROR_QUIET + OUTPUT_STRIP_TRAILING_WHITESPACE) + else() + find_program(LD64_EXECUTABLE NAMES ld-classic DOC "The ld64 linker") + endif() + + # Otherwise look for ld directly. + if(NOT LD64_EXECUTABLE) + if(CMAKE_XCRUN) + execute_process(COMMAND ${CMAKE_XCRUN} -find ld + OUTPUT_VARIABLE LD64_EXECUTABLE + OUTPUT_STRIP_TRAILING_WHITESPACE) + else() + find_program(LD64_EXECUTABLE NAMES ld DOC "The ld64 linker") + endif() + endif() + endif() + + if(LD64_EXECUTABLE) + set(LD64_EXECUTABLE ${LD64_EXECUTABLE} CACHE PATH "ld64 executable") + message(STATUS "Found ld64 - ${LD64_EXECUTABLE}") + endif() +endif() + +# Keep the version requirements in sync with bindings/ocaml/README.txt. +set(RCC_BINDINGS "") +include(FindOCaml) +include(AddOCaml) +if(WIN32 OR NOT RCC_ENABLE_BINDINGS) + message(STATUS "OCaml bindings disabled.") +else() + find_package(OCaml) + if( NOT OCAML_FOUND ) + message(STATUS "OCaml bindings disabled.") + else() + if( OCAML_VERSION VERSION_LESS "4.00.0" ) + message(STATUS "OCaml bindings disabled, need OCaml >=4.00.0.") + else() + find_ocamlfind_package(ctypes VERSION 0.4 OPTIONAL) + if( HAVE_OCAML_CTYPES ) + message(STATUS "OCaml bindings enabled.") + set(RCC_BINDINGS "${RCC_BINDINGS} ocaml") + + set(RCC_OCAML_INSTALL_PATH "${OCAML_STDLIB_PATH}" CACHE STRING + "Install directory for RCC OCaml packages") + else() + message(STATUS "OCaml bindings disabled, need ctypes >=0.4.") + endif() + endif() + endif() +endif() + +string(REPLACE " " ";" RCC_BINDINGS_LIST "${RCC_BINDINGS}") + +function(find_python_module module) + string(REPLACE "." "_" module_name ${module}) + string(TOUPPER ${module_name} module_upper) + set(FOUND_VAR PY_${module_upper}_FOUND) + if (DEFINED ${FOUND_VAR}) + return() + endif() + + execute_process(COMMAND "${Python3_EXECUTABLE}" "-c" "import ${module}" + RESULT_VARIABLE status + ERROR_QUIET) + + if(status) + set(${FOUND_VAR} OFF CACHE BOOL "Failed to find python module '${module}'") + message(STATUS "Could NOT find Python module ${module}") + else() + set(${FOUND_VAR} ON CACHE BOOL "Found python module '${module}'") + message(STATUS "Found Python module ${module}") + endif() +endfunction() + +set (PYTHON_MODULES + pygments + # Some systems still don't have pygments.lexers.c_cpp which was introduced in + # version 2.0 in 2014... + pygments.lexers.c_cpp + yaml + ) +foreach(module ${PYTHON_MODULES}) + find_python_module(${module}) +endforeach() + +if(PY_PYGMENTS_FOUND AND PY_PYGMENTS_LEXERS_C_CPP_FOUND AND PY_YAML_FOUND) + set (RCC_HAVE_OPT_VIEWER_MODULES 1) +else() + set (RCC_HAVE_OPT_VIEWER_MODULES 0) +endif() + +function(rcc_get_host_prefixes_and_suffixes) + # Not all platform files will set these variables (relying on them being + # implicitly empty if they're unset), so unset the variables before including + # the platform file, to prevent any values from the target system leaking. + unset(CMAKE_STATIC_LIBRARY_PREFIX) + unset(CMAKE_STATIC_LIBRARY_SUFFIX) + unset(CMAKE_SHARED_LIBRARY_PREFIX) + unset(CMAKE_SHARED_LIBRARY_SUFFIX) + unset(CMAKE_IMPORT_LIBRARY_PREFIX) + unset(CMAKE_IMPORT_LIBRARY_SUFFIX) + unset(CMAKE_EXECUTABLE_SUFFIX) + unset(CMAKE_LINK_LIBRARY_SUFFIX) + include(Platform/${CMAKE_HOST_SYSTEM_NAME} OPTIONAL RESULT_VARIABLE _includedFile) + if (_includedFile) + set(RCC_HOST_STATIC_LIBRARY_PREFIX ${CMAKE_STATIC_LIBRARY_PREFIX} PARENT_SCOPE) + set(RCC_HOST_STATIC_LIBRARY_SUFFIX ${CMAKE_STATIC_LIBRARY_SUFFIX} PARENT_SCOPE) + set(RCC_HOST_SHARED_LIBRARY_PREFIX ${CMAKE_SHARED_LIBRARY_PREFIX} PARENT_SCOPE) + set(RCC_HOST_SHARED_LIBRARY_SUFFIX ${CMAKE_SHARED_LIBRARY_SUFFIX} PARENT_SCOPE) + set(RCC_HOST_IMPORT_LIBRARY_PREFIX ${CMAKE_IMPORT_LIBRARY_PREFIX} PARENT_SCOPE) + set(RCC_HOST_IMPORT_LIBRARY_SUFFIX ${CMAKE_IMPORT_LIBRARY_SUFFIX} PARENT_SCOPE) + set(RCC_HOST_EXECUTABLE_SUFFIX ${CMAKE_EXECUTABLE_SUFFIX} PARENT_SCOPE) + set(RCC_HOST_LINK_LIBRARY_SUFFIX ${CMAKE_LINK_LIBRARY_SUFFIX} PARENT_SCOPE) + endif() +endfunction() + +rcc_get_host_prefixes_and_suffixes() diff --git a/rcc/cmake/config.guess b/rcc/cmake/config.guess new file mode 100644 index 0000000..8934c0f --- /dev/null +++ b/rcc/cmake/config.guess @@ -0,0 +1,1605 @@ +#! /bin/sh +# Attempt to guess a canonical system name. +# Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, +# 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, +# 2011 Free Software Foundation, Inc. + +timestamp='2024-06-07' + +# This file is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA +# 02110-1301, USA. +# +# As a special exception to the GNU General Public License, if you +# distribute this file as part of a program that contains a +# configuration script generated by Autoconf, you may include it under +# the same distribution terms that you use for the rest of that program. + + +# Originally written by Per Bothner. Please send patches (context +# diff format) to <config-patches@gnu.org> and include a ChangeLog +# entry. +# +# This script attempts to guess a canonical system name similar to +# config.sub. If it succeeds, it prints the system name on stdout, and +# exits with 0. Otherwise, it exits with 1. +# +# You can get the latest version of this script from: +# http://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.guess;hb=HEAD + +me=`echo "$0" | sed -e 's,.*/,,'` + +usage="\ +Usage: $0 [OPTION] + +Output the configuration name of the system \`$me' is run on. + +Operation modes: + -h, --help print this help, then exit + -t, --time-stamp print date of last modification, then exit + -v, --version print version number, then exit + +Report bugs and patches to <config-patches@gnu.org>." + +version="\ +GNU config.guess ($timestamp) + +Originally written by Per Bothner. +Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, +2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011 Free +Software Foundation, Inc. + +This is free software; see the source for copying conditions. There is NO +warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE." + +help=" +Try \`$me --help' for more information." + +# Parse command line +while test $# -gt 0 ; do + case $1 in + --time-stamp | --time* | -t ) + echo "$timestamp" ; exit ;; + --version | -v ) + echo "$version" ; exit ;; + --help | --h* | -h ) + echo "$usage"; exit ;; + -- ) # Stop option processing + shift; break ;; + - ) # Use stdin as input. + break ;; + -* ) + echo "$me: invalid option $1$help" >&2 + exit 1 ;; + * ) + break ;; + esac +done + +if test $# != 0; then + echo "$me: too many arguments$help" >&2 + exit 1 +fi + +trap 'exit 1' 1 2 15 + +# CC_FOR_BUILD -- compiler used by this script. Note that the use of a +# compiler to aid in system detection is discouraged as it requires +# temporary files to be created and, as you can see below, it is a +# headache to deal with in a portable fashion. + +# Historically, `CC_FOR_BUILD' used to be named `HOST_CC'. We still +# use `HOST_CC' if defined, but it is deprecated. + +# Portable tmp directory creation inspired by the Autoconf team. + +set_cc_for_build=' +trap "exitcode=\$?; (rm -f \$tmpfiles 2>/dev/null; rmdir \$tmp 2>/dev/null) && exit \$exitcode" 0 ; +trap "rm -f \$tmpfiles 2>/dev/null; rmdir \$tmp 2>/dev/null; exit 1" 1 2 13 15 ; +: ${TMPDIR=/tmp} ; + { tmp=`(umask 077 && mktemp -d "$TMPDIR/cgXXXXXX") 2>/dev/null` && test -n "$tmp" && test -d "$tmp" ; } || + { test -n "$RANDOM" && tmp=$TMPDIR/cg$$-$RANDOM && (umask 077 && mkdir $tmp) ; } || + { tmp=$TMPDIR/cg-$$ && (umask 077 && mkdir $tmp) && echo "Warning: creating insecure temp directory" >&2 ; } || + { echo "$me: cannot create a temporary directory in $TMPDIR" >&2 ; exit 1 ; } ; +dummy=$tmp/dummy ; +tmpfiles="$dummy.c $dummy.o $dummy.rel $dummy" ; +case $CC_FOR_BUILD,$HOST_CC,$CC in + ,,) echo "int x;" > $dummy.c ; + for c in cc gcc c89 c99 ; do + if ($c -c -o $dummy.o $dummy.c) >/dev/null 2>&1 ; then + CC_FOR_BUILD="$c"; break ; + fi ; + done ; + if test x"$CC_FOR_BUILD" = x ; then + CC_FOR_BUILD=no_compiler_found ; + fi + ;; + ,,*) CC_FOR_BUILD=$CC ;; + ,*,*) CC_FOR_BUILD=$HOST_CC ;; +esac ; set_cc_for_build= ;' + +# This is needed to find uname on a Pyramid OSx when run in the BSD universe. +# (ghazi@noc.rutgers.edu 1994-08-24) +if (test -f /.attbin/uname) >/dev/null 2>&1 ; then + PATH=$PATH:/.attbin ; export PATH +fi + +UNAME_MACHINE=`(uname -m) 2>/dev/null` || UNAME_MACHINE=unknown +UNAME_RELEASE=`(uname -r) 2>/dev/null` || UNAME_RELEASE=unknown +UNAME_SYSTEM=`(uname -s) 2>/dev/null` || UNAME_SYSTEM=unknown +UNAME_VERSION=`(uname -v) 2>/dev/null` || UNAME_VERSION=unknown + +# Note: order is significant - the case branches are not exclusive. + +case "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" in + *:NetBSD:*:*) + # NetBSD (nbsd) targets should (where applicable) match one or + # more of the tupples: *-*-netbsdelf*, *-*-netbsdaout*, + # *-*-netbsdecoff* and *-*-netbsd*. For targets that recently + # switched to ELF, *-*-netbsd* would select the old + # object file format. This provides both forward + # compatibility and a consistent mechanism for selecting the + # object file format. + # + # Note: NetBSD doesn't particularly care about the vendor + # portion of the name. We always set it to "unknown". + sysctl="sysctl -n hw.machine_arch" + UNAME_MACHINE_ARCH=`(/sbin/$sysctl 2>/dev/null || \ + /usr/sbin/$sysctl 2>/dev/null || echo unknown)` + case "${UNAME_MACHINE_ARCH}" in + armeb) machine=armeb-unknown ;; + arm*) machine=arm-unknown ;; + sh3el) machine=shl-unknown ;; + sh3eb) machine=sh-unknown ;; + sh5el) machine=sh5le-unknown ;; + *) machine=${UNAME_MACHINE_ARCH}-unknown ;; + esac + # The Operating System including object format, if it has switched + # to ELF recently, or will in the future. + case "${UNAME_MACHINE_ARCH}" in + arm*|i386|m68k|ns32k|sh3*|sparc|vax) + eval $set_cc_for_build + if echo __ELF__ | $CC_FOR_BUILD -E - 2>/dev/null \ + | grep -q __ELF__ + then + # Once all utilities can be ECOFF (netbsdecoff) or a.out (netbsdaout). + # Return netbsd for either. FIX? + os=netbsd + else + os=netbsdelf + fi + ;; + *) + os=netbsd + ;; + esac + # The OS release + # Debian GNU/NetBSD machines have a different userland, and + # thus, need a distinct triplet. However, they do not need + # kernel version information, so it can be replaced with a + # suitable tag, in the style of linux-gnu. + case "${UNAME_VERSION}" in + Debian*) + release='-gnu' + ;; + *) + release=`echo ${UNAME_RELEASE}|sed -e 's/[-_].*/\./'` + ;; + esac + # Since CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM: + # contains redundant information, the shorter form: + # CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM is used. + echo "${machine}-${os}${release}" + exit ;; + *:OpenBSD:*:*) + UNAME_MACHINE_ARCH=`arch | sed 's/OpenBSD.//'` + echo ${UNAME_MACHINE_ARCH}-unknown-openbsd${UNAME_RELEASE} + exit ;; + *:ekkoBSD:*:*) + echo ${UNAME_MACHINE}-unknown-ekkobsd${UNAME_RELEASE} + exit ;; + *:SolidBSD:*:*) + echo ${UNAME_MACHINE}-unknown-solidbsd${UNAME_RELEASE} + exit ;; + macppc:MirBSD:*:*) + echo powerpc-unknown-mirbsd${UNAME_RELEASE} + exit ;; + *:MirBSD:*:*) + echo ${UNAME_MACHINE}-unknown-mirbsd${UNAME_RELEASE} + exit ;; + alpha:OSF1:*:*) + case $UNAME_RELEASE in + *4.0) + UNAME_RELEASE=`/usr/sbin/sizer -v | awk '{print $3}'` + ;; + *5.*) + UNAME_RELEASE=`/usr/sbin/sizer -v | awk '{print $4}'` + ;; + esac + # According to Compaq, /usr/sbin/psrinfo has been available on + # OSF/1 and Tru64 systems produced since 1995. I hope that + # covers most systems running today. This code pipes the CPU + # types through head -n 1, so we only detect the type of CPU 0. + ALPHA_CPU_TYPE=`/usr/sbin/psrinfo -v | sed -n -e 's/^ The alpha \(.*\) processor.*$/\1/p' | head -n 1` + case "$ALPHA_CPU_TYPE" in + "EV4 (21064)") + UNAME_MACHINE="alpha" ;; + "EV4.5 (21064)") + UNAME_MACHINE="alpha" ;; + "LCA4 (21066/21068)") + UNAME_MACHINE="alpha" ;; + "EV5 (21164)") + UNAME_MACHINE="alphaev5" ;; + "EV5.6 (21164A)") + UNAME_MACHINE="alphaev56" ;; + "EV5.6 (21164PC)") + UNAME_MACHINE="alphapca56" ;; + "EV5.7 (21164PC)") + UNAME_MACHINE="alphapca57" ;; + "EV6 (21264)") + UNAME_MACHINE="alphaev6" ;; + "EV6.7 (21264A)") + UNAME_MACHINE="alphaev67" ;; + "EV6.8CB (21264C)") + UNAME_MACHINE="alphaev68" ;; + "EV6.8AL (21264B)") + UNAME_MACHINE="alphaev68" ;; + "EV6.8CX (21264D)") + UNAME_MACHINE="alphaev68" ;; + "EV6.9A (21264/EV69A)") + UNAME_MACHINE="alphaev69" ;; + "EV7 (21364)") + UNAME_MACHINE="alphaev7" ;; + "EV7.9 (21364A)") + UNAME_MACHINE="alphaev79" ;; + esac + # A Pn.n version is a patched version. + # A Vn.n version is a released version. + # A Tn.n version is a released field test version. + # A Xn.n version is an unreleased experimental baselevel. + # 1.2 uses "1.2" for uname -r. + echo ${UNAME_MACHINE}-dec-osf`echo ${UNAME_RELEASE} | sed -e 's/^[PVTX]//' | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz'` + # Reset EXIT trap before exiting to avoid spurious non-zero exit code. + exitcode=$? + trap '' 0 + exit $exitcode ;; + Alpha\ *:Windows_NT*:*) + # How do we know it's Interix rather than the generic POSIX subsystem? + # Should we change UNAME_MACHINE based on the output of uname instead + # of the specific Alpha model? + echo alpha-pc-interix + exit ;; + 21064:Windows_NT:50:3) + echo alpha-dec-winnt3.5 + exit ;; + Amiga*:UNIX_System_V:4.0:*) + echo m68k-unknown-sysv4 + exit ;; + *:[Aa]miga[Oo][Ss]:*:*) + echo ${UNAME_MACHINE}-unknown-amigaos + exit ;; + *:[Mm]orph[Oo][Ss]:*:*) + echo ${UNAME_MACHINE}-unknown-morphos + exit ;; + *:OS/390:*:*) + echo i370-ibm-openedition + exit ;; + *:z/VM:*:*) + echo s390-ibm-zvmoe + exit ;; + *:OS400:*:*) + echo powerpc-ibm-os400 + exit ;; + arm:RISC*:1.[012]*:*|arm:riscix:1.[012]*:*) + echo arm-acorn-riscix${UNAME_RELEASE} + exit ;; + arm:riscos:*:*|arm:RISCOS:*:*) + echo arm-unknown-riscos + exit ;; + SR2?01:HI-UX/MPP:*:* | SR8000:HI-UX/MPP:*:*) + echo hppa1.1-hitachi-hiuxmpp + exit ;; + Pyramid*:OSx*:*:* | MIS*:OSx*:*:* | MIS*:SMP_DC-OSx*:*:*) + # akee@wpdis03.wpafb.af.mil (Earle F. Ake) contributed MIS and NILE. + if test "`(/bin/universe) 2>/dev/null`" = att ; then + echo pyramid-pyramid-sysv3 + else + echo pyramid-pyramid-bsd + fi + exit ;; + NILE*:*:*:dcosx) + echo pyramid-pyramid-svr4 + exit ;; + DRS?6000:unix:4.0:6*) + echo sparc-icl-nx6 + exit ;; + DRS?6000:UNIX_SV:4.2*:7* | DRS?6000:isis:4.2*:7*) + case `/usr/bin/uname -p` in + sparc) echo sparc-icl-nx7; exit ;; + esac ;; + s390x:SunOS:*:*) + echo ${UNAME_MACHINE}-ibm-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` + exit ;; + sun4H:SunOS:5.*:*) + echo sparc-hal-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` + exit ;; + sun4*:SunOS:5.*:* | tadpole*:SunOS:5.*:*) + echo sparc-sun-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` + exit ;; + i86pc:AuroraUX:5.*:* | i86xen:AuroraUX:5.*:*) + echo i386-pc-auroraux${UNAME_RELEASE} + exit ;; + i86pc:SunOS:5.*:* | i86xen:SunOS:5.*:*) + eval $set_cc_for_build + SUN_ARCH="i386" + # If there is a compiler, see if it is configured for 64-bit objects. + # Note that the Sun cc does not turn __LP64__ into 1 like gcc does. + # This test works for both compilers. + if [ "$CC_FOR_BUILD" != 'no_compiler_found' ]; then + if (echo '#ifdef __amd64'; echo IS_64BIT_ARCH; echo '#endif') | \ + (CCOPTS= $CC_FOR_BUILD -E - 2>/dev/null) | \ + grep IS_64BIT_ARCH >/dev/null + then + SUN_ARCH="x86_64" + fi + fi + echo ${SUN_ARCH}-pc-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` + exit ;; + sun4*:SunOS:6*:*) + # According to config.sub, this is the proper way to canonicalize + # SunOS6. Hard to guess exactly what SunOS6 will be like, but + # it's likely to be more like Solaris than SunOS4. + echo sparc-sun-solaris3`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` + exit ;; + sun4*:SunOS:*:*) + case "`/usr/bin/arch -k`" in + Series*|S4*) + UNAME_RELEASE=`uname -v` + ;; + esac + # Japanese Language versions have a version number like `4.1.3-JL'. + echo sparc-sun-sunos`echo ${UNAME_RELEASE}|sed -e 's/-/_/'` + exit ;; + sun3*:SunOS:*:*) + echo m68k-sun-sunos${UNAME_RELEASE} + exit ;; + sun*:*:4.2BSD:*) + UNAME_RELEASE=`(sed 1q /etc/motd | awk '{print substr($5,1,3)}') 2>/dev/null` + test "x${UNAME_RELEASE}" = "x" && UNAME_RELEASE=3 + case "`/bin/arch`" in + sun3) + echo m68k-sun-sunos${UNAME_RELEASE} + ;; + sun4) + echo sparc-sun-sunos${UNAME_RELEASE} + ;; + esac + exit ;; + aushp:SunOS:*:*) + echo sparc-auspex-sunos${UNAME_RELEASE} + exit ;; + # The situation for MiNT is a little confusing. The machine name + # can be virtually everything (everything which is not + # "atarist" or "atariste" at least should have a processor + # > m68000). The system name ranges from "MiNT" over "FreeMiNT" + # to the lowercase version "mint" (or "freemint"). Finally + # the system name "TOS" denotes a system which is actually not + # MiNT. But MiNT is downward compatible to TOS, so this should + # be no problem. + atarist[e]:*MiNT:*:* | atarist[e]:*mint:*:* | atarist[e]:*TOS:*:*) + echo m68k-atari-mint${UNAME_RELEASE} + exit ;; + atari*:*MiNT:*:* | atari*:*mint:*:* | atarist[e]:*TOS:*:*) + echo m68k-atari-mint${UNAME_RELEASE} + exit ;; + *falcon*:*MiNT:*:* | *falcon*:*mint:*:* | *falcon*:*TOS:*:*) + echo m68k-atari-mint${UNAME_RELEASE} + exit ;; + milan*:*MiNT:*:* | milan*:*mint:*:* | *milan*:*TOS:*:*) + echo m68k-milan-mint${UNAME_RELEASE} + exit ;; + hades*:*MiNT:*:* | hades*:*mint:*:* | *hades*:*TOS:*:*) + echo m68k-hades-mint${UNAME_RELEASE} + exit ;; + *:*MiNT:*:* | *:*mint:*:* | *:*TOS:*:*) + echo m68k-unknown-mint${UNAME_RELEASE} + exit ;; + m68k:machten:*:*) + echo m68k-apple-machten${UNAME_RELEASE} + exit ;; + powerpc:machten:*:*) + echo powerpc-apple-machten${UNAME_RELEASE} + exit ;; + RISC*:Mach:*:*) + echo mips-dec-mach_bsd4.3 + exit ;; + RISC*:ULTRIX:*:*) + echo mips-dec-ultrix${UNAME_RELEASE} + exit ;; + VAX*:ULTRIX*:*:*) + echo vax-dec-ultrix${UNAME_RELEASE} + exit ;; + 2020:CLIX:*:* | 2430:CLIX:*:*) + echo clipper-intergraph-clix${UNAME_RELEASE} + exit ;; + mips:*:*:UMIPS | mips:*:*:RISCos) + eval $set_cc_for_build + sed 's/^ //' << EOF >$dummy.c +#ifdef __cplusplus +#include <stdio.h> /* for printf() prototype */ + int main (int argc, char *argv[]) { +#else + int main (argc, argv) int argc; char *argv[]; { +#endif + #if defined (host_mips) && defined (MIPSEB) + #if defined (SYSTYPE_SYSV) + printf ("mips-mips-riscos%ssysv\n", argv[1]); exit (0); + #endif + #if defined (SYSTYPE_SVR4) + printf ("mips-mips-riscos%ssvr4\n", argv[1]); exit (0); + #endif + #if defined (SYSTYPE_BSD43) || defined(SYSTYPE_BSD) + printf ("mips-mips-riscos%sbsd\n", argv[1]); exit (0); + #endif + #endif + exit (-1); + } +EOF + $CC_FOR_BUILD -o $dummy $dummy.c && + dummyarg=`echo "${UNAME_RELEASE}" | sed -n 's/\([0-9]*\).*/\1/p'` && + SYSTEM_NAME=`$dummy $dummyarg` && + { echo "$SYSTEM_NAME"; exit; } + echo mips-mips-riscos${UNAME_RELEASE} + exit ;; + Motorola:PowerMAX_OS:*:*) + echo powerpc-motorola-powermax + exit ;; + Motorola:*:4.3:PL8-*) + echo powerpc-harris-powermax + exit ;; + Night_Hawk:*:*:PowerMAX_OS | Synergy:PowerMAX_OS:*:*) + echo powerpc-harris-powermax + exit ;; + Night_Hawk:Power_UNIX:*:*) + echo powerpc-harris-powerunix + exit ;; + m88k:CX/UX:7*:*) + echo m88k-harris-cxux7 + exit ;; + m88k:*:4*:R4*) + echo m88k-motorola-sysv4 + exit ;; + m88k:*:3*:R3*) + echo m88k-motorola-sysv3 + exit ;; + AViiON:dgux:*:*) + # DG/UX returns AViiON for all architectures + UNAME_PROCESSOR=`/usr/bin/uname -p` + if [ $UNAME_PROCESSOR = mc88100 ] || [ $UNAME_PROCESSOR = mc88110 ] + then + if [ ${TARGET_BINARY_INTERFACE}x = m88kdguxelfx ] || \ + [ ${TARGET_BINARY_INTERFACE}x = x ] + then + echo m88k-dg-dgux${UNAME_RELEASE} + else + echo m88k-dg-dguxbcs${UNAME_RELEASE} + fi + else + echo i586-dg-dgux${UNAME_RELEASE} + fi + exit ;; + M88*:DolphinOS:*:*) # DolphinOS (SVR3) + echo m88k-dolphin-sysv3 + exit ;; + M88*:*:R3*:*) + # Delta 88k system running SVR3 + echo m88k-motorola-sysv3 + exit ;; + XD88*:*:*:*) # Tektronix XD88 system running UTekV (SVR3) + echo m88k-tektronix-sysv3 + exit ;; + Tek43[0-9][0-9]:UTek:*:*) # Tektronix 4300 system running UTek (BSD) + echo m68k-tektronix-bsd + exit ;; + *:IRIX*:*:*) + echo mips-sgi-irix`echo ${UNAME_RELEASE}|sed -e 's/-/_/g'` + exit ;; + ????????:AIX?:[12].1:2) # AIX 2.2.1 or AIX 2.1.1 is RT/PC AIX. + echo romp-ibm-aix # uname -m gives an 8 hex-code CPU id + exit ;; # Note that: echo "'`uname -s`'" gives 'AIX ' + i*86:AIX:*:*) + echo i386-ibm-aix + exit ;; + ia64:AIX:*:*) + if [ -x /usr/bin/oslevel ] ; then + IBM_REV=`/usr/bin/oslevel` + else + IBM_REV=${UNAME_VERSION}.${UNAME_RELEASE} + fi + echo ${UNAME_MACHINE}-ibm-aix${IBM_REV} + exit ;; + *:AIX:2:3) + if grep bos325 /usr/include/stdio.h >/dev/null 2>&1; then + eval $set_cc_for_build + sed 's/^ //' << EOF >$dummy.c + #include <sys/systemcfg.h> + + main() + { + if (!__power_pc()) + exit(1); + puts("powerpc-ibm-aix3.2.5"); + exit(0); + } +EOF + if $CC_FOR_BUILD -o $dummy $dummy.c && SYSTEM_NAME=`$dummy` + then + echo "$SYSTEM_NAME" + else + echo rs6000-ibm-aix3.2.5 + fi + elif grep bos324 /usr/include/stdio.h >/dev/null 2>&1; then + echo rs6000-ibm-aix3.2.4 + else + echo rs6000-ibm-aix3.2 + fi + exit ;; + *:AIX:*:[4567]) + IBM_CPU_ID=`/usr/sbin/lsdev -C -c processor -S available | sed 1q | awk '{ print $1 }'` + if /usr/sbin/lsattr -El ${IBM_CPU_ID} | grep ' POWER' >/dev/null 2>&1; then + IBM_ARCH=rs6000 + else + IBM_ARCH=powerpc + fi + if [ -x /usr/bin/oslevel ] ; then + IBM_REV=`/usr/bin/oslevel` + else + IBM_REV=${UNAME_VERSION}.${UNAME_RELEASE} + fi + echo ${IBM_ARCH}-ibm-aix${IBM_REV} + exit ;; + *:AIX:*:*) + echo rs6000-ibm-aix + exit ;; + ibmrt:4.4BSD:*|romp-ibm:BSD:*) + echo romp-ibm-bsd4.4 + exit ;; + ibmrt:*BSD:*|romp-ibm:BSD:*) # covers RT/PC BSD and + echo romp-ibm-bsd${UNAME_RELEASE} # 4.3 with uname added to + exit ;; # report: romp-ibm BSD 4.3 + *:BOSX:*:*) + echo rs6000-bull-bosx + exit ;; + DPX/2?00:B.O.S.:*:*) + echo m68k-bull-sysv3 + exit ;; + 9000/[34]??:4.3bsd:1.*:*) + echo m68k-hp-bsd + exit ;; + hp300:4.4BSD:*:* | 9000/[34]??:4.3bsd:2.*:*) + echo m68k-hp-bsd4.4 + exit ;; + 9000/[34678]??:HP-UX:*:*) + HPUX_REV=`echo ${UNAME_RELEASE}|sed -e 's/[^.]*.[0B]*//'` + case "${UNAME_MACHINE}" in + 9000/31? ) HP_ARCH=m68000 ;; + 9000/[34]?? ) HP_ARCH=m68k ;; + 9000/[678][0-9][0-9]) + if [ -x /usr/bin/getconf ]; then + sc_cpu_version=`/usr/bin/getconf SC_CPU_VERSION 2>/dev/null` + sc_kernel_bits=`/usr/bin/getconf SC_KERNEL_BITS 2>/dev/null` + case "${sc_cpu_version}" in + 523) HP_ARCH="hppa1.0" ;; # CPU_PA_RISC1_0 + 528) HP_ARCH="hppa1.1" ;; # CPU_PA_RISC1_1 + 532) # CPU_PA_RISC2_0 + case "${sc_kernel_bits}" in + 32) HP_ARCH="hppa2.0n" ;; + 64) HP_ARCH="hppa2.0w" ;; + '') HP_ARCH="hppa2.0" ;; # HP-UX 10.20 + esac ;; + esac + fi + if [ "${HP_ARCH}" = "" ]; then + eval $set_cc_for_build + sed 's/^ //' << EOF >$dummy.c + + #define _HPUX_SOURCE + #include <stdlib.h> + #include <unistd.h> + + int main () + { + #if defined(_SC_KERNEL_BITS) + long bits = sysconf(_SC_KERNEL_BITS); + #endif + long cpu = sysconf (_SC_CPU_VERSION); + + switch (cpu) + { + case CPU_PA_RISC1_0: puts ("hppa1.0"); break; + case CPU_PA_RISC1_1: puts ("hppa1.1"); break; + case CPU_PA_RISC2_0: + #if defined(_SC_KERNEL_BITS) + switch (bits) + { + case 64: puts ("hppa2.0w"); break; + case 32: puts ("hppa2.0n"); break; + default: puts ("hppa2.0"); break; + } break; + #else /* !defined(_SC_KERNEL_BITS) */ + puts ("hppa2.0"); break; + #endif + default: puts ("hppa1.0"); break; + } + exit (0); + } +EOF + (CCOPTS= $CC_FOR_BUILD -o $dummy $dummy.c 2>/dev/null) && HP_ARCH=`$dummy` + test -z "$HP_ARCH" && HP_ARCH=hppa + fi ;; + esac + if [ ${HP_ARCH} = "hppa2.0w" ] + then + eval $set_cc_for_build + + # hppa2.0w-hp-hpux* has a 64-bit kernel and a compiler generating + # 32-bit code. hppa64-hp-hpux* has the same kernel and a compiler + # generating 64-bit code. GNU and HP use different nomenclature: + # + # $ CC_FOR_BUILD=cc ./config.guess + # => hppa2.0w-hp-hpux11.23 + # $ CC_FOR_BUILD="cc +DA2.0w" ./config.guess + # => hppa64-hp-hpux11.23 + + if echo __LP64__ | (CCOPTS= $CC_FOR_BUILD -E - 2>/dev/null) | + grep -q __LP64__ + then + HP_ARCH="hppa2.0w" + else + HP_ARCH="hppa64" + fi + fi + echo ${HP_ARCH}-hp-hpux${HPUX_REV} + exit ;; + ia64:HP-UX:*:*) + HPUX_REV=`echo ${UNAME_RELEASE}|sed -e 's/[^.]*.[0B]*//'` + echo ia64-hp-hpux${HPUX_REV} + exit ;; + 3050*:HI-UX:*:*) + eval $set_cc_for_build + sed 's/^ //' << EOF >$dummy.c + #include <unistd.h> + int + main () + { + long cpu = sysconf (_SC_CPU_VERSION); + /* The order matters, because CPU_IS_HP_MC68K erroneously returns + true for CPU_PA_RISC1_0. CPU_IS_PA_RISC returns correct + results, however. */ + if (CPU_IS_PA_RISC (cpu)) + { + switch (cpu) + { + case CPU_PA_RISC1_0: puts ("hppa1.0-hitachi-hiuxwe2"); break; + case CPU_PA_RISC1_1: puts ("hppa1.1-hitachi-hiuxwe2"); break; + case CPU_PA_RISC2_0: puts ("hppa2.0-hitachi-hiuxwe2"); break; + default: puts ("hppa-hitachi-hiuxwe2"); break; + } + } + else if (CPU_IS_HP_MC68K (cpu)) + puts ("m68k-hitachi-hiuxwe2"); + else puts ("unknown-hitachi-hiuxwe2"); + exit (0); + } +EOF + $CC_FOR_BUILD -o $dummy $dummy.c && SYSTEM_NAME=`$dummy` && + { echo "$SYSTEM_NAME"; exit; } + echo unknown-hitachi-hiuxwe2 + exit ;; + 9000/7??:4.3bsd:*:* | 9000/8?[79]:4.3bsd:*:* ) + echo hppa1.1-hp-bsd + exit ;; + 9000/8??:4.3bsd:*:*) + echo hppa1.0-hp-bsd + exit ;; + *9??*:MPE/iX:*:* | *3000*:MPE/iX:*:*) + echo hppa1.0-hp-mpeix + exit ;; + hp7??:OSF1:*:* | hp8?[79]:OSF1:*:* ) + echo hppa1.1-hp-osf + exit ;; + hp8??:OSF1:*:*) + echo hppa1.0-hp-osf + exit ;; + i*86:OSF1:*:*) + if [ -x /usr/sbin/sysversion ] ; then + echo ${UNAME_MACHINE}-unknown-osf1mk + else + echo ${UNAME_MACHINE}-unknown-osf1 + fi + exit ;; + parisc*:Lites*:*:*) + echo hppa1.1-hp-lites + exit ;; + C1*:ConvexOS:*:* | convex:ConvexOS:C1*:*) + echo c1-convex-bsd + exit ;; + C2*:ConvexOS:*:* | convex:ConvexOS:C2*:*) + if getsysinfo -f scalar_acc + then echo c32-convex-bsd + else echo c2-convex-bsd + fi + exit ;; + C34*:ConvexOS:*:* | convex:ConvexOS:C34*:*) + echo c34-convex-bsd + exit ;; + C38*:ConvexOS:*:* | convex:ConvexOS:C38*:*) + echo c38-convex-bsd + exit ;; + C4*:ConvexOS:*:* | convex:ConvexOS:C4*:*) + echo c4-convex-bsd + exit ;; + CRAY*Y-MP:*:*:*) + echo ymp-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' + exit ;; + CRAY*[A-Z]90:*:*:*) + echo ${UNAME_MACHINE}-cray-unicos${UNAME_RELEASE} \ + | sed -e 's/CRAY.*\([A-Z]90\)/\1/' \ + -e y/ABCDEFGHIJKLMNOPQRSTUVWXYZ/abcdefghijklmnopqrstuvwxyz/ \ + -e 's/\.[^.]*$/.X/' + exit ;; + CRAY*TS:*:*:*) + echo t90-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' + exit ;; + CRAY*T3E:*:*:*) + echo alphaev5-cray-unicosmk${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' + exit ;; + CRAY*SV1:*:*:*) + echo sv1-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' + exit ;; + *:UNICOS/mp:*:*) + echo craynv-cray-unicosmp${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' + exit ;; + F30[01]:UNIX_System_V:*:* | F700:UNIX_System_V:*:*) + FUJITSU_PROC=`uname -m | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz'` + FUJITSU_SYS=`uname -p | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz' | sed -e 's/\///'` + FUJITSU_REL=`echo ${UNAME_RELEASE} | sed -e 's/ /_/'` + echo "${FUJITSU_PROC}-fujitsu-${FUJITSU_SYS}${FUJITSU_REL}" + exit ;; + 5000:UNIX_System_V:4.*:*) + FUJITSU_SYS=`uname -p | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz' | sed -e 's/\///'` + FUJITSU_REL=`echo ${UNAME_RELEASE} | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz' | sed -e 's/ /_/'` + echo "sparc-fujitsu-${FUJITSU_SYS}${FUJITSU_REL}" + exit ;; + i*86:BSD/386:*:* | i*86:BSD/OS:*:* | *:Ascend\ Embedded/OS:*:*) + echo ${UNAME_MACHINE}-pc-bsdi${UNAME_RELEASE} + exit ;; + sparc*:BSD/OS:*:*) + echo sparc-unknown-bsdi${UNAME_RELEASE} + exit ;; + *:BSD/OS:*:*) + echo ${UNAME_MACHINE}-unknown-bsdi${UNAME_RELEASE} + exit ;; + *:FreeBSD:*:*) + UNAME_PROCESSOR=`/usr/bin/uname -p` + case ${UNAME_PROCESSOR} in + amd64) + echo x86_64-unknown-freebsd`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'` ;; + *) + echo ${UNAME_PROCESSOR}-unknown-freebsd`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'` ;; + esac + exit ;; + i*:CYGWIN*:*) + echo ${UNAME_MACHINE}-pc-cygwin + exit ;; + *:MINGW*:*) + echo ${UNAME_MACHINE}-pc-mingw32 + exit ;; + *:MSYS*:*) + echo ${UNAME_MACHINE}-pc-msys + exit ;; + i*:windows32*:*) + # uname -m includes "-pc" on this system. + echo ${UNAME_MACHINE}-mingw32 + exit ;; + i*:PW*:*) + echo ${UNAME_MACHINE}-pc-pw32 + exit ;; + *:SerenityOS:*:*) + echo ${UNAME_MACHINE}-pc-serenity + exit ;; + *:Interix*:*) + case ${UNAME_MACHINE} in + x86) + echo i586-pc-interix${UNAME_RELEASE} + exit ;; + authenticamd | genuineintel | EM64T) + echo x86_64-unknown-interix${UNAME_RELEASE} + exit ;; + IA64) + echo ia64-unknown-interix${UNAME_RELEASE} + exit ;; + esac ;; + [345]86:Windows_95:* | [345]86:Windows_98:* | [345]86:Windows_NT:*) + echo i${UNAME_MACHINE}-pc-mks + exit ;; + 8664:Windows_NT:*) + echo x86_64-pc-mks + exit ;; + i*:Windows_NT*:* | Pentium*:Windows_NT*:*) + # How do we know it's Interix rather than the generic POSIX subsystem? + # It also conflicts with pre-2.0 versions of AT&T UWIN. Should we + # UNAME_MACHINE based on the output of uname instead of i386? + echo i586-pc-interix + exit ;; + i*:UWIN*:*) + echo ${UNAME_MACHINE}-pc-uwin + exit ;; + amd64:CYGWIN*:*:* | x86_64:CYGWIN*:*:*) + echo x86_64-unknown-cygwin + exit ;; + p*:CYGWIN*:*) + echo powerpcle-unknown-cygwin + exit ;; + prep*:SunOS:5.*:*) + echo powerpcle-unknown-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` + exit ;; + *:GNU:*:*) + # the GNU system + echo `echo ${UNAME_MACHINE}|sed -e 's,[-/].*$,,'`-unknown-gnu`echo ${UNAME_RELEASE}|sed -e 's,/.*$,,'` + exit ;; + *:GNU/*:*:*) + # other systems with GNU libc and userland + echo ${UNAME_MACHINE}-unknown-`echo ${UNAME_SYSTEM} | sed 's,^[^/]*/,,' | tr '[A-Z]' '[a-z]'``echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'`-gnu + exit ;; + i*86:Minix:*:*) + echo ${UNAME_MACHINE}-pc-minix + exit ;; + aarch64*:Linux:*) + echo ${UNAME_MACHINE}-unknown-linux-gnu + exit ;; + alpha:Linux:*:*) + case `sed -n '/^cpu model/s/^.*: \(.*\)/\1/p' < /proc/cpuinfo` in + EV5) UNAME_MACHINE=alphaev5 ;; + EV56) UNAME_MACHINE=alphaev56 ;; + PCA56) UNAME_MACHINE=alphapca56 ;; + PCA57) UNAME_MACHINE=alphapca56 ;; + EV6) UNAME_MACHINE=alphaev6 ;; + EV67) UNAME_MACHINE=alphaev67 ;; + EV68*) UNAME_MACHINE=alphaev68 ;; + esac + objdump --private-headers /bin/sh | grep -q ld.so.1 + if test "$?" = 0 ; then LIBC="libc1" ; else LIBC="" ; fi + echo ${UNAME_MACHINE}-unknown-linux-gnu${LIBC} + exit ;; + arm*:Linux:*:*) + eval $set_cc_for_build + if echo __ARM_EABI__ | $CC_FOR_BUILD -E - 2>/dev/null \ + | grep -q __ARM_EABI__ + then + echo ${UNAME_MACHINE}-unknown-linux-gnu + else + if echo __ARM_PCS_VFP | $CC_FOR_BUILD -E - 2>/dev/null \ + | grep -q __ARM_PCS_VFP + then + echo ${UNAME_MACHINE}-unknown-linux-gnueabi + else + echo ${UNAME_MACHINE}-unknown-linux-gnueabihf + fi + fi + exit ;; + avr32*:Linux:*:*) + echo ${UNAME_MACHINE}-unknown-linux-gnu + exit ;; + cris:Linux:*:*) + echo cris-axis-linux-gnu + exit ;; + crisv32:Linux:*:*) + echo crisv32-axis-linux-gnu + exit ;; + e2k:Linux:*:*) + echo ${UNAME_MACHINE}-unknown-linux-gnu + exit ;; + frv:Linux:*:*) + echo frv-unknown-linux-gnu + exit ;; + i*86:Linux:*:*) + LIBC=gnu + eval $set_cc_for_build + sed 's/^ //' << EOF >$dummy.c + #ifdef __dietlibc__ + LIBC=dietlibc + #endif +EOF + eval `$CC_FOR_BUILD -E $dummy.c 2>/dev/null | grep '^LIBC'` + echo "${UNAME_MACHINE}-pc-linux-${LIBC}" + exit ;; + ia64:Linux:*:*) + echo ${UNAME_MACHINE}-unknown-linux-gnu + exit ;; + loongarch32:Linux:*:* | loongarch64:Linux:*:* | loongarchx32:Linux:*:*) + LIBC=gnu + eval $set_cc_for_build + # Do not check for __GLIBC__ because uclibc defines it too + sed 's/^ //' << EOF >$dummy.c + #include <features.h> + #if defined(__UCLIBC__) + LIBC=uclibc + #elif defined(__dietlibc__) + LIBC=dietlibc + #endif +EOF + eval `$CC_FOR_BUILD -E $dummy.c 2>/dev/null | grep '^LIBC'` + + # There is no features test macro for musl + # Follow the GNU's config.guess approach of + # checking the output of ldd + if command -v ldd >/dev/null && \ + ldd --version 2>&1 | grep -q ^musl; then + LIBC=musl + fi + + echo "${UNAME_MACHINE}-unknown-linux-${LIBC}" + exit ;; + m32r*:Linux:*:*) + echo ${UNAME_MACHINE}-unknown-linux-gnu + exit ;; + m68*:Linux:*:*) + echo ${UNAME_MACHINE}-unknown-linux-gnu + exit ;; + mips:Linux:*:* | mips64:Linux:*:*) + eval $set_cc_for_build + sed 's/^ //' << EOF >$dummy.c + #undef CPU + #undef ${UNAME_MACHINE} + #undef ${UNAME_MACHINE}el + #if defined(__MIPSEL__) || defined(__MIPSEL) || defined(_MIPSEL) || defined(MIPSEL) + CPU=${UNAME_MACHINE}el + #else + #if defined(__MIPSEB__) || defined(__MIPSEB) || defined(_MIPSEB) || defined(MIPSEB) + CPU=${UNAME_MACHINE} + #else + CPU= + #endif + #endif +EOF + eval `$CC_FOR_BUILD -E $dummy.c 2>/dev/null | grep '^CPU'` + test x"${CPU}" != x && { echo "${CPU}-unknown-linux-gnu"; exit; } + ;; + or32:Linux:*:*) + echo or32-unknown-linux-gnu + exit ;; + padre:Linux:*:*) + echo sparc-unknown-linux-gnu + exit ;; + parisc64:Linux:*:* | hppa64:Linux:*:*) + echo hppa64-unknown-linux-gnu + exit ;; + parisc:Linux:*:* | hppa:Linux:*:*) + # Look for CPU level + case `grep '^cpu[^a-z]*:' /proc/cpuinfo 2>/dev/null | cut -d' ' -f2` in + PA7*) echo hppa1.1-unknown-linux-gnu ;; + PA8*) echo hppa2.0-unknown-linux-gnu ;; + *) echo hppa-unknown-linux-gnu ;; + esac + exit ;; + ppc64:Linux:*:*) + echo powerpc64-unknown-linux-gnu + exit ;; + ppc64le:Linux:*:*) + echo powerpc64le-unknown-linux-gnu + exit ;; + ppc:Linux:*:*) + echo powerpc-unknown-linux-gnu + exit ;; + ppcle:Linux:*:*) + echo powerpcle-unknown-linux-gnu + exit ;; + riscv32:Linux:*:* | riscv64:Linux:*:* | riscv32be:Linux:*:* | riscv64be:Linux:*:*) + LIBC=gnu + eval $set_cc_for_build + # Do not check for __GLIBC__ because uclibc defines it too + sed 's/^ //' << EOF >$dummy.c + #include <features.h> + #if defined(__UCLIBC__) + LIBC=uclibc + #elif defined(__dietlibc__) + LIBC=dietlibc + #endif +EOF + eval `$CC_FOR_BUILD -E $dummy.c 2>/dev/null | grep '^LIBC'` + + # There is no features test macro for musl + # Follow the GNU's config.guess approach of + # checking the output of ldd + if command -v ldd >/dev/null && \ + ldd --version 2>&1 | grep -q ^musl; then + LIBC=musl + fi + + echo ${UNAME_MACHINE}-unknown-linux-${LIBC} + exit ;; + s390:Linux:*:* | s390x:Linux:*:*) + echo ${UNAME_MACHINE}-unknown-linux-gnu + exit ;; + sh64*:Linux:*:*) + echo ${UNAME_MACHINE}-unknown-linux-gnu + exit ;; + sh*:Linux:*:*) + echo ${UNAME_MACHINE}-unknown-linux-gnu + exit ;; + sparc:Linux:*:* | sparc64:Linux:*:*) + echo ${UNAME_MACHINE}-unknown-linux-gnu + exit ;; + tile*:Linux:*:*) + echo ${UNAME_MACHINE}-unknown-linux-gnu + exit ;; + vax:Linux:*:*) + echo ${UNAME_MACHINE}-dec-linux-gnu + exit ;; + x86_64:Linux:*:*) + echo x86_64-unknown-linux-gnu + exit ;; + xtensa*:Linux:*:*) + echo ${UNAME_MACHINE}-unknown-linux-gnu + exit ;; + i*86:DYNIX/ptx:4*:*) + # ptx 4.0 does uname -s correctly, with DYNIX/ptx in there. + # earlier versions are messed up and put the nodename in both + # sysname and nodename. + echo i386-sequent-sysv4 + exit ;; + i*86:UNIX_SV:4.2MP:2.*) + # Unixware is an offshoot of SVR4, but it has its own version + # number series starting with 2... + # I am not positive that other SVR4 systems won't match this, + # I just have to hope. -- rms. + # Use sysv4.2uw... so that sysv4* matches it. + echo ${UNAME_MACHINE}-pc-sysv4.2uw${UNAME_VERSION} + exit ;; + i*86:OS/2:*:*) + # If we were able to find `uname', then EMX Unix compatibility + # is probably installed. + echo ${UNAME_MACHINE}-pc-os2-emx + exit ;; + i*86:XTS-300:*:STOP) + echo ${UNAME_MACHINE}-unknown-stop + exit ;; + i*86:atheos:*:*) + echo ${UNAME_MACHINE}-unknown-atheos + exit ;; + i*86:syllable:*:*) + echo ${UNAME_MACHINE}-pc-syllable + exit ;; + i*86:LynxOS:2.*:* | i*86:LynxOS:3.[01]*:* | i*86:LynxOS:4.[02]*:*) + echo i386-unknown-lynxos${UNAME_RELEASE} + exit ;; + i*86:*DOS:*:*) + echo ${UNAME_MACHINE}-pc-msdosdjgpp + exit ;; + i*86:*:4.*:* | i*86:SYSTEM_V:4.*:*) + UNAME_REL=`echo ${UNAME_RELEASE} | sed 's/\/MP$//'` + if grep Novell /usr/include/link.h >/dev/null 2>/dev/null; then + echo ${UNAME_MACHINE}-univel-sysv${UNAME_REL} + else + echo ${UNAME_MACHINE}-pc-sysv${UNAME_REL} + fi + exit ;; + i*86:*:5:[678]*) + # UnixWare 7.x, OpenUNIX and OpenServer 6. + case `/bin/uname -X | grep "^Machine"` in + *486*) UNAME_MACHINE=i486 ;; + *Pentium) UNAME_MACHINE=i586 ;; + *Pent*|*Celeron) UNAME_MACHINE=i686 ;; + esac + echo ${UNAME_MACHINE}-unknown-sysv${UNAME_RELEASE}${UNAME_SYSTEM}${UNAME_VERSION} + exit ;; + i*86:*:3.2:*) + if test -f /usr/options/cb.name; then + UNAME_REL=`sed -n 's/.*Version //p' </usr/options/cb.name` + echo ${UNAME_MACHINE}-pc-isc$UNAME_REL + elif /bin/uname -X 2>/dev/null >/dev/null ; then + UNAME_REL=`(/bin/uname -X|grep Release|sed -e 's/.*= //')` + (/bin/uname -X|grep i80486 >/dev/null) && UNAME_MACHINE=i486 + (/bin/uname -X|grep '^Machine.*Pentium' >/dev/null) \ + && UNAME_MACHINE=i586 + (/bin/uname -X|grep '^Machine.*Pent *II' >/dev/null) \ + && UNAME_MACHINE=i686 + (/bin/uname -X|grep '^Machine.*Pentium Pro' >/dev/null) \ + && UNAME_MACHINE=i686 + echo ${UNAME_MACHINE}-pc-sco$UNAME_REL + else + echo ${UNAME_MACHINE}-pc-sysv32 + fi + exit ;; + pc:*:*:*) + # Left here for compatibility: + # uname -m prints for DJGPP always 'pc', but it prints nothing about + # the processor, so we play safe by assuming i586. + # Note: whatever this is, it MUST be the same as what config.sub + # prints for the "djgpp" host, or else GDB configury will decide that + # this is a cross-build. + echo i586-pc-msdosdjgpp + exit ;; + Intel:Mach:3*:*) + echo i386-pc-mach3 + exit ;; + paragon:*:*:*) + echo i860-intel-osf1 + exit ;; + i860:*:4.*:*) # i860-SVR4 + if grep Stardent /usr/include/sys/uadmin.h >/dev/null 2>&1 ; then + echo i860-stardent-sysv${UNAME_RELEASE} # Stardent Vistra i860-SVR4 + else # Add other i860-SVR4 vendors below as they are discovered. + echo i860-unknown-sysv${UNAME_RELEASE} # Unknown i860-SVR4 + fi + exit ;; + mini*:CTIX:SYS*5:*) + # "miniframe" + echo m68010-convergent-sysv + exit ;; + mc68k:UNIX:SYSTEM5:3.51m) + echo m68k-convergent-sysv + exit ;; + M680?0:D-NIX:5.3:*) + echo m68k-diab-dnix + exit ;; + M68*:*:R3V[5678]*:*) + test -r /sysV68 && { echo 'm68k-motorola-sysv'; exit; } ;; + 3[345]??:*:4.0:3.0 | 3[34]??A:*:4.0:3.0 | 3[34]??,*:*:4.0:3.0 | 3[34]??/*:*:4.0:3.0 | 4400:*:4.0:3.0 | 4850:*:4.0:3.0 | SKA40:*:4.0:3.0 | SDS2:*:4.0:3.0 | SHG2:*:4.0:3.0 | S7501*:*:4.0:3.0) + OS_REL='' + test -r /etc/.relid \ + && OS_REL=.`sed -n 's/[^ ]* [^ ]* \([0-9][0-9]\).*/\1/p' < /etc/.relid` + /bin/uname -p 2>/dev/null | grep 86 >/dev/null \ + && { echo i486-ncr-sysv4.3${OS_REL}; exit; } + /bin/uname -p 2>/dev/null | /bin/grep entium >/dev/null \ + && { echo i586-ncr-sysv4.3${OS_REL}; exit; } ;; + 3[34]??:*:4.0:* | 3[34]??,*:*:4.0:*) + /bin/uname -p 2>/dev/null | grep 86 >/dev/null \ + && { echo i486-ncr-sysv4; exit; } ;; + NCR*:*:4.2:* | MPRAS*:*:4.2:*) + OS_REL='.3' + test -r /etc/.relid \ + && OS_REL=.`sed -n 's/[^ ]* [^ ]* \([0-9][0-9]\).*/\1/p' < /etc/.relid` + /bin/uname -p 2>/dev/null | grep 86 >/dev/null \ + && { echo i486-ncr-sysv4.3${OS_REL}; exit; } + /bin/uname -p 2>/dev/null | /bin/grep entium >/dev/null \ + && { echo i586-ncr-sysv4.3${OS_REL}; exit; } + /bin/uname -p 2>/dev/null | /bin/grep pteron >/dev/null \ + && { echo i586-ncr-sysv4.3${OS_REL}; exit; } ;; + m68*:LynxOS:2.*:* | m68*:LynxOS:3.0*:*) + echo m68k-unknown-lynxos${UNAME_RELEASE} + exit ;; + mc68030:UNIX_System_V:4.*:*) + echo m68k-atari-sysv4 + exit ;; + TSUNAMI:LynxOS:2.*:*) + echo sparc-unknown-lynxos${UNAME_RELEASE} + exit ;; + rs6000:LynxOS:2.*:*) + echo rs6000-unknown-lynxos${UNAME_RELEASE} + exit ;; + PowerPC:LynxOS:2.*:* | PowerPC:LynxOS:3.[01]*:* | PowerPC:LynxOS:4.[02]*:*) + echo powerpc-unknown-lynxos${UNAME_RELEASE} + exit ;; + SM[BE]S:UNIX_SV:*:*) + echo mips-dde-sysv${UNAME_RELEASE} + exit ;; + RM*:ReliantUNIX-*:*:*) + echo mips-sni-sysv4 + exit ;; + RM*:SINIX-*:*:*) + echo mips-sni-sysv4 + exit ;; + *:SINIX-*:*:*) + if uname -p 2>/dev/null >/dev/null ; then + UNAME_MACHINE=`(uname -p) 2>/dev/null` + echo ${UNAME_MACHINE}-sni-sysv4 + else + echo ns32k-sni-sysv + fi + exit ;; + PENTIUM:*:4.0*:*) # Unisys `ClearPath HMP IX 4000' SVR4/MP effort + # says <Richard.M.Bartel@ccMail.Census.GOV> + echo i586-unisys-sysv4 + exit ;; + *:UNIX_System_V:4*:FTX*) + # From Gerald Hewes <hewes@openmarket.com>. + # How about differentiating between stratus architectures? -djm + echo hppa1.1-stratus-sysv4 + exit ;; + *:*:*:FTX*) + # From seanf@swdc.stratus.com. + echo i860-stratus-sysv4 + exit ;; + i*86:VOS:*:*) + # From Paul.Green@stratus.com. + echo ${UNAME_MACHINE}-stratus-vos + exit ;; + *:VOS:*:*) + # From Paul.Green@stratus.com. + echo hppa1.1-stratus-vos + exit ;; + mc68*:A/UX:*:*) + echo m68k-apple-aux${UNAME_RELEASE} + exit ;; + news*:NEWS-OS:6*:*) + echo mips-sony-newsos6 + exit ;; + R[34]000:*System_V*:*:* | R4000:UNIX_SYSV:*:* | R*000:UNIX_SV:*:*) + if [ -d /usr/nec ]; then + echo mips-nec-sysv${UNAME_RELEASE} + else + echo mips-unknown-sysv${UNAME_RELEASE} + fi + exit ;; + BeBox:BeOS:*:*) # BeOS running on hardware made by Be, PPC only. + echo powerpc-be-beos + exit ;; + BeMac:BeOS:*:*) # BeOS running on Mac or Mac clone, PPC only. + echo powerpc-apple-beos + exit ;; + BePC:BeOS:*:*) # BeOS running on Intel PC compatible. + echo i586-pc-beos + exit ;; + BePC:Haiku:*:*) # Haiku running on Intel PC compatible. + echo i586-pc-haiku + exit ;; + ppc:Haiku:*:*) # Haiku running on Apple PowerPC + echo powerpc-apple-haiku + exit ;; + *:Haiku:*:*) # Haiku modern gcc (not bound by BeOS compat) + echo ${UNAME_MACHINE}-unknown-haiku + exit ;; + SX-4:SUPER-UX:*:*) + echo sx4-nec-superux${UNAME_RELEASE} + exit ;; + SX-5:SUPER-UX:*:*) + echo sx5-nec-superux${UNAME_RELEASE} + exit ;; + SX-6:SUPER-UX:*:*) + echo sx6-nec-superux${UNAME_RELEASE} + exit ;; + SX-7:SUPER-UX:*:*) + echo sx7-nec-superux${UNAME_RELEASE} + exit ;; + SX-8:SUPER-UX:*:*) + echo sx8-nec-superux${UNAME_RELEASE} + exit ;; + SX-8R:SUPER-UX:*:*) + echo sx8r-nec-superux${UNAME_RELEASE} + exit ;; + Power*:Rhapsody:*:*) + echo powerpc-apple-rhapsody${UNAME_RELEASE} + exit ;; + *:Rhapsody:*:*) + echo ${UNAME_MACHINE}-apple-rhapsody${UNAME_RELEASE} + exit ;; + *:Darwin:*:*) + UNAME_PROCESSOR=`uname -p` || UNAME_PROCESSOR=unknown + case $UNAME_PROCESSOR in + i386) + eval $set_cc_for_build + if [ "$CC_FOR_BUILD" != 'no_compiler_found' ]; then + if (echo '#ifdef __LP64__'; echo IS_64BIT_ARCH; echo '#endif') | \ + (CCOPTS= $CC_FOR_BUILD -E - 2>/dev/null) | \ + grep IS_64BIT_ARCH >/dev/null + then + UNAME_PROCESSOR="x86_64" + fi + fi ;; + arm) + eval $set_cc_for_build + if [ "$CC_FOR_BUILD" != 'no_compiler_found' ]; then + if (echo '#ifdef __LP64__'; echo IS_64BIT_ARCH; echo '#endif') | \ + (CCOPTS= $CC_FOR_BUILD -E - 2>/dev/null) | \ + grep IS_64BIT_ARCH >/dev/null + then + if (echo '#ifdef __PTRAUTH_INTRINSICS__'; echo HAS_AUTH; echo '#endif') | \ + (CCOPTS= $CC_FOR_BUILD -E - 2>/dev/null) | \ + grep HAS_AUTH >/dev/null + then + UNAME_PROCESSOR="arm64e" + else + UNAME_PROCESSOR="arm64" + fi + fi + fi ;; + unknown) UNAME_PROCESSOR=powerpc ;; + esac + echo ${UNAME_PROCESSOR}-apple-darwin${UNAME_RELEASE} + exit ;; + *:procnto*:*:* | *:QNX:[0123456789]*:*) + UNAME_PROCESSOR=`uname -p` + if test "$UNAME_PROCESSOR" = "x86"; then + UNAME_PROCESSOR=i386 + UNAME_MACHINE=pc + fi + echo ${UNAME_PROCESSOR}-${UNAME_MACHINE}-nto-qnx${UNAME_RELEASE} + exit ;; + *:QNX:*:4*) + echo i386-pc-qnx + exit ;; + NEO-?:NONSTOP_KERNEL:*:*) + echo neo-tandem-nsk${UNAME_RELEASE} + exit ;; + NSE-?:NONSTOP_KERNEL:*:*) + echo nse-tandem-nsk${UNAME_RELEASE} + exit ;; + NSR-?:NONSTOP_KERNEL:*:*) + echo nsr-tandem-nsk${UNAME_RELEASE} + exit ;; + *:NonStop-UX:*:*) + echo mips-compaq-nonstopux + exit ;; + BS2000:POSIX*:*:*) + echo bs2000-siemens-sysv + exit ;; + DS/*:UNIX_System_V:*:*) + echo ${UNAME_MACHINE}-${UNAME_SYSTEM}-${UNAME_RELEASE} + exit ;; + *:Plan9:*:*) + # "uname -m" is not consistent, so use $cputype instead. 386 + # is converted to i386 for consistency with other x86 + # operating systems. + if test "$cputype" = "386"; then + UNAME_MACHINE=i386 + else + UNAME_MACHINE="$cputype" + fi + echo ${UNAME_MACHINE}-unknown-plan9 + exit ;; + *:TOPS-10:*:*) + echo pdp10-unknown-tops10 + exit ;; + *:TENEX:*:*) + echo pdp10-unknown-tenex + exit ;; + KS10:TOPS-20:*:* | KL10:TOPS-20:*:* | TYPE4:TOPS-20:*:*) + echo pdp10-dec-tops20 + exit ;; + XKL-1:TOPS-20:*:* | TYPE5:TOPS-20:*:*) + echo pdp10-xkl-tops20 + exit ;; + *:TOPS-20:*:*) + echo pdp10-unknown-tops20 + exit ;; + *:ITS:*:*) + echo pdp10-unknown-its + exit ;; + SEI:*:*:SEIUX) + echo mips-sei-seiux${UNAME_RELEASE} + exit ;; + *:DragonFly:*:*) + echo ${UNAME_MACHINE}-unknown-dragonfly`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'` + exit ;; + *:*VMS:*:*) + UNAME_MACHINE=`(uname -p) 2>/dev/null` + case "${UNAME_MACHINE}" in + A*) echo alpha-dec-vms ; exit ;; + I*) echo ia64-dec-vms ; exit ;; + V*) echo vax-dec-vms ; exit ;; + esac ;; + *:XENIX:*:SysV) + echo i386-pc-xenix + exit ;; + i*86:skyos:*:*) + echo ${UNAME_MACHINE}-pc-skyos`echo ${UNAME_RELEASE}` | sed -e 's/ .*$//' + exit ;; + i*86:rdos:*:*) + echo ${UNAME_MACHINE}-pc-rdos + exit ;; + i*86:AROS:*:*) + echo ${UNAME_MACHINE}-pc-aros + exit ;; +esac + +#echo '(No uname command or uname output not recognized.)' 1>&2 +#echo "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" 1>&2 + +eval $set_cc_for_build +cat >$dummy.c <<EOF +#ifdef _SEQUENT_ +# include <sys/types.h> +# include <sys/utsname.h> +#endif +main () +{ +#if defined (sony) +#if defined (MIPSEB) + /* BFD wants "bsd" instead of "newsos". Perhaps BFD should be changed, + I don't know.... */ + printf ("mips-sony-bsd\n"); exit (0); +#else +#include <sys/param.h> + printf ("m68k-sony-newsos%s\n", +#ifdef NEWSOS4 + "4" +#else + "" +#endif + ); exit (0); +#endif +#endif + +#if defined (__arm) && defined (__acorn) && defined (__unix) + printf ("arm-acorn-riscix\n"); exit (0); +#endif + +#if defined (hp300) && !defined (hpux) + printf ("m68k-hp-bsd\n"); exit (0); +#endif + +#if defined (NeXT) +#if !defined (__ARCHITECTURE__) +#define __ARCHITECTURE__ "m68k" +#endif + int version; + version=`(hostinfo | sed -n 's/.*NeXT Mach \([0-9]*\).*/\1/p') 2>/dev/null`; + if (version < 4) + printf ("%s-next-nextstep%d\n", __ARCHITECTURE__, version); + else + printf ("%s-next-openstep%d\n", __ARCHITECTURE__, version); + exit (0); +#endif + +#if defined (MULTIMAX) || defined (n16) +#if defined (UMAXV) + printf ("ns32k-encore-sysv\n"); exit (0); +#else +#if defined (CMU) + printf ("ns32k-encore-mach\n"); exit (0); +#else + printf ("ns32k-encore-bsd\n"); exit (0); +#endif +#endif +#endif + +#if defined (__386BSD__) + printf ("i386-pc-bsd\n"); exit (0); +#endif + +#if defined (sequent) +#if defined (i386) + printf ("i386-sequent-dynix\n"); exit (0); +#endif +#if defined (ns32000) + printf ("ns32k-sequent-dynix\n"); exit (0); +#endif +#endif + +#if defined (_SEQUENT_) + struct utsname un; + + uname(&un); + + if (strncmp(un.version, "V2", 2) == 0) { + printf ("i386-sequent-ptx2\n"); exit (0); + } + if (strncmp(un.version, "V1", 2) == 0) { /* XXX is V1 correct? */ + printf ("i386-sequent-ptx1\n"); exit (0); + } + printf ("i386-sequent-ptx\n"); exit (0); + +#endif + +#if defined (vax) +# if !defined (ultrix) +# include <sys/param.h> +# if defined (BSD) +# if BSD == 43 + printf ("vax-dec-bsd4.3\n"); exit (0); +# else +# if BSD == 199006 + printf ("vax-dec-bsd4.3reno\n"); exit (0); +# else + printf ("vax-dec-bsd\n"); exit (0); +# endif +# endif +# else + printf ("vax-dec-bsd\n"); exit (0); +# endif +# else + printf ("vax-dec-ultrix\n"); exit (0); +# endif +#endif + +#if defined (alliant) && defined (i860) + printf ("i860-alliant-bsd\n"); exit (0); +#endif + + exit (1); +} +EOF + +$CC_FOR_BUILD -o $dummy $dummy.c 2>/dev/null && SYSTEM_NAME=`$dummy` && + { echo "$SYSTEM_NAME"; exit; } + +# Apollos put the system type in the environment. + +test -d /usr/apollo && { echo ${ISP}-apollo-${SYSTYPE}; exit; } + +# Convex versions that predate uname can use getsysinfo(1) + +if [ -x /usr/convex/getsysinfo ] +then + case `getsysinfo -f cpu_type` in + c1*) + echo c1-convex-bsd + exit ;; + c2*) + if getsysinfo -f scalar_acc + then echo c32-convex-bsd + else echo c2-convex-bsd + fi + exit ;; + c34*) + echo c34-convex-bsd + exit ;; + c38*) + echo c38-convex-bsd + exit ;; + c4*) + echo c4-convex-bsd + exit ;; + esac +fi + +cat >&2 <<EOF +$0: unable to guess system type + +This script, last modified $timestamp, has failed to recognize +the operating system you are using. It is advised that you +download the most up to date version of the config scripts from + + http://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.guess;hb=HEAD +and + http://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.sub;hb=HEAD + +If the version you run ($0) is already up to date, please +send the following data and any information you think might be +pertinent to <config-patches@gnu.org> in order to provide the needed +information to handle your system. + +config.guess timestamp = $timestamp + +uname -m = `(uname -m) 2>/dev/null || echo unknown` +uname -r = `(uname -r) 2>/dev/null || echo unknown` +uname -s = `(uname -s) 2>/dev/null || echo unknown` +uname -v = `(uname -v) 2>/dev/null || echo unknown` + +/usr/bin/uname -p = `(/usr/bin/uname -p) 2>/dev/null` +/bin/uname -X = `(/bin/uname -X) 2>/dev/null` + +hostinfo = `(hostinfo) 2>/dev/null` +/bin/universe = `(/bin/universe) 2>/dev/null` +/usr/bin/arch -k = `(/usr/bin/arch -k) 2>/dev/null` +/bin/arch = `(/bin/arch) 2>/dev/null` +/usr/bin/oslevel = `(/usr/bin/oslevel) 2>/dev/null` +/usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null` + +UNAME_MACHINE = ${UNAME_MACHINE} +UNAME_RELEASE = ${UNAME_RELEASE} +UNAME_SYSTEM = ${UNAME_SYSTEM} +UNAME_VERSION = ${UNAME_VERSION} +EOF + +exit 1 + +# Local variables: +# eval: (add-hook 'write-file-hooks 'time-stamp) +# time-stamp-start: "timestamp='" +# time-stamp-format: "%:y-%02m-%02d" +# time-stamp-end: "'" +# End: diff --git a/rcc/cmake/modules/AddOCaml.cmake b/rcc/cmake/modules/AddOCaml.cmake new file mode 100644 index 0000000..01fb5a1 --- /dev/null +++ b/rcc/cmake/modules/AddOCaml.cmake @@ -0,0 +1,239 @@ +# CMake build rules for the OCaml language. +# Assumes FindOCaml is used. +# http://ocaml.org/ +# +# Example usage: +# +# add_ocaml_library(pkg_a OCAML mod_a OCAMLDEP pkg_b C mod_a_stubs PKG ctypes RCC core) +# +# Unnamed parameters: +# +# * Library name. +# +# Named parameters: +# +# OCAML OCaml module names. Imply presence of a corresponding .ml and .mli files. +# OCAMLDEP Names of libraries this library depends on. +# C C stub sources. Imply presence of a corresponding .c file. +# CFLAGS Additional arguments passed when compiling C stubs. +# PKG Names of ocamlfind packages this library depends on. +# RCC Names of RCC libraries this library depends on. +# NOCOPY Do not automatically copy sources (.c, .ml, .mli) from the source directory, +# e.g. if they are generated. +# + +function(add_ocaml_library name) + CMAKE_PARSE_ARGUMENTS(ARG "NOCOPY" "" "OCAML;OCAMLDEP;C;CFLAGS;PKG;RCC" ${ARGN}) + + set(src ${CMAKE_CURRENT_SOURCE_DIR}) + set(bin ${CMAKE_CURRENT_BINARY_DIR}) + + set(ocaml_pkgs) + foreach( ocaml_pkg ${ARG_PKG} ) + list(APPEND ocaml_pkgs "-package" "${ocaml_pkg}") + endforeach() + + set(sources) + + set(ocaml_inputs) + + set(ocaml_outputs "${bin}/${name}.cma") + if( ARG_C ) + list(APPEND ocaml_outputs + "${bin}/lib${name}${CMAKE_STATIC_LIBRARY_SUFFIX}") + if ( BUILD_SHARED_LIBS ) + list(APPEND ocaml_outputs + "${bin}/dll${name}${CMAKE_SHARED_LIBRARY_SUFFIX}") + endif() + endif() + if( HAVE_OCAMLOPT ) + list(APPEND ocaml_outputs + "${bin}/${name}.cmxa" + "${bin}/${name}${CMAKE_STATIC_LIBRARY_SUFFIX}") + endif() + + set(ocaml_flags "-lstdc++" "-ldopt" "-L${RCC_LIBRARY_DIR}" + "-ccopt" "-L\\$CAMLORIGIN/../.." + "-ccopt" "-Wl,-rpath,\\$CAMLORIGIN/../.." + ${ocaml_pkgs}) + + set(ocaml_dep_inputs) + foreach( ocaml_dep ${ARG_OCAMLDEP} ) + get_target_property(dep_ocaml_flags "ocaml_${ocaml_dep}" OCAML_FLAGS) + list(APPEND ocaml_flags ${dep_ocaml_flags}) + get_target_property(dep_ocaml_outputs "ocaml_${ocaml_dep}" OCAML_OUTPUTS) + list(APPEND ocaml_dep_inputs ${dep_ocaml_outputs}) + endforeach() + + if( NOT BUILD_SHARED_LIBS ) + list(APPEND ocaml_flags "-custom") + endif() + + if(RCC_LINK_RCC_DYLIB) + list(APPEND ocaml_flags "-lRCC") + else() + explicit_map_components_to_libraries(rcc_libs ${ARG_RCC}) + foreach( rcc_lib ${rcc_libs} ) + list(APPEND ocaml_flags "-l${rcc_lib}" ) + endforeach() + + get_property(system_libs TARGET RCCSupport PROPERTY RCC_SYSTEM_LIBS) + foreach(system_lib ${system_libs}) + if (system_lib MATCHES "^-") + # If it's an option, pass it without changes. + list(APPEND ocaml_flags "${system_lib}" ) + else() + # Otherwise assume it's a library name we need to link with. + list(APPEND ocaml_flags "-l${system_lib}" ) + endif() + endforeach() + endif() + + string(REPLACE ";" " " ARG_CFLAGS "${ARG_CFLAGS}") + set(c_flags "${ARG_CFLAGS} ${RCC_DEFINITIONS}") + foreach( include_dir ${RCC_INCLUDE_DIR} ${RCC_MAIN_INCLUDE_DIR} ) + set(c_flags "${c_flags} -I${include_dir}") + endforeach() + # include -D/-UNDEBUG to match dump function visibility + # regex from HandleRCCOptions.cmake + string(REGEX MATCH "(^| )[/-][UD] *NDEBUG($| )" flag_matches + "${CMAKE_C_FLAGS_${uppercase_CMAKE_BUILD_TYPE}} ${CMAKE_C_FLAGS}") + set(c_flags "${c_flags} ${flag_matches}") + + foreach( ocaml_file ${ARG_OCAML} ) + list(APPEND sources "${ocaml_file}.mli" "${ocaml_file}.ml") + + list(APPEND ocaml_inputs "${bin}/${ocaml_file}.mli" "${bin}/${ocaml_file}.ml") + + list(APPEND ocaml_outputs "${bin}/${ocaml_file}.cmi" "${bin}/${ocaml_file}.cmo") + + list(APPEND ocaml_outputs "${bin}/${ocaml_file}.cmti" "${bin}/${ocaml_file}.cmt") + + if( HAVE_OCAMLOPT ) + list(APPEND ocaml_outputs + "${bin}/${ocaml_file}.cmx" + "${bin}/${ocaml_file}${CMAKE_C_OUTPUT_EXTENSION}") + endif() + endforeach() + + foreach( c_file ${ARG_C} ) + list(APPEND sources "${c_file}.c") + + list(APPEND c_inputs "${bin}/${c_file}.c") + list(APPEND c_outputs "${bin}/${c_file}${CMAKE_C_OUTPUT_EXTENSION}") + endforeach() + + if( NOT ARG_NOCOPY ) + foreach( source ${sources} ) + add_custom_command( + OUTPUT "${bin}/${source}" + COMMAND "${CMAKE_COMMAND}" "-E" "copy" "${src}/${source}" "${bin}" + DEPENDS "${src}/${source}" + COMMENT "Copying ${source} to build area") + endforeach() + endif() + + foreach( c_input ${c_inputs} ) + get_filename_component(basename "${c_input}" NAME_WE) + add_custom_command( + OUTPUT "${basename}${CMAKE_C_OUTPUT_EXTENSION}" + COMMAND "${OCAMLFIND}" "ocamlc" "-c" "${c_input}" -ccopt ${c_flags} + DEPENDS "${c_input}" + COMMENT "Building OCaml stub object file ${basename}${CMAKE_C_OUTPUT_EXTENSION}" + VERBATIM) + endforeach() + + set(ocaml_params) + foreach( ocaml_input ${ocaml_inputs} ${c_outputs}) + get_filename_component(filename "${ocaml_input}" NAME) + list(APPEND ocaml_params "${filename}") + endforeach() + + if( APPLE ) + set(ocaml_rpath "@executable_path/../../../lib${RCC_LIBDIR_SUFFIX}") + elseif( UNIX ) + set(ocaml_rpath "\\$ORIGIN/../../../lib${RCC_LIBDIR_SUFFIX}") + endif() + list(APPEND ocaml_flags "-ldopt" "-Wl,-rpath,${ocaml_rpath}") + + add_custom_command( + OUTPUT ${ocaml_outputs} + COMMAND "${OCAMLFIND}" "ocamlmklib" "-ocamlcflags" "-bin-annot" + "-o" "${name}" ${ocaml_flags} ${ocaml_params} + DEPENDS ${ocaml_inputs} ${ocaml_dep_inputs} ${c_outputs} + COMMENT "Building OCaml library ${name}" + VERBATIM) + + add_custom_command( + OUTPUT "${bin}/${name}.odoc" + COMMAND "${OCAMLFIND}" "ocamldoc" + "-I" "${bin}" + "-I" "${RCC_LIBRARY_DIR}/ocaml/rcc/" + "-dump" "${bin}/${name}.odoc" + ${ocaml_pkgs} ${ocaml_inputs} + DEPENDS ${ocaml_inputs} ${ocaml_dep_inputs} ${ocaml_outputs} + COMMENT "Building OCaml documentation for ${name}" + VERBATIM) + + add_custom_target("ocaml_${name}" ALL DEPENDS ${ocaml_outputs} "${bin}/${name}.odoc") + get_subproject_title(subproject_title) + set_target_properties("ocaml_${name}" PROPERTIES FOLDER "${subproject_title}/Bindings/OCaml") + + set_target_properties("ocaml_${name}" PROPERTIES + OCAML_FLAGS "-I;${bin}") + set_target_properties("ocaml_${name}" PROPERTIES + OCAML_ODOC "${bin}/${name}.odoc") + set_target_properties("ocaml_${name}" PROPERTIES + OCAML_OUTPUTS "${ocaml_outputs}") + + foreach( ocaml_dep ${ARG_OCAMLDEP} ) + add_dependencies("ocaml_${name}" "ocaml_${ocaml_dep}") + endforeach() + + if( NOT RCC_OCAML_OUT_OF_TREE ) + foreach( rcc_lib ${rcc_libs} ) + add_dependencies("ocaml_${name}" "${rcc_lib}") + endforeach() + endif() + + add_dependencies("ocaml_all" "ocaml_${name}") + + set(install_files) + set(install_shlibs) + foreach( ocaml_output ${ocaml_inputs} ${ocaml_outputs} ) + get_filename_component(ext "${ocaml_output}" EXT) + + if( NOT (ext STREQUAL ".cmo" OR + ext STREQUAL ".ml" OR + ext STREQUAL CMAKE_C_OUTPUT_EXTENSION OR + ext STREQUAL CMAKE_SHARED_LIBRARY_SUFFIX) ) + list(APPEND install_files "${ocaml_output}") + elseif( ext STREQUAL CMAKE_SHARED_LIBRARY_SUFFIX) + list(APPEND install_shlibs "${ocaml_output}") + endif() + endforeach() + + install(FILES ${install_files} + DESTINATION "${RCC_OCAML_INSTALL_PATH}/rcc") + install(FILES ${install_shlibs} + PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE + GROUP_READ GROUP_EXECUTE + WORLD_READ WORLD_EXECUTE + DESTINATION "${RCC_OCAML_INSTALL_PATH}/stublibs") + + foreach( install_file ${install_files} ${install_shlibs} ) + get_filename_component(filename "${install_file}" NAME) + add_custom_command(TARGET "ocaml_${name}" POST_BUILD + COMMAND "${CMAKE_COMMAND}" "-E" "copy" "${install_file}" + "${RCC_LIBRARY_DIR}/ocaml/rcc/" + COMMENT "Copying OCaml library component ${filename} to intermediate area" + VERBATIM) + add_dependencies("ocaml_${name}" ocaml_make_directory) + endforeach() +endfunction() + +add_custom_target(ocaml_make_directory + COMMAND "${CMAKE_COMMAND}" "-E" "make_directory" "${RCC_LIBRARY_DIR}/ocaml/rcc") +add_custom_target("ocaml_all") +set_target_properties(ocaml_all PROPERTIES FOLDER "RCC/Bindings/OCaml") +set_target_properties(ocaml_make_directory PROPERTIES FOLDER "RCC/Bindings/OCaml") diff --git a/rcc/cmake/modules/AddRCC.cmake b/rcc/cmake/modules/AddRCC.cmake new file mode 100644 index 0000000..738df5f --- /dev/null +++ b/rcc/cmake/modules/AddRCC.cmake @@ -0,0 +1,194 @@ +# SPDX-License-Identifier: GPL-3.0-or-later + +include_guard(GLOBAL) +include(GNUInstallDirs) +include(HandleRCCOptions) + +macro(rcc_configure_output_directories) + set(RCC_RUNTIME_OUTPUT_INTDIR "${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR}/bin") + set(RCC_LIBRARY_OUTPUT_INTDIR "${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR}/lib") + set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${RCC_RUNTIME_OUTPUT_INTDIR}") + set(CMAKE_LIBRARY_OUTPUT_DIRECTORY "${RCC_LIBRARY_OUTPUT_INTDIR}") + set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY "${RCC_LIBRARY_OUTPUT_INTDIR}") +endmacro() + +function(rcc_set_output_directory target) + if(WIN32 OR CYGWIN) + set(moduleDir "${RCC_RUNTIME_OUTPUT_INTDIR}") + else() + set(moduleDir "${RCC_LIBRARY_OUTPUT_INTDIR}") + endif() + if(NOT CMAKE_CFG_INTDIR STREQUAL ".") + foreach(buildMode IN LISTS CMAKE_CONFIGURATION_TYPES) + string(TOUPPER "${buildMode}" configSuffix) + if(RCC_RUNTIME_OUTPUT_INTDIR) + string(REPLACE "${CMAKE_CFG_INTDIR}" "${buildMode}" binaryDir "${RCC_RUNTIME_OUTPUT_INTDIR}") + set_target_properties(${target} PROPERTIES RUNTIME_OUTPUT_DIRECTORY_${configSuffix} "${binaryDir}") + endif() + if(RCC_LIBRARY_OUTPUT_INTDIR) + string(REPLACE "${CMAKE_CFG_INTDIR}" "${buildMode}" libraryDir "${RCC_LIBRARY_OUTPUT_INTDIR}") + set_target_properties(${target} PROPERTIES ARCHIVE_OUTPUT_DIRECTORY_${configSuffix} "${libraryDir}") + endif() + if(moduleDir) + string(REPLACE "${CMAKE_CFG_INTDIR}" "${buildMode}" configModuleDir "${moduleDir}") + set_target_properties(${target} PROPERTIES LIBRARY_OUTPUT_DIRECTORY_${configSuffix} "${configModuleDir}") + endif() + endforeach() + else() + if(RCC_RUNTIME_OUTPUT_INTDIR) + set_target_properties(${target} PROPERTIES RUNTIME_OUTPUT_DIRECTORY "${RCC_RUNTIME_OUTPUT_INTDIR}") + endif() + if(RCC_LIBRARY_OUTPUT_INTDIR) + set_target_properties(${target} PROPERTIES ARCHIVE_OUTPUT_DIRECTORY "${RCC_LIBRARY_OUTPUT_INTDIR}") + endif() + if(moduleDir) + set_target_properties(${target} PROPERTIES LIBRARY_OUTPUT_DIRECTORY "${moduleDir}") + endif() + endif() +endfunction() + +function(rcc_update_pch target) + if(RCC_REQUIRES_RTTI OR RCC_REQUIRES_EH) + set(arg_DISABLE_PCH_REUSE ON) + endif() + get_target_property(sources ${target} SOURCES) + foreach(source IN LISTS sources) + get_filename_component(extension "${source}" EXT) + if(extension STREQUAL ".c" AND NOT arg_PRECOMPILE_HEADERS) + set(arg_DISABLE_PCH_REUSE ON) + elseif(extension STREQUAL ".mm") + set(arg_DISABLE_PCH_REUSE ON) + endif() + endforeach() + get_target_property(targetType ${target} TYPE) + if(targetType STREQUAL "EXECUTABLE") + get_target_property(positionIndependent ${target} POSITION_INDEPENDENT_CODE) + if(positionIndependent) + set(arg_DISABLE_PCH_REUSE ON) + endif() + endif() + + set(pchPriority 0) + foreach(library IN LISTS arg_LINK_LIBS) + if(TARGET ${library}) + get_target_property(libraryPriority ${library} RCC_PCH_PRIORITY) + get_target_property(libraryPCHDisabled ${library} DISABLE_PRECOMPILE_HEADERS) + if(libraryPriority GREATER pchPriority AND NOT libraryPCHDisabled) + set(pchPriority "${libraryPriority}") + set(pchReuse "${library}") + endif() + endif() + endforeach() + if(arg_PRECOMPILE_HEADERS) + message(DEBUG "Adding PCH ${arg_PRECOMPILE_HEADERS} for ${target} (prio ${pchPriority})") + target_precompile_headers(${target} PRIVATE "$<$<COMPILE_LANGUAGE:CXX>:${arg_PRECOMPILE_HEADERS}>") + if(NOT arg_DISABLE_PCH_REUSE) + math(EXPR pchPriority "${pchPriority} + 1") + set_target_properties(${target} PROPERTIES RCC_PCH_PRIORITY "${pchPriority}") + endif() + elseif(pchReuse AND NOT arg_DISABLE_PCH_REUSE) + message(DEBUG "Using PCH ${pchReuse} for ${target} (prio ${pchPriority})") + target_precompile_headers(${target} REUSE_FROM ${pchReuse}) + else() + message(DEBUG "Using NO PCH for ${target}") + endif() +endfunction() + +function(rcc_setup_rpath target) + if(CMAKE_INSTALL_RPATH) + return() + endif() + set(extraLibraryDir "") + if(RCC_INSTALL_PREFIX AND NOT RCC_INSTALL_PREFIX STREQUAL CMAKE_INSTALL_PREFIX) + set(extraLibraryDir "${RCC_LIBRARY_DIR}") + elseif(RCC_BUILD_LIBRARY_DIR) + set(extraLibraryDir "${RCC_LIBRARY_DIR}") + endif() + get_target_property(targetType ${target} TYPE) + if(targetType STREQUAL "EXECUTABLE") + set(installDirectory "${CMAKE_INSTALL_FULL_BINDIR}") + else() + set(installDirectory "${CMAKE_INSTALL_FULL_LIBDIR}") + endif() + file(RELATIVE_PATH relativeLibraryDir "${installDirectory}" "${CMAKE_INSTALL_FULL_LIBDIR}") + + if(APPLE) + set(installName INSTALL_NAME_DIR "@rpath") + set(installRpath "@loader_path/${relativeLibraryDir}" ${extraLibraryDir}) + elseif(CMAKE_SYSTEM_NAME MATCHES "AIX" AND BUILD_SHARED_LIBS) + set(installRpath "${RCC_LIBRARY_OUTPUT_INTDIR}" "${CMAKE_INSTALL_FULL_LIBDIR}" ${extraLibraryDir}) + elseif(UNIX) + set(buildRpath "\$ORIGIN/../lib" ${extraLibraryDir}) + set(installRpath "\$ORIGIN/${relativeLibraryDir}") + if(CMAKE_SYSTEM_NAME MATCHES "FreeBSD|DragonFly") + target_link_options(${target} PRIVATE "LINKER:-z,origin") + endif() + if(RCC_LINKER_IS_GNULD AND RCC_LIBRARY_OUTPUT_INTDIR) + target_link_options(${target} PRIVATE "LINKER:-rpath-link,${RCC_LIBRARY_OUTPUT_INTDIR}") + endif() + else() + return() + endif() + if("${CMAKE_BUILD_RPATH}" STREQUAL "") + if(CMAKE_SYSTEM_NAME MATCHES "Darwin|AIX") + set_target_properties(${target} PROPERTIES BUILD_WITH_INSTALL_RPATH ON) + else() + set_property(TARGET ${target} APPEND PROPERTY BUILD_RPATH "${buildRpath}") + endif() + endif() + set_target_properties(${target} PROPERTIES INSTALL_RPATH "${installRpath}" ${installName}) +endfunction() + +function(rcc_configure_target target) + rcc_apply_options(${target}) + rcc_set_output_directory(${target}) + rcc_update_pch(${target}) + get_target_property(targetType ${target} TYPE) + if(targetType MATCHES "^(EXECUTABLE|SHARED_LIBRARY|MODULE_LIBRARY)$" AND NOT arg_NO_INSTALL_RPATH) + rcc_setup_rpath(${target}) + endif() + if(targetType STREQUAL "SHARED_LIBRARY") + if(MSVC) + set_target_properties(${target} PROPERTIES PREFIX "") + endif() + if(UNIX) + set_target_properties(${target} PROPERTIES + VERSION "${RCC_VERSION_MAJOR}.${RCC_VERSION_MINOR}" + SOVERSION "${RCC_VERSION_MAJOR}.${RCC_VERSION_MINOR}") + endif() + endif() +endfunction() + +function(add_rcc_library name) + cmake_parse_arguments(PARSE_ARGV 1 arg "DISABLE_PCH_REUSE;NO_INSTALL_RPATH" "" "LINK_LIBS;PRECOMPILE_HEADERS") + add_library(${name} ${arg_UNPARSED_ARGUMENTS}) + target_link_libraries(${name} PUBLIC RCCCore ${arg_LINK_LIBS}) + target_compile_features(${name} PUBLIC cxx_std_23) + target_compile_definitions(${name} PRIVATE RCC_EXPORTS) + target_include_directories(${name} PUBLIC + "$<BUILD_INTERFACE:${RCC_SOURCE_DIR}/include>" + "$<BUILD_INTERFACE:${RCC_BINARY_DIR}/include>" + "$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>") + rcc_configure_target(${name}) + install(TARGETS ${name} EXPORT RCCTargets + ARCHIVE DESTINATION "${CMAKE_INSTALL_LIBDIR}" + LIBRARY DESTINATION "${CMAKE_INSTALL_LIBDIR}" + RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}") +endfunction() + +function(add_rcc_executable name) + cmake_parse_arguments(PARSE_ARGV 1 arg "DISABLE_PCH_REUSE;NO_INSTALL_RPATH" "" "LINK_LIBS;PRECOMPILE_HEADERS") + add_executable(${name} ${arg_UNPARSED_ARGUMENTS}) + target_link_libraries(${name} PRIVATE RCCCore ${arg_LINK_LIBS}) + target_compile_features(${name} PRIVATE cxx_std_23) + target_include_directories(${name} PRIVATE + "${RCC_SOURCE_DIR}/include" + "${RCC_BINARY_DIR}/include") + rcc_configure_target(${name}) +endfunction() + +function(add_rcc_tool name) + add_rcc_executable(${name} ${ARGN}) + install(TARGETS ${name} EXPORT RCCTargets + RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}") +endfunction() diff --git a/rcc/cmake/modules/CMakeLists.txt b/rcc/cmake/modules/CMakeLists.txt new file mode 100644 index 0000000..39b6d97 --- /dev/null +++ b/rcc/cmake/modules/CMakeLists.txt @@ -0,0 +1,47 @@ +# SPDX-License-Identifier: GPL-3.0-or-later + +include(CMakePackageConfigHelpers) + +get_directory_property(RCC_DEFINITIONS DIRECTORY "${RCC_SOURCE_DIR}" COMPILE_DEFINITIONS) +set(RCC_PACKAGE_MSVC_RUNTIME_LIBRARY "${CMAKE_MSVC_RUNTIME_LIBRARY}") +if(NOT DEFINED CMAKE_MSVC_RUNTIME_LIBRARY) + set(RCC_PACKAGE_MSVC_RUNTIME_LIBRARY "MultiThreaded$<$<CONFIG:Debug>:Debug>DLL") +endif() +set(rccPackageDir "${RCC_BINARY_DIR}/lib/cmake/RCC") +set(rccInstallPackageDir "${CMAKE_INSTALL_LIBDIR}/cmake/RCC") +set(RCC_PACKAGE_MODULE_DIR "${RCC_CMAKE_DIR}") +set(RCC_PACKAGE_INCLUDE_DIRS "${RCC_INCLUDE_DIRS}") +set(RCC_PACKAGE_LIBRARY_DIR "${RCC_LIBRARY_DIR}") +set(RCC_PACKAGE_BUILD_LIBRARY_DIR "${RCC_LIBRARY_DIR}") +set(RCC_PACKAGE_INSTALL_PREFIX "") +configure_package_config_file(RCCConfig.cmake.in "${rccPackageDir}/RCCConfig.cmake" + INSTALL_DESTINATION "${rccPackageDir}" + NO_SET_AND_CHECK_MACRO NO_CHECK_REQUIRED_COMPONENTS_MACRO) +export(EXPORT RCCTargets FILE "${rccPackageDir}/RCCTargets.cmake") + +set(RCC_PACKAGE_MODULE_DIR "\${CMAKE_CURRENT_LIST_DIR}") +if(IS_ABSOLUTE "${CMAKE_INSTALL_INCLUDEDIR}") + set(RCC_PACKAGE_INCLUDE_DIRS "${CMAKE_INSTALL_INCLUDEDIR}") +else() + set(RCC_PACKAGE_INCLUDE_DIRS "\${PACKAGE_PREFIX_DIR}/${CMAKE_INSTALL_INCLUDEDIR}") +endif() +if(IS_ABSOLUTE "${CMAKE_INSTALL_LIBDIR}") + set(RCC_PACKAGE_LIBRARY_DIR "${CMAKE_INSTALL_LIBDIR}") +else() + set(RCC_PACKAGE_LIBRARY_DIR "\${PACKAGE_PREFIX_DIR}/${CMAKE_INSTALL_LIBDIR}") +endif() +set(RCC_PACKAGE_BUILD_LIBRARY_DIR "") +set(RCC_PACKAGE_INSTALL_PREFIX "\${PACKAGE_PREFIX_DIR}") +configure_package_config_file(RCCConfig.cmake.in RCCConfig.install.cmake + INSTALL_DESTINATION "${rccInstallPackageDir}" + NO_SET_AND_CHECK_MACRO NO_CHECK_REQUIRED_COMPONENTS_MACRO) +configure_file(RCCConfigVersion.cmake.in + "${rccPackageDir}/RCCConfigVersion.cmake" @ONLY) +install(FILES "${CMAKE_CURRENT_BINARY_DIR}/RCCConfig.install.cmake" + DESTINATION "${rccInstallPackageDir}" RENAME RCCConfig.cmake) +install(FILES + "${rccPackageDir}/RCCConfigVersion.cmake" + AddRCC.cmake HandleRCCOptions.cmake + DESTINATION "${rccInstallPackageDir}") +install(EXPORT RCCTargets FILE RCCTargets.cmake + DESTINATION "${rccInstallPackageDir}") diff --git a/rcc/cmake/modules/CheckAtomic.cmake b/rcc/cmake/modules/CheckAtomic.cmake new file mode 100644 index 0000000..b9ec06e --- /dev/null +++ b/rcc/cmake/modules/CheckAtomic.cmake @@ -0,0 +1,125 @@ +# atomic builtins are required for threading support. + +INCLUDE(CheckCXXSourceCompiles) +INCLUDE(CheckLibraryExists) + +# Sometimes linking against libatomic is required for atomic ops, if +# the platform doesn't support lock-free atomics. + +function(check_working_cxx_atomics varname) + set(OLD_CMAKE_REQUIRED_FLAGS ${CMAKE_REQUIRED_FLAGS}) + set(CMAKE_REQUIRED_FLAGS "${CMAKE_REQUIRED_FLAGS} -std=c++11") + CHECK_CXX_SOURCE_COMPILES(" +#include <atomic> +std::atomic<int> x; +std::atomic<short> y; +std::atomic<char> z; +int main() { + ++z; + ++y; + return ++x; +} +" ${varname}) + set(CMAKE_REQUIRED_FLAGS ${OLD_CMAKE_REQUIRED_FLAGS}) +endfunction(check_working_cxx_atomics) + +function(check_working_cxx_atomics64 varname) + set(OLD_CMAKE_REQUIRED_FLAGS ${CMAKE_REQUIRED_FLAGS}) + set(CMAKE_REQUIRED_FLAGS "-std=c++11 ${CMAKE_REQUIRED_FLAGS}") + CHECK_CXX_SOURCE_COMPILES(" +#include <atomic> +#include <cstdint> +std::atomic<uint64_t> x (0); +int main() { + uint64_t i = x.load(std::memory_order_relaxed); + (void)i; + return 0; +} +" ${varname}) + set(CMAKE_REQUIRED_FLAGS ${OLD_CMAKE_REQUIRED_FLAGS}) +endfunction(check_working_cxx_atomics64) + + +# Check for (non-64-bit) atomic operations. +if(MSVC) + set(HAVE_CXX_ATOMICS_WITHOUT_LIB True) +elseif(RCC_COMPILER_IS_GCC_COMPATIBLE OR CMAKE_CXX_COMPILER_ID MATCHES "XL") + # First check if atomics work without the library. + check_working_cxx_atomics(HAVE_CXX_ATOMICS_WITHOUT_LIB) + # If not, check if the library exists, and atomics work with it. + if(NOT HAVE_CXX_ATOMICS_WITHOUT_LIB) + check_library_exists(atomic __atomic_fetch_add_4 "" HAVE_LIBATOMIC) + if(HAVE_LIBATOMIC) + list(APPEND CMAKE_REQUIRED_LIBRARIES "atomic") + check_working_cxx_atomics(HAVE_CXX_ATOMICS_WITH_LIB) + if (NOT HAVE_CXX_ATOMICS_WITH_LIB) + message(FATAL_ERROR "Host compiler must support std::atomic!") + endif() + else() + message(FATAL_ERROR "Host compiler appears to require libatomic, but cannot find it.") + endif() + endif() +endif() + +# Check for 64 bit atomic operations. +if(MSVC) + set(HAVE_CXX_ATOMICS64_WITHOUT_LIB True) +elseif(RCC_COMPILER_IS_GCC_COMPATIBLE OR CMAKE_CXX_COMPILER_ID MATCHES "XL") + # First check if atomics work without the library. + check_working_cxx_atomics64(HAVE_CXX_ATOMICS64_WITHOUT_LIB) + # If not, check if the library exists, and atomics work with it. + if(NOT HAVE_CXX_ATOMICS64_WITHOUT_LIB) + check_library_exists(atomic __atomic_load_8 "" HAVE_CXX_LIBATOMICS64) + if(HAVE_CXX_LIBATOMICS64) + list(APPEND CMAKE_REQUIRED_LIBRARIES "atomic") + check_working_cxx_atomics64(HAVE_CXX_ATOMICS64_WITH_LIB) + if (NOT HAVE_CXX_ATOMICS64_WITH_LIB) + message(FATAL_ERROR "Host compiler must support 64-bit std::atomic!") + endif() + else() + message(FATAL_ERROR "Host compiler appears to require libatomic for 64-bit operations, but cannot find it.") + endif() + endif() +endif() + +# Set variable RCC_ATOMIC_LIB specifying flags for linking against libatomic. +if(HAVE_CXX_ATOMICS_WITH_LIB OR HAVE_CXX_ATOMICS64_WITH_LIB) + # Use options --push-state, --as-needed and --pop-state if linker is known to support them. + # Use single option -Wl of compiler driver to avoid incorrect re-ordering of options by CMake. + if(RCC_LINKER_IS_GNULD OR RCC_LINKER_IS_GOLD OR RCC_LINKER_IS_LLD OR RCC_LINKER_IS_MOLD) + set(RCC_ATOMIC_LIB "-Wl,--push-state,--as-needed,-latomic,--pop-state") + else() + set(RCC_ATOMIC_LIB "-latomic") + endif() +else() + set(RCC_ATOMIC_LIB) +endif() + +## TODO: This define is only used for the legacy atomic operations in +## rcc's Atomic.h, which should be replaced. Other code simply +## assumes C++11 <atomic> works. +CHECK_CXX_SOURCE_COMPILES(" +#ifdef _MSC_VER +#include <windows.h> +#endif +int main() { +#ifdef _MSC_VER + volatile LONG val = 1; + MemoryBarrier(); + InterlockedCompareExchange(&val, 0, 1); + InterlockedIncrement(&val); + InterlockedDecrement(&val); +#else + volatile unsigned long val = 1; + __sync_synchronize(); + __sync_val_compare_and_swap(&val, 1, 0); + __sync_add_and_fetch(&val, 1); + __sync_sub_and_fetch(&val, 1); +#endif + return 0; + } +" RCC_HAS_ATOMICS) + +if( NOT RCC_HAS_ATOMICS ) + message(STATUS "Warning: RCC will be built thread-unsafe because atomic builtins are missing") +endif() diff --git a/rcc/cmake/modules/CheckCompilerVersion.cmake b/rcc/cmake/modules/CheckCompilerVersion.cmake new file mode 100644 index 0000000..7a9f964 --- /dev/null +++ b/rcc/cmake/modules/CheckCompilerVersion.cmake @@ -0,0 +1,110 @@ +# Check if the host compiler is new enough. +# These versions are updated based on the following policy: +# rcc.org/docs/DeveloperPolicy.html#toolchain + +include(CheckCXXSourceCompiles) + +set(GCC_MIN 7.4) +set(GCC_SOFT_ERROR 7.4) +set(CLANG_MIN 5.0) +set(CLANG_SOFT_ERROR 5.0) +set(APPLECLANG_MIN 10.0) +set(APPLECLANG_SOFT_ERROR 10.0) + +# https://en.wikipedia.org/wiki/Microsoft_Visual_C#Internal_version_numbering +# _MSC_VER == 1928 MSVC++ 19.27 Visual Studio 2019 Version 16.8 + 16.9 +set(MSVC_MIN 19.28) +set(MSVC_SOFT_ERROR 19.28) + +set(LIBSTDCXX_MIN 7) +set(LIBSTDCXX_SOFT_ERROR 7) + + +if(DEFINED RCC_COMPILER_CHECKED) + return() +endif() +set(RCC_COMPILER_CHECKED ON) + +if(RCC_FORCE_USE_OLD_TOOLCHAIN) + return() +endif() + +function(check_compiler_version NAME NICE_NAME MINIMUM_VERSION SOFT_ERROR_VERSION) + if(NOT CMAKE_CXX_COMPILER_ID STREQUAL NAME) + return() + endif() + if(CMAKE_CXX_COMPILER_VERSION VERSION_LESS MINIMUM_VERSION) + message(FATAL_ERROR "Host ${NICE_NAME} version must be at least ${MINIMUM_VERSION}, your version is ${CMAKE_CXX_COMPILER_VERSION}.") + elseif(CMAKE_CXX_COMPILER_VERSION VERSION_LESS SOFT_ERROR_VERSION) + if(RCC_TEMPORARILY_ALLOW_OLD_TOOLCHAIN) + message(WARNING "Host ${NICE_NAME} version should be at least ${SOFT_ERROR_VERSION} because RCC will soon use new C++ features which your toolchain version doesn't support. Your version is ${CMAKE_CXX_COMPILER_VERSION}. Ignoring because you've set RCC_TEMPORARILY_ALLOW_OLD_TOOLCHAIN, but very soon your toolchain won't be supported.") + else() + message(FATAL_ERROR "Host ${NICE_NAME} version should be at least ${SOFT_ERROR_VERSION} because RCC will soon use new C++ features which your toolchain version doesn't support. Your version is ${CMAKE_CXX_COMPILER_VERSION}. You can temporarily opt out using RCC_TEMPORARILY_ALLOW_OLD_TOOLCHAIN, but very soon your toolchain won't be supported.") + endif() + endif() +endfunction(check_compiler_version) + +check_compiler_version("GNU" "GCC" ${GCC_MIN} ${GCC_SOFT_ERROR}) +check_compiler_version("Clang" "Clang" ${CLANG_MIN} ${CLANG_SOFT_ERROR}) +check_compiler_version("AppleClang" "Apple Clang" ${APPLECLANG_MIN} ${APPLECLANG_SOFT_ERROR}) +check_compiler_version("MSVC" "Visual Studio" ${MSVC_MIN} ${MSVC_SOFT_ERROR}) + +# See https://developercommunity.visualstudio.com/content/problem/845933/miscompile-boolean-condition-deduced-to-be-always.html +# and thread "[rcc-dev] Longstanding failing tests - clang-tidy, MachO, Polly" +# on rcc-dev Jan 21-23 2020. +if ((${CMAKE_CXX_COMPILER_ID} STREQUAL MSVC) AND + (19.24 VERSION_LESS_EQUAL ${CMAKE_CXX_COMPILER_VERSION}) AND + (${CMAKE_CXX_COMPILER_VERSION} VERSION_LESS 19.25)) + if(RCC_TEMPORARILY_ALLOW_OLD_TOOLCHAIN) + message(WARNING "Host Visual Studio version 16.4 is known to miscompile part of RCC") + else() + message(FATAL_ERROR "Host Visual Studio version 16.4 is known to miscompile part of RCC, please use clang-cl or upgrade to 16.5 or above (use -DRCC_TEMPORARILY_ALLOW_OLD_TOOLCHAIN=ON to ignore)") + endif() +endif() + + +if(CMAKE_CXX_COMPILER_ID STREQUAL "Clang") + if (CMAKE_CXX_SIMULATE_ID MATCHES "MSVC") + if (CMAKE_CXX_SIMULATE_VERSION VERSION_LESS MSVC_MIN) + message(FATAL_ERROR "Host Clang must have at least -fms-compatibility-version=${MSVC_MIN}, your version is ${CMAKE_CXX_SIMULATE_VERSION}.") + endif() + set(CLANG_CL 1) + elseif(NOT RCC_ENABLE_LIBCXX) + # Test that we aren't using too old of a version of libstdc++. + set(OLD_CMAKE_REQUIRED_FLAGS ${CMAKE_REQUIRED_FLAGS}) + set(OLD_CMAKE_REQUIRED_LIBRARIES ${CMAKE_REQUIRED_LIBRARIES}) + set(CMAKE_REQUIRED_FLAGS "${CMAKE_REQUIRED_FLAGS} -std=c++0x") + check_cxx_source_compiles(" +#include <iosfwd> +#if defined(__GLIBCXX__) +#if !defined(_GLIBCXX_RELEASE) || _GLIBCXX_RELEASE < ${LIBSTDCXX_MIN} +#error Unsupported libstdc++ version +#endif +#endif +int main() { return 0; } +" + RCC_LIBSTDCXX_MIN) + if(NOT RCC_LIBSTDCXX_MIN) + message(FATAL_ERROR "libstdc++ version must be at least ${GCC_MIN}.") + endif() + check_cxx_source_compiles(" +#include <iosfwd> +#if defined(__GLIBCXX__) +#if !defined(_GLIBCXX_RELEASE) || _GLIBCXX_RELEASE < ${LIBSTDCXX_SOFT_ERROR} +#error Unsupported libstdc++ version +#endif +#endif +int main() { return 0; } +" + RCC_LIBSTDCXX_SOFT_ERROR) + if(NOT RCC_LIBSTDCXX_SOFT_ERROR) + if(RCC_TEMPORARILY_ALLOW_OLD_TOOLCHAIN) + message(WARNING "libstdc++ version should be at least ${LIBSTDCXX_SOFT_ERROR} because RCC will soon use new C++ features which your toolchain version doesn't support. Ignoring because you've set RCC_TEMPORARILY_ALLOW_OLD_TOOLCHAIN, but very soon your toolchain won't be supported.") + else() + message(FATAL_ERROR "libstdc++ version should be at least ${LIBSTDCXX_SOFT_ERROR} because RCC will soon use new C++ features which your toolchain version doesn't support. You can temporarily opt out using RCC_TEMPORARILY_ALLOW_OLD_TOOLCHAIN, but very soon your toolchain won't be supported.") + endif() + endif() + set(CMAKE_REQUIRED_FLAGS ${OLD_CMAKE_REQUIRED_FLAGS}) + set(CMAKE_REQUIRED_LIBRARIES ${OLD_CMAKE_REQUIRED_LIBRARIES}) + endif() +endif() diff --git a/rcc/cmake/modules/CheckProblematicConfigurations.cmake b/rcc/cmake/modules/CheckProblematicConfigurations.cmake new file mode 100644 index 0000000..3b96db9 --- /dev/null +++ b/rcc/cmake/modules/CheckProblematicConfigurations.cmake @@ -0,0 +1,19 @@ + +option(RCC_ALLOW_PROBLEMATIC_CONFIGURATIONS OFF "Set this option to ON to allow problematic toolchain configurations. Use on your own risk.") + +macro(log_problematic MESSAGE) + if(RCC_ALLOW_PROBLEMATIC_CONFIGURATIONS) + message(WARNING "${MESSAGE}") + else() + message(FATAL_ERROR "${MESSAGE}\nYou can force usage of this configuration by passing -DRCC_ALLOW_PROBLEMATIC_CONFIGURATIONS=ON") + endif() +endmacro() + +# MSVC and /arch:AVX is untested and have created problems before. See: +# https://github.com/rcc/rcc-project/issues/54645 +if(CMAKE_CXX_COMPILER_ID STREQUAL "MSVC") + string(TOLOWER "${CMAKE_CXX_FLAGS} ${CMAKE_C_FLAGS}" _FLAGS) + if(_FLAGS MATCHES "/arch:avx[0-9]*") + log_problematic("Compiling RCC with MSVC and the /arch:AVX flag is known to cause issues with parts of RCC.\nSee https://github.com/rcc/rcc-project/issues/54645 for details.\nUse clang-cl if you want to enable AVX instructions.") + endif() +endif() diff --git a/rcc/cmake/modules/DetermineGCCCompatible.cmake b/rcc/cmake/modules/DetermineGCCCompatible.cmake new file mode 100644 index 0000000..b645e45 --- /dev/null +++ b/rcc/cmake/modules/DetermineGCCCompatible.cmake @@ -0,0 +1,13 @@ +# Determine if the compiler has GCC-compatible command-line syntax. + +if(NOT DEFINED RCC_COMPILER_IS_GCC_COMPATIBLE) + if(CMAKE_COMPILER_IS_GNUCXX) + set(RCC_COMPILER_IS_GCC_COMPATIBLE ON) + elseif( MSVC ) + set(RCC_COMPILER_IS_GCC_COMPATIBLE OFF) + elseif( "${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang" ) + set(RCC_COMPILER_IS_GCC_COMPATIBLE ON) + elseif( "${CMAKE_CXX_COMPILER_ID}" MATCHES "Intel" ) + set(RCC_COMPILER_IS_GCC_COMPATIBLE ON) + endif() +endif() diff --git a/rcc/cmake/modules/FindFFI.cmake b/rcc/cmake/modules/FindFFI.cmake new file mode 100644 index 0000000..b1f6452 --- /dev/null +++ b/rcc/cmake/modules/FindFFI.cmake @@ -0,0 +1,106 @@ +# Attempts to discover ffi library with a linkable ffi_call function. +# +# Example usage: +# +# find_package(FFI) +# +# FFI_REQUIRE_INCLUDE may be set to consider ffi found if the includes +# are present in addition to the library. This is useful to keep off +# for the imported package on platforms that install the library but +# not the headers. +# +# FFI_LIBRARY_DIR may be set to define search paths for the ffi library. +# +# If successful, the following variables will be defined: +# FFI_FOUND +# FFI_INCLUDE_DIRS +# FFI_LIBRARIES +# FFI_STATIC_LIBRARIES +# HAVE_FFI_CALL +# +# HAVE_FFI_H or HAVE_FFI_FFI_H is defined depending on the ffi.h include path. +# +# Additionally, the following import target will be defined: +# FFI::ffi + +find_package(PkgConfig QUIET) +pkg_check_modules(PC_LIBFFI QUIET libffi) + +find_path(FFI_INCLUDE_DIRS ffi.h PATHS ${FFI_INCLUDE_DIR} ${PC_LIBFFI_INCLUDE_DIRS}) +if( EXISTS "${FFI_INCLUDE_DIRS}/ffi.h" ) + set(FFI_HEADER ffi.h CACHE INTERNAL "") + set(HAVE_FFI_H 1 CACHE INTERNAL "") +else() + find_path(FFI_INCLUDE_DIRS ffi/ffi.h PATHS ${FFI_INCLUDE_DIR}) + if( EXISTS "${FFI_INCLUDE_DIRS}/ffi/ffi.h" ) + set(FFI_HEADER ffi/ffi.h CACHE INTERNAL "") + set(HAVE_FFI_FFI_H 1 CACHE INTERNAL "") + endif() +endif() + +find_library(FFI_LIBRARIES NAMES ffi PATHS ${FFI_LIBRARY_DIR} ${PC_LIBFFI_LIBRARY_DIRS}) +find_library(FFI_STATIC_LIBRARIES NAMES libffi.a PATHS ${FFI_LIBRARY_DIR} ${PC_LIBFFI_LIBRARY_DIRS}) + +if(FFI_LIBRARIES) + include(CMakePushCheckState) + cmake_push_check_state() + list(APPEND CMAKE_REQUIRED_LIBRARIES ${FFI_LIBRARIES}) + set(HAVE_FFI_CALL_SRC [=[ + #ifdef __cplusplus + extern "C" { + #endif + struct ffi_cif; + typedef struct ffi_cif ffi_cif; + void ffi_call(ffi_cif *cif, void (*fn)(void), void *rvalue, void **avalue); + #ifdef __cplusplus + } + #endif + int main(void) { ffi_call(0, 0, 0, 0); } + ]=]) + if(DEFINED CMAKE_C_COMPILER) + include(CheckCSourceCompiles) + check_c_source_compiles("${HAVE_FFI_CALL_SRC}" HAVE_FFI_CALL) + else() + include(CheckCXXSourceCompiles) + check_cxx_source_compiles("${HAVE_FFI_CALL_SRC}" HAVE_FFI_CALL) + endif() + cmake_pop_check_state() +endif() + +unset(required_includes) +if(FFI_REQUIRE_INCLUDE) + set(required_includes FFI_INCLUDE_DIRS) +endif() + +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args(FFI + FOUND_VAR + FFI_FOUND + REQUIRED_VARS + FFI_LIBRARIES + ${required_includes} + HAVE_FFI_CALL) +mark_as_advanced(FFI_LIBRARIES + FFI_STATIC_LIBRARIES + FFI_INCLUDE_DIRS + HAVE_FFI_CALL + FFI_HEADER + HAVE_FFI_H + HAVE_FFI_FFI_H) + +if(FFI_FOUND) + if(NOT TARGET FFI::ffi AND FFI_LIBRARIES) + add_library(FFI::ffi UNKNOWN IMPORTED) + set_target_properties(FFI::ffi PROPERTIES IMPORTED_LOCATION "${FFI_LIBRARIES}") + if(FFI_INCLUDE_DIRS) + set_target_properties(FFI::ffi PROPERTIES INTERFACE_INCLUDE_DIRECTORIES "${FFI_INCLUDE_DIRS}") + endif() + endif() + if(NOT TARGET FFI::ffi_static AND FFI_STATIC_LIBRARIES) + add_library(FFI::ffi_static UNKNOWN IMPORTED) + set_target_properties(FFI::ffi_static PROPERTIES IMPORTED_LOCATION "${FFI_STATIC_LIBRARIES}") + if(FFI_INCLUDE_DIRS) + set_target_properties(FFI::ffi_static PROPERTIES INTERFACE_INCLUDE_DIRECTORIES "${FFI_INCLUDE_DIRS}") + endif() + endif() +endif() diff --git a/rcc/cmake/modules/FindLibEdit.cmake b/rcc/cmake/modules/FindLibEdit.cmake new file mode 100644 index 0000000..b3f49e1 --- /dev/null +++ b/rcc/cmake/modules/FindLibEdit.cmake @@ -0,0 +1,65 @@ +#.rst: +# FindLibEdit +# ----------- +# +# Find libedit library and headers +# +# The module defines the following variables: +# +# :: +# +# LibEdit_FOUND - true if libedit was found +# LibEdit_INCLUDE_DIRS - include search path +# LibEdit_LIBRARIES - libraries to link +# LibEdit_VERSION_STRING - version number + +find_package(PkgConfig QUIET) +pkg_check_modules(PC_LIBEDIT QUIET libedit) + +find_path(LibEdit_INCLUDE_DIRS NAMES histedit.h HINTS ${PC_LIBEDIT_INCLUDE_DIRS}) +find_library(LibEdit_LIBRARIES NAMES edit HINTS ${PC_LIBEDIT_LIBRARY_DIRS}) + +include(CheckIncludeFile) +if(LibEdit_INCLUDE_DIRS AND EXISTS "${LibEdit_INCLUDE_DIRS}/histedit.h") + include(CMakePushCheckState) + cmake_push_check_state() + list(APPEND CMAKE_REQUIRED_INCLUDES ${LibEdit_INCLUDE_DIRS}) + list(APPEND CMAKE_REQUIRED_LIBRARIES ${LibEdit_LIBRARIES}) + check_include_file(histedit.h HAVE_HISTEDIT_H) + cmake_pop_check_state() + if (HAVE_HISTEDIT_H) + file(STRINGS "${LibEdit_INCLUDE_DIRS}/histedit.h" + libedit_major_version_str + REGEX "^#define[ \t]+LIBEDIT_MAJOR[ \t]+[0-9]+") + string(REGEX REPLACE "^#define[ \t]+LIBEDIT_MAJOR[ \t]+([0-9]+)" "\\1" + libedit_major_version "${libedit_major_version_str}") + + file(STRINGS "${LibEdit_INCLUDE_DIRS}/histedit.h" + libedit_minor_version_str + REGEX "^#define[ \t]+LIBEDIT_MINOR[ \t]+[0-9]+") + string(REGEX REPLACE "^#define[ \t]+LIBEDIT_MINOR[ \t]+([0-9]+)" "\\1" + libedit_minor_version "${libedit_minor_version_str}") + + set(LibEdit_VERSION_STRING "${libedit_major_version}.${libedit_minor_version}") + else() + set(LibEdit_INCLUDE_DIRS "") + set(LibEdit_LIBRARIES "") + endif() +endif() + +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args(LibEdit + FOUND_VAR + LibEdit_FOUND + REQUIRED_VARS + LibEdit_INCLUDE_DIRS + LibEdit_LIBRARIES + VERSION_VAR + LibEdit_VERSION_STRING) +mark_as_advanced(LibEdit_INCLUDE_DIRS LibEdit_LIBRARIES) + +if (LibEdit_FOUND AND NOT TARGET LibEdit::LibEdit) + add_library(LibEdit::LibEdit INTERFACE IMPORTED) + target_link_libraries(LibEdit::LibEdit INTERFACE ${LibEdit_LIBRARIES}) + target_include_directories(LibEdit::LibEdit INTERFACE ${LibEdit_INCLUDE_DIRS}) +endif() diff --git a/rcc/cmake/modules/FindLibpfm.cmake b/rcc/cmake/modules/FindLibpfm.cmake new file mode 100644 index 0000000..9e5900b --- /dev/null +++ b/rcc/cmake/modules/FindLibpfm.cmake @@ -0,0 +1,37 @@ +# CMake module for finding libpfm4. +# +# If successful, the following variables will be defined: +# HAVE_LIBPFM +# +# Libpfm can be disabled by setting RCC_ENABLE_LIBPFM to 0. + +include(CheckIncludeFile) +include(CheckLibraryExists) +include(CheckCXXSourceCompiles) + +if (RCC_ENABLE_LIBPFM) + check_library_exists(pfm pfm_initialize "" HAVE_LIBPFM_INITIALIZE) + if(HAVE_LIBPFM_INITIALIZE) + check_include_file(perfmon/perf_event.h HAVE_PERFMON_PERF_EVENT_H) + check_include_file(perfmon/pfmlib.h HAVE_PERFMON_PFMLIB_H) + check_include_file(perfmon/pfmlib_perf_event.h HAVE_PERFMON_PFMLIB_PERF_EVENT_H) + if(HAVE_PERFMON_PERF_EVENT_H AND HAVE_PERFMON_PFMLIB_H AND HAVE_PERFMON_PFMLIB_PERF_EVENT_H) + set(HAVE_LIBPFM 1) + # Check to see if perf_branch_entry has the field 'cycles'. + # We couldn't use CheckStructHasMember here because 'cycles' is a bit field which is not + # supported by CheckStructHasMember. + CHECK_CXX_SOURCE_COMPILES(" + #include <perfmon/perf_event.h> + int main() { + perf_branch_entry entry; + entry.cycles = 2; + return 0; + }" COMPILE_WITH_CYCLES) + if(COMPILE_WITH_CYCLES) + set(LIBPFM_HAS_FIELD_CYCLES 1) + endif() + endif() + endif() +endif() + + diff --git a/rcc/cmake/modules/FindOCaml.cmake b/rcc/cmake/modules/FindOCaml.cmake new file mode 100644 index 0000000..8eba212 --- /dev/null +++ b/rcc/cmake/modules/FindOCaml.cmake @@ -0,0 +1,103 @@ +# CMake find_package() module for the OCaml language. +# Assumes ocamlfind will be used for compilation. +# http://ocaml.org/ +# +# Example usage: +# +# find_package(OCaml) +# +# If successful, the following variables will be defined: +# OCAMLFIND +# OCAML_VERSION +# OCAML_STDLIB_PATH +# HAVE_OCAMLOPT +# +# Also provides find_ocamlfind_package() macro. +# +# Example usage: +# +# find_ocamlfind_package(ctypes) +# +# In any case, the following variables are defined: +# +# HAVE_OCAML_${pkg} +# +# If successful, the following variables will be defined: +# +# OCAML_${pkg}_VERSION + +include( FindPackageHandleStandardArgs ) + +find_program(OCAMLFIND + NAMES ocamlfind) + +if( OCAMLFIND ) + execute_process( + COMMAND ${OCAMLFIND} ocamlc -version + OUTPUT_VARIABLE OCAML_VERSION + OUTPUT_STRIP_TRAILING_WHITESPACE) + + execute_process( + COMMAND ${OCAMLFIND} ocamlc -where + OUTPUT_VARIABLE OCAML_STDLIB_PATH + OUTPUT_STRIP_TRAILING_WHITESPACE) + + execute_process( + COMMAND ${OCAMLFIND} ocamlc -version + OUTPUT_QUIET + RESULT_VARIABLE find_ocaml_result) + if( find_ocaml_result EQUAL 0 ) + set(HAVE_OCAMLOPT TRUE) + else() + set(HAVE_OCAMLOPT FALSE) + endif() +endif() + +find_package_handle_standard_args( OCaml DEFAULT_MSG + OCAMLFIND + OCAML_VERSION + OCAML_STDLIB_PATH) + +mark_as_advanced( + OCAMLFIND) + +function(find_ocamlfind_package pkg) + CMAKE_PARSE_ARGUMENTS(ARG "OPTIONAL" "VERSION" "" ${ARGN}) + + execute_process( + COMMAND "${OCAMLFIND}" "query" "${pkg}" "-format" "%v" + RESULT_VARIABLE result + OUTPUT_VARIABLE version + ERROR_VARIABLE error + OUTPUT_STRIP_TRAILING_WHITESPACE + ERROR_STRIP_TRAILING_WHITESPACE) + + if( NOT result EQUAL 0 AND NOT ARG_OPTIONAL ) + message(FATAL_ERROR ${error}) + endif() + + if( result EQUAL 0 ) + set(found TRUE) + else() + set(found FALSE) + endif() + + if( found AND ARG_VERSION ) + if( version VERSION_LESS ARG_VERSION AND ARG_OPTIONAL ) + # If it's optional and the constraint is not satisfied, pretend + # it wasn't found. + set(found FALSE) + elseif( version VERSION_LESS ARG_VERSION ) + message(FATAL_ERROR + "ocamlfind package ${pkg} should have version ${ARG_VERSION} or newer") + endif() + endif() + + string(TOUPPER ${pkg} pkg) + + set(HAVE_OCAML_${pkg} ${found} + PARENT_SCOPE) + + set(OCAML_${pkg}_VERSION ${version} + PARENT_SCOPE) +endfunction() diff --git a/rcc/cmake/modules/GetErrcMessages.cmake b/rcc/cmake/modules/GetErrcMessages.cmake new file mode 100644 index 0000000..ad94f6a --- /dev/null +++ b/rcc/cmake/modules/GetErrcMessages.cmake @@ -0,0 +1,45 @@ +# This function returns the messages of various POSIX error codes as they are returned by std::error_code. +# The purpose of this function is to supply those error messages to rcc-lit using the errc_messages config. +# Currently supplied and needed error codes: ENOENT, EISDIR, EINVAL and EACCES. +# Messages are semi colon separated. +# Keep amount, order and tested error codes in sync with rcc/utils/lit/lit/rcc/config.py. +function(get_errc_messages outvar) + if(CMAKE_CROSSCOMPILING AND NOT CMAKE_CROSSCOMPILING_EMULATOR AND NOT DEFINED errc_exit_code) + set(${outvar} "" PARENT_SCOPE) + message(STATUS "Can't get errc messages in cross-compilation mode") + return() + endif() + + set(errc_test_code ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeTmp/getErrc.cpp) + + file(WRITE ${errc_test_code} " + #include <cerrno> + #include <iostream> + #include <string> + #include <system_error> + + std::string getMessageFor(int err) { + return std::make_error_code(static_cast<std::errc>(err)).message(); + } + + int main() { + std::cout << getMessageFor(ENOENT) << ';' << getMessageFor(EISDIR); + std::cout << ';' << getMessageFor(EINVAL) << ';' << getMessageFor(EACCES); + return 0; + } + ") + + try_run(errc_exit_code + errc_compiled + ${CMAKE_BINARY_DIR} + ${errc_test_code} + RUN_OUTPUT_VARIABLE errc_result + COMPILE_OUTPUT_VARIABLE errc_compile_errors) + if (errc_compiled AND "${errc_exit_code}" STREQUAL "0") + set(${outvar} ${errc_result} PARENT_SCOPE) + else() + set(${outvar} "" PARENT_SCOPE) + message(NOTICE "${errc_compile_errors}") + message(STATUS "Failed to get errc messages") + endif () +endfunction() diff --git a/rcc/cmake/modules/GetHostTriple.cmake b/rcc/cmake/modules/GetHostTriple.cmake new file mode 100644 index 0000000..046fc36 --- /dev/null +++ b/rcc/cmake/modules/GetHostTriple.cmake @@ -0,0 +1,98 @@ +# Returns the host triple. +# Invokes config.guess, except for platforms it cannot identify. + +function( get_host_triple var ) + if( MSVC OR CMAKE_CXX_SIMULATE_ID STREQUAL "MSVC") + if( CMAKE_C_COMPILER_ARCHITECTURE_ID MATCHES "ARM64.*" ) + set( value "aarch64-pc-windows-msvc" ) + elseif( CMAKE_C_COMPILER_ARCHITECTURE_ID MATCHES "ARM.*" ) + set( value "armv7-pc-windows-msvc" ) + elseif( CMAKE_C_COMPILER_ARCHITECTURE_ID STREQUAL "x64" ) + set( value "x86_64-pc-windows-msvc" ) + elseif( CMAKE_C_COMPILER_ARCHITECTURE_ID STREQUAL "X86" ) + set( value "i686-pc-windows-msvc" ) + elseif( CMAKE_SIZEOF_VOID_P EQUAL 8 ) + set( value "x86_64-pc-windows-msvc" ) + else() + set( value "i686-pc-windows-msvc" ) + endif() + elseif( MINGW AND NOT MSYS ) + # CMake doesn't provide COMPILER_ARCHITECTURE_ID for Clang/GCC, + # but it does for MSVC. + if( CMAKE_HOST_SYSTEM_PROCESSOR MATCHES "ARM.*" ) + if( CMAKE_SIZEOF_VOID_P EQUAL 8 ) + set( value "aarch64-w64-windows-gnu" ) + else() + set( value "armv7-w64-windows-gnu" ) + endif() + else() + if( CMAKE_SIZEOF_VOID_P EQUAL 8 ) + set( value "x86_64-w64-windows-gnu" ) + else() + set( value "i686-w64-windows-gnu" ) + endif() + endif() + elseif( CMAKE_SYSTEM_NAME MATCHES "OS390" ) + set( value "s390x-ibm-zos" ) + elseif( CMAKE_SYSTEM_NAME STREQUAL "AIX" ) + # We defer to dynamic detection of the host AIX version. + if( CMAKE_SIZEOF_VOID_P EQUAL 8 ) + set( value "powerpc64-ibm-aix" ) + else() + set( value "powerpc-ibm-aix" ) + endif() + elseif( CMAKE_HOST_SYSTEM_NAME MATCHES "HarmonyOS|OpenHarmony|OHOS" ) + # config.guess cannot identify HarmonyOS: uname -s reports "HarmonyOS", + # which config.guess does not recognize, and its probing is rejected by + # the system sandbox. Build the triple from CMake's host detection using + # the form clang targets on OHOS (e.g. aarch64-unknown-linux-ohos). CMake + # itself may report the host processor as "unknown" because uname -p does, + # so fall back to asking uname -m directly. + set( TT_MACHINE "${CMAKE_HOST_SYSTEM_PROCESSOR}" ) + if( NOT TT_MACHINE OR TT_MACHINE STREQUAL "unknown" ) + find_program( TT_UNAME uname PATHS /bin /usr/bin /usr/local/bin + NO_CMAKE_FIND_ROOT_PATH ) + set( TT_RV 1 ) + if( TT_UNAME ) + execute_process( COMMAND ${TT_UNAME} -m + RESULT_VARIABLE TT_RV + OUTPUT_VARIABLE TT_MACHINE + OUTPUT_STRIP_TRAILING_WHITESPACE + ERROR_QUIET ) + endif() + if( NOT TT_RV EQUAL 0 ) + set( TT_MACHINE "" ) + endif() + endif() + if( TT_MACHINE MATCHES "aarch64|arm64" ) + set( value "aarch64-unknown-linux-ohos" ) + elseif( TT_MACHINE MATCHES "^arm" ) + set( value "arm-unknown-linux-ohos" ) + elseif( TT_MACHINE MATCHES "^x86_64" ) + set( value "x86_64-unknown-linux-ohos" ) + elseif( TT_MACHINE MATCHES "^riscv64" ) + set( value "riscv64-unknown-linux-ohos" ) + elseif( TT_MACHINE MATCHES "^loongarch64" ) + set( value "loongarch64-unknown-linux-ohos" ) + else() + message( FATAL_ERROR + "Failed to determine host triple for ${CMAKE_HOST_SYSTEM_NAME}: " + "host processor \"${TT_MACHINE}\" is not supported" ) + endif() + else() + if(CMAKE_HOST_SYSTEM_NAME STREQUAL Windows AND NOT MSYS) + message(WARNING "unable to determine host target triple") + else() + set(config_guess ${RCC_MAIN_SRC_DIR}/cmake/config.guess) + execute_process(COMMAND sh ${config_guess} + RESULT_VARIABLE TT_RV + OUTPUT_VARIABLE TT_OUT + OUTPUT_STRIP_TRAILING_WHITESPACE) + if( NOT TT_RV EQUAL 0 ) + message(FATAL_ERROR "Failed to execute ${config_guess}") + endif( NOT TT_RV EQUAL 0 ) + set( value ${TT_OUT} ) + endif() + endif() + set( ${var} ${value} PARENT_SCOPE ) +endfunction( get_host_triple var ) diff --git a/rcc/cmake/modules/HandleRCCOptions.cmake b/rcc/cmake/modules/HandleRCCOptions.cmake new file mode 100644 index 0000000..6b99829 --- /dev/null +++ b/rcc/cmake/modules/HandleRCCOptions.cmake @@ -0,0 +1,1182 @@ +# SPDX-License-Identifier: GPL-3.0-or-later + +include_guard(GLOBAL) + +include(CheckCCompilerFlag) +include(CheckCSourceCompiles) +include(CheckCXXCompilerFlag) +include(CheckCXXSourceCompiles) +include(CMakePushCheckState) +include(CheckLinkerFlag) +include(CheckSymbolExists) + +function(rcc_append_flags value) + foreach(variable IN LISTS ARGN) + set(${variable} "${${variable}} ${value}" PARENT_SCOPE) + endforeach() +endfunction() + +macro(rcc_add_supported_warning flag name) + check_c_compiler_flag("-Werror ${flag}" RCC_C_SUPPORTS_${name}) + if(RCC_C_SUPPORTS_${name}) + rcc_append_flags("${flag}" CMAKE_C_FLAGS) + endif() + check_cxx_compiler_flag("-Werror ${flag}" RCC_CXX_SUPPORTS_${name}) + if(RCC_CXX_SUPPORTS_${name}) + rcc_append_flags("${flag}" CMAKE_CXX_FLAGS) + endif() +endmacro() + +macro(rcc_configure_job_pools) + set(RCC_PARALLEL_COMPILE_JOBS "" CACHE STRING "Maximum concurrent compilation jobs (Ninja only)") + set(RCC_PARALLEL_LINK_JOBS "" CACHE STRING "Maximum concurrent link jobs (Ninja only)") + if(RCC_RAM_PER_COMPILE_JOB OR RCC_RAM_PER_LINK_JOB) + cmake_host_system_information(RESULT rccAvailableMemory QUERY AVAILABLE_PHYSICAL_MEMORY) + cmake_host_system_information(RESULT rccLogicalCores QUERY NUMBER_OF_LOGICAL_CORES) + endif() + foreach(rccJobKind COMPILE LINK) + if(RCC_RAM_PER_${rccJobKind}_JOB) + math(EXPR rccMemoryJobs "${rccAvailableMemory} / ${RCC_RAM_PER_${rccJobKind}_JOB}" OUTPUT_FORMAT DECIMAL) + if(rccMemoryJobs LESS 1) + set(rccMemoryJobs 1) + endif() + if(rccMemoryJobs LESS rccLogicalCores) + set(RCC_PARALLEL_${rccJobKind}_JOBS "${rccMemoryJobs}") + else() + set(RCC_PARALLEL_${rccJobKind}_JOBS "${rccLogicalCores}") + endif() + endif() + endforeach() + if(RCC_PARALLEL_COMPILE_JOBS) + if(CMAKE_GENERATOR MATCHES "Ninja") + set_property(GLOBAL APPEND PROPERTY JOB_POOLS compile_job_pool=${RCC_PARALLEL_COMPILE_JOBS}) + set(CMAKE_JOB_POOL_COMPILE compile_job_pool) + else() + message(WARNING "Job pooling is only available with Ninja generators.") + endif() + endif() + if(CMAKE_GENERATOR MATCHES "Ninja") + if(NOT RCC_PARALLEL_LINK_JOBS AND rccLTOMode STREQUAL "THIN") + message(STATUS "ThinLTO provides its own parallel linking - limiting parallel link jobs to 2.") + set(RCC_PARALLEL_LINK_JOBS 2) + endif() + if(RCC_PARALLEL_LINK_JOBS) + set_property(GLOBAL APPEND PROPERTY JOB_POOLS link_job_pool=${RCC_PARALLEL_LINK_JOBS}) + set(CMAKE_JOB_POOL_LINK link_job_pool) + endif() + elseif(RCC_PARALLEL_LINK_JOBS) + message(WARNING "Job pooling is only available with Ninja generators.") + endif() +endmacro() + +macro(rcc_configure_platform_checks) + if(UNIX AND CMAKE_SYSTEM_NAME MATCHES "AIX") + list(APPEND CMAKE_REQUIRED_DEFINITIONS "-D_XOPEN_SOURCE=700" "-D_LARGE_FILE_API") + endif() + if(UNIX AND CMAKE_SYSTEM_NAME MATCHES "SunOS") + list(APPEND CMAKE_REQUIRED_DEFINITIONS "-D_FILE_OFFSET_BITS=64") + endif() + check_symbol_exists(__GLIBC__ stdio.h RCC_USING_GLIBC) + if(RCC_USING_GLIBC OR CYGWIN) + add_compile_definitions(_GNU_SOURCE) + list(APPEND CMAKE_REQUIRED_DEFINITIONS "-D_GNU_SOURCE") + endif() + if(RCC_USING_GLIBC AND CMAKE_SIZEOF_VOID_P EQUAL 4) + add_compile_definitions(_FILE_OFFSET_BITS=64) + list(APPEND CMAKE_REQUIRED_DEFINITIONS "-D_FILE_OFFSET_BITS=64") + endif() + if(NOT RCC_ENABLE_PIC) + rcc_append_flags("-fno-pie" CMAKE_EXE_LINKER_FLAGS) + endif() +endmacro() + +macro(rcc_configure_platform_link_options) + if(APPLE) + rcc_append_flags("-Wl,-flat_namespace -Wl,-undefined -Wl,dynamic_lookup" CMAKE_MODULE_LINKER_FLAGS) + endif() + if(CMAKE_SYSTEM_NAME MATCHES "AIX") + if(CMAKE_CXX_COMPILER_ID MATCHES "GNU|Clang") + rcc_append_flags("-mcmodel=large" CMAKE_C_FLAGS CMAKE_CXX_FLAGS) + rcc_append_flags("-Wl,-bglink=large" CMAKE_EXE_LINKER_FLAGS CMAKE_MODULE_LINKER_FLAGS CMAKE_SHARED_LINKER_FLAGS) + elseif(CMAKE_CXX_COMPILER_ID MATCHES "XL") + rcc_append_flags("-Wl,-bbigtoc -bcdtors:mbr" + CMAKE_EXE_LINKER_FLAGS CMAKE_MODULE_LINKER_FLAGS CMAKE_SHARED_LINKER_FLAGS) + endif() + if(BUILD_SHARED_LIBS) + message(WARNING "Build and install environment path info may be exposed; binaries will also be unrelocatable.") + endif() + endif() + if(CMAKE_SYSTEM_NAME MATCHES "OS390") + rcc_append_flags("-Wl,-bcompress=yes" CMAKE_EXE_LINKER_FLAGS CMAKE_MODULE_LINKER_FLAGS CMAKE_SHARED_LINKER_FLAGS) + endif() + if(NOT (CMAKE_SYSTEM_NAME MATCHES "Darwin|FreeBSD|OpenBSD|DragonFly|AIX|OS390|Emscripten" + OR WIN32 OR CYGWIN) AND NOT RCC_USE_SANITIZER) + rcc_append_flags("-Wl,-z,defs" CMAKE_SHARED_LINKER_FLAGS) + endif() +endmacro() + +macro(rcc_configure_platform_definitions) + if(UNIX AND CMAKE_SYSTEM_NAME MATCHES "AIX") + add_compile_definitions(_XOPEN_SOURCE=700 _LARGE_FILE_API) + add_compile_options(-pthread) + rcc_append_flags("-shared -Wl,-G" CMAKE_MODULE_LINKER_FLAGS) + rcc_append_flags("-shared" CMAKE_SHARED_LINKER_FLAGS) + endif() + if(CMAKE_SYSTEM_NAME MATCHES "OS390") + add_compile_definitions(_XOPEN_SOURCE=600 _XPLATFORM_SOURCE _OPEN_SYS _OPEN_SYS_FILE_EXT _EXT) + rcc_append_flags("-fzos-le-char-mode=ascii -m64" CMAKE_C_FLAGS CMAKE_CXX_FLAGS) + endif() + if(UNIX AND CMAKE_SYSTEM_NAME MATCHES "SunOS") + add_compile_definitions(_FILE_OFFSET_BITS=64) + endif() +endmacro() + +macro(rcc_configure_warning_options) + if(MSVC) + if(RCC_ENABLE_WERROR) + rcc_append_flags("/WX" CMAKE_C_FLAGS CMAKE_CXX_FLAGS) + endif() + if(RCC_ENABLE_WARNINGS) + string(REGEX REPLACE " /W[0-4]" "" CMAKE_C_FLAGS "${CMAKE_C_FLAGS}") + string(REGEX REPLACE " /W[0-4]" "" CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}") + rcc_append_flags("/W4" CMAKE_C_FLAGS CMAKE_CXX_FLAGS) + endif() + if(NOT RCC_CLANG_CL) + if(RCC_ENABLE_WARNING_SUPPRESSIONS) + rcc_append_flags( + "-wd4146 -wd4244 -wd4267 -wd4456 -wd4457 -wd4458 -wd4459 -wd4624 -wd4100 -wd4127 -wd4505 -wd4702 -wd4245 -wd4310 -wd4701 -wd4703 -wd4389 -wd4805 -wd4577 -wd4319 -wd4324 -wd4251 -wd4275" + CMAKE_C_FLAGS CMAKE_CXX_FLAGS) + endif() + rcc_append_flags("-w14062 -we4238" CMAKE_C_FLAGS CMAKE_CXX_FLAGS) + endif() + endif() + + if(RCC_GCC_COMPATIBLE AND RCC_ENABLE_WERROR) + rcc_append_flags("-Werror" CMAKE_C_FLAGS CMAKE_CXX_FLAGS) + rcc_append_flags("-Wno-error" CMAKE_REQUIRED_FLAGS) + endif() + if(RCC_GCC_COMPATIBLE OR CMAKE_CXX_COMPILER_ID MATCHES "XL") + rcc_append_flags("-Werror=date-time" CMAKE_C_FLAGS CMAKE_CXX_FLAGS) + endif() + if(CMAKE_CXX_COMPILER_ID MATCHES "Clang") + rcc_append_flags("-Werror=unguarded-availability-new" CMAKE_C_FLAGS CMAKE_CXX_FLAGS) + endif() + + set(rccInitialCFlags "${CMAKE_C_FLAGS}") + set(rccInitialCXXFlags "${CMAKE_CXX_FLAGS}") + if(RCC_ENABLE_WARNINGS AND (RCC_GCC_COMPATIBLE OR RCC_CLANG_CL)) + if(NOT RCC_CLANG_CL) + rcc_append_flags("-Wall" CMAKE_C_FLAGS CMAKE_CXX_FLAGS) + endif() + rcc_append_flags("-Wextra -Wno-unused-parameter -Wwrite-strings -Wcast-qual" + CMAKE_C_FLAGS CMAKE_CXX_FLAGS) + if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU") + rcc_append_flags("-Wno-missing-field-initializers" CMAKE_C_FLAGS CMAKE_CXX_FLAGS) + else() + rcc_append_flags("-Wmissing-field-initializers" CMAKE_C_FLAGS CMAKE_CXX_FLAGS) + endif() + + if(RCC_ENABLE_PEDANTIC AND RCC_GCC_COMPATIBLE) + rcc_append_flags("-pedantic -Wno-long-long" CMAKE_C_FLAGS CMAKE_CXX_FLAGS) + rcc_add_supported_warning("-Wc++98-compat-extra-semi" CXX98_COMPAT_EXTRA_SEMI) + endif() + rcc_append_flags("-Wimplicit-fallthrough" CMAKE_C_FLAGS CMAKE_CXX_FLAGS) + if(CMAKE_CXX_COMPILER_ID MATCHES "Clang") + rcc_append_flags("-Wcovered-switch-default" CMAKE_C_FLAGS CMAKE_CXX_FLAGS) + endif() + + if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU") + # Match LLVM's GCC warning suppression policy and version conditions. + if(CMAKE_CXX_COMPILER_VERSION VERSION_LESS 15.0) + rcc_append_flags("-Wno-uninitialized" CMAKE_CXX_FLAGS) + else() + rcc_append_flags("-Wno-maybe-uninitialized" CMAKE_CXX_FLAGS) + endif() + rcc_append_flags("-Wno-nonnull" CMAKE_CXX_FLAGS) + if(CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL 8.1) + rcc_append_flags("-Wno-class-memaccess" CMAKE_CXX_FLAGS) + endif() + if(CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL 13.1) + rcc_append_flags("-Wno-dangling-reference" CMAKE_CXX_FLAGS) + endif() + if(CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL 9.1) + rcc_append_flags("-Wno-redundant-move -Wno-pessimizing-move" CMAKE_CXX_FLAGS) + endif() + rcc_append_flags("-Wno-array-bounds" CMAKE_CXX_FLAGS) + if(CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL 11.1) + rcc_append_flags("-Wno-stringop-overread" CMAKE_CXX_FLAGS) + endif() + if(CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL 12.1) + rcc_append_flags("-Wno-dangling-pointer" CMAKE_CXX_FLAGS) + endif() + if(CMAKE_CXX_COMPILER_VERSION VERSION_LESS 14.0) + rcc_append_flags("-Wno-unused-but-set-parameter" CMAKE_CXX_FLAGS) + endif() + endif() + rcc_append_flags("-Wno-noexcept-type" CMAKE_CXX_FLAGS) + if(CMAKE_CXX_COMPILER_ID MATCHES "Clang") + rcc_append_flags("-Wnon-virtual-dtor" CMAKE_CXX_FLAGS) + endif() + rcc_append_flags("-Wdelete-non-virtual-dtor" CMAKE_CXX_FLAGS) + + check_cxx_compiler_flag("-Wsuggest-override" RCC_SUPPORTS_SUGGEST_OVERRIDE) + if(RCC_SUPPORTS_SUGGEST_OVERRIDE) + cmake_push_check_state() + string(APPEND CMAKE_REQUIRED_FLAGS " -Werror=suggest-override") + check_cxx_source_compiles([=[ + class Base { public: virtual void anchor(); }; + class Derived : Base { public: void anchor() final; }; + int main() { return 0; } + ]=] RCC_SUGGEST_OVERRIDE_ACCEPTS_FINAL) + cmake_pop_check_state() + if(RCC_SUGGEST_OVERRIDE_ACCEPTS_FINAL) + rcc_append_flags("-Wsuggest-override" CMAKE_CXX_FLAGS) + endif() + endif() + + cmake_push_check_state() + string(APPEND CMAKE_REQUIRED_FLAGS " -Werror -Wcomment") + check_c_source_compiles("// \\\\\\n//\\nint main(void) { return 0; }" + RCC_C_WCOMMENT_ACCEPTS_LINE_WRAP) + cmake_pop_check_state() + if(NOT RCC_C_WCOMMENT_ACCEPTS_LINE_WRAP) + rcc_append_flags("-Wno-comment" CMAKE_C_FLAGS CMAKE_CXX_FLAGS) + endif() + + if(CMAKE_CXX_COMPILER_ID MATCHES "Clang") + rcc_append_flags("-Wstring-conversion" CMAKE_C_FLAGS CMAKE_CXX_FLAGS) + rcc_append_flags("-Wno-pass-failed" CMAKE_CXX_FLAGS) + endif() + if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU") + rcc_append_flags("-Wno-misleading-indentation" CMAKE_CXX_FLAGS) + else() + rcc_add_supported_warning("-Wmisleading-indentation" MISLEADING_INDENTATION) + endif() + rcc_add_supported_warning("-Wctad-maybe-unsupported" CTAD_MAYBE_UNSUPPORTED) + elseif(RCC_GCC_COMPATIBLE AND NOT RCC_ENABLE_WARNINGS) + rcc_append_flags("-w" CMAKE_C_FLAGS CMAKE_CXX_FLAGS) + endif() + + if(NOT RCC_ENABLE_WARNING_SUPPRESSIONS AND (RCC_GCC_COMPATIBLE OR RCC_CLANG_CL)) + foreach(rccLanguage C CXX) + string(LENGTH "${rccInitial${rccLanguage}Flags}" rccInitialLength) + string(SUBSTRING "${CMAKE_${rccLanguage}_FLAGS}" ${rccInitialLength} -1 rccAddedFlags) + string(REGEX REPLACE "(^| +)-Wno-[a-zA-Z0-9_-]+" "" rccAddedFlags "${rccAddedFlags}") + set(CMAKE_${rccLanguage}_FLAGS "${rccInitial${rccLanguage}Flags}${rccAddedFlags}") + endforeach() + endif() +endmacro() + +# Configure in the caller's directory scope, as LLVM's option modules do. +macro(rcc_configure_options) + if(DEFINED CMAKE_CXX_STANDARD AND CMAKE_CXX_STANDARD LESS 23) + message(FATAL_ERROR "RCC requires CMAKE_CXX_STANDARD to be at least 23") + endif() + set(CMAKE_CXX_STANDARD 23 CACHE STRING "C++ standard to conform to") + set(CMAKE_CXX_STANDARD_REQUIRED YES) + set(CMAKE_CXX_EXTENSIONS NO) + + if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES) + message(WARNING "No build type selected. Defaulted CMAKE_BUILD_TYPE=Release.") + set(CMAKE_BUILD_TYPE Release CACHE STRING "Build type: Debug, Release, RelWithDebInfo, MinSizeRel" FORCE) + endif() + if(CMAKE_DEFAULT_BUILD_TYPE) + set(CMAKE_TRY_COMPILE_CONFIGURATION "${CMAKE_DEFAULT_BUILD_TYPE}") + elseif(CMAKE_CONFIGURATION_TYPES) + list(GET CMAKE_CONFIGURATION_TYPES 0 CMAKE_TRY_COMPILE_CONFIGURATION) + elseif(CMAKE_BUILD_TYPE) + set(CMAKE_TRY_COMPILE_CONFIGURATION "${CMAKE_BUILD_TYPE}") + endif() + set(RCC_ADDITIONAL_BUILD_TYPES "" CACHE STRING "Additional allowed CMake build types") + set(rccAllowedBuildTypes DEBUG RELEASE RELWITHDEBINFO MINSIZEREL ${RCC_ADDITIONAL_BUILD_TYPES}) + string(REPLACE ";" "|" rccAllowedBuildTypes "${rccAllowedBuildTypes}") + string(TOUPPER "${rccAllowedBuildTypes}" rccAllowedBuildTypes) + set(assertionsDefault OFF) + string(TOUPPER "${CMAKE_BUILD_TYPE}" buildType) + if(CMAKE_BUILD_TYPE AND NOT buildType MATCHES "^(${rccAllowedBuildTypes})$") + message(FATAL_ERROR "Unknown value for CMAKE_BUILD_TYPE: ${CMAKE_BUILD_TYPE}") + endif() + if(buildType STREQUAL "DEBUG") + set(assertionsDefault ON) + endif() + option(RCC_ENABLE_WARNINGS "Enable compiler warnings" ON) + option(RCC_ENABLE_WARNING_SUPPRESSIONS "Suppress compiler warnings" ON) + option(RCC_ENABLE_PEDANTIC "Compile with pedantic enabled" ON) + option(RCC_ENABLE_WERROR "Treat compiler warnings as errors" OFF) + option(RCC_ENABLE_ASSERTIONS "Enable assertions" "${assertionsDefault}") + option(RCC_ENABLE_EH "Enable C++ exceptions" OFF) + option(RCC_ENABLE_RTTI "Enable C++ runtime type information" OFF) + option(RCC_ENABLE_UNWIND_TABLES "Emit unwind tables for the libraries" ON) + set(RCC_USE_SANITIZER "" CACHE STRING + "Sanitizer used to build binaries; use LLVM's platform-specific combinations") + option(RCC_OPTIMIZE_SANITIZED_BUILDS "Use -O1 for Debug sanitizer builds" ON) + set(RCC_UBSAN_FLAGS + "-fsanitize=undefined -fno-sanitize=vptr,function -fno-sanitize-recover=all" + CACHE STRING "Compiler flags for the Undefined sanitizer") + + if(RCC_ENABLE_EH AND NOT RCC_ENABLE_RTTI) + message(FATAL_ERROR "RCC_ENABLE_EH requires RCC_ENABLE_RTTI.") + endif() + + if(NOT DEFINED RCC_GCC_COMPATIBLE) + if(CMAKE_COMPILER_IS_GNUCXX) + set(RCC_GCC_COMPATIBLE ON) + elseif(MSVC) + set(RCC_GCC_COMPATIBLE OFF) + elseif(CMAKE_CXX_COMPILER_ID MATCHES "Clang|Intel") + set(RCC_GCC_COMPATIBLE ON) + endif() + endif() + set(RCC_CLANG_CL OFF) + if(MSVC AND CMAKE_CXX_COMPILER_ID MATCHES "Clang") + set(RCC_CLANG_CL ON) + endif() + + if(WIN32) + set(RCC_ON_UNIX OFF) + elseif(FUCHSIA OR UNIX OR CYGWIN) + set(RCC_ON_UNIX ON) + elseif(CMAKE_SYSTEM_NAME STREQUAL "Generic") + set(RCC_ON_UNIX OFF) + else() + message(SEND_ERROR "Unable to determine platform") + endif() + + rcc_configure_additional_options() + rcc_configure_standard_library() + rcc_configure_platform_checks() + rcc_configure_job_pools() + + if(RCC_ENABLE_ASSERTIONS) + if(NOT WIN32 OR MINGW) + add_compile_definitions(_DEBUG) + endif() + if(NOT buildType STREQUAL "DEBUG") + add_compile_options("$<$<COMPILE_LANGUAGE:C,CXX>:-UNDEBUG>") + if(MSVC) + foreach(rccFlagsVariable + CMAKE_CXX_FLAGS_RELEASE CMAKE_CXX_FLAGS_RELWITHDEBINFO CMAKE_CXX_FLAGS_MINSIZEREL + CMAKE_C_FLAGS_RELEASE CMAKE_C_FLAGS_RELWITHDEBINFO CMAKE_C_FLAGS_MINSIZEREL) + string(REGEX REPLACE "(^| )[/-]D *NDEBUG($| )" " " + ${rccFlagsVariable} "${${rccFlagsVariable}}") + endforeach() + endif() + endif() + add_compile_definitions(_GLIBCXX_ASSERTIONS) + check_cxx_source_compiles([=[ + #define _LIBCPP_HARDENING_MODE _LIBCPP_HARDENING_MODE_EXTENSIVE + #include <string> + int main() { return 0; } + ]=] RCC_SUPPORTS_LIBCXX_HARDENING_MODE) + if(RCC_SUPPORTS_LIBCXX_HARDENING_MODE) + add_compile_definitions(_LIBCPP_HARDENING_MODE=_LIBCPP_HARDENING_MODE_EXTENSIVE) + endif() + endif() + + rcc_configure_standard_library_abi() + rcc_configure_platform_link_options() + rcc_configure_linker() + rcc_configure_platform_options() + rcc_configure_warning_options() + + if(RCC_USE_SANITIZER) + rcc_configure_sanitizer_options() + endif() + rcc_configure_debug_options() + rcc_configure_sections() + if(MSVC) + string(REGEX REPLACE "(^| ) */EH[-cs]+ *( |$)" "\\1 \\2" + CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}") + string(REGEX REPLACE "(^| ) */GR-? *( |$)" "\\1 \\2" + CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}") + endif() + rcc_configure_profile_options() + rcc_configure_pch_and_cache() + rcc_configure_lto() + rcc_configure_relative_paths() + rcc_configure_platform_definitions() +endmacro() + +macro(rcc_configure_sanitizer_options) + set(ubsanFlags "${RCC_UBSAN_FLAGS}") + set(sanitizerFlags) + set(commonFlags ON) + if(RCC_ON_UNIX) + if(RCC_USE_SANITIZER STREQUAL "Address") + list(APPEND sanitizerFlags -fsanitize=address) + elseif(RCC_USE_SANITIZER STREQUAL "HWAddress") + list(APPEND sanitizerFlags -fsanitize=hwaddress) + elseif(RCC_USE_SANITIZER MATCHES "Memory(WithOrigins)?") + list(APPEND sanitizerFlags -fsanitize=memory) + if(RCC_USE_SANITIZER STREQUAL "MemoryWithOrigins") + list(APPEND sanitizerFlags -fsanitize-memory-track-origins) + endif() + elseif(RCC_USE_SANITIZER STREQUAL "Undefined") + list(APPEND sanitizerFlags ${ubsanFlags}) + elseif(RCC_USE_SANITIZER STREQUAL "Thread") + list(APPEND sanitizerFlags -fsanitize=thread) + elseif(RCC_USE_SANITIZER STREQUAL "DataFlow") + list(APPEND sanitizerFlags -fsanitize=dataflow) + set(commonFlags OFF) + elseif(RCC_USE_SANITIZER STREQUAL "Address;Undefined" OR RCC_USE_SANITIZER STREQUAL "Undefined;Address") + list(APPEND sanitizerFlags -fsanitize=address ${ubsanFlags}) + elseif(RCC_USE_SANITIZER STREQUAL "Leaks") + list(APPEND sanitizerFlags -fsanitize=leak) + else() + message(FATAL_ERROR "Unsupported RCC_USE_SANITIZER value: ${RCC_USE_SANITIZER}") + endif() + elseif(MINGW OR MSVC) + if(NOT RCC_USE_SANITIZER MATCHES "^(Address|Undefined|Address;Undefined|Undefined;Address)$") + message(FATAL_ERROR "Unsupported sanitizer in this Windows environment: ${RCC_USE_SANITIZER}") + endif() + if("Address" IN_LIST RCC_USE_SANITIZER) + if(MSVC AND NOT RCC_CLANG_CL) + list(APPEND sanitizerFlags /fsanitize=address) + else() + list(APPEND sanitizerFlags -fsanitize=address) + endif() + endif() + if("Undefined" IN_LIST RCC_USE_SANITIZER) + if(MSVC AND NOT RCC_CLANG_CL) + message(FATAL_ERROR "Undefined sanitizer requires clang-cl in the MSVC environment") + endif() + list(APPEND sanitizerFlags ${ubsanFlags}) + endif() + else() + message(FATAL_ERROR "RCC_USE_SANITIZER is not supported on this platform") + endif() + + if(commonFlags) + if(MSVC AND NOT RCC_CLANG_CL) + rcc_append_flags("/Z7" CMAKE_C_FLAGS CMAKE_CXX_FLAGS) + rcc_append_flags("/debug" CMAKE_EXE_LINKER_FLAGS CMAKE_MODULE_LINKER_FLAGS CMAKE_SHARED_LINKER_FLAGS) + foreach(rccFlagsVariable CMAKE_EXE_LINKER_FLAGS CMAKE_MODULE_LINKER_FLAGS CMAKE_SHARED_LINKER_FLAGS) + string(REGEX REPLACE "(^| )/INCREMENTAL($| )" " /INCREMENTAL:NO " + ${rccFlagsVariable} "${${rccFlagsVariable}}") + endforeach() + if(CMAKE_C_COMPILER_ARCHITECTURE_ID STREQUAL "X86") + rcc_append_flags("/Oy-" CMAKE_C_FLAGS CMAKE_CXX_FLAGS) + endif() + else() + rcc_append_flags("-fno-omit-frame-pointer" CMAKE_C_FLAGS CMAKE_CXX_FLAGS) + if(NOT buildType STREQUAL "DEBUG" AND NOT buildType STREQUAL "RELWITHDEBINFO" + AND CMAKE_CXX_COMPILER_ID MATCHES "Clang") + rcc_append_flags("-gline-tables-only" CMAKE_C_FLAGS CMAKE_CXX_FLAGS) + endif() + if(buildType STREQUAL "DEBUG" AND RCC_OPTIMIZE_SANITIZED_BUILDS) + rcc_append_flags("-O1" CMAKE_C_FLAGS CMAKE_CXX_FLAGS) + endif() + endif() + endif() + string(JOIN " " rccSanitizerFlags ${sanitizerFlags}) + rcc_append_flags("${rccSanitizerFlags}" CMAKE_C_FLAGS CMAKE_CXX_FLAGS) + + if(MSVC) + if(NOT RCC_CLANG_CL AND "Address" IN_LIST RCC_USE_SANITIZER) + foreach(rccFlagsVariable CMAKE_CXX_FLAGS_${buildType} CMAKE_C_FLAGS_${buildType}) + string(REGEX REPLACE "(^| )/RTC[1csu]*($| )" " " + ${rccFlagsVariable} "${${rccFlagsVariable}}") + endforeach() + endif() + if(RCC_LINKER_IS_LLD_LINK AND "Address" IN_LIST RCC_USE_SANITIZER) + if(CMAKE_C_COMPILER_ARCHITECTURE_ID STREQUAL "X86") + set(runtimeArch i386) + else() + set(runtimeArch x86_64) + endif() + rcc_append_flags("/opt:nolldtailmerge clang_rt.asan_dynamic-${runtimeArch}.lib" + CMAKE_EXE_LINKER_FLAGS CMAKE_MODULE_LINKER_FLAGS CMAKE_SHARED_LINKER_FLAGS) + if(CMAKE_MSVC_RUNTIME_LIBRARY MATCHES "^(MultiThreaded|MultiThreadedDebug)$") + set(rccRuntimeThunk "clang_rt.asan_static_runtime_thunk-${runtimeArch}.lib") + else() + set(rccRuntimeThunk "clang_rt.asan_dynamic_runtime_thunk-${runtimeArch}.lib") + endif() + rcc_append_flags("/wholearchive:${rccRuntimeThunk}" + CMAKE_EXE_LINKER_FLAGS CMAKE_MODULE_LINKER_FLAGS CMAKE_SHARED_LINKER_FLAGS) + endif() + endif() + + if(RCC_USE_SANITIZE_COVERAGE) + rcc_append_flags("-fsanitize=fuzzer-no-link" CMAKE_C_FLAGS CMAKE_CXX_FLAGS) + endif() + if(RCC_USE_SANITIZER MATCHES ".*Undefined.*") + set(rccIgnorelist "${PROJECT_SOURCE_DIR}/utils/sanitizers/ubsan_ignorelist.txt") + if(EXISTS "${rccIgnorelist}") + rcc_append_flags("-fsanitize-ignorelist=\"${rccIgnorelist}\"" CMAKE_C_FLAGS CMAKE_CXX_FLAGS) + endif() + endif() +endmacro() + +macro(rcc_configure_compiler_rt_path) + if(RCC_CLANG_CL AND (RCC_BUILD_INSTRUMENTED OR RCC_USE_SANITIZER)) + execute_process( + COMMAND "${CMAKE_CXX_COMPILER}" /clang:-print-libgcc-file-name /clang:--rtlib=compiler-rt + OUTPUT_VARIABLE rccCompilerRTFile + ERROR_VARIABLE rccClangCLError + RESULT_VARIABLE rccClangCLResult + OUTPUT_STRIP_TRAILING_WHITESPACE + ERROR_STRIP_TRAILING_WHITESPACE) + if(NOT rccClangCLResult STREQUAL "0") + message(FATAL_ERROR "Unable to invoke clang-cl to find resource dir: ${rccClangCLError}") + endif() + file(TO_CMAKE_PATH "${rccCompilerRTFile}" rccCompilerRTFile) + get_filename_component(rccRuntimeDir "${rccCompilerRTFile}" DIRECTORY) + foreach(rccFlagsVariable CMAKE_EXE_LINKER_FLAGS CMAKE_MODULE_LINKER_FLAGS CMAKE_SHARED_LINKER_FLAGS) + set(${rccFlagsVariable} + "${CMAKE_CXX_LINKER_WRAPPER_FLAG}/libpath:\"${rccRuntimeDir}\" ${${rccFlagsVariable}}") + endforeach() + endif() +endmacro() + +function(rcc_apply_options target) + set(enableEH ${RCC_ENABLE_EH}) + set(enableRTTI ${RCC_ENABLE_RTTI}) + if(RCC_REQUIRES_EH) + set(enableEH ON) + endif() + if(RCC_REQUIRES_RTTI) + set(enableRTTI ON) + endif() + if(enableEH AND NOT enableRTTI) + message(AUTHOR_WARNING "Exception handling requires RTTI; enabling RTTI for ${target}") + set(enableRTTI ON) + endif() + set(cxxFlags) + if(MSVC) + if(enableEH) + list(APPEND cxxFlags /EHsc) + else() + list(APPEND cxxFlags /EHs-c-) + target_compile_definitions(${target} PRIVATE _HAS_EXCEPTIONS=0) + endif() + if(enableRTTI) + list(APPEND cxxFlags /GR) + else() + list(APPEND cxxFlags /GR-) + endif() + elseif(RCC_GCC_COMPATIBLE) + if(NOT enableEH) + list(APPEND cxxFlags -fno-exceptions) + if(RCC_ENABLE_UNWIND_TABLES) + list(APPEND cxxFlags -funwind-tables) + else() + list(APPEND cxxFlags -fno-unwind-tables -fno-asynchronous-unwind-tables) + endif() + endif() + if(NOT enableRTTI) + list(APPEND cxxFlags -fno-rtti) + endif() + elseif(CMAKE_CXX_COMPILER_ID MATCHES "XL") + if(NOT enableEH) + list(APPEND cxxFlags -qnoeh) + endif() + if(NOT enableRTTI) + list(APPEND cxxFlags -qnortti) + endif() + endif() + target_compile_options(${target} PRIVATE "$<$<COMPILE_LANGUAGE:CXX>:${cxxFlags}>") + rcc_apply_link_options(${target}) +endfunction() + +macro(rcc_configure_additional_options) + option(BUILD_SHARED_LIBS "Build libraries as shared libraries instead of static" OFF) + if(NOT CMAKE_SYSTEM_NAME MATCHES "OS390") + option(RCC_ENABLE_PIC "Build position-independent code" ON) + endif() + option(RCC_ENABLE_LIBCXX "Use libc++ if available" OFF) + option(RCC_STATIC_LINK_CXX_STDLIB "Statically link the C++ standard library" OFF) + option(RCC_ENABLE_LLD "Use lld as the linker" OFF) + set(RCC_USE_LINKER "" CACHE STRING "Linker to select through the compiler driver") + set(RCC_ENABLE_LTO OFF CACHE STRING "Enable LTO; use Thin or Full to select its mode") + string(TOUPPER "${RCC_ENABLE_LTO}" rccLTOMode) + option(RCC_ENABLE_FATLTO "Build with fat LTO objects" OFF) + set(RCC_THINLTO_CACHE_PATH "${PROJECT_BINARY_DIR}/lto.cache" CACHE STRING "ThinLTO cache directory") + option(RCC_NO_DEAD_STRIP "Disable removal of unused sections" OFF) + option(RCC_USE_SPLIT_DWARF "Use split DWARF in debug builds" OFF) + option(RCC_USE_SANITIZE_COVERAGE "Enable sanitizer coverage without the fuzzer main function" OFF) + set(RCC_BUILD_INSTRUMENTED OFF CACHE STRING "PGO instrumentation: IR, Frontend, CSIR, CSSPGO") + option(RCC_BUILD_INSTRUMENTED_COVERAGE "Build with code coverage instrumentation" OFF) + mark_as_advanced(RCC_BUILD_INSTRUMENTED RCC_BUILD_INSTRUMENTED_COVERAGE) + set(RCC_PROFDATA_FILE "" CACHE FILEPATH "Instrumentation profile to use when compiling") + set(RCC_SPROFDATA_FILE "" CACHE FILEPATH "Sampling profile to use when compiling") + set(RCC_CCACHE_BUILD OFF CACHE BOOL "Enable ccache for compilation") + set(RCC_SOURCE_PREFIX "" CACHE STRING "Source prefix in mapped paths") + option(RCC_USE_RELATIVE_PATHS_IN_DEBUG_INFO "Use relative paths in debug information" OFF) + option(RCC_USE_RELATIVE_PATHS_IN_FILES "Use relative paths in source and debug information" OFF) + if(CMAKE_SIZEOF_VOID_P EQUAL 8 AND NOT (WIN32 OR CMAKE_SYSTEM_NAME MATCHES "AIX")) + option(RCC_BUILD_32_BITS "Build 32-bit binaries on a 64-bit platform" OFF) + endif() +endmacro() + +macro(rcc_configure_standard_library) + set(RCC_LIBCXX_USED OFF) + if(RCC_ENABLE_LIBCXX) + if(RCC_GCC_COMPATIBLE) + check_cxx_compiler_flag("-stdlib=libc++" RCC_COMPILER_SUPPORTS_LIBCXX) + check_linker_flag(CXX "-stdlib=libc++" RCC_LINKER_SUPPORTS_LIBCXX) + if(RCC_COMPILER_SUPPORTS_LIBCXX AND RCC_LINKER_SUPPORTS_LIBCXX) + rcc_append_flags("-stdlib=libc++" CMAKE_CXX_FLAGS + CMAKE_EXE_LINKER_FLAGS CMAKE_SHARED_LINKER_FLAGS CMAKE_MODULE_LINKER_FLAGS) + set(RCC_LIBCXX_USED ON) + else() + message(WARNING "Cannot select libc++ with -stdlib=") + endif() + else() + message(WARNING "No libc++ selection flag is known for this compiler") + endif() + endif() + if(RCC_STATIC_LINK_CXX_STDLIB) + if(RCC_GCC_COMPATIBLE) + check_cxx_compiler_flag("-static-libstdc++" RCC_COMPILER_SUPPORTS_STATIC_STDLIB) + check_linker_flag(CXX "-static-libstdc++" RCC_LINKER_SUPPORTS_STATIC_STDLIB) + if(RCC_COMPILER_SUPPORTS_STATIC_STDLIB AND RCC_LINKER_SUPPORTS_STATIC_STDLIB) + rcc_append_flags("-static-libstdc++" + CMAKE_EXE_LINKER_FLAGS CMAKE_SHARED_LINKER_FLAGS CMAKE_MODULE_LINKER_FLAGS) + else() + message(WARNING "Cannot select static linking for the C++ standard library") + endif() + else() + message(WARNING "No static C++ standard library selection flag is known for this compiler") + endif() + endif() +endmacro() + +macro(rcc_configure_standard_library_abi) + check_cxx_source_compiles([=[ + #include <iosfwd> + #if !defined(__GLIBCXX__) + #error Not libstdc++ + #endif + int main() { return 0; } + ]=] RCC_USES_LIBSTDCXX) + check_cxx_source_compiles([=[ + #include <string> + #if _GLIBCXX_USE_CXX11_ABI == 0 + #error C++11 ABI not active + #endif + int main() { return 0; } + ]=] RCC_DEFAULT_GLIBCXX_CXX11_ABI) + option(GLIBCXX_USE_CXX11_ABI "Use the libstdc++ C++11 ABI" ${RCC_DEFAULT_GLIBCXX_CXX11_ABI}) + if(RCC_USES_LIBSTDCXX) + if(GLIBCXX_USE_CXX11_ABI) + add_compile_definitions(_GLIBCXX_USE_CXX11_ABI=1) + else() + add_compile_definitions(_GLIBCXX_USE_CXX11_ABI=0) + endif() + endif() +endmacro() + +macro(rcc_configure_linker) + if(RCC_ENABLE_LLD) + if(RCC_USE_LINKER) + message(FATAL_ERROR "RCC_ENABLE_LLD and RCC_USE_LINKER cannot be set together") + endif() + if(MSVC) + if(NOT CMAKE_LINKER MATCHES "lld-link") + get_filename_component(rccCXXCompilerDir "${CMAKE_CXX_COMPILER}" DIRECTORY) + get_filename_component(rccCCompilerDir "${CMAKE_C_COMPILER}" DIRECTORY) + find_program(RCC_LLD_LINK NAMES lld-link lld-link.exe + HINTS "${rccCXXCompilerDir}" "${rccCCompilerDir}") + if(NOT RCC_LLD_LINK) + message(FATAL_ERROR "RCC_ENABLE_LLD requires lld-link; set CMAKE_LINKER to its path") + endif() + set(CMAKE_LINKER "${RCC_LLD_LINK}") + endif() + else() + set(RCC_USE_LINKER lld) + endif() + endif() + if(RCC_USE_LINKER) + check_cxx_source_compiles("int main() { return 0; }" RCC_CAN_LINK_EXECUTABLE) + rcc_append_flags("-fuse-ld=${RCC_USE_LINKER}" + CMAKE_EXE_LINKER_FLAGS CMAKE_MODULE_LINKER_FLAGS CMAKE_SHARED_LINKER_FLAGS) + if(RCC_CAN_LINK_EXECUTABLE) + check_cxx_source_compiles("int main() { return 0; }" RCC_SUPPORTS_CUSTOM_LINKER) + if(NOT RCC_SUPPORTS_CUSTOM_LINKER) + message(FATAL_ERROR "The compiler cannot link with -fuse-ld=${RCC_USE_LINKER}") + endif() + else() + message(STATUS "Skipping custom linker check: the target cannot link an executable") + endif() + endif() + set(RCC_LINKER_IS_LLD OFF) + set(RCC_LINKER_IS_LLD_LINK OFF) + if(CMAKE_LINKER MATCHES ".*lld" OR RCC_USE_LINKER STREQUAL "lld" OR RCC_ENABLE_LLD) + set(RCC_LINKER_IS_LLD ON) + endif() + if(CMAKE_LINKER MATCHES "lld-link" OR (MSVC AND (RCC_USE_LINKER STREQUAL "lld" OR RCC_ENABLE_LLD))) + set(RCC_LINKER_IS_LLD_LINK ON) + endif() + if(NOT WIN32) + if(APPLE) + set(rccLinkerVersionFlag "-Wl,-v") + else() + set(rccLinkerVersionFlag "-Wl,--version") + endif() + if(CMAKE_HOST_WIN32) + set(rccLinkerDevNull "NUL") + else() + set(rccLinkerDevNull "/dev/null") + endif() + if(RCC_USE_LINKER) + set(rccLinkerCommand ${CMAKE_C_COMPILER} -fuse-ld=${RCC_USE_LINKER} ${rccLinkerVersionFlag} -o ${rccLinkerDevNull}) + else() + separate_arguments(rccLinkerFlags UNIX_COMMAND "${CMAKE_EXE_LINKER_FLAGS}") + set(rccLinkerCommand ${CMAKE_C_COMPILER} ${rccLinkerFlags} ${rccLinkerVersionFlag} -o ${rccLinkerDevNull}) + endif() + execute_process(COMMAND ${rccLinkerCommand} + OUTPUT_VARIABLE rccLinkerOutput ERROR_VARIABLE rccLinkerError) + set(RCC_LINKER_IS_SOLARISLD OFF) + set(RCC_LINKER_IS_GNULD OFF) + if(rccLinkerOutput MATCHES "GNU ld") + set(RCC_LINKER_IS_GNULD ON) + endif() + if(rccLinkerOutput MATCHES "Solaris Link Editors" OR rccLinkerError MATCHES "Solaris Link Editors") + set(RCC_LINKER_IS_SOLARISLD ON) + endif() + endif() + if(APPLE) + if(NOT RCC_USE_LINKER) + check_linker_flag(C "-Wl,-no_warn_duplicate_libraries" RCC_LINKER_SUPPORTS_NO_WARN_DUPLICATE_LIBRARIES) + else() + set(RCC_LINKER_SUPPORTS_NO_WARN_DUPLICATE_LIBRARIES OFF CACHE INTERNAL "") + endif() + endif() +endmacro() + +function(rcc_has_no_incremental flags output) + set(${output} OFF PARENT_SCOPE) + string(FIND "${flags}" "/INCREMENTAL" position REVERSE) + if(position GREATER -1) + string(SUBSTRING "${flags}" ${position} 15 option) + if(option MATCHES "/INCREMENTAL:NO") + set(${output} ON PARENT_SCOPE) + endif() + endif() +endfunction() + +macro(rcc_configure_msvc_options) + add_compile_definitions(_CRT_SECURE_NO_DEPRECATE _CRT_SECURE_NO_WARNINGS + _CRT_NONSTDC_NO_DEPRECATE _CRT_NONSTDC_NO_WARNINGS + _SCL_SECURE_NO_DEPRECATE _SCL_SECURE_NO_WARNINGS UNICODE _UNICODE) + rcc_append_flags("/Zc:inline /Oi /permissive-" CMAKE_C_FLAGS CMAKE_CXX_FLAGS) + if(CMAKE_CXX_COMPILER_ID STREQUAL "MSVC") + rcc_append_flags("/Zc:preprocessor" CMAKE_C_FLAGS CMAKE_CXX_FLAGS) + endif() + rcc_append_flags("/Zc:__cplusplus /bigobj" CMAKE_CXX_FLAGS) + set(RCC_WINSYSROOT "" CACHE STRING "Argument to clang-cl's /winsysroot") + if(RCC_WINSYSROOT) + if(NOT RCC_CLANG_CL) + message(FATAL_ERROR "RCC_WINSYSROOT requires clang-cl") + endif() + rcc_append_flags("/winsysroot\"${RCC_WINSYSROOT}\"" CMAKE_C_FLAGS CMAKE_CXX_FLAGS) + if(RCC_LINKER_IS_LLD_LINK) + rcc_append_flags("/winsysroot:\"${RCC_WINSYSROOT}\"" + CMAKE_EXE_LINKER_FLAGS CMAKE_MODULE_LINKER_FLAGS CMAKE_SHARED_LINKER_FLAGS) + endif() + endif() + option(RCC_ENABLE_PDB "Generate PDB files in Release builds" OFF) + if(RCC_ENABLE_PDB AND buildType STREQUAL "RELEASE") + rcc_append_flags("/Zi" CMAKE_C_FLAGS_RELEASE CMAKE_CXX_FLAGS_RELEASE) + rcc_append_flags("/DEBUG /OPT:REF /OPT:ICF" + CMAKE_EXE_LINKER_FLAGS CMAKE_MODULE_LINKER_FLAGS CMAKE_SHARED_LINKER_FLAGS) + endif() + set(rccAllLinkerFlags "") + foreach(rccKind EXE MODULE SHARED) + string(APPEND rccAllLinkerFlags + " ${CMAKE_${rccKind}_LINKER_FLAGS_${buildType}} ${CMAKE_${rccKind}_LINKER_FLAGS}") + endforeach() + string(TOUPPER "${rccAllLinkerFlags}" rccAllLinkerFlags) + if(RCC_CLANG_CL AND RCC_LINKER_IS_LLD AND rccAllLinkerFlags MATCHES "/DEBUG") + rcc_add_supported_warning("-gcodeview-ghash" GCODEVIEW_GHASH) + endif() + if(CMAKE_CXX_COMPILER_ID MATCHES "Clang" AND NOT RCC_ENABLE_LTO) + foreach(rccKind EXE MODULE SHARED) + rcc_has_no_incremental( + "${CMAKE_${rccKind}_LINKER_FLAGS_${buildType}} ${CMAKE_${rccKind}_LINKER_FLAGS}" + rccNoIncremental${rccKind}) + endforeach() + if(rccNoIncrementalEXE AND rccNoIncrementalMODULE AND rccNoIncrementalSHARED) + add_compile_options("$<$<COMPILE_LANGUAGE:C,CXX>:/Brepro>") + add_link_options("LINKER:/Brepro") + else() + message(WARNING "/Brepro is incompatible with incremental linking; output will not be deterministic") + endif() + endif() +endmacro() + +macro(rcc_configure_platform_options) + if(RCC_ENABLE_PIC) + if(XCODE) + message(WARNING "-fPIC is not supported with Xcode") + elseif(NOT (WIN32 OR CYGWIN)) + check_c_compiler_flag("-Werror -fPIC" RCC_C_SUPPORTS_FPIC) + check_cxx_compiler_flag("-Werror -fPIC" RCC_CXX_SUPPORTS_FPIC) + if(RCC_C_SUPPORTS_FPIC AND RCC_CXX_SUPPORTS_FPIC) + message(STATUS "Building with -fPIC") + rcc_append_flags("-fPIC" CMAKE_C_FLAGS CMAKE_CXX_FLAGS CMAKE_ASM_FLAGS) + else() + message(WARNING "-fPIC is not supported") + endif() + if(NOT CMAKE_SYSTEM_NAME MATCHES "AIX" AND CMAKE_CXX_COMPILER_ID MATCHES "GNU|Clang") + rcc_add_supported_warning("-fno-semantic-interposition" FNO_SEMANTIC_INTERPOSITION) + endif() + endif() + endif() + if(NOT CMAKE_SYSTEM_NAME MATCHES "AIX" AND + (NOT (WIN32 OR CYGWIN) OR ((MINGW OR CYGWIN) AND CMAKE_CXX_COMPILER_ID MATCHES "Clang"))) + check_cxx_compiler_flag("-fvisibility-inlines-hidden" RCC_SUPPORTS_HIDDEN_INLINES) + if(RCC_SUPPORTS_HIDDEN_INLINES) + rcc_append_flags("-fvisibility-inlines-hidden" CMAKE_CXX_FLAGS) + endif() + endif() + + if((CMAKE_SIZEOF_VOID_P EQUAL 8 AND MINGW) OR CMAKE_SIZEOF_VOID_P EQUAL 4) + add_compile_definitions(_FILE_OFFSET_BITS=64) + endif() + if(CMAKE_SIZEOF_VOID_P EQUAL 4) + add_compile_definitions(_LARGEFILE_SOURCE) + endif() + if(CMAKE_SIZEOF_VOID_P EQUAL 8 AND NOT WIN32 AND RCC_BUILD_32_BITS) + message(STATUS "Building 32 bits executables and libraries.") + rcc_append_flags("-m32" CMAKE_C_FLAGS CMAKE_CXX_FLAGS + CMAKE_EXE_LINKER_FLAGS CMAKE_SHARED_LINKER_FLAGS CMAKE_MODULE_LINKER_FLAGS) + add_compile_definitions(_LARGEFILE_SOURCE _FILE_OFFSET_BITS=64) + endif() + + if(MSVC OR CMAKE_CXX_SIMULATE_ID STREQUAL "MSVC") + add_link_options("$<$<STREQUAL:$<TARGET_PROPERTY:TYPE>,EXECUTABLE>:LINKER:/STACK:10000000>") + elseif(MINGW OR CYGWIN) + add_link_options("$<$<STREQUAL:$<TARGET_PROPERTY:TYPE>,EXECUTABLE>:LINKER:--stack,16777216>") + if(NOT CMAKE_CXX_COMPILER_ID MATCHES "Clang") + rcc_append_flags("-Wa,-mbig-obj" CMAKE_C_FLAGS CMAKE_CXX_FLAGS) + endif() + endif() + if(MSVC) + rcc_configure_msvc_options() + endif() + if(XCODE) + foreach(rccWarning + GCC_WARN_ABOUT_RETURN_TYPE GCC_WARN_ABOUT_MISSING_NEWLINE GCC_WARN_UNUSED_VALUE + GCC_WARN_UNUSED_VARIABLE GCC_WARN_SIGN_COMPARE GCC_WARN_UNUSED_FUNCTION + GCC_WARN_INITIALIZER_NOT_FULLY_BRACKETED GCC_WARN_HIDDEN_VIRTUAL_FUNCTIONS + GCC_WARN_UNINITIALIZED_AUTOS CLANG_WARN_BOOL_CONVERSION CLANG_WARN_EMPTY_BODY + CLANG_WARN_ENUM_CONVERSION CLANG_WARN_INT_CONVERSION CLANG_WARN_CONSTANT_CONVERSION + GCC_WARN_NON_VIRTUAL_DESTRUCTOR) + set(CMAKE_XCODE_ATTRIBUTE_${rccWarning} YES) + endforeach() + endif() + if(MSVC_IDE) + set(RCC_COMPILER_JOBS "0" CACHE STRING "Parallel compiler jobs; 0 uses all processors") + if(RCC_COMPILER_JOBS STREQUAL "0") + add_compile_options(/MP) + elseif(NOT RCC_COMPILER_JOBS STREQUAL "1") + message(STATUS "Number of parallel compiler jobs set to ${RCC_COMPILER_JOBS}") + add_compile_options("/MP${RCC_COMPILER_JOBS}") + else() + message(STATUS "Parallel compilation disabled") + endif() + endif() + +endmacro() + +macro(rcc_configure_sections) + if(NOT CYGWIN AND NOT MSVC) + if(NOT CMAKE_SYSTEM_NAME MATCHES "Darwin" AND NOT buildType STREQUAL "DEBUG") + if(CMAKE_CXX_COMPILER_ID MATCHES "XL") + rcc_append_flags("-qfuncsect" CMAKE_C_FLAGS CMAKE_CXX_FLAGS) + else() + rcc_append_flags("-ffunction-sections" CMAKE_C_FLAGS CMAKE_CXX_FLAGS) + endif() + rcc_append_flags("-fdata-sections" CMAKE_C_FLAGS CMAKE_CXX_FLAGS) + endif() + elseif(MSVC AND NOT buildType STREQUAL "DEBUG") + rcc_append_flags("/Gw" CMAKE_C_FLAGS CMAKE_CXX_FLAGS) + endif() +endmacro() + +macro(rcc_configure_debug_options) + if(RCC_USE_SPLIT_DWARF AND (buildType STREQUAL "DEBUG" OR buildType STREQUAL "RELWITHDEBINFO") + AND CMAKE_CXX_COMPILER_ID MATCHES "Clang|GNU") + add_compile_options("$<$<COMPILE_LANGUAGE:C,CXX>:-gsplit-dwarf>") + check_linker_flag(CXX "-Wl,--gdb-index" RCC_LINKER_SUPPORTS_GDB_INDEX) + if(RCC_LINKER_SUPPORTS_GDB_INDEX) + rcc_append_flags("-Wl,--gdb-index" CMAKE_EXE_LINKER_FLAGS CMAKE_MODULE_LINKER_FLAGS CMAKE_SHARED_LINKER_FLAGS) + endif() + endif() + if(RCC_ENABLE_PIC AND RCC_USING_GLIBC) + check_cxx_source_compiles([=[ + #include <cstdio> + #if __GLIBC__ < 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ < 36) + #error RELR requires glibc 2.36 + #endif + int main() { return 0; } + ]=] RCC_GLIBC_SUPPORTS_RELR) + if(RCC_GLIBC_SUPPORTS_RELR) + check_linker_flag(CXX "-Wl,-z,pack-relative-relocs" RCC_LINKER_SUPPORTS_RELR) + if(RCC_LINKER_SUPPORTS_RELR) + rcc_append_flags("-Wl,-z,pack-relative-relocs" + CMAKE_EXE_LINKER_FLAGS CMAKE_MODULE_LINKER_FLAGS CMAKE_SHARED_LINKER_FLAGS) + endif() + endif() + endif() + add_compile_definitions(__STDC_CONSTANT_MACROS __STDC_FORMAT_MACROS __STDC_LIMIT_MACROS) + if(UNIX AND CMAKE_GENERATOR MATCHES "Ninja") + if(CMAKE_CXX_COMPILER_ID MATCHES "Clang|GNU") + rcc_append_flags("-fdiagnostics-color" CMAKE_C_FLAGS CMAKE_CXX_FLAGS) + endif() + check_linker_flag(CXX "-Wl,--color-diagnostics" RCC_LINKER_SUPPORTS_COLOR_DIAGNOSTICS) + if(RCC_LINKER_SUPPORTS_COLOR_DIAGNOSTICS) + rcc_append_flags("-Wl,--color-diagnostics" + CMAKE_EXE_LINKER_FLAGS CMAKE_MODULE_LINKER_FLAGS CMAKE_SHARED_LINKER_FLAGS) + endif() + endif() +endmacro() + +macro(rcc_configure_profile_options) + string(TOUPPER "${RCC_BUILD_INSTRUMENTED}" rccProfileMode) + if(RCC_BUILD_INSTRUMENTED OR RCC_BUILD_INSTRUMENTED_COVERAGE) + if(NOT RCC_PROFILE_MERGE_POOL_SIZE) + set(RCC_PROFILE_MERGE_POOL_SIZE 4) + endif() + if(NOT RCC_PROFILE_FILE_PATTERN) + if(NOT RCC_PROFILE_DATA_DIR) + file(TO_NATIVE_PATH "${PROJECT_BINARY_DIR}/profiles" RCC_PROFILE_DATA_DIR) + endif() + file(TO_NATIVE_PATH "${RCC_PROFILE_DATA_DIR}/%${RCC_PROFILE_MERGE_POOL_SIZE}m.profraw" RCC_PROFILE_FILE_PATTERN) + endif() + if(NOT RCC_CSPROFILE_FILE_PATTERN) + if(NOT RCC_CSPROFILE_DATA_DIR) + file(TO_NATIVE_PATH "${PROJECT_BINARY_DIR}/csprofiles" RCC_CSPROFILE_DATA_DIR) + endif() + file(TO_NATIVE_PATH "${RCC_CSPROFILE_DATA_DIR}/%${RCC_PROFILE_MERGE_POOL_SIZE}m.profraw" RCC_CSPROFILE_FILE_PATTERN) + endif() + endif() + if(RCC_BUILD_INSTRUMENTED AND RCC_BUILD_INSTRUMENTED_COVERAGE) + message(FATAL_ERROR "PGO instrumentation and coverage instrumentation cannot both be enabled") + endif() + if(RCC_BUILD_INSTRUMENTED) + if(rccProfileMode STREQUAL "IR") + set(rccProfileFlags "-fprofile-generate=\"${RCC_PROFILE_DATA_DIR}\"") + elseif(rccProfileMode STREQUAL "CSIR") + set(rccProfileFlags "-fcs-profile-generate=\"${RCC_CSPROFILE_DATA_DIR}\"") + elseif(rccProfileMode STREQUAL "CSSPGO") + if(NOT CMAKE_CXX_COMPILER_ID MATCHES "Clang") + message(FATAL_ERROR "RCC_BUILD_INSTRUMENTED=CSSPGO requires Clang") + endif() + set(rccProfileFlags + "-fno-omit-frame-pointer -mno-omit-leaf-frame-pointer -fno-optimize-sibling-calls -fpseudo-probe-for-profiling -fdebug-info-for-profiling") + else() + set(rccProfileFlags "-fprofile-instr-generate=\"${RCC_PROFILE_FILE_PATTERN}\"") + endif() + rcc_append_flags("${rccProfileFlags}" CMAKE_C_FLAGS CMAKE_CXX_FLAGS) + if(NOT RCC_LINKER_IS_LLD_LINK) + rcc_append_flags("${rccProfileFlags}" CMAKE_EXE_LINKER_FLAGS CMAKE_SHARED_LINKER_FLAGS) + endif() + endif() + rcc_configure_compiler_rt_path() + + if(RCC_PROFDATA_FILE AND EXISTS "${RCC_PROFDATA_FILE}") + if(NOT CMAKE_CXX_COMPILER_ID MATCHES "Clang") + message(FATAL_ERROR "RCC_PROFDATA_FILE requires Clang") + endif() + rcc_append_flags("-fprofile-instr-use=\"${RCC_PROFDATA_FILE}\"" CMAKE_C_FLAGS CMAKE_CXX_FLAGS) + if(NOT RCC_LINKER_IS_LLD_LINK) + rcc_append_flags("-fprofile-instr-use=\"${RCC_PROFDATA_FILE}\"" + CMAKE_EXE_LINKER_FLAGS CMAKE_SHARED_LINKER_FLAGS) + endif() + elseif(RCC_PROFDATA_FILE) + message(WARNING "RCC_PROFDATA_FILE does not exist: ${RCC_PROFDATA_FILE}") + endif() + if(RCC_SPROFDATA_FILE AND EXISTS "${RCC_SPROFDATA_FILE}") + if(NOT CMAKE_CXX_COMPILER_ID MATCHES "Clang") + message(FATAL_ERROR "RCC_SPROFDATA_FILE requires Clang") + endif() + set(rccSampleFlags "-fpseudo-probe-for-profiling -fprofile-sample-use=\"${RCC_SPROFDATA_FILE}\"") + rcc_append_flags("${rccSampleFlags}" CMAKE_C_FLAGS CMAKE_CXX_FLAGS) + if(NOT RCC_LINKER_IS_LLD_LINK) + rcc_append_flags("${rccSampleFlags}" CMAKE_EXE_LINKER_FLAGS CMAKE_SHARED_LINKER_FLAGS) + endif() + endif() + if(RCC_BUILD_INSTRUMENTED_COVERAGE) + rcc_append_flags("-fprofile-instr-generate=\"${RCC_PROFILE_FILE_PATTERN}\" -fcoverage-mapping" + CMAKE_C_FLAGS CMAKE_CXX_FLAGS CMAKE_EXE_LINKER_FLAGS CMAKE_SHARED_LINKER_FLAGS) + endif() +endmacro() + +macro(rcc_configure_lto) + if(RCC_ENABLE_LTO AND WIN32 AND NOT RCC_LINKER_IS_LLD_LINK AND NOT MINGW) + message(FATAL_ERROR "LTO on Windows requires lld-link; set CMAKE_LINKER to its path") + endif() + if(RCC_ENABLE_LTO) + if(rccLTOMode STREQUAL "THIN") + set(rccLTOFlag "-flto=thin") + elseif(rccLTOMode STREQUAL "FULL") + set(rccLTOFlag "-flto=full") + else() + set(rccLTOFlag "-flto") + endif() + rcc_append_flags("${rccLTOFlag}" CMAKE_C_FLAGS CMAKE_CXX_FLAGS) + if(NOT RCC_LINKER_IS_LLD_LINK) + rcc_append_flags("${rccLTOFlag}" CMAKE_EXE_LINKER_FLAGS CMAKE_SHARED_LINKER_FLAGS) + endif() + endif() + if(rccLTOMode STREQUAL "THIN") + if(APPLE) + rcc_append_flags("-Wl,-cache_path_lto,${RCC_THINLTO_CACHE_PATH}" + CMAKE_EXE_LINKER_FLAGS CMAKE_SHARED_LINKER_FLAGS) + elseif(CMAKE_SYSTEM_NAME MATCHES "AIX") + rcc_append_flags("-bplugin_opt:-legacy-thinlto-cache-dir=${RCC_THINLTO_CACHE_PATH}" + CMAKE_EXE_LINKER_FLAGS CMAKE_SHARED_LINKER_FLAGS) + elseif((UNIX OR MINGW) AND RCC_USE_LINKER STREQUAL "lld") + rcc_append_flags("-Wl,--thinlto-cache-dir=${RCC_THINLTO_CACHE_PATH}" + CMAKE_EXE_LINKER_FLAGS CMAKE_SHARED_LINKER_FLAGS) + elseif(RCC_USE_LINKER STREQUAL "gold") + rcc_append_flags("-Wl,--plugin-opt,cache-dir=${RCC_THINLTO_CACHE_PATH}" + CMAKE_EXE_LINKER_FLAGS CMAKE_SHARED_LINKER_FLAGS) + elseif(RCC_LINKER_IS_LLD_LINK) + rcc_append_flags("/lldltocache:${RCC_THINLTO_CACHE_PATH}" + CMAKE_EXE_LINKER_FLAGS CMAKE_SHARED_LINKER_FLAGS) + endif() + endif() + if(RCC_ENABLE_FATLTO AND ((UNIX AND NOT APPLE) OR FUCHSIA)) + rcc_append_flags("-ffat-lto-objects" CMAKE_C_FLAGS CMAKE_CXX_FLAGS) + if(NOT RCC_LINKER_IS_LLD_LINK) + rcc_append_flags("-ffat-lto-objects" + CMAKE_EXE_LINKER_FLAGS CMAKE_SHARED_LINKER_FLAGS CMAKE_MODULE_LINKER_FLAGS) + endif() + endif() +endmacro() + +macro(rcc_configure_pch_and_cache) + if(NOT DEFINED CMAKE_DISABLE_PRECOMPILE_HEADERS) + if(CMAKE_SYSTEM_NAME MATCHES "OS390") + message(NOTICE "Precompiled headers are disabled by default on z/OS. " + "Pass -DCMAKE_DISABLE_PRECOMPILE_HEADERS=OFF to override.") + set(CMAKE_DISABLE_PRECOMPILE_HEADERS ON) + endif() + if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU") + message(NOTICE "Precompiled headers are disabled by default with GCC. " + "Pass -DCMAKE_DISABLE_PRECOMPILE_HEADERS=OFF to override.") + set(CMAKE_DISABLE_PRECOMPILE_HEADERS ON) + endif() + if(CMAKE_CXX_COMPILER_LAUNCHER MATCHES "sccache") + message(NOTICE "Precompiled headers are disabled by default with sccache. " + "Pass -DCMAKE_DISABLE_PRECOMPILE_HEADERS=OFF to override.") + set(CMAKE_DISABLE_PRECOMPILE_HEADERS ON) + elseif(CMAKE_CXX_COMPILER_LAUNCHER MATCHES "clang-cache") + message(NOTICE "Precompiled headers are disabled by default with clang-cache. " + "Pass -DCMAKE_DISABLE_PRECOMPILE_HEADERS=OFF to override.") + set(CMAKE_DISABLE_PRECOMPILE_HEADERS ON) + elseif(RCC_CCACHE_BUILD OR CMAKE_CXX_COMPILER_LAUNCHER MATCHES "ccache") + if(NOT CMAKE_CXX_COMPILER_ID MATCHES "Clang") + message(WARNING "Using ccache with precompiled headers with non-Clang " + "compilers is not supported and may lead to false positives. " + "Set CMAKE_DISABLE_PRECOMPILE_HEADERS to ON/OFF to silence this warning.") + set(CMAKE_DISABLE_PRECOMPILE_HEADERS ON) + endif() + if(CMAKE_HOST_WIN32) + message(WARNING "Using ccache with precompiled headers on Windows is " + "currently not supported. " + "Pass -DCMAKE_DISABLE_PRECOMPILE_HEADERS=OFF to override.") + set(CMAKE_DISABLE_PRECOMPILE_HEADERS ON) + endif() + endif() + endif() + if(NOT CMAKE_DISABLE_PRECOMPILE_HEADERS) + message(STATUS "Precompiled headers enabled.") + set(CMAKE_PCH_PROLOGUE "") + if(CMAKE_CXX_COMPILER_ID MATCHES "Clang") + rcc_append_flags("-Xclang -fno-pch-timestamp" CMAKE_CXX_FLAGS) + elseif(CMAKE_CXX_COMPILER_ID STREQUAL "GNU") + rcc_append_flags("-fpch-preprocess" CMAKE_CXX_FLAGS) + endif() + else() + message(STATUS "Precompiled headers disabled.") + endif() + if(RCC_CCACHE_BUILD) + find_program(RCC_CCACHE_PROGRAM ccache) + if(NOT RCC_CCACHE_PROGRAM) + message(FATAL_ERROR "ccache was not found; install it or disable RCC_CCACHE_BUILD") + endif() + set(RCC_CCACHE_MAXSIZE "" CACHE STRING "ccache size limit") + set(RCC_CCACHE_DIR "" CACHE STRING "ccache storage directory") + set(RCC_CCACHE_PARAMS "CCACHE_CPP2=yes CCACHE_HASHDIR=yes CCACHE_SLOPPINESS=pch_defines,time_macros" + CACHE STRING "Environment assignments for ccache") + if(NOT CMAKE_HOST_WIN32) + set(rccCacheCommand "${RCC_CCACHE_PARAMS} \"${RCC_CCACHE_PROGRAM}\"") + if(NOT CMAKE_DISABLE_PRECOMPILE_HEADERS) + set(rccCacheCommand "CCACHE_DEPEND=1 ${rccCacheCommand}") + endif() + if(RCC_CCACHE_MAXSIZE) + set(rccCacheCommand "CCACHE_MAXSIZE=${RCC_CCACHE_MAXSIZE} ${rccCacheCommand}") + endif() + if(RCC_CCACHE_DIR) + set(rccCacheCommand "CCACHE_DIR=\"${RCC_CCACHE_DIR}\" ${rccCacheCommand}") + endif() + set_property(GLOBAL PROPERTY RULE_LAUNCH_COMPILE "${rccCacheCommand}") + message(STATUS "ccache program: ${rccCacheCommand}") + else() + if(RCC_CCACHE_MAXSIZE OR RCC_CCACHE_DIR OR + NOT RCC_CCACHE_PARAMS MATCHES "CCACHE_CPP2=yes CCACHE_HASHDIR=yes CCACHE_SLOPPINESS=pch_defines,time_macros") + message(FATAL_ERROR "Configure ccache through environment variables on Windows") + endif() + set(CMAKE_C_COMPILER_LAUNCHER "${RCC_CCACHE_PROGRAM}") + set(CMAKE_CXX_COMPILER_LAUNCHER "${RCC_CCACHE_PROGRAM}") + message(STATUS "ccache program: ${RCC_CCACHE_PROGRAM}") + endif() + endif() +endmacro() + +macro(rcc_configure_relative_paths) + if(RCC_USE_RELATIVE_PATHS_IN_DEBUG_INFO OR RCC_USE_RELATIVE_PATHS_IN_FILES) + if(RCC_ENABLE_PROJECTS) + get_filename_component(rccSourceRoot "${PROJECT_SOURCE_DIR}/.." ABSOLUTE) + else() + set(rccSourceRoot "${PROJECT_SOURCE_DIR}") + endif() + file(RELATIVE_PATH rccRelativeRoot "${CMAKE_BINARY_DIR}" "${rccSourceRoot}") + endif() + if(RCC_USE_RELATIVE_PATHS_IN_DEBUG_INFO) + check_c_compiler_flag("-fdebug-prefix-map=foo=bar" RCC_SUPPORTS_FDEBUG_PREFIX_MAP) + if(RCC_SUPPORTS_FDEBUG_PREFIX_MAP) + rcc_append_flags("-fdebug-prefix-map=\"${CMAKE_BINARY_DIR}=${rccRelativeRoot}\" -fdebug-prefix-map=\"${rccSourceRoot}/=${RCC_SOURCE_PREFIX}\"" + CMAKE_C_FLAGS CMAKE_CXX_FLAGS) + endif() + if(RCC_GCC_COMPATIBLE) + rcc_append_flags("-no-canonical-prefixes" CMAKE_C_FLAGS CMAKE_CXX_FLAGS) + endif() + endif() + if(RCC_USE_RELATIVE_PATHS_IN_FILES) + check_c_compiler_flag("-ffile-prefix-map=foo=bar" RCC_SUPPORTS_FFILE_PREFIX_MAP) + if(RCC_SUPPORTS_FFILE_PREFIX_MAP) + rcc_append_flags("-ffile-prefix-map=\"${CMAKE_BINARY_DIR}=${rccRelativeRoot}\" -ffile-prefix-map=\"${rccSourceRoot}/=${RCC_SOURCE_PREFIX}\"" + CMAKE_C_FLAGS CMAKE_CXX_FLAGS) + endif() + if(RCC_GCC_COMPATIBLE) + rcc_append_flags("-no-canonical-prefixes" CMAKE_C_FLAGS CMAKE_CXX_FLAGS) + endif() + endif() +endmacro() + +function(rcc_apply_link_options target) + if(NOT buildType STREQUAL "DEBUG") + if(NOT RCC_NO_DEAD_STRIP) + if(CMAKE_SYSTEM_NAME MATCHES "Darwin") + target_link_options(${target} PRIVATE "LINKER:-dead_strip") + elseif(CMAKE_SYSTEM_NAME STREQUAL "SunOS" AND RCC_LINKER_IS_SOLARISLD) + check_linker_flag(CXX "-Wl,-z,discard-unused=sections" RCC_LINKER_SUPPORTS_DISCARD_UNUSED) + if(RCC_LINKER_SUPPORTS_DISCARD_UNUSED) + target_link_options(${target} PRIVATE "LINKER:-z,discard-unused=sections") + endif() + elseif(NOT MSVC AND NOT CMAKE_CXX_SIMULATE_ID STREQUAL "MSVC" AND NOT CMAKE_SYSTEM_NAME MATCHES "AIX|OS390") + target_link_options(${target} PRIVATE "LINKER:--gc-sections") + endif() + elseif(CMAKE_SYSTEM_NAME MATCHES "AIX") + target_link_options(${target} PRIVATE "LINKER:-bnogc") + endif() + endif() + if(RCC_LINKER_SUPPORTS_NO_WARN_DUPLICATE_LIBRARIES) + target_link_options(${target} PRIVATE "LINKER:-no_warn_duplicate_libraries") + endif() +endfunction() diff --git a/rcc/cmake/modules/HandleRCCStdlib.cmake b/rcc/cmake/modules/HandleRCCStdlib.cmake new file mode 100644 index 0000000..5dcaf2e --- /dev/null +++ b/rcc/cmake/modules/HandleRCCStdlib.cmake @@ -0,0 +1,54 @@ +# This CMake module is responsible for setting the standard library to libc++ +# if the user has requested it. + +include(DetermineGCCCompatible) + +if(NOT DEFINED RCC_STDLIB_HANDLED) + set(RCC_STDLIB_HANDLED ON) + + function(append value) + foreach(variable ${ARGN}) + set(${variable} "${${variable}} ${value}" PARENT_SCOPE) + endforeach(variable) + endfunction() + + include(CheckCXXCompilerFlag) + include(CheckLinkerFlag) + set(RCC_LIBCXX_USED 0) + if(RCC_ENABLE_LIBCXX) + if(RCC_COMPILER_IS_GCC_COMPATIBLE) + check_cxx_compiler_flag("-stdlib=libc++" CXX_COMPILER_SUPPORTS_STDLIB) + check_linker_flag(CXX "-stdlib=libc++" CXX_LINKER_SUPPORTS_STDLIB) + if(CXX_COMPILER_SUPPORTS_STDLIB AND CXX_LINKER_SUPPORTS_STDLIB) + append("-stdlib=libc++" + CMAKE_CXX_FLAGS CMAKE_EXE_LINKER_FLAGS CMAKE_SHARED_LINKER_FLAGS + CMAKE_MODULE_LINKER_FLAGS) + set(RCC_LIBCXX_USED 1) + else() + message(WARNING "Can't specify libc++ with '-stdlib='") + endif() + else() + message(WARNING "Not sure how to specify libc++ for this compiler") + endif() + endif() + + if(RCC_STATIC_LINK_CXX_STDLIB) + if(RCC_COMPILER_IS_GCC_COMPATIBLE) + check_cxx_compiler_flag("-static-libstdc++" + CXX_COMPILER_SUPPORTS_STATIC_STDLIB) + check_linker_flag(CXX "-static-libstdc++" CXX_LINKER_SUPPORTS_STATIC_STDLIB) + if(CXX_COMPILER_SUPPORTS_STATIC_STDLIB AND + CXX_LINKER_SUPPORTS_STATIC_STDLIB) + append("-static-libstdc++" + CMAKE_EXE_LINKER_FLAGS CMAKE_SHARED_LINKER_FLAGS + CMAKE_MODULE_LINKER_FLAGS) + else() + message(WARNING + "Can't specify static linking for the C++ standard library") + endif() + else() + message(WARNING "Not sure how to specify static linking of C++ standard " + "library for this compiler") + endif() + endif() +endif() diff --git a/rcc/cmake/modules/RCCConfig.cmake.in b/rcc/cmake/modules/RCCConfig.cmake.in new file mode 100644 index 0000000..3454a58 --- /dev/null +++ b/rcc/cmake/modules/RCCConfig.cmake.in @@ -0,0 +1,27 @@ +# SPDX-License-Identifier: GPL-3.0-or-later + +@PACKAGE_INIT@ + +set(RCC_VERSION "@RCC_VERSION@") +set(RCC_VERSION_MAJOR "@RCC_VERSION_MAJOR@") +set(RCC_VERSION_MINOR "@RCC_VERSION_MINOR@") +set(RCC_VERSION_PATCH "@RCC_VERSION_PATCH@") +set(RCC_CMAKE_DIR "@RCC_PACKAGE_MODULE_DIR@") +set(RCC_INCLUDE_DIRS "@RCC_PACKAGE_INCLUDE_DIRS@") +set(RCC_LIBRARY_DIR "@RCC_PACKAGE_LIBRARY_DIR@") +set(RCC_BUILD_LIBRARY_DIR "@RCC_PACKAGE_BUILD_LIBRARY_DIR@") +set(RCC_INSTALL_PREFIX "@RCC_PACKAGE_INSTALL_PREFIX@") +set(RCC_ENABLE_ASSERTIONS "@RCC_ENABLE_ASSERTIONS@") +set(RCC_ENABLE_EH "@RCC_ENABLE_EH@") +set(RCC_ENABLE_RTTI "@RCC_ENABLE_RTTI@") +set(RCC_ENABLE_UNWIND_TABLES "@RCC_ENABLE_UNWIND_TABLES@") +set(RCC_ENABLE_WARNINGS "@RCC_ENABLE_WARNINGS@") +set(RCC_ENABLE_WARNING_SUPPRESSIONS "@RCC_ENABLE_WARNING_SUPPRESSIONS@") +set(RCC_ENABLE_LIBCXX "@RCC_LIBCXX_USED@") +set(RCC_STATIC_LINK_CXX_STDLIB "@RCC_STATIC_LINK_CXX_STDLIB@") +set(GLIBCXX_USE_CXX11_ABI "@GLIBCXX_USE_CXX11_ABI@") +set(RCC_DEFINITIONS "@RCC_DEFINITIONS@") +if(MSVC) + set(CMAKE_MSVC_RUNTIME_LIBRARY "@RCC_PACKAGE_MSVC_RUNTIME_LIBRARY@") +endif() +include("${CMAKE_CURRENT_LIST_DIR}/RCCTargets.cmake") diff --git a/rcc/cmake/modules/RCCConfigVersion.cmake.in b/rcc/cmake/modules/RCCConfigVersion.cmake.in new file mode 100644 index 0000000..f7d5ea4 --- /dev/null +++ b/rcc/cmake/modules/RCCConfigVersion.cmake.in @@ -0,0 +1,13 @@ +# SPDX-License-Identifier: GPL-3.0-or-later + +set(PACKAGE_VERSION "@PROJECT_VERSION@") + +# Match major and minor versions; accept the requested patch version or newer. +if("@PROJECT_VERSION_MAJOR@.@PROJECT_VERSION_MINOR@" VERSION_EQUAL + "${PACKAGE_FIND_VERSION_MAJOR}.${PACKAGE_FIND_VERSION_MINOR}" + AND NOT "@PROJECT_VERSION_PATCH@" VERSION_LESS "${PACKAGE_FIND_VERSION_PATCH}") + set(PACKAGE_VERSION_COMPATIBLE TRUE) + if("@PROJECT_VERSION_PATCH@" VERSION_EQUAL "${PACKAGE_FIND_VERSION_PATCH}") + set(PACKAGE_VERSION_EXACT TRUE) + endif() +endif() diff --git a/rcc/cmake/unwind.h b/rcc/cmake/unwind.h new file mode 100644 index 0000000..6c03411 --- /dev/null +++ b/rcc/cmake/unwind.h @@ -0,0 +1,11 @@ +// NOLINT: llvm-header-guard +// __register_frame() is used with dynamically generated code to register the +// FDE for a generated (JIT) code. This header provides protypes, since the gcc +// version of unwind.h may not, so CMake can check if the corresponding symbols +// exist in the runtime. +extern void +__register_frame (const void *fde); // NOLINT +extern void +__deregister_frame (const void *fde); // NOLINT +extern void +__unw_add_dynamic_fde (); // NOLINT diff --git a/rcc/include/rcc/Config/config.h.cmake b/rcc/include/rcc/Config/config.h.cmake new file mode 100644 index 0000000..8830412 --- /dev/null +++ b/rcc/include/rcc/Config/config.h.cmake @@ -0,0 +1,82 @@ +#ifndef RCC_PRIVATE_CONFIG_H +#define RCC_PRIVATE_CONFIG_H + +// Include this header only under the rcc source tree. +// This is a private header. + +/* Exported configuration */ +#include "rcc/Config/rcc-config.h" + +/* Bug report URL. */ +#define BUG_REPORT_URL "${BUG_REPORT_URL}" + +/* Define to 1 if we can register EH frames on this platform. */ +#cmakedefine HAVE_REGISTER_FRAME ${HAVE_REGISTER_FRAME} + +/* Define to 1 if we can deregister EH frames on this platform. */ +#cmakedefine HAVE_DEREGISTER_FRAME ${HAVE_DEREGISTER_FRAME} + +/* Define if __unw_add_dynamic_fde() is available on this platform. */ +#cmakedefine HAVE_UNW_ADD_DYNAMIC_FDE ${HAVE_UNW_ADD_DYNAMIC_FDE} + +/* Define if libffi is available on this platform. */ +#cmakedefine HAVE_FFI_CALL ${HAVE_FFI_CALL} + +/* Define to 1 if you have the <ffi/ffi.h> header file. */ +#cmakedefine HAVE_FFI_FFI_H ${HAVE_FFI_FFI_H} + +/* Define to 1 if you have the <ffi.h> header file. */ +#cmakedefine HAVE_FFI_H ${HAVE_FFI_H} + +/* Define to 1 if you have the `pfm' library (-lpfm). */ +#cmakedefine HAVE_LIBPFM ${HAVE_LIBPFM} + +/* Define to 1 if the `perf_branch_entry' struct has field cycles. */ +#cmakedefine LIBPFM_HAS_FIELD_CYCLES ${LIBPFM_HAS_FIELD_CYCLES} + +/* Linker version detected at compile time. */ +#cmakedefine HOST_LINK_VERSION "${HOST_LINK_VERSION}" + +/* Define if overriding target triple is enabled */ +#cmakedefine RCC_TARGET_TRIPLE_ENV "${RCC_TARGET_TRIPLE_ENV}" + +/* Whether tools show host and target info when invoked with --version */ +#cmakedefine01 RCC_VERSION_PRINTER_SHOW_HOST_TARGET_INFO + +/* Whether tools show optional build config flags when invoked with --version */ +#cmakedefine01 RCC_VERSION_PRINTER_SHOW_BUILD_CONFIG + +/* Define if libxml2 is supported on this platform. */ +#cmakedefine RCC_ENABLE_LIBXML2 ${RCC_ENABLE_LIBXML2} + +/* Define to the extension used for shared libraries, say, ".so". */ +#cmakedefine LTDL_SHLIB_EXT "${LTDL_SHLIB_EXT}" + +/* Define to the extension used for plugin libraries, say, ".so". */ +#cmakedefine RCC_PLUGIN_EXT "${RCC_PLUGIN_EXT}" + +/* Define to the address where bug reports for this package should be sent. */ +#cmakedefine PACKAGE_BUGREPORT "${PACKAGE_BUGREPORT}" + +/* Define to the full name of this package. */ +#cmakedefine PACKAGE_NAME "${PACKAGE_NAME}" + +/* Define to the full name and version of this package. */ +#cmakedefine PACKAGE_STRING "${PACKAGE_STRING}" + +/* Define to the version of this package. */ +#cmakedefine PACKAGE_VERSION "${PACKAGE_VERSION}" + +/* Define to the vendor of this package. */ +#cmakedefine PACKAGE_VENDOR "${PACKAGE_VENDOR}" + +/* Whether GlobalISel rule coverage is being collected */ +#cmakedefine01 RCC_GISEL_COV_ENABLED + +/* Define to the default GlobalISel coverage file prefix */ +#cmakedefine RCC_GISEL_COV_PREFIX "${RCC_GISEL_COV_PREFIX}" + +/* Have host's builtin thread pointer. */ +#cmakedefine HAVE_BUILTIN_THREAD_POINTER ${HAVE_BUILTIN_THREAD_POINTER} + +#endif diff --git a/rcc/include/rcc/Config/rcc-config.h.cmake b/rcc/include/rcc/Config/rcc-config.h.cmake new file mode 100644 index 0000000..fb76d22 --- /dev/null +++ b/rcc/include/rcc/Config/rcc-config.h.cmake @@ -0,0 +1,98 @@ +/*===------- rcc/Config/rcc-config.h - RCC configuration -------*- C -*-===*/ +/* */ +/* Part of the LLVM Project, under the Apache License v2.0 with LLVM */ +/* Exceptions. */ +/* See https://llvm.org/LICENSE.txt for license information. */ +/* SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ +/* */ +/*===----------------------------------------------------------------------===*/ + +/* This file enumerates variables from the RCC configuration so that they + can be in exported headers and won't override package specific directives. + This is a C header that can be included in the RCC C headers. */ + +#ifndef RCC_CONFIG_H +#define RCC_CONFIG_H + +/* Target triple RCC will generate code for by default */ +/* Doesn't use `cmakedefine` because it is allowed to be empty. */ +#define RCC_DEFAULT_TARGET_TRIPLE "${RCC_DEFAULT_TARGET_TRIPLE}" + +/* Host triple RCC will be executed on */ +#cmakedefine RCC_HOST_TRIPLE "${RCC_HOST_TRIPLE}" + +/* RCC architecture name for the native architecture, if available */ +#cmakedefine RCC_NATIVE_ARCH ${RCC_NATIVE_ARCH} + +/* RCC name for the native AsmParser init function, if available */ +#cmakedefine RCC_NATIVE_ASMPARSER RCCInitialize${RCC_NATIVE_ARCH}AsmParser + +/* RCC name for the native AsmPrinter init function, if available */ +#cmakedefine RCC_NATIVE_ASMPRINTER RCCInitialize${RCC_NATIVE_ARCH}AsmPrinter + +/* RCC name for the native Disassembler init function, if available */ +#cmakedefine RCC_NATIVE_DISASSEMBLER RCCInitialize${RCC_NATIVE_ARCH}Disassembler + +/* RCC name for the native Target init function, if available */ +#cmakedefine RCC_NATIVE_TARGET RCCInitialize${RCC_NATIVE_ARCH}Target + +/* RCC name for the native TargetInfo init function, if available */ +#cmakedefine RCC_NATIVE_TARGETINFO RCCInitialize${RCC_NATIVE_ARCH}TargetInfo + +/* RCC name for the native target MC init function, if available */ +#cmakedefine RCC_NATIVE_TARGETMC RCCInitialize${RCC_NATIVE_ARCH}TargetMC + +/* RCC name for the native target MCA init function, if available */ +#cmakedefine RCC_NATIVE_TARGETMCA RCCInitialize${RCC_NATIVE_ARCH}TargetMCA + +/* Define if we have the Intel JIT API runtime support library */ +#cmakedefine01 RCC_USE_INTEL_JITEVENTS + +/* Define if we have the oprofile JIT-support library */ +#cmakedefine01 RCC_USE_OPROFILE + +/* Define if we have the perf JIT-support library */ +#cmakedefine01 RCC_USE_PERF + +/* Major version of the RCC API */ +#define RCC_VERSION_MAJOR ${RCC_VERSION_MAJOR} + +/* Minor version of the RCC API */ +#define RCC_VERSION_MINOR ${RCC_VERSION_MINOR} + +/* Patch version of the RCC API */ +#define RCC_VERSION_PATCH ${RCC_VERSION_PATCH} + +/* RCC version string */ +#define RCC_VERSION_STRING "${PACKAGE_VERSION}" + +/* Define if RCC is using tflite */ +#cmakedefine RCC_HAVE_TFLITE + +/* Define if we want to check profile consistency in lit tests */ +#cmakedefine RCC_ENABLE_PROFCHECK + +/* Define if building libRCC shared library */ +#cmakedefine RCC_BUILD_RCC_DYLIB + +/* Define if exporting RCC-C public interface for shared library */ +#cmakedefine RCC_ENABLE_RCC_C_EXPORT_ANNOTATIONS + +/* Define to 1 if you have the DIA SDK installed, and to 0 if you don't. */ +#cmakedefine01 RCC_ENABLE_DIA_SDK + +/* Define if plugins enabled */ +#cmakedefine RCC_ENABLE_PLUGINS + +/* Define if building RCC with RCC_ENABLE_TELEMETRY */ +#cmakedefine01 RCC_ENABLE_TELEMETRY + +/* Define to 1 to enable expensive checks for debug location coverage checking, + and to 0 otherwise. */ +#cmakedefine01 RCC_ENABLE_DEBUGLOC_TRACKING_COVERAGE + +/* Define to 1 to enable expensive tracking of the origin of debug location + coverage bugs, and to 0 otherwise. */ +#cmakedefine01 RCC_ENABLE_DEBUGLOC_TRACKING_ORIGIN + +#endif diff --git a/rcc/include/rcc/Support/Compiler.h b/rcc/include/rcc/Support/Compiler.h new file mode 100644 index 0000000..1076827 --- /dev/null +++ b/rcc/include/rcc/Support/Compiler.h @@ -0,0 +1,106 @@ +/* + RCC-Project + + Copyright (C) 2026 Richard Qin + + This file is part of RCC-Project. + + RCC-Project is free software; you can redistribute it and/or modify it under + the terms of the GNU General Public License as published by the Free + Software Foundation; either version 3 of the License, or (at your option) any + later version. + + RCC-Project is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along with + RCC-Project; see the file COPYING. If not see + <http://www.gnu.org/licenses/>. +*/ + +//===-- rcc/Support/Compiler.h - RCC ABI support ---------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef RCC_SUPPORT_COMPILER_H +#define RCC_SUPPORT_COMPILER_H + +#include "core/compiler/Compiler.h" +#include "rcc/Config/rcc-config.h" + +/// RCC_EXTERNAL_VISIBILITY - classes, functions, and variables marked with +/// this attribute will be made public and visible outside of a shared library. +#if defined(RCC_BUILD_RCC_DYLIB) || defined(RCC_BUILD_SHARED_LIBS) +#define RCC_EXTERNAL_VISIBILITY LIBBASE_ATTRIBUTE_VISIBILITY_DEFAULT +#else +#define RCC_EXTERNAL_VISIBILITY +#endif + +/// RCC_LIBRARY_VISIBILITY - Mark symbols private to the RCC shared library. +#if (!(defined(_WIN32) || defined(__CYGWIN__)) || \ + ((defined(__MINGW32__) || defined(__CYGWIN__)) && defined(__clang__))) +#define RCC_LIBRARY_VISIBILITY LIBBASE_ATTRIBUTE_VISIBILITY_HIDDEN +#if defined(__clang__) && __clang_major__ < 15 +#define RCC_LIBRARY_VISIBILITY_NAMESPACE [[gnu::visibility("hidden")]] +#else +#define RCC_LIBRARY_VISIBILITY_NAMESPACE LIBBASE_ATTRIBUTE_VISIBILITY_HIDDEN +#endif +#define RCC_ALWAYS_EXPORT LIBBASE_ATTRIBUTE_VISIBILITY_DEFAULT +#elif defined(_WIN32) +#define RCC_ALWAYS_EXPORT __declspec(dllexport) +#define RCC_LIBRARY_VISIBILITY +#define RCC_LIBRARY_VISIBILITY_NAMESPACE +#else +#define RCC_LIBRARY_VISIBILITY +#define RCC_ALWAYS_EXPORT +#define RCC_LIBRARY_VISIBILITY_NAMESPACE +#endif + +/// RCC_ABI is the main export macro for the RCC public interface. +/// RCC_ABI_EXPORT always exports a symbol from the defining library. +/// RCC_EXPORT_TEMPLATE marks explicit template instantiations. +/// RCC_TEMPLATE_ABI marks extern template declarations. +#ifndef RCC_ABI_GENERATING_ANNOTATIONS +#define RCC_ABI_NOT_EXPORTED +#if defined(RCC_ENABLE_RCC_EXPORT_ANNOTATIONS) && !defined(RCC_BUILD_STATIC) +#if defined(_WIN32) && !defined(__MINGW32__) +#if defined(RCC_EXPORTS) +#define RCC_ABI __declspec(dllexport) +#define RCC_TEMPLATE_ABI +#define RCC_EXPORT_TEMPLATE __declspec(dllexport) +#else +#define RCC_ABI __declspec(dllimport) +#define RCC_TEMPLATE_ABI __declspec(dllimport) +#define RCC_EXPORT_TEMPLATE +#endif +#define RCC_ABI_EXPORT __declspec(dllexport) +#elif __has_attribute(visibility) +#if defined(__ELF__) || defined(__MINGW32__) || defined(_AIX) || \ + defined(__MVS__) || defined(__CYGWIN__) +#define RCC_ABI LIBBASE_ATTRIBUTE_VISIBILITY_DEFAULT +#define RCC_TEMPLATE_ABI RCC_ABI +#define RCC_EXPORT_TEMPLATE +#define RCC_ABI_EXPORT RCC_ABI +#elif defined(__MACH__) || defined(__wasm__) || defined(__EMSCRIPTEN__) +#define RCC_ABI LIBBASE_ATTRIBUTE_VISIBILITY_DEFAULT +#define RCC_TEMPLATE_ABI +#define RCC_EXPORT_TEMPLATE +#define RCC_ABI_EXPORT RCC_ABI +#endif +#endif +#endif +#if !defined(RCC_ABI) +#define RCC_ABI +#define RCC_TEMPLATE_ABI +#define RCC_EXPORT_TEMPLATE +#define RCC_ABI_EXPORT +#endif +#define RCC_ABI_FOR_TEST RCC_ABI +#endif + +#endif diff --git a/rcc/tools/CMakeLists.txt b/rcc/tools/CMakeLists.txt new file mode 100644 index 0000000..5702399 --- /dev/null +++ b/rcc/tools/CMakeLists.txt @@ -0,0 +1,3 @@ +# SPDX-License-Identifier: GPL-3.0-or-later + +# Add tool subdirectories here as their implementations are introduced. diff --git a/rcc/utils/clang-format/git-clang-format b/rcc/utils/clang-format/git-clang-format new file mode 100755 index 0000000..d9b2d50 --- /dev/null +++ b/rcc/utils/clang-format/git-clang-format @@ -0,0 +1,891 @@ +#!/usr/bin/env python3 +# +# ===- git-clang-format - ClangFormat Git Integration -------*- python -*--=== # +# +# Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +# See https://llvm.org/LICENSE.txt for license information. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +# +# ===----------------------------------------------------------------------=== # + +r""" +clang-format git integration +============================ + +This file provides a clang-format integration for git. Put it somewhere in your +path and ensure that it is executable. Then, "git clang-format" will invoke +clang-format on the changes in current files or a specific commit. + +For further details, run: +git clang-format -h + +Requires Python version >=3.8 +""" + +from __future__ import absolute_import, division, print_function +import argparse +import collections +import contextlib +import errno +import os +import re +import subprocess +import sys +import tempfile + +usage = "git clang-format [OPTIONS] [<commit>] [<commit>|--staged] [--] [<file>...]" + +desc = """ +If zero or one commits are given, run clang-format on all lines that differ +between the working directory and <commit>, which defaults to HEAD. Changes are +only applied to the working directory, or in the stage/index. + +Examples: + To format staged changes, i.e everything that's been `git add`ed: + git clang-format + + To also format everything touched in the most recent commit: + git clang-format HEAD~1 + + If you're on a branch off main, to format everything touched on your branch: + git clang-format main + +If two commits are given (requires --diff), run clang-format on all lines in the +second <commit> that differ from the first <commit>. + +The following git-config settings set the default of the corresponding option: + clangFormat.binary + clangFormat.commit + clangFormat.extensions + clangFormat.style + +When running inside a pre-commit CI environment (i.e., no commits are given +on the command line and the PRE_COMMIT_FROM_REF and PRE_COMMIT_TO_REF +environment variables are set), the diff range is taken from those variables +and --diff is implied. +""" + +# Name of the temporary index file in which save the output of clang-format. +# This file is created within the .git directory. +temp_index_basename = "clang-format-index" + + +Range = collections.namedtuple("Range", "start, count") + + +def main(): + config = load_git_config() + + # In order to keep '--' yet allow options after positionals, we need to + # check for '--' ourselves. (Setting nargs='*' throws away the '--', while + # nargs=argparse.REMAINDER disallows options after positionals.) + argv = sys.argv[1:] + try: + idx = argv.index("--") + except ValueError: + dash_dash = [] + else: + dash_dash = argv[idx:] + argv = argv[:idx] + + default_extensions = ",".join( + [ + # From clang/lib/Frontend/FrontendOptions.cpp, all lower case + "c", + "h", # C + "m", # ObjC + "mm", # ObjC++ + "cc", + "cp", + "cpp", + "c++", + "cxx", + "hh", + "hpp", + "hxx", + "inc", # C++ + "ccm", + "cppm", + "cxxm", + "c++m", # C++ Modules + "cu", + "cuh", # CUDA + "cl", # OpenCL + # Other languages that clang-format supports + "proto", + "protodevel", # Protocol Buffers + "java", # Java + "js", + "mjs", + "cjs", # JavaScript + "ts", # TypeScript + "cs", # C Sharp + "json", + "ipynb", # JSON + "sv", + "svh", + "v", + "vh", # Verilog + "td", # TableGen + "txtpb", + "textpb", + "pb.txt", + "textproto", + "asciipb", # TextProto + ] + ) + + p = argparse.ArgumentParser( + usage=usage, + formatter_class=argparse.RawDescriptionHelpFormatter, + description=desc, + ) + p.add_argument( + "--binary", + default=config.get("clangformat.binary", "clang-format"), + help="path to clang-format", + ), + p.add_argument( + "--commit", + default=config.get("clangformat.commit", "HEAD"), + help="default commit to use if none is specified", + ), + p.add_argument( + "--diff", + action="store_true", + help="print a diff instead of applying the changes", + ) + p.add_argument( + "--diffstat", + action="store_true", + help="print a diffstat instead of applying the changes", + ) + p.add_argument( + "--extensions", + default=config.get("clangformat.extensions", default_extensions), + help=( + "comma-separated list of file extensions to format, " + "excluding the period and case-insensitive" + ), + ), + p.add_argument( + "-f", + "--force", + action="store_true", + help="allow changes to unstaged files", + ) + p.add_argument( + "-p", "--patch", action="store_true", help="select hunks interactively" + ) + p.add_argument( + "-q", + "--quiet", + action="count", + default=0, + help="print less information", + ) + p.add_argument( + "--staged", + "--cached", + action="store_true", + help="format lines in the stage instead of the working dir", + ) + p.add_argument( + "--style", + default=config.get("clangformat.style", None), + help="passed to clang-format", + ), + p.add_argument( + "-v", + "--verbose", + action="count", + default=0, + help="print extra information", + ) + p.add_argument( + "--whole-file", + action="store_true", + help="format whole file instead of only modified lines", + ) + p.add_argument( + "--diff_from_common_commit", + action="store_true", + help=( + "diff from the last common commit for commits in " + "separate branches rather than the exact point of the " + "commits" + ), + ) + # We gather all the remaining positional arguments into 'args' since we need + # to use some heuristics to determine whether or not <commit> was present. + # However, to print pretty messages, we make use of metavar and help. + p.add_argument( + "args", + nargs="*", + metavar="<commit>", + help="revision from which to compute the diff", + ) + p.add_argument( + "ignored", + nargs="*", + metavar="<file>...", + help="if specified, only consider differences in these files", + ) + opts = p.parse_args(argv) + + # When no commits are given explicitly and the pre-commit CI framework's + # environment variables are set, use them to define the diff range. + if not opts.args and not dash_dash: + from_ref = os.environ.get("PRE_COMMIT_FROM_REF") + to_ref = os.environ.get("PRE_COMMIT_TO_REF") + if from_ref and to_ref: + opts.args = [from_ref, to_ref] + if not opts.diff and not opts.diffstat: + opts.diff = True + + opts.verbose -= opts.quiet + del opts.quiet + + commits, files = interpret_args(opts.args, dash_dash, opts.commit) + if len(commits) > 2: + die("at most two commits allowed; %d given" % len(commits)) + if len(commits) == 2: + if opts.staged: + die("--staged is not allowed when two commits are given") + if not opts.diff: + die("--diff is required when two commits are given") + elif opts.diff_from_common_commit: + die("--diff_from_common_commit is only allowed when two commits are given") + + if os.path.dirname(opts.binary): + opts.binary = os.path.abspath(opts.binary) + + changed_lines = compute_diff_and_extract_lines( + commits, files, opts.staged, opts.whole_file, opts.diff_from_common_commit + ) + if opts.verbose >= 1: + ignored_files = set(changed_lines) + filter_by_extension(changed_lines, opts.extensions.lower().split(",")) + # The computed diff outputs absolute paths, so we must cd before accessing + # those files. + cd_to_toplevel() + filter_symlinks(changed_lines) + filter_ignored_files(changed_lines, binary=opts.binary) + if opts.verbose >= 1: + ignored_files.difference_update(changed_lines) + if ignored_files: + print( + "Ignoring the following files (wrong extension, symlink, or " + "ignored by clang-format):" + ) + for filename in ignored_files: + print(" %s" % filename) + if changed_lines: + print("Running clang-format on the following files:") + for filename in changed_lines: + print(" %s" % filename) + + if not changed_lines: + if opts.verbose >= 0: + print("no modified files to format") + return 0 + + if len(commits) > 1: + old_tree = commits[1] + revision = old_tree + elif opts.staged: + old_tree = create_tree_from_index(changed_lines) + revision = "" + else: + old_tree = create_tree_from_workdir(changed_lines) + revision = None + new_tree = run_clang_format_and_save_to_tree( + changed_lines, revision, binary=opts.binary, style=opts.style + ) + if opts.verbose >= 1: + print("old tree: %s" % old_tree) + print("new tree: %s" % new_tree) + + if old_tree == new_tree: + if opts.verbose >= 0: + print("clang-format did not modify any files") + return 0 + + if opts.diff: + return print_diff(old_tree, new_tree) + if opts.diffstat: + return print_diffstat(old_tree, new_tree) + + changed_files = apply_changes( + old_tree, new_tree, force=opts.force, patch_mode=opts.patch + ) + if (opts.verbose >= 0 and not opts.patch) or opts.verbose >= 1: + print("changed files:") + for filename in changed_files: + print(" %s" % filename) + + return 1 + + +def load_git_config(non_string_options=None): + """Return the git configuration as a dictionary. + + All options are assumed to be strings unless in `non_string_options`, in + which is a dictionary mapping option name (in lower case) to either "--bool" + or "--int".""" + if non_string_options is None: + non_string_options = {} + out = {} + for entry in run("git", "config", "--list", "--null").split("\0"): + if entry: + if "\n" in entry: + name, value = entry.split("\n", 1) + else: + # A setting with no '=' ('\n' with --null) is implicitly 'true' + name = entry + value = "true" + if name in non_string_options: + value = run("git", "config", non_string_options[name], name) + out[name] = value + return out + + +def interpret_args(args, dash_dash, default_commit): + """Interpret `args` as "[commits] [--] [files]" and return (commits, files). + + It is assumed that "--" and everything that follows has been removed from + args and placed in `dash_dash`. + + If "--" is present (i.e., `dash_dash` is non-empty), the arguments to its + left (if present) are taken as commits. Otherwise, the arguments are + checked from left to right if they are commits or files. If commits are not + given, a list with `default_commit` is used.""" + if dash_dash: + if len(args) == 0: + commits = [default_commit] + else: + commits = args + for commit in commits: + object_type = get_object_type(commit) + if object_type not in ("commit", "tag"): + if object_type is None: + die("'%s' is not a commit" % commit) + else: + die( + "'%s' is a %s, but a commit was expected" + % (commit, object_type) + ) + files = dash_dash[1:] + elif args: + commits = [] + while args: + if not disambiguate_revision(args[0]): + break + commits.append(args.pop(0)) + if not commits: + commits = [default_commit] + files = args + else: + commits = [default_commit] + files = [] + return commits, files + + +def disambiguate_revision(value): + """Returns True if `value` is a revision, False if it is a file, or dies.""" + # If `value` is ambiguous (neither a commit nor a file), the following + # command will die with an appropriate error message. + run("git", "rev-parse", value, verbose=False) + object_type = get_object_type(value) + if object_type is None: + return False + if object_type in ("commit", "tag"): + return True + die("`%s` is a %s, but a commit or filename was expected" % (value, object_type)) + + +def get_object_type(value): + """Returns a string description of an object's type, or None if it is not + a valid git object.""" + cmd = ["git", "cat-file", "-t", value] + p = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE) + stdout, stderr = p.communicate() + if p.returncode != 0: + return None + return convert_string(stdout.strip()) + + +def compute_diff_and_extract_lines( + commits, files, staged, whole_file, diff_common_commit +): + """Calls compute_diff() followed by extract_lines().""" + diff_process = compute_diff(commits, files, staged, diff_common_commit) + changed_lines = extract_lines(diff_process.stdout, whole_file) + diff_process.stdout.close() + diff_process.wait() + if diff_process.returncode != 0: + # Assume error was already printed to stderr. + sys.exit(2) + return changed_lines + + +def compute_diff(commits, files, staged, diff_common_commit): + """Return a subprocess object producing the diff from `commits`. + + The return value's `stdin` file object will produce a patch with the + differences between the working directory (or stage if --staged is used) and + the first commit if a single one was specified, or the difference between + both specified commits, filtered on `files` (if non-empty). + Zero context lines are used in the patch.""" + git_tool = "diff-index" + extra_args = [] + if len(commits) == 2: + git_tool = "diff-tree" + if diff_common_commit: + extra_args += ["--merge-base"] + elif staged: + extra_args += ["--cached"] + + cmd = ["git", git_tool, "-p", "-U0"] + extra_args + commits + ["--"] + cmd.extend(files) + p = subprocess.Popen(cmd, stdin=subprocess.PIPE, stdout=subprocess.PIPE) + p.stdin.close() + return p + + +def extract_lines(patch_file, whole_file): + """Extract the changed lines in `patch_file`. + + The return value is a dictionary mapping filename to a list of (start_line, + line_count) pairs. + + The input must have been produced with ``-U0``, meaning unidiff format with + zero lines of context. The return value is a dict mapping filename to a + list of line `Range`s.""" + matches = {} + for line in patch_file: + line = convert_string(line) + match = re.search(r"^\+\+\+\ [^/]+/(.*)", line) + if match: + filename = match.group(1).rstrip("\r\n\t") + if whole_file: + # Initialize the key for filename but ensure ranges is an empty + # list. This will prevent any filtering in clang_format_to_blob(). + matches[filename] = [] + continue + if not whole_file: + match = re.search(r"^@@ -[0-9,]+ \+(\d+)(,(\d+))?", line) + if match: + start_line = int(match.group(1)) + line_count = 1 + if match.group(3): + line_count = int(match.group(3)) + # A hunk that adds no lines is a pure deletion. start_line + # refers to the preceding line (0 when the deletion is at the + # start of a file), which the deletion left alone. + if line_count == 0: + continue + matches.setdefault(filename, []).append(Range(start_line, line_count)) + return matches + + +def filter_by_extension(dictionary, allowed_extensions): + """Delete every key in `dictionary` that doesn't have an allowed extension. + + `allowed_extensions` must be a collection of lowercase file extensions, + excluding the period.""" + allowed_extensions = frozenset(allowed_extensions) + for filename in list(dictionary.keys()): + base_ext = filename.rsplit(".", 1) + if len(base_ext) == 1 and "" in allowed_extensions: + continue + if len(base_ext) == 1 or base_ext[1].lower() not in allowed_extensions: + del dictionary[filename] + + +def filter_symlinks(dictionary): + """Delete every key in `dictionary` that is a symlink.""" + for filename in list(dictionary.keys()): + if os.path.islink(filename): + del dictionary[filename] + + +def filter_ignored_files(dictionary, binary): + """Delete every key in `dictionary` that is ignored by clang-format.""" + ignored_files = run(binary, "-list-ignored", *dictionary.keys()) + if not ignored_files: + return + ignored_files = ignored_files.split("\n") + for filename in ignored_files: + del dictionary[filename] + + +def cd_to_toplevel(): + """Change to the top level of the git repository.""" + toplevel = run("git", "rev-parse", "--show-toplevel") + os.chdir(toplevel) + + +def create_tree_from_workdir(filenames): + """Create a new git tree with the given files from the working directory. + + Returns the object ID (SHA-1) of the created tree.""" + return create_tree(filenames, "--stdin") + + +def create_tree_from_index(filenames): + # Copy the environment, because the files have to be read from the original + # index. + env = os.environ.copy() + + def index_contents_generator(): + for filename in filenames: + git_ls_files_cmd = [ + "git", + "ls-files", + "--stage", + "-z", + "--", + filename, + ] + git_ls_files = subprocess.Popen( + git_ls_files_cmd, + env=env, + stdin=subprocess.PIPE, + stdout=subprocess.PIPE, + ) + stdout = git_ls_files.communicate()[0] + yield convert_string(stdout.split(b"\0")[0]) + + return create_tree(index_contents_generator(), "--index-info") + + +def run_clang_format_and_save_to_tree( + changed_lines, revision=None, binary="clang-format", style=None +): + """Run clang-format on each file and save the result to a git tree. + + Returns the object ID (SHA-1) of the created tree.""" + # Copy the environment when formatting the files in the index, because the + # files have to be read from the original index. + env = os.environ.copy() if revision == "" else None + + def iteritems(container): + try: + return container.iteritems() # Python 2 + except AttributeError: + return container.items() # Python 3 + + def index_info_generator(): + for filename, line_ranges in iteritems(changed_lines): + if revision is not None: + if len(revision) > 0: + git_metadata_cmd = [ + "git", + "ls-tree", + "%s:%s" % (revision, os.path.dirname(filename)), + os.path.basename(filename), + ] + else: + git_metadata_cmd = [ + "git", + "ls-files", + "--stage", + "--", + filename, + ] + git_metadata = subprocess.Popen( + git_metadata_cmd, + env=env, + stdin=subprocess.PIPE, + stdout=subprocess.PIPE, + ) + stdout = git_metadata.communicate()[0] + mode = oct(int(stdout.split()[0], 8)) + else: + mode = oct(os.stat(filename).st_mode) + # Adjust python3 octal format so that it matches what git expects + if mode.startswith("0o"): + mode = "0" + mode[2:] + blob_id = clang_format_to_blob( + filename, + line_ranges, + revision=revision, + binary=binary, + style=style, + env=env, + ) + yield "%s %s\t%s" % (mode, blob_id, filename) + + return create_tree(index_info_generator(), "--index-info") + + +def create_tree(input_lines, mode): + """Create a tree object from the given input. + + If mode is '--stdin', it must be a list of filenames. If mode is + '--index-info' is must be a list of values suitable for "git update-index + --index-info", such as "<mode> <SP> <sha1> <TAB> <filename>". Any other + mode is invalid.""" + assert mode in ("--stdin", "--index-info") + cmd = ["git", "update-index", "--add", "-z", mode] + with temporary_index_file(): + p = subprocess.Popen(cmd, stdin=subprocess.PIPE) + for line in input_lines: + p.stdin.write(to_bytes("%s\0" % line)) + p.stdin.close() + if p.wait() != 0: + die("`%s` failed" % " ".join(cmd)) + tree_id = run("git", "write-tree") + return tree_id + + +def clang_format_to_blob( + filename, + line_ranges, + revision=None, + binary="clang-format", + style=None, + env=None, +): + """Run clang-format on the given file and save the result to a git blob. + + Runs on the file in `revision` if not None, or on the file in the working + directory if `revision` is None. Revision can be set to an empty string to + run clang-format on the file in the index. + + Returns the object ID (SHA-1) of the created blob.""" + clang_format_cmd = [binary] + if style: + clang_format_cmd.extend(["--style=" + style]) + clang_format_cmd.extend( + [ + "--lines=%s:%s" % (start_line, start_line + line_count - 1) + for start_line, line_count in line_ranges + ] + ) + if revision is not None: + clang_format_cmd.extend(["--assume-filename=" + filename]) + git_show_cmd = [ + "git", + "cat-file", + "blob", + "%s:%s" % (revision, filename), + ] + git_show = subprocess.Popen( + git_show_cmd, env=env, stdin=subprocess.PIPE, stdout=subprocess.PIPE + ) + git_show.stdin.close() + clang_format_stdin = git_show.stdout + else: + clang_format_cmd.extend([filename]) + git_show = None + clang_format_stdin = subprocess.PIPE + try: + clang_format = subprocess.Popen( + clang_format_cmd, stdin=clang_format_stdin, stdout=subprocess.PIPE + ) + if clang_format_stdin == subprocess.PIPE: + clang_format_stdin = clang_format.stdin + except OSError as e: + if e.errno == errno.ENOENT: + die('cannot find executable "%s"' % binary) + else: + raise + clang_format_stdin.close() + hash_object_cmd = [ + "git", + "hash-object", + "-w", + "--path=" + filename, + "--stdin", + ] + hash_object = subprocess.Popen( + hash_object_cmd, stdin=clang_format.stdout, stdout=subprocess.PIPE + ) + clang_format.stdout.close() + stdout = hash_object.communicate()[0] + if hash_object.returncode != 0: + die("`%s` failed" % " ".join(hash_object_cmd)) + if clang_format.wait() != 0: + die("`%s` failed" % " ".join(clang_format_cmd)) + if git_show and git_show.wait() != 0: + die("`%s` failed" % " ".join(git_show_cmd)) + return convert_string(stdout).rstrip("\r\n") + + +@contextlib.contextmanager +def temporary_index_file(tree=None): + """Context manager for setting GIT_INDEX_FILE to a temporary file and + deleting the file afterward.""" + index_path = create_temporary_index(tree) + old_index_path = os.environ.get("GIT_INDEX_FILE") + os.environ["GIT_INDEX_FILE"] = index_path + try: + yield + finally: + if old_index_path is None: + del os.environ["GIT_INDEX_FILE"] + else: + os.environ["GIT_INDEX_FILE"] = old_index_path + os.remove(index_path) + + +def create_temporary_index(tree=None): + """Create a temporary index file and return the created file's path. + + If `tree` is not None, use that as the tree to read in. Otherwise, an + empty index is created.""" + gitdir = run("git", "rev-parse", "--git-dir") + # Use a unique filename to avoid stale .lock files when concurrent git + # operations (e.g. pre-commit hooks) race with this process. + fd, path = tempfile.mkstemp(prefix=temp_index_basename + "-", dir=gitdir) + os.close(fd) + if tree is None: + tree = "--empty" + run("git", "read-tree", "--index-output=" + path, tree) + return path + + +def print_diff(old_tree, new_tree): + """Print the diff between the two trees to stdout.""" + # We use the porcelain 'diff' and not plumbing 'diff-tree' because the + # output is expected to be viewed by the user, and only the former does nice + # things like color and pagination. + # + # We also only print modified files since `new_tree` only contains the files + # that were modified, so unmodified files would show as deleted without the + # filter. + return subprocess.run( + ["git", "diff", "--diff-filter=M", "--exit-code", old_tree, new_tree] + ).returncode + + +def print_diffstat(old_tree, new_tree): + """Print the diffstat between the two trees to stdout.""" + # We use the porcelain 'diff' and not plumbing 'diff-tree' because the + # output is expected to be viewed by the user, and only the former does nice + # things like color and pagination. + # + # We also only print modified files since `new_tree` only contains the files + # that were modified, so unmodified files would show as deleted without the + # filter. + return subprocess.run( + [ + "git", + "diff", + "--diff-filter=M", + "--exit-code", + "--stat", + old_tree, + new_tree, + ] + ).returncode + + +def apply_changes(old_tree, new_tree, force=False, patch_mode=False): + """Apply the changes in `new_tree` to the working directory. + + Bails if there are local changes in those files and not `force`. If + `patch_mode`, runs `git checkout --patch` to select hunks interactively.""" + changed_files = ( + run( + "git", + "diff-tree", + "--diff-filter=M", + "-r", + "-z", + "--name-only", + old_tree, + new_tree, + ) + .rstrip("\0") + .split("\0") + ) + if not force: + unstaged_files = run("git", "diff-files", "--name-status", *changed_files) + if unstaged_files: + print( + "The following files would be modified but have unstaged changes:", + file=sys.stderr, + ) + print(unstaged_files, file=sys.stderr) + print("Please commit, stage, or stash them first.", file=sys.stderr) + sys.exit(2) + if patch_mode: + # In patch mode, we could just as well create an index from the new tree + # and checkout from that, but then the user will be presented with a + # message saying "Discard ... from worktree". Instead, we use the old + # tree as the index and checkout from new_tree, which gives the slightly + # better message, "Apply ... to index and worktree". This is not quite + # right, since it won't be applied to the user's index, but oh well. + with temporary_index_file(old_tree): + subprocess.run(["git", "checkout", "--patch", new_tree], check=True) + index_tree = old_tree + else: + with temporary_index_file(new_tree): + run("git", "checkout-index", "-f", "--", *changed_files) + return changed_files + + +def run(*args, **kwargs): + stdin = kwargs.pop("stdin", "") + verbose = kwargs.pop("verbose", True) + strip = kwargs.pop("strip", True) + for name in kwargs: + raise TypeError("run() got an unexpected keyword argument '%s'" % name) + p = subprocess.Popen( + args, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + stdin=subprocess.PIPE, + ) + stdout, stderr = p.communicate(input=stdin) + + stdout = convert_string(stdout) + stderr = convert_string(stderr) + + if p.returncode == 0: + if stderr: + if verbose: + print("`%s` printed to stderr:" % " ".join(args), file=sys.stderr) + print(stderr.rstrip(), file=sys.stderr) + if strip: + stdout = stdout.rstrip("\r\n") + return stdout + if verbose: + print("`%s` returned %s" % (" ".join(args), p.returncode), file=sys.stderr) + if stderr: + print(stderr.rstrip(), file=sys.stderr) + sys.exit(2) + + +def die(message): + print("error:", message, file=sys.stderr) + sys.exit(2) + + +def to_bytes(str_input): + # Encode to UTF-8 to get binary data. + if isinstance(str_input, bytes): + return str_input + return str_input.encode("utf-8") + + +def to_string(bytes_input): + if isinstance(bytes_input, str): + return bytes_input + return bytes_input.encode("utf-8") + + +def convert_string(bytes_input): + try: + return to_string(bytes_input.decode("utf-8")) + except AttributeError: # 'str' object has no attribute 'decode'. + return str(bytes_input) + except UnicodeError: + return str(bytes_input) + + +if __name__ == "__main__": + sys.exit(main()) diff --git a/rcc/utils/clang-tidy/clang-tidy-diff.py b/rcc/utils/clang-tidy/clang-tidy-diff.py new file mode 100755 index 0000000..059b2a2 --- /dev/null +++ b/rcc/utils/clang-tidy/clang-tidy-diff.py @@ -0,0 +1,442 @@ +#!/usr/bin/env python3 +# +# ===-----------------------------------------------------------------------===# +# +# Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +# See https://llvm.org/LICENSE.txt for license information. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +# +# ===-----------------------------------------------------------------------===# + +r""" +ClangTidy Diff Checker +====================== + +This script reads input from a unified diff, runs clang-tidy on all changed +files and outputs clang-tidy warnings in changed lines only. This is useful to +detect clang-tidy regressions in the lines touched by a specific patch. +Example usage for git/svn users: + + git diff -U0 HEAD^ | clang-tidy-diff.py -p1 + svn diff --diff-cmd=diff -x-U0 | \ + clang-tidy-diff.py -fix -checks=-*,modernize-use-override + +""" + +import argparse +import glob +import json +import multiprocessing +import os +import queue +import re +import shutil +import subprocess +import sys +import tempfile +import threading +import traceback +from pathlib import Path + +try: + import yaml +except ImportError: + yaml = None + + +def run_tidy(task_queue, lock, timeout, failed_files): + watchdog = None + while True: + command = task_queue.get() + try: + proc = subprocess.Popen( + command, stdout=subprocess.PIPE, stderr=subprocess.PIPE + ) + + if timeout is not None: + watchdog = threading.Timer(timeout, proc.kill) + watchdog.start() + + stdout, stderr = proc.communicate() + if proc.returncode != 0: + if proc.returncode < 0: + msg = "Terminated by signal %d : %s\n" % ( + -proc.returncode, + " ".join(command), + ) + stderr += msg.encode("utf-8") + failed_files.append(command) + + with lock: + if stdout: + sys.stdout.write(stdout.decode("utf-8") + "\n") + sys.stdout.flush() + if stderr: + sys.stderr.write(stderr.decode("utf-8") + "\n") + sys.stderr.flush() + except Exception as e: + with lock: + sys.stderr.write("Failed: " + str(e) + ": ".join(command) + "\n") + finally: + with lock: + if not (timeout is None or watchdog is None): + if not watchdog.is_alive(): + sys.stderr.write( + "Terminated by timeout: " + " ".join(command) + "\n" + ) + watchdog.cancel() + task_queue.task_done() + + +def start_workers(max_tasks, tidy_caller, arguments): + for _ in range(max_tasks): + t = threading.Thread(target=tidy_caller, args=arguments) + t.daemon = True + t.start() + + +def merge_replacement_files(tmpdir, mergefile): + """Merge all replacement files in a directory into a single file""" + # The fixes suggested by clang-tidy >= 4.0.0 are given under + # the top level key 'Diagnostics' in the output yaml files + mergekey = "Diagnostics" + merged = [] + for replacefile in glob.iglob(os.path.join(tmpdir, "*.yaml")): + content = yaml.safe_load(open(replacefile, "r")) + if not content: + continue # Skip empty files. + merged.extend(content.get(mergekey, [])) + + if merged: + # MainSourceFile: The key is required by the definition inside + # include/clang/Tooling/ReplacementsYaml.h, but the value + # is actually never used inside clang-apply-replacements, + # so we set it to '' here. + output = {"MainSourceFile": "", mergekey: merged} + with open(mergefile, "w") as out: + yaml.safe_dump(output, out) + else: + # Empty the file: + open(mergefile, "w").close() + + +def get_compiling_files(args): + """Read a compile_commands.json database and return a set of file paths""" + current_dir = Path.cwd() + compile_commands_json = ( + (current_dir / args.build_path) if args.build_path else current_dir + ) + compile_commands_json = compile_commands_json / "compile_commands.json" + files = set() + with open(compile_commands_json) as db_file: + db_json = json.load(db_file) + for entry in db_json: + if "file" not in entry: + continue + files.add(Path(entry["file"])) + return files + + +def main(): + parser = argparse.ArgumentParser( + description="Run clang-tidy against changed files, and " + "output diagnostics only for modified " + "lines." + ) + parser.add_argument( + "-clang-tidy-binary", + metavar="PATH", + default="clang-tidy", + help="path to clang-tidy binary", + ) + parser.add_argument( + "-p", + metavar="NUM", + default=0, + help="strip the smallest prefix containing P slashes", + ) + parser.add_argument( + "-regex", + metavar="PATTERN", + default=None, + help="custom pattern selecting file paths to check " + "(case sensitive, overrides -iregex)", + ) + parser.add_argument( + "-iregex", + metavar="PATTERN", + default=r".*\.(cpp|cc|c\+\+|cxx|c|cl|h|hpp|m|mm|inc)", + help="custom pattern selecting file paths to check " + "(case insensitive, overridden by -regex)", + ) + parser.add_argument( + "-j", + type=int, + default=0, + help="number of tidy instances to be run in parallel.", + ) + parser.add_argument( + "-timeout", type=int, default=None, help="timeout per each file in seconds." + ) + parser.add_argument( + "-fix", action="store_true", default=False, help="apply suggested fixes" + ) + parser.add_argument( + "-checks", + help="checks filter, when not specified, use clang-tidy " "default", + default="", + ) + parser.add_argument( + "-config-file", + dest="config_file", + help="Specify the path of .clang-tidy or custom config file", + default="", + ) + parser.add_argument("-use-color", action="store_true", help="Use colors in output") + parser.add_argument( + "-path", dest="build_path", help="Path used to read a compile command database." + ) + if yaml: + parser.add_argument( + "-export-fixes", + metavar="FILE_OR_DIRECTORY", + dest="export_fixes", + help="A directory or a yaml file to store suggested fixes in, " + "which can be applied with clang-apply-replacements. If the " + "parameter is a directory, the fixes of each compilation unit are " + "stored in individual yaml files in the directory.", + ) + else: + parser.add_argument( + "-export-fixes", + metavar="DIRECTORY", + dest="export_fixes", + help="A directory to store suggested fixes in, which can be applied " + "with clang-apply-replacements. The fixes of each compilation unit are " + "stored in individual yaml files in the directory.", + ) + parser.add_argument( + "-extra-arg", + dest="extra_arg", + action="append", + default=[], + help="Additional argument to append to the compiler " "command line.", + ) + parser.add_argument( + "-extra-arg-before", + dest="extra_arg_before", + action="append", + default=[], + help="Additional argument to prepend to the compiler " "command line.", + ) + parser.add_argument( + "-removed-arg", + dest="removed_arg", + action="append", + default=[], + help="Arguments to remove from the compiler command line.", + ) + parser.add_argument( + "-quiet", + action="store_true", + default=False, + help="Run clang-tidy in quiet mode", + ) + parser.add_argument( + "-load", + dest="plugins", + action="append", + default=[], + help="Load the specified plugin in clang-tidy.", + ) + parser.add_argument( + "-allow-no-checks", + action="store_true", + help="Allow empty enabled checks.", + ) + parser.add_argument( + "-only-check-in-db", + dest="skip_non_compiling", + default=False, + action="store_true", + help="Only check files in the compilation database", + ) + parser.add_argument( + "-warnings-as-errors", + help="Upgrades clang-tidy warnings to errors. Same format as '-checks'.", + default="", + ) + parser.add_argument( + "-hide-progress", + action="store_true", + help="Hide progress", + ) + + clang_tidy_args = [] + argv = sys.argv[1:] + if "--" in argv: + clang_tidy_args.extend(argv[argv.index("--") :]) + argv = argv[: argv.index("--")] + + args = parser.parse_args(argv) + + compiling_files = get_compiling_files(args) if args.skip_non_compiling else None + + # Extract changed lines for each file. + filename = None + lines_by_file = {} + for line in sys.stdin: + match = re.search(r'^\+\+\+\ "?(.*?/){%s}([^ \t\n"]*)' % args.p, line) + if match: + filename = match.group(2) + if filename is None: + continue + + if args.regex is not None: + if not re.match("^%s$" % args.regex, filename): + continue + else: + if not re.match("^%s$" % args.iregex, filename, re.IGNORECASE): + continue + + # Skip any files not in the compiling list + if ( + compiling_files is not None + and (Path.cwd() / filename) not in compiling_files + ): + continue + + match = re.search(r"^@@.*\+(\d+)(,(\d+))?", line) + if match: + start_line = int(match.group(1)) + line_count = 1 + if match.group(3): + line_count = int(match.group(3)) + if line_count == 0: + continue + end_line = start_line + line_count - 1 + lines_by_file.setdefault(filename, []).append([start_line, end_line]) + + if not any(lines_by_file): + print("No relevant changes found.") + sys.exit(0) + + max_task_count = args.j + if max_task_count == 0: + max_task_count = multiprocessing.cpu_count() + max_task_count = min(len(lines_by_file), max_task_count) + if not args.hide_progress: + print(f"Running clang-tidy in {max_task_count} threads...") + + combine_fixes = False + export_fixes_dir = None + delete_fixes_dir = False + if args.export_fixes is not None: + # if a directory is given, create it if it does not exist + if args.export_fixes.endswith(os.path.sep) and not os.path.isdir( + args.export_fixes + ): + os.makedirs(args.export_fixes) + + if not os.path.isdir(args.export_fixes): + if not yaml: + raise RuntimeError( + "Cannot combine fixes in one yaml file. Either install PyYAML or specify an output directory." + ) + + combine_fixes = True + + if os.path.isdir(args.export_fixes): + export_fixes_dir = args.export_fixes + + if combine_fixes: + export_fixes_dir = tempfile.mkdtemp() + delete_fixes_dir = True + + # Tasks for clang-tidy. + task_queue = queue.Queue(max_task_count) + # A lock for console output. + lock = threading.Lock() + + # List of files with a non-zero return code. + failed_files = [] + + # Run a pool of clang-tidy workers. + start_workers( + max_task_count, run_tidy, (task_queue, lock, args.timeout, failed_files) + ) + + # Form the common args list. + common_clang_tidy_args = [] + if args.fix: + common_clang_tidy_args.append("-fix") + if args.checks != "": + common_clang_tidy_args.append("-checks=" + args.checks) + if args.config_file != "": + common_clang_tidy_args.append("-config-file=" + args.config_file) + if args.quiet: + common_clang_tidy_args.append("-quiet") + if args.build_path is not None: + common_clang_tidy_args.append("-p=%s" % args.build_path) + if args.use_color: + common_clang_tidy_args.append("--use-color") + if args.allow_no_checks: + common_clang_tidy_args.append("--allow-no-checks") + for arg in args.extra_arg: + common_clang_tidy_args.append("-extra-arg=%s" % arg) + for arg in args.extra_arg_before: + common_clang_tidy_args.append("-extra-arg-before=%s" % arg) + for arg in args.removed_arg: + common_clang_tidy_args.append("-removed-arg=%s" % arg) + for plugin in args.plugins: + common_clang_tidy_args.append("-load=%s" % plugin) + if args.warnings_as_errors: + common_clang_tidy_args.append("-warnings-as-errors=" + args.warnings_as_errors) + + for name in lines_by_file: + line_filter_json = json.dumps( + [{"name": name, "lines": lines_by_file[name]}], separators=(",", ":") + ) + + # Run clang-tidy on files containing changes. + command = [args.clang_tidy_binary] + command.append("-line-filter=" + line_filter_json) + if args.export_fixes is not None: + # Get a temporary file. We immediately close the handle so clang-tidy can + # overwrite it. + (handle, tmp_name) = tempfile.mkstemp(suffix=".yaml", dir=export_fixes_dir) + os.close(handle) + command.append("-export-fixes=" + tmp_name) + command.extend(common_clang_tidy_args) + command.append(name) + command.extend(clang_tidy_args) + + task_queue.put(command) + + # Application return code + return_code = 0 + + # Wait for all threads to be done. + task_queue.join() + # Application return code + return_code = 0 + if failed_files: + return_code = 1 + + if combine_fixes: + if not args.hide_progress: + print(f"Writing fixes to {args.export_fixes} ...") + try: + merge_replacement_files(export_fixes_dir, args.export_fixes) + except Exception: + sys.stderr.write("Error exporting fixes.\n") + traceback.print_exc() + return_code = 1 + + if delete_fixes_dir: + shutil.rmtree(export_fixes_dir) + sys.exit(return_code) + + +if __name__ == "__main__": + main() diff --git a/rcc/utils/clang-tidy/run-clang-tidy.py b/rcc/utils/clang-tidy/run-clang-tidy.py new file mode 100755 index 0000000..f827ef4 --- /dev/null +++ b/rcc/utils/clang-tidy/run-clang-tidy.py @@ -0,0 +1,796 @@ +#!/usr/bin/env python3 +# +# ===-----------------------------------------------------------------------===# +# +# Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +# See https://llvm.org/LICENSE.txt for license information. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +# +# ===-----------------------------------------------------------------------===# +# FIXME: Integrate with clang-tidy-diff.py + + +""" +Parallel clang-tidy runner +========================== + +Runs clang-tidy over all files in a compilation database. Requires clang-tidy +and clang-apply-replacements in $PATH. + +Example invocations. +- Run clang-tidy on all files in the current working directory with a default + set of checks and show warnings in the cpp files and all project headers. + run-clang-tidy.py $PWD + +- Fix all header guards. + run-clang-tidy.py -fix -checks=-*,llvm-header-guard + +- Fix all header guards included from clang-tidy and header guards + for clang-tidy headers. + run-clang-tidy.py -fix -checks=-*,llvm-header-guard extra/clang-tidy \ + -header-filter=extra/clang-tidy + +Compilation database setup: +https://clang.llvm.org/docs/HowToSetupToolingForLLVM.html +""" + +import argparse +import asyncio +from dataclasses import dataclass +import glob +import json +import multiprocessing +import os +import re +import shlex +import shutil +import subprocess +import sys +import tempfile +import time +import traceback +from types import ModuleType +from typing import Any, Awaitable, Callable, Dict, List, Optional, Tuple, TypeVar + + +yaml: Optional[ModuleType] = None +try: + import yaml +except ImportError: + yaml = None + + +def strtobool(val: str) -> bool: + """Convert a string representation of truth to a bool following LLVM's CLI argument parsing.""" + + val = val.lower() + if val in ["", "true", "1"]: + return True + elif val in ["false", "0"]: + return False + + # Return ArgumentTypeError so that argparse does not substitute its own error message + raise argparse.ArgumentTypeError( + f"'{val}' is invalid value for boolean argument! Try 0 or 1." + ) + + +def find_compilation_database(path: str) -> str: + """Adjusts the directory until a compilation database is found.""" + result = os.path.realpath("./") + while not os.path.isfile(os.path.join(result, path)): + parent = os.path.dirname(result) + if result == parent: + print("Error: could not find compilation database.") + sys.exit(1) + result = parent + return result + + +def get_tidy_invocation( + f: Optional[str], + clang_tidy_binary: str, + checks: str, + tmpdir: Optional[str], + build_path: str, + header_filter: Optional[str], + allow_enabling_alpha_checkers: bool, + extra_arg: List[str], + extra_arg_before: List[str], + removed_arg: List[str], + quiet: bool, + config_file_path: str, + config: str, + line_filter: Optional[str], + use_color: bool, + plugins: List[str], + warnings_as_errors: Optional[str], + exclude_header_filter: Optional[str], + allow_no_checks: bool, + store_check_profile: Optional[str], +) -> List[str]: + """Gets a command line for clang-tidy.""" + start = [clang_tidy_binary] + if allow_enabling_alpha_checkers: + start.append("-allow-enabling-analyzer-alpha-checkers") + if exclude_header_filter is not None: + start.append(f"--exclude-header-filter={exclude_header_filter}") + if header_filter is not None: + start.append(f"-header-filter={header_filter}") + if line_filter is not None: + start.append(f"-line-filter={line_filter}") + if use_color is not None: + if use_color: + start.append("--use-color") + else: + start.append("--use-color=false") + if checks: + start.append(f"-checks={checks}") + if tmpdir is not None: + start.append("-export-fixes") + # Get a temporary file. We immediately close the handle so clang-tidy can + # overwrite it. + (handle, name) = tempfile.mkstemp(suffix=".yaml", dir=tmpdir) + os.close(handle) + start.append(name) + for arg in extra_arg: + start.append(f"-extra-arg={arg}") + for arg in extra_arg_before: + start.append(f"-extra-arg-before={arg}") + for arg in removed_arg: + start.append(f"-removed-arg={arg}") + start.append(f"-p={build_path}") + if quiet: + start.append("-quiet") + if config_file_path: + start.append(f"--config-file={config_file_path}") + elif config: + start.append(f"-config={config}") + for plugin in plugins: + start.append(f"-load={plugin}") + if warnings_as_errors: + start.append(f"--warnings-as-errors={warnings_as_errors}") + if allow_no_checks: + start.append("--allow-no-checks") + if store_check_profile: + start.append("--enable-check-profile") + start.append(f"--store-check-profile={store_check_profile}") + if f: + start.append(f) + return start + + +def merge_replacement_files(tmpdir: str, mergefile: str) -> None: + """Merge all replacement files in a directory into a single file""" + assert yaml + # The fixes suggested by clang-tidy >= 4.0.0 are given under + # the top level key 'Diagnostics' in the output yaml files + mergekey = "Diagnostics" + merged = [] + for replacefile in glob.iglob(os.path.join(tmpdir, "*.yaml")): + content = yaml.safe_load(open(replacefile, "r")) + if not content: + continue # Skip empty files. + merged.extend(content.get(mergekey, [])) + + if merged: + # MainSourceFile: The key is required by the definition inside + # include/clang/Tooling/ReplacementsYaml.h, but the value + # is actually never used inside clang-apply-replacements, + # so we set it to '' here. + output = {"MainSourceFile": "", mergekey: merged} + with open(mergefile, "w") as out: + yaml.safe_dump(output, out) + else: + # Empty the file: + open(mergefile, "w").close() + + +def aggregate_profiles(profile_dir: str) -> Dict[str, float]: + """Aggregate timing data from multiple profile JSON files""" + aggregated: Dict[str, float] = {} + + for profile_file in glob.iglob(os.path.join(profile_dir, "*.json")): + try: + with open(profile_file, "r", encoding="utf-8") as f: + data = json.load(f) + profile_data: Dict[str, float] = data.get("profile", {}) + + for key, value in profile_data.items(): + if key.startswith("time.clang-tidy."): + if key in aggregated: + aggregated[key] += value + else: + aggregated[key] = value + except (json.JSONDecodeError, KeyError, IOError) as e: + print(f"Error: invalid json file {profile_file}: {e}", file=sys.stderr) + continue + + return aggregated + + +def print_profile_data(aggregated_data: Dict[str, float]) -> None: + """Print aggregated checks profile data in the same format as clang-tidy""" + if not aggregated_data: + return + + # Extract checker names and their timing data + checkers: Dict[str, Dict[str, float]] = {} + for key, value in aggregated_data.items(): + parts = key.split(".") + if len(parts) >= 4 and parts[0] == "time" and parts[1] == "clang-tidy": + checker_name = ".".join( + parts[2:-1] + ) # Everything between "clang-tidy" and the timing type + timing_type = parts[-1] # wall, user, or sys + + if checker_name not in checkers: + checkers[checker_name] = {"wall": 0.0, "user": 0.0, "sys": 0.0} + + checkers[checker_name][timing_type] = value + + if not checkers: + return + + total_user = sum(data["user"] for data in checkers.values()) + total_sys = sum(data["sys"] for data in checkers.values()) + total_wall = sum(data["wall"] for data in checkers.values()) + + sorted_checkers: List[Tuple[str, Dict[str, float]]] = sorted( + checkers.items(), key=lambda x: x[1]["user"] + x[1]["sys"], reverse=True + ) + + def print_stderr(*args: Any, **kwargs: Any) -> None: + print(*args, file=sys.stderr, **kwargs) + + print_stderr( + "===-------------------------------------------------------------------------===" + ) + print_stderr(" clang-tidy checks profiling") + print_stderr( + "===-------------------------------------------------------------------------===" + ) + print_stderr( + f" Total Execution Time: {total_user + total_sys:.4f} seconds ({total_wall:.4f} wall clock)\n" + ) + + # Calculate field widths based on the Total line which has the largest values + total_combined = total_user + total_sys + user_width = len(f"{total_user:.4f}") + sys_width = len(f"{total_sys:.4f}") + combined_width = len(f"{total_combined:.4f}") + wall_width = len(f"{total_wall:.4f}") + + # Header with proper alignment + additional_width = 9 # for " (100.0%)" + user_header = "---User Time---".center(user_width + additional_width) + sys_header = "--System Time--".center(sys_width + additional_width) + combined_header = "--User+System--".center(combined_width + additional_width) + wall_header = "---Wall Time---".center(wall_width + additional_width) + + print_stderr( + f" {user_header} {sys_header} {combined_header} {wall_header} --- Name ---" + ) + + for checker_name, data in sorted_checkers: + user_time = data["user"] + sys_time = data["sys"] + wall_time = data["wall"] + combined_time = user_time + sys_time + + user_percent = (user_time / total_user * 100) if total_user > 0 else 0 + sys_percent = (sys_time / total_sys * 100) if total_sys > 0 else 0 + combined_percent = ( + (combined_time / total_combined * 100) if total_combined > 0 else 0 + ) + wall_percent = (wall_time / total_wall * 100) if total_wall > 0 else 0 + + user_str = f"{user_time:{user_width}.4f} ({user_percent:5.1f}%)" + sys_str = f"{sys_time:{sys_width}.4f} ({sys_percent:5.1f}%)" + combined_str = f"{combined_time:{combined_width}.4f} ({combined_percent:5.1f}%)" + wall_str = f"{wall_time:{wall_width}.4f} ({wall_percent:5.1f}%)" + + print_stderr( + f" {user_str} {sys_str} {combined_str} {wall_str} {checker_name}" + ) + + user_total_str = f"{total_user:{user_width}.4f} (100.0%)" + sys_total_str = f"{total_sys:{sys_width}.4f} (100.0%)" + combined_total_str = f"{total_combined:{combined_width}.4f} (100.0%)" + wall_total_str = f"{total_wall:{wall_width}.4f} (100.0%)" + + print_stderr( + f" {user_total_str} {sys_total_str} {combined_total_str} {wall_total_str} Total" + ) + + +def find_binary(arg: str, name: str, build_path: str) -> str: + """Get the path for a binary or exit""" + if arg: + if shutil.which(arg): + return arg + else: + raise SystemExit( + f"error: passed binary '{arg}' was not found or is not executable" + ) + + built_path = os.path.join(build_path, "bin", name) + binary = shutil.which(name) or shutil.which(built_path) + if binary: + return binary + else: + raise SystemExit(f"error: failed to find {name} in $PATH or at {built_path}") + + +def apply_fixes( + args: argparse.Namespace, clang_apply_replacements_binary: str, tmpdir: str +) -> None: + """Calls clang-apply-fixes on a given directory.""" + invocation = [clang_apply_replacements_binary] + invocation.append("-ignore-insert-conflict") + if args.format: + invocation.append("-format") + if args.style: + invocation.append(f"-style={args.style}") + invocation.append(tmpdir) + subprocess.call(invocation) + + +# FIXME Python 3.12: This can be simplified out with run_with_semaphore[T](...). +T = TypeVar("T") + + +async def run_with_semaphore( + semaphore: asyncio.Semaphore, + f: Callable[..., Awaitable[T]], + *args: Any, + **kwargs: Any, +) -> T: + async with semaphore: + return await f(*args, **kwargs) + + +@dataclass +class ClangTidyResult: + filename: str + invocation: List[str] + returncode: int + stdout: str + stderr: str + elapsed: float + + +async def run_tidy( + args: argparse.Namespace, + name: str, + clang_tidy_binary: str, + tmpdir: str, + build_path: str, + store_check_profile: Optional[str], +) -> ClangTidyResult: + """ + Runs clang-tidy on a single file and returns the result. + """ + invocation = get_tidy_invocation( + name, + clang_tidy_binary, + args.checks, + tmpdir, + build_path, + args.header_filter, + args.allow_enabling_alpha_checkers, + args.extra_arg, + args.extra_arg_before, + args.removed_arg, + args.quiet, + args.config_file, + args.config, + args.line_filter, + args.use_color, + args.plugins, + args.warnings_as_errors, + args.exclude_header_filter, + args.allow_no_checks, + store_check_profile, + ) + + try: + process = await asyncio.create_subprocess_exec( + *invocation, stdout=subprocess.PIPE, stderr=subprocess.PIPE + ) + start = time.time() + stdout, stderr = await process.communicate() + end = time.time() + except asyncio.CancelledError: + process.terminate() + await process.wait() + raise + + assert process.returncode is not None + return ClangTidyResult( + name, + invocation, + process.returncode, + stdout.decode("UTF-8"), + stderr.decode("UTF-8"), + end - start, + ) + + +async def main() -> None: + parser = argparse.ArgumentParser( + description="Runs clang-tidy over all files " + "in a compilation database. Requires " + "clang-tidy and clang-apply-replacements in " + "$PATH or in your build directory." + ) + parser.add_argument( + "-allow-enabling-alpha-checkers", + action="store_true", + help="Allow alpha checkers from clang-analyzer.", + ) + parser.add_argument( + "-clang-tidy-binary", metavar="PATH", help="Path to clang-tidy binary." + ) + parser.add_argument( + "-clang-apply-replacements-binary", + metavar="PATH", + help="Path to clang-apply-replacements binary.", + ) + parser.add_argument( + "-checks", + default=None, + help="Checks filter, when not specified, use clang-tidy default.", + ) + config_group = parser.add_mutually_exclusive_group() + config_group.add_argument( + "-config", + default=None, + help="Specifies a configuration in YAML/JSON format: " + " -config=\"{Checks: '*', " + ' CheckOptions: {x: y}}" ' + "When the value is empty, clang-tidy will " + "attempt to find a file named .clang-tidy for " + "each source file in its parent directories.", + ) + config_group.add_argument( + "-config-file", + default=None, + help="Specify the path of .clang-tidy or custom config " + "file: e.g. -config-file=/some/path/myTidyConfigFile. " + "This option internally works exactly the same way as " + "-config option after reading specified config file. " + "Use either -config-file or -config, not both.", + ) + parser.add_argument( + "-exclude-header-filter", + default=None, + help="Regular expression matching the names of the " + "headers to exclude diagnostics from. Diagnostics from " + "the main file of each translation unit are always " + "displayed.", + ) + parser.add_argument( + "-header-filter", + default=None, + help="Regular expression matching the names of the " + "headers to output diagnostics from. Diagnostics from " + "the main file of each translation unit are always " + "displayed.", + ) + parser.add_argument( + "-source-filter", + default=None, + help="Regular expression matching the names of the " + "source files from compilation database to output " + "diagnostics from.", + ) + parser.add_argument( + "-line-filter", + default=None, + help="List of files and line ranges to output diagnostics from.", + ) + if yaml: + parser.add_argument( + "-export-fixes", + metavar="file_or_directory", + dest="export_fixes", + help="A directory or a yaml file to store suggested fixes in, " + "which can be applied with clang-apply-replacements. If the " + "parameter is a directory, the fixes of each compilation unit are " + "stored in individual yaml files in the directory.", + ) + else: + parser.add_argument( + "-export-fixes", + metavar="directory", + dest="export_fixes", + help="A directory to store suggested fixes in, which can be applied " + "with clang-apply-replacements. The fixes of each compilation unit are " + "stored in individual yaml files in the directory.", + ) + parser.add_argument( + "-j", + type=int, + default=0, + help="Number of tidy instances to be run in parallel.", + ) + parser.add_argument( + "files", + nargs="*", + default=[".*"], + help="Files to be processed (regex on path).", + ) + parser.add_argument("-fix", action="store_true", help="apply fix-its.") + parser.add_argument( + "-format", action="store_true", help="Reformat code after applying fixes." + ) + parser.add_argument( + "-style", + default="file", + help="The style of reformat code after applying fixes.", + ) + parser.add_argument( + "-use-color", + type=strtobool, + nargs="?", + const=True, + help="Use colors in diagnostics, overriding clang-tidy's" + " default behavior. This option overrides the 'UseColor" + "' option in .clang-tidy file, if any.", + ) + parser.add_argument( + "-p", dest="build_path", help="Path used to read a compile command database." + ) + parser.add_argument( + "-extra-arg", + dest="extra_arg", + action="append", + default=[], + help="Additional argument to append to the compiler command line.", + ) + parser.add_argument( + "-extra-arg-before", + dest="extra_arg_before", + action="append", + default=[], + help="Additional argument to prepend to the compiler command line.", + ) + parser.add_argument( + "-removed-arg", + dest="removed_arg", + action="append", + default=[], + help="Arguments to remove from the compiler command line.", + ) + parser.add_argument( + "-quiet", action="store_true", help="Run clang-tidy in quiet mode." + ) + parser.add_argument( + "-load", + dest="plugins", + action="append", + default=[], + help="Load the specified plugin in clang-tidy.", + ) + parser.add_argument( + "-warnings-as-errors", + default=None, + help="Upgrades warnings to errors. Same format as '-checks'.", + ) + parser.add_argument( + "-allow-no-checks", + action="store_true", + help="Allow empty enabled checks.", + ) + parser.add_argument( + "-enable-check-profile", + action="store_true", + help="Enable per-check timing profiles, and print a report", + ) + parser.add_argument( + "-hide-progress", + action="store_true", + help="Hide progress", + ) + args = parser.parse_args() + + db_path = "compile_commands.json" + + if args.build_path is not None: + build_path = args.build_path + else: + # Find our database + build_path = find_compilation_database(db_path) + + clang_tidy_binary = find_binary(args.clang_tidy_binary, "clang-tidy", build_path) + + if args.fix: + clang_apply_replacements_binary = find_binary( + args.clang_apply_replacements_binary, "clang-apply-replacements", build_path + ) + + combine_fixes = False + export_fixes_dir: Optional[str] = None + delete_fixes_dir = False + if args.export_fixes is not None: + # if a directory is given, create it if it does not exist + if args.export_fixes.endswith(os.path.sep) and not os.path.isdir( + args.export_fixes + ): + os.makedirs(args.export_fixes) + + if not os.path.isdir(args.export_fixes): + if not yaml: + raise RuntimeError( + "Cannot combine fixes in one yaml file. Either install PyYAML or specify an output directory." + ) + + combine_fixes = True + + if os.path.isdir(args.export_fixes): + export_fixes_dir = args.export_fixes + + if export_fixes_dir is None and (args.fix or combine_fixes): + export_fixes_dir = tempfile.mkdtemp() + delete_fixes_dir = True + + profile_dir: Optional[str] = None + if args.enable_check_profile: + profile_dir = tempfile.mkdtemp() + + try: + invocation = get_tidy_invocation( + None, + clang_tidy_binary, + args.checks, + None, + build_path, + args.header_filter, + args.allow_enabling_alpha_checkers, + args.extra_arg, + args.extra_arg_before, + args.removed_arg, + args.quiet, + args.config_file, + args.config, + args.line_filter, + args.use_color, + args.plugins, + args.warnings_as_errors, + args.exclude_header_filter, + args.allow_no_checks, + None, # No profiling for the list-checks invocation + ) + invocation.append("-list-checks") + invocation.append("-") + # Even with -quiet we still want to check if we can call clang-tidy. + subprocess.check_call( + invocation, stdout=subprocess.DEVNULL if args.quiet else None + ) + except Exception: + print("Unable to run clang-tidy.", file=sys.stderr) + sys.exit(1) + + # Load the database and extract all files. + with open(os.path.join(build_path, db_path)) as f: + database = json.load(f) + files = {os.path.abspath(os.path.join(e["directory"], e["file"])) for e in database} + number_files_in_database = len(files) + + # Filter source files from compilation database. + if args.source_filter: + try: + source_filter_re = re.compile(args.source_filter) + except Exception: + print( + "Error: unable to compile regex from arg -source-filter:", + file=sys.stderr, + ) + traceback.print_exc() + sys.exit(1) + files = {f for f in files if source_filter_re.match(f)} + + max_task = args.j + if max_task == 0: + max_task = multiprocessing.cpu_count() + + # Build up a big regexy filter from all command line arguments. + file_name_re = re.compile("|".join(args.files)) + files = {f for f in files if file_name_re.search(f)} + + if not args.hide_progress: + print( + f"Running clang-tidy in {max_task} threads for {len(files)} files " + f"out of {number_files_in_database} in compilation database ..." + ) + + returncode = 0 + semaphore = asyncio.Semaphore(max_task) + tasks = [ + asyncio.create_task( + run_with_semaphore( + semaphore, + run_tidy, + args, + f, + clang_tidy_binary, + export_fixes_dir, + build_path, + profile_dir, + ) + ) + for f in files + ] + + try: + for i, coro in enumerate(asyncio.as_completed(tasks)): + result = await coro + if result.returncode != 0: + returncode = 1 + if result.returncode < 0: + result.stderr += f"{result.filename}: terminated by signal {-result.returncode}\n" + progress = f"[{i + 1: >{len(f'{len(files)}')}}/{len(files)}]" + runtime = f"[{result.elapsed:.1f}s]" + if not args.hide_progress: + print(f"{progress}{runtime} {shlex.join(result.invocation)}") + if result.stdout: + print(result.stdout, end=("" if result.stderr else "\n")) + if result.stderr: + print(result.stderr) + except asyncio.CancelledError: + if not args.hide_progress: + print("\nCtrl-C detected, goodbye.") + for task in tasks: + task.cancel() + if delete_fixes_dir: + assert export_fixes_dir + shutil.rmtree(export_fixes_dir) + if profile_dir: + shutil.rmtree(profile_dir) + return + + if args.enable_check_profile and profile_dir: + # Ensure all clang-tidy stdout is flushed before printing profiling + sys.stdout.flush() + aggregated_data = aggregate_profiles(profile_dir) + if aggregated_data: + print_profile_data(aggregated_data) + else: + print("No profiling data found.") + + if combine_fixes: + if not args.hide_progress: + print(f"Writing fixes to {args.export_fixes} ...") + try: + assert export_fixes_dir + merge_replacement_files(export_fixes_dir, args.export_fixes) + except Exception: + print("Error exporting fixes.\n", file=sys.stderr) + traceback.print_exc() + returncode = 1 + + if args.fix: + if not args.hide_progress: + print("Applying fixes ...") + try: + assert export_fixes_dir + apply_fixes(args, clang_apply_replacements_binary, export_fixes_dir) + except Exception: + print("Error applying fixes.\n", file=sys.stderr) + traceback.print_exc() + returncode = 1 + + if delete_fixes_dir: + assert export_fixes_dir + shutil.rmtree(export_fixes_dir) + if profile_dir: + shutil.rmtree(profile_dir) + sys.exit(returncode) + + +if __name__ == "__main__": + try: + asyncio.run(main()) + except KeyboardInterrupt: + pass diff --git a/rcc/utils/git/code-format-helper.py b/rcc/utils/git/code-format-helper.py new file mode 100755 index 0000000..e7a82b8 --- /dev/null +++ b/rcc/utils/git/code-format-helper.py @@ -0,0 +1,677 @@ +#!/usr/bin/env python3 +# +# ====- code-format-helper, runs code formatters from the ci or in a hook --*- python -*--==# +# +# Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +# See https://llvm.org/LICENSE.txt for license information. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +# +# ==--------------------------------------------------------------------------------------==# + +import argparse +import os +import re +import shlex +import subprocess +import sys +from typing import List, Optional + +""" +This script is run by GitHub actions to ensure that the code in PR's conform to +the coding style of LLVM. It can also be installed as a pre-commit git hook to +check the coding style before submitting it. The canonical source of this script +is in the LLVM source tree under llvm/utils/git. + +For C/C++ code it uses clang-format, for Python code it uses darker (which +in turn invokes black), and for LLVM-libc CMake files it uses cmake_format.py. + +You can learn more about the LLVM coding style on llvm.org: +https://llvm.org/docs/CodingStandards.html + +You can install this script as a git hook by symlinking it to the .git/hooks +directory: + +ln -s $(pwd)/llvm/utils/git/code-format-helper.py .git/hooks/pre-commit + +You can control the exact path to clang-format, darker, or cmake_format.py with +the following environment variables: $CLANG_FORMAT_PATH, $DARKER_FORMAT_PATH, +and $CMAKE_FORMAT_PATH. +""" + + +class FormatArgs: + start_rev: str = None + end_rev: str = None + repo: str = None + changed_files: List[str] = [] + token: str = None + verbose: bool = True + issue_number: int = 0 + write_comment_to_file: bool = False + + def __init__(self, args: argparse.Namespace = None) -> None: + if not args is None: + self.start_rev = args.start_rev + self.end_rev = args.end_rev + self.repo = args.repo + self.token = args.token + self.changed_files = args.changed_files + self.issue_number = args.issue_number + self.write_comment_to_file = args.write_comment_to_file + + +class FormatHelper: + COMMENT_TAG = "<!--LLVM CODE FORMAT COMMENT: {fmt}-->" + name: str + friendly_name: str + comment: dict = None + + @property + def comment_tag(self) -> str: + return self.COMMENT_TAG.replace("fmt", self.name) + + @property + def instructions(self) -> str: + raise NotImplementedError() + + def has_tool(self) -> bool: + raise NotImplementedError() + + def format_run(self, changed_files: List[str], args: FormatArgs) -> Optional[str]: + raise NotImplementedError() + + def pr_comment_text_for_diff(self, diff: str) -> str: + return f""" +:warning: {self.friendly_name}, {self.name} found issues in your code. :warning: + +<details> +<summary> +You can test this locally with the following command: +</summary> + +``````````bash +{self.instructions} +`````````` + +:warning: +The reproduction instructions above might return results for more than one PR +in a stack if you are using a stacked PR workflow. You can limit the results by +changing `origin/main` to the base branch/commit you want to compare against. +:warning: + +</details> + +<details> +<summary> +View the diff from {self.name} here. +</summary> + +``````````diff +{diff} +`````````` + +</details> +""" + + # TODO: any type should be replaced with the correct github type, but it requires refactoring to + # not require the github module to be installed everywhere. + def find_comment(self, pr: any) -> any: + for comment in pr.as_issue().get_comments(): + if self.comment_tag in comment.body: + return comment + return None + + def update_pr(self, comment_text: str, args: FormatArgs, create_new: bool) -> None: + import github + from github import IssueComment, PullRequest + + repo = github.Github(auth=github.Auth.Token(args.token)).get_repo(args.repo) + pr = repo.get_issue(args.issue_number).as_pull_request() + + comment_text = self.comment_tag + "\n\n" + comment_text + + existing_comment = self.find_comment(pr) + + if args.write_comment_to_file: + if create_new or existing_comment: + self.comment = {"body": comment_text} + if existing_comment: + self.comment["id"] = existing_comment.id + return + + if existing_comment: + existing_comment.edit(comment_text) + elif create_new: + pr.as_issue().create_comment(comment_text) + + def run(self, changed_files: List[str], args: FormatArgs) -> bool: + changed_files = [arg for arg in changed_files if "third-party" not in arg] + diff = self.format_run(changed_files, args) + should_update_gh = args.token is not None and args.repo is not None + + if diff is None: + if should_update_gh: + comment_text = ( + ":white_check_mark: With the latest revision " + f"this PR passed the {self.friendly_name}." + ) + self.update_pr(comment_text, args, create_new=False) + return True + elif len(diff) > 0: + if should_update_gh: + comment_text = self.pr_comment_text_for_diff(diff) + self.update_pr(comment_text, args, create_new=True) + else: + print( + f"Warning: {self.friendly_name}, {self.name} detected " + "some issues with your code formatting..." + ) + return False + else: + # The formatter failed but didn't output a diff (e.g. some sort of + # infrastructure failure). + comment_text = ( + f":warning: The {self.friendly_name} failed without printing " + "a diff. Check the logs for stderr output. :warning:" + ) + if should_update_gh: + self.update_pr(comment_text, args, create_new=False) + return False + + +class ClangFormatHelper(FormatHelper): + name = "clang-format" + friendly_name = "C/C++ code formatter" + + def _construct_command(self, diff_expression: list[str] | None): + cf_cmd = [self.clang_fmt_path, "--diff"] + + if diff_expression: + cf_cmd.extend(diff_expression) + + # Gather the extension of all modified files and pass them explicitly to git-clang-format. + # This prevents git-clang-format from applying its own filtering rules on top of ours. + extensions = set() + for file in self._cpp_files: + _, ext = os.path.splitext(file) + extensions.add( + ext.strip(".") + ) # Exclude periods since git-clang-format takes extensions without them + cf_cmd.append("--extensions") + cf_cmd.append(",".join(extensions)) + + cf_cmd.append("--") + cf_cmd += self._cpp_files + return cf_cmd + + @property + def instructions(self) -> str: + return ( + " ".join(self._construct_command(["origin/main", "HEAD"])) + + " --diff_from_common_commit" + ) + + def should_include_extensionless_file(self, path: str) -> bool: + return path.startswith("libcxx/include") + + def filter_changed_files(self, changed_files: List[str]) -> List[str]: + filtered_files = [] + for path in changed_files: + _, ext = os.path.splitext(path) + if ext in ( + ".cpp", + ".c", + ".h", + ".hpp", + ".hxx", + ".cxx", + ".inc", + ".cppm", + ".cl", + ): + filtered_files.append(path) + elif ext == "" and self.should_include_extensionless_file(path): + filtered_files.append(path) + return filtered_files + + @property + def clang_fmt_path(self) -> str: + if "CLANG_FORMAT_PATH" in os.environ: + return os.environ["CLANG_FORMAT_PATH"] + return "git-clang-format" + + def has_tool(self) -> bool: + cmd = [self.clang_fmt_path, "-h"] + proc = None + try: + proc = subprocess.run(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE) + except: + return False + return proc.returncode == 0 + + def format_run(self, changed_files: List[str], args: FormatArgs) -> Optional[str]: + self._cpp_files = self.filter_changed_files(changed_files) + if not self._cpp_files: + return None + + diff_expression = [] + if args.start_rev and args.end_rev: + diff_expression.append(args.start_rev) + diff_expression.append(args.end_rev) + + cf_cmd = self._construct_command(diff_expression) + + if args.verbose: + print(f"Running: {' '.join(cf_cmd)}") + proc = subprocess.run(cf_cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE) + sys.stdout.write(proc.stderr.decode("utf-8")) + + if proc.returncode != 0: + # formatting needed, or the command otherwise failed + if args.verbose: + print(f"error: {self.name} exited with code {proc.returncode}") + # Print the diff in the log so that it is viewable there + print(proc.stdout.decode("utf-8")) + return proc.stdout.decode("utf-8") + else: + return None + + +class DarkerFormatHelper(FormatHelper): + name = "darker" + friendly_name = "Python code formatter" + + def _construct_command(self, diff_expression: str | None) -> str: + darker_cmd = [ + self.darker_fmt_path, + "--check", + "--diff", + ] + if diff_expression: + darker_cmd += ["-r", diff_expression] + darker_cmd += self._py_files + return darker_cmd + + @property + def instructions(self) -> str: + return " ".join(self._construct_command("origin/main...HEAD")) + + def filter_changed_files(self, changed_files: List[str]) -> List[str]: + filtered_files = [] + for path in changed_files: + name, ext = os.path.splitext(path) + if ext == ".py": + filtered_files.append(path) + + return filtered_files + + @property + def darker_fmt_path(self) -> str: + if "DARKER_FORMAT_PATH" in os.environ: + return os.environ["DARKER_FORMAT_PATH"] + return "darker" + + def has_tool(self) -> bool: + cmd = [self.darker_fmt_path, "--version"] + proc = None + try: + proc = subprocess.run(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE) + except: + return False + return proc.returncode == 0 + + def format_run(self, changed_files: List[str], args: FormatArgs) -> Optional[str]: + py_files = self.filter_changed_files(changed_files) + if not py_files: + return None + self._py_files = py_files + diff_expression = None + if args.start_rev and args.end_rev: + diff_expression = f"{args.start_rev}...{args.end_rev}" + darker_cmd = self._construct_command(diff_expression) + if args.verbose: + print(f"Running: {' '.join(darker_cmd)}") + proc = subprocess.run( + darker_cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE + ) + if args.verbose: + sys.stdout.write(proc.stderr.decode("utf-8")) + + if proc.returncode != 0: + # formatting needed, or the command otherwise failed + if args.verbose: + print(f"error: {self.name} exited with code {proc.returncode}") + # Print the diff in the log so that it is viewable there + print(proc.stdout.decode("utf-8")) + return proc.stdout.decode("utf-8") + else: + sys.stdout.write(proc.stdout.decode("utf-8")) + return None + + +class UndefGetFormatHelper(FormatHelper): + name = "undef deprecator" + friendly_name = "undef deprecator" + + @property + def instructions(self) -> str: + return " ".join(shlex.quote(c) for c in self.cmd) + + def filter_changed_files(self, changed_files: List[str]) -> List[str]: + filtered_files = [] + for path in changed_files: + _, ext = os.path.splitext(path) + if ext in (".cpp", ".c", ".h", ".hpp", ".hxx", ".cxx", ".inc", ".cppm", ".ll"): + filtered_files.append(path) + return filtered_files + + def has_tool(self) -> bool: + return True + + def pr_comment_text_for_diff(self, diff: str) -> str: + return f""" +:warning: {self.name} found issues in your code. :warning: + +<details> +<summary> +You can test this locally with the following command: +</summary> + +``````````bash +{self.instructions} +`````````` + +</details> + +{diff} +""" + + def format_run(self, changed_files: List[str], args: FormatArgs) -> Optional[str]: + files = self.filter_changed_files(changed_files) + if not files: + return None + + # Use git to find files that have had a change in the number of undefs + regex = "([^a-zA-Z0-9#_-]undef([^a-zA-Z0-9_-]|$)|UndefValue::get)" + cmd = ["git", "diff", "-U0", "--pickaxe-regex", "-S", regex] + + if args.start_rev and args.end_rev: + cmd.append(args.start_rev) + cmd.append(args.end_rev) + + cmd += files + self.cmd = cmd + + if args.verbose: + print(f"Running: {self.instructions}") + + proc = subprocess.run( + cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, encoding="utf-8" + ) + sys.stdout.write(proc.stderr) + stdout = proc.stdout + + if not stdout: + return None + + files = [] + + # Split the diff so we have one array entry per file. + # Each file is prefixed like: + # diff --git a/file b/file + for file in re.split("^diff --git ", stdout, 0, re.MULTILINE): + lines = file.splitlines() + match = re.match("a/([^ ]+)", lines[0] if lines else "") + filename = match[1] if match else "" + if filename.endswith(".ll"): + undef_regex = r"^[+][^;\n]*(?<!%)\bundef\b" + else: + undef_regex = r"^[+].*UndefValue::get" + # search for additions of undef + if re.search(undef_regex, file, re.MULTILINE): + files.append(filename) + + if not files: + return None + + files = "\n".join(" - " + f for f in files) + report = f""" +The following files introduce new uses of undef: +{files} + +[Undef](https://llvm.org/docs/LangRef.html#undefined-values) is now deprecated and should only be used in the rare cases where no replacement is possible. For example, a load of uninitialized memory yields `undef`. You should use `poison` values for placeholders instead. + +In tests, avoid using `undef` and having tests that trigger undefined behavior. If you need an operand with some unimportant value, you can add a new argument to the function and use that instead. + +For example, this is considered a bad practice: +```llvm +define void @fn() {{ + ... + br i1 undef, ... +}} +``` + +Please use the following instead: +```llvm +define void @fn(i1 %cond) {{ + ... + br i1 %cond, ... +}} +``` + +Please refer to the [Undefined Behavior Manual](https://llvm.org/docs/UndefinedBehavior.html) for more information. +""" + if args.verbose: + print(f"error: {self.name} failed") + print(report) + return report + + +class CMakeFormatHelper(FormatHelper): + name = "cmake-format" + friendly_name = "CMake code formatter" + + def _construct_command(self) -> List[str]: + return [ + sys.executable, + self.cmake_fmt_path, + "--dry-run", + "--diff", + ] + self._cmake_files + + @property + def instructions(self) -> str: + return " ".join(["python3", self.cmake_fmt_path, "-i"] + self._cmake_files) + + @property + def check_instructions(self) -> str: + return " ".join( + ["python3", self.cmake_fmt_path, "--dry-run", "--diff"] + self._cmake_files + ) + + def pr_comment_text_for_diff(self, diff: str) -> str: + return f""" +:warning: {self.friendly_name}, {self.name} found issues in your code. :warning: + +<details> +<summary> +You can fix this locally with the following command: +</summary> + +``````````bash +{self.instructions} +`````````` + +You can also preview the formatting diff without modifying files: + +``````````bash +{self.check_instructions} +`````````` + +</details> + +<details> +<summary> +View the diff from {self.name} here. +</summary> + +``````````diff +{diff} +`````````` + +</details> +""" + + def filter_changed_files(self, changed_files: List[str]) -> List[str]: + filtered_files = [] + for path in changed_files: + if not path.startswith("libc/"): + continue + if os.path.basename(path) == "CMakeLists.txt" or path.endswith(".cmake"): + if os.path.exists(path): + filtered_files.append(path) + return filtered_files + + @property + def cmake_fmt_path(self) -> str: + if "CMAKE_FORMAT_PATH" in os.environ: + return os.environ["CMAKE_FORMAT_PATH"] + return "libc/utils/cmake_format.py" + + def has_tool(self) -> bool: + return os.path.isfile(self.cmake_fmt_path) + + def format_run(self, changed_files: List[str], args: FormatArgs) -> Optional[str]: + cmake_files = self.filter_changed_files(changed_files) + if not cmake_files: + return None + self._cmake_files = cmake_files + cmake_cmd = self._construct_command() + if args.verbose: + print(f"Running: {' '.join(cmake_cmd)}") + proc = subprocess.run(cmake_cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE) + if args.verbose: + sys.stdout.write(proc.stderr.decode("utf-8")) + + if proc.returncode != 0: + # formatting needed, or the command otherwise failed + stdout = proc.stdout.decode("utf-8") + if args.verbose: + print(f"error: {self.name} exited with code {proc.returncode}") + if stdout: + print( + "error: CMake files are not formatted according to " + "LLVM-libc style." + ) + print(f"To fix this locally, run:\n {self.instructions}") + print( + "To preview the formatting diff locally, run:\n" + f" {self.check_instructions}" + ) + # Print the diff in the log so that it is viewable there + print(stdout) + elif stdout: + print(f"To fix CMake formatting, run:\n {self.instructions}") + return stdout + else: + sys.stdout.write(proc.stdout.decode("utf-8")) + return None + + +ALL_FORMATTERS = ( + DarkerFormatHelper(), + ClangFormatHelper(), + CMakeFormatHelper(), + UndefGetFormatHelper(), +) + + +def hook_main(): + # fill out args + args = FormatArgs() + args.verbose = os.getenv("FORMAT_HOOK_VERBOSE", False) + + # find the changed files + cmd = ["git", "diff", "--cached", "--name-only", "--diff-filter=d"] + proc = subprocess.run(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE) + output = proc.stdout.decode("utf-8") + for line in output.splitlines(): + args.changed_files.append(line) + + failed_fmts = [] + for fmt in ALL_FORMATTERS: + if fmt.has_tool(): + if not fmt.run(args.changed_files, args): + failed_fmts.append(fmt.name) + else: + print(f"Couldn't find {fmt.name}, can't check " + fmt.friendly_name.lower()) + + if len(failed_fmts) > 0: + print( + "Pre-commit format hook failed, rerun with FORMAT_HOOK_VERBOSE=1 environment for verbose output" + ) + sys.exit(1) + + sys.exit(0) + + +if __name__ == "__main__": + script_path = os.path.abspath(__file__) + if ".git/hooks" in script_path: + hook_main() + sys.exit(0) + + parser = argparse.ArgumentParser() + parser.add_argument( + "--token", type=str, required=True, help="GitHub authentication token" + ) + parser.add_argument( + "--repo", + type=str, + default=os.getenv("GITHUB_REPOSITORY", "llvm/llvm-project"), + help="The GitHub repository that we are working with in the form of <owner>/<repo> (e.g. llvm/llvm-project)", + ) + parser.add_argument("--issue-number", type=int, required=True) + parser.add_argument( + "--start-rev", + type=str, + required=True, + help="Compute changes from this revision.", + ) + parser.add_argument( + "--end-rev", type=str, required=True, help="Compute changes to this revision" + ) + parser.add_argument( + "--changed-files", + type=str, + help="Comma separated list of files that has been changed", + ) + parser.add_argument( + "--write-comment-to-file", + action="store_true", + help="Don't post comments on the PR, instead write the comments and metadata a file called 'comment'", + ) + + args = FormatArgs(parser.parse_args()) + + changed_files = [] + if args.changed_files: + changed_files = args.changed_files.split(",") + + failed_formatters = [] + comments = [] + for fmt in ALL_FORMATTERS: + if not fmt.run(changed_files, args): + failed_formatters.append(fmt.name) + if fmt.comment: + comments.append(fmt.comment) + + if len(comments): + with open("comments", "w") as f: + import json + + json.dump(comments, f) + + if len(failed_formatters) > 0: + print(f"error: some formatters failed: {' '.join(failed_formatters)}") + sys.exit(1) |
