diff options
Diffstat (limited to 'rcc')
38 files changed, 11047 insertions, 0 deletions
diff --git a/rcc/CMakeLists.txt b/rcc/CMakeLists.txt new file mode 100644 index 0000000..86f1c80 --- /dev/null +++ b/rcc/CMakeLists.txt @@ -0,0 +1,80 @@ +# 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") + +add_library(RCCHeaders INTERFACE) +target_compile_features(RCCHeaders INTERFACE cxx_std_23) +target_include_directories(RCCHeaders INTERFACE + "$<BUILD_INTERFACE:${RCC_SOURCE_DIR}/include>" + "$<BUILD_INTERFACE:${RCC_BINARY_DIR}/include>" + "$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>") +install(TARGETS RCCHeaders EXPORT RCCTargets) +install(DIRECTORY include/rcc DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}" + FILES_MATCHING PATTERN "*.h" PATTERN "*.inc" PATTERN "*.def") +install(DIRECTORY "${RCC_BINARY_DIR}/include/rcc" DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}" + FILES_MATCHING PATTERN "*.h" PATTERN "*.inc" PATTERN "*.def" + PATTERN "CMakeFiles" EXCLUDE PATTERN "config.h" EXCLUDE) + +add_subdirectory(lib) +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..11bd92e --- /dev/null +++ b/rcc/cmake/modules/AddRCC.cmake @@ -0,0 +1,184 @@ +# 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 RCCHeaders ${arg_LINK_LIBS}) + 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 RCCHeaders ${arg_LINK_LIBS}) + 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/ADT/STLFunctionalExtras.h b/rcc/include/rcc/ADT/STLFunctionalExtras.h new file mode 100644 index 0000000..71a5b85 --- /dev/null +++ b/rcc/include/rcc/ADT/STLFunctionalExtras.h @@ -0,0 +1,112 @@ +/* + R *CC-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/>. + */ + +//===- llvm/ADT/STLFunctionalExtras.h - Extras for <functional> -*- 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 contains some extension to <functional>. +// +// No library is required when using these functions. +// +//===----------------------------------------------------------------------===// + +#ifndef RCC_ADT_STLFUNCTIONALEXTRAS_H +#define RCC_ADT_STLFUNCTIONALEXTRAS_H + +#include "rcc/Support/Compiler.h" + +#include <cstddef +#include <cstdint> +#include <type_traits> +#include <utility> + +namespace rcc +{ + +//===----------------------------------------------------------------------===// +// Extra additions to <functional> +//===----------------------------------------------------------------------===// + +/// An efficient, type-erasing, non-owning reference to a callable. This is +/// intended for use as the type of a function parameter that is not used +/// after the function in question returns. +/// +/// This class does not own the callable, so it is not in general safe to store +/// a function_ref. +template <typename Fn> class function_ref; + +template <typename Ret, typename... Params> +class RCC_GSL_POINTER function_ref<Ret (Params...)> +{ + Ret (*callback) (intptr_t callable, Params... params) = nullptr; + intptr_t callable = 0; + + template <typename Callable> + static Ret + callback_fn (intptr_t callable, Params... params) + { + return (*reinterpret_cast<Callable *> (callable)) ( + std::forward<Params> (params)...); + } + +public: + function_ref () = default; + function_ref (std::nullptr_t) + { + } + + template <typename Callable> + function_ref ( + Callable &&callable RCC_LIFETIME_BOUND, + // This is not the copy-constructor. + std::enable_if_t<!std::is_same<std::remove_cvref_t<Callable>, + function_ref>::value> * = nullptr, + // Functor must be callable and return a suitable type. + std::enable_if_t<std::is_void<Ret>::value || + std::is_convertible<decltype (std::declval<Callable> () ( + std::declval<Params> ()...)), + Ret>::value> * = nullptr) + : callback (callback_fn<std::remove_reference_t<Callable>>), + callable (reinterpret_cast<intptr_t> (&callable)) + { + } + + Ret + operator() (Params... params) const + { + return callback (callable, std::forward<Params> (params)...); + } + + explicit + operator bool () const + { + return callback; + } +}; + +} // end namespace rcc + +#endif // RCC_ADT_STLFUNCTIONALEXTRAS_H diff --git a/rcc/include/rcc/Config/config.h.cmake b/rcc/include/rcc/Config/config.h.cmake new file mode 100644 index 0000000..32761c1 --- /dev/null +++ b/rcc/include/rcc/Config/config.h.cmake @@ -0,0 +1,291 @@ +#ifndef CONFIG_H +#define 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 to enable backtraces, and to 0 otherwise. */ +#cmakedefine01 ENABLE_BACKTRACES + +/* Define to 1 to enable crash overrides, and to 0 otherwise. */ +#cmakedefine01 ENABLE_CRASH_OVERRIDES + +/* Define to 1 to enable crash memory dumps, and to 0 otherwise. */ +#cmakedefine01 RCC_ENABLE_CRASH_DUMPS + +/* Define to 1 to prefer forward slashes on Windows, and to 0 prefer + backslashes. */ +#cmakedefine01 RCC_WINDOWS_PREFER_FORWARD_SLASH + +/* Define to 1 if you have the `backtrace' function. */ +#cmakedefine HAVE_BACKTRACE ${HAVE_BACKTRACE} + +#define BACKTRACE_HEADER <${BACKTRACE_HEADER}> + +/* can use __crashreporter_info__ */ +#cmakedefine01 HAVE_CRASHREPORTER_INFO + +/* Define to 1 if you have the declaration of `arc4random', and to 0 if you + don't. */ +#cmakedefine01 HAVE_DECL_ARC4RANDOM + +/* Define to 1 if you have the declaration of `strerror_s', and to 0 if you + don't. */ +#cmakedefine01 HAVE_DECL_STRERROR_S + +/* Define if dlopen() is available on this platform. */ +#cmakedefine HAVE_DLOPEN ${HAVE_DLOPEN} + +/* 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 `futimens' function. */ +#cmakedefine HAVE_FUTIMENS ${HAVE_FUTIMENS} + +/* Define to 1 if you have the `futimes' function. */ +#cmakedefine HAVE_FUTIMES ${HAVE_FUTIMES} + +/* Define to 1 if you have the `getpagesize' function. */ +#cmakedefine HAVE_GETPAGESIZE ${HAVE_GETPAGESIZE} + +/* Define to 1 if you have the `getrusage' function. */ +#cmakedefine HAVE_GETRUSAGE ${HAVE_GETRUSAGE} + +/* Define to 1 if you have the `isatty' function. */ +#cmakedefine HAVE_ISATTY 1 + +/* Define to 1 if you have the `edit' library (-ledit). */ +#cmakedefine HAVE_LIBEDIT ${HAVE_LIBEDIT} + +/* 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} + +/* Define to 1 if you have the `psapi' library (-lpsapi). */ +#cmakedefine HAVE_LIBPSAPI ${HAVE_LIBPSAPI} + +/* Define to 1 if you have the `pthread' library (-lpthread). */ +#cmakedefine HAVE_LIBPTHREAD ${HAVE_LIBPTHREAD} + +/* Define to 1 if you have the `pthread_getname_np' function. */ +#cmakedefine HAVE_PTHREAD_GETNAME_NP ${HAVE_PTHREAD_GETNAME_NP} + +/* Define to 1 if you have the `pthread_setname_np' function. */ +#cmakedefine HAVE_PTHREAD_SETNAME_NP ${HAVE_PTHREAD_SETNAME_NP} + +/* Define to 1 if you have the `pthread_get_name_np' function. */ +#cmakedefine HAVE_PTHREAD_GET_NAME_NP ${HAVE_PTHREAD_GET_NAME_NP} + +/* Define to 1 if you have the `pthread_set_name_np' function. */ +#cmakedefine HAVE_PTHREAD_SET_NAME_NP ${HAVE_PTHREAD_SET_NAME_NP} + +/* Define to 1 if you have the <mach/mach.h> header file. */ +#cmakedefine HAVE_MACH_MACH_H ${HAVE_MACH_MACH_H} + +/* Define to 1 if you have the `mallctl' function. */ +#cmakedefine HAVE_MALLCTL ${HAVE_MALLCTL} + +/* Define to 1 if you have the `mallinfo' function. */ +#cmakedefine HAVE_MALLINFO ${HAVE_MALLINFO} + +/* Define to 1 if you have the `mallinfo2' function. */ +#cmakedefine HAVE_MALLINFO2 ${HAVE_MALLINFO2} + +/* Define to 1 if you have the <malloc/malloc.h> header file. */ +#cmakedefine HAVE_MALLOC_MALLOC_H ${HAVE_MALLOC_MALLOC_H} + +/* Define to 1 if you have the `malloc_zone_statistics' function. */ +#cmakedefine HAVE_MALLOC_ZONE_STATISTICS ${HAVE_MALLOC_ZONE_STATISTICS} + +/* Define to 1 if you have the `posix_spawn' function. */ +#cmakedefine HAVE_POSIX_SPAWN ${HAVE_POSIX_SPAWN} + +/* Define to 1 if you have the `pread' function. */ +#cmakedefine HAVE_PREAD ${HAVE_PREAD} + +/* Define to 1 if you have the <pthread.h> header file. */ +#cmakedefine HAVE_PTHREAD_H ${HAVE_PTHREAD_H} + +/* Have pthread_rwlock_init */ +#cmakedefine HAVE_PTHREAD_RWLOCK_INIT ${HAVE_PTHREAD_RWLOCK_INIT} + +/* Define to 1 if you have the `sbrk' function. */ +#cmakedefine HAVE_SBRK ${HAVE_SBRK} + +/* Define to 1 if you have the `setenv' function. */ +#cmakedefine HAVE_SETENV ${HAVE_SETENV} + +/* Define to 1 if you have the `sigaltstack' function. */ +#cmakedefine HAVE_SIGALTSTACK ${HAVE_SIGALTSTACK} + +/* Define to 1 if you have the `strerror_r' function. */ +#cmakedefine HAVE_STRERROR_R ${HAVE_STRERROR_R} + +/* Define to 1 if you have the `sysconf' function. */ +#cmakedefine HAVE_SYSCONF ${HAVE_SYSCONF} + +/* Define to 1 if you have the <sys/mman.h> header file. */ +#cmakedefine HAVE_SYS_MMAN_H ${HAVE_SYS_MMAN_H} + +/* Define to 1 if you have the <sys/ioctl.h> header file. */ +#cmakedefine HAVE_SYS_IOCTL_H ${HAVE_SYS_IOCTL_H} + +/* Define to 1 if stat struct has st_mtimespec member .*/ +#cmakedefine HAVE_STRUCT_STAT_ST_MTIMESPEC_TV_NSEC ${HAVE_STRUCT_STAT_ST_MTIMESPEC_TV_NSEC} + +/* Define to 1 if stat struct has st_mtim member. */ +#cmakedefine HAVE_STRUCT_STAT_ST_MTIM_TV_NSEC ${HAVE_STRUCT_STAT_ST_MTIM_TV_NSEC} + +/* Define to 1 if you have the <unistd.h> header file. */ +#cmakedefine HAVE_UNISTD_H ${HAVE_UNISTD_H} + +/* Have host's _alloca */ +#cmakedefine HAVE__ALLOCA ${HAVE__ALLOCA} + +/* Define to 1 if you have the `_chsize_s' function. */ +#cmakedefine HAVE__CHSIZE_S ${HAVE__CHSIZE_S} + +/* Define to 1 if you have the `_Unwind_Backtrace' function. */ +#cmakedefine HAVE__UNWIND_BACKTRACE ${HAVE__UNWIND_BACKTRACE} + +/* Have host's __alloca */ +#cmakedefine HAVE___ALLOCA ${HAVE___ALLOCA} + +/* Have host's __ashldi3 */ +#cmakedefine HAVE___ASHLDI3 ${HAVE___ASHLDI3} + +/* Have host's __ashrdi3 */ +#cmakedefine HAVE___ASHRDI3 ${HAVE___ASHRDI3} + +/* Have host's __chkstk */ +#cmakedefine HAVE___CHKSTK ${HAVE___CHKSTK} + +/* Have host's __chkstk_ms */ +#cmakedefine HAVE___CHKSTK_MS ${HAVE___CHKSTK_MS} + +/* Have host's __cmpdi2 */ +#cmakedefine HAVE___CMPDI2 ${HAVE___CMPDI2} + +/* Have host's __divdi3 */ +#cmakedefine HAVE___DIVDI3 ${HAVE___DIVDI3} + +/* Have host's __fixdfdi */ +#cmakedefine HAVE___FIXDFDI ${HAVE___FIXDFDI} + +/* Have host's __fixsfdi */ +#cmakedefine HAVE___FIXSFDI ${HAVE___FIXSFDI} + +/* Have host's __floatdidf */ +#cmakedefine HAVE___FLOATDIDF ${HAVE___FLOATDIDF} + +/* Have host's __lshrdi3 */ +#cmakedefine HAVE___LSHRDI3 ${HAVE___LSHRDI3} + +/* Have host's __main */ +#cmakedefine HAVE___MAIN ${HAVE___MAIN} + +/* Have host's __moddi3 */ +#cmakedefine HAVE___MODDI3 ${HAVE___MODDI3} + +/* Have host's __udivdi3 */ +#cmakedefine HAVE___UDIVDI3 ${HAVE___UDIVDI3} + +/* Have host's __umoddi3 */ +#cmakedefine HAVE___UMODDI3 ${HAVE___UMODDI3} + +/* Have host's ___chkstk */ +#cmakedefine HAVE____CHKSTK ${HAVE____CHKSTK} + +/* Have host's ___chkstk_ms */ +#cmakedefine HAVE____CHKSTK_MS ${HAVE____CHKSTK_MS} + +/* Define if ICU library is available */ +#cmakedefine01 HAVE_ICU + +/* Define if Windows vendored ICU is available */ +#cmakedefine01 HAVE_WINDOWS_ICU + +/* Define if iconv library is available */ +#cmakedefine01 HAVE_ICONV + +/* 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}" + +/* Define to a function implementing stricmp */ +#cmakedefine stricmp ${stricmp} + +/* Define to a function implementing strdup */ +#cmakedefine strdup ${strdup} + +/* 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}" + +/* Whether Timers signpost passes in Xcode Instruments */ +#cmakedefine01 RCC_SUPPORT_XCODE_SIGNPOSTS + +#cmakedefine HAVE_PROC_PID_RUSAGE 1 + +#cmakedefine HAVE_BUILTIN_THREAD_POINTER ${HAVE_BUILTIN_THREAD_POINTER} + +#cmakedefine HAVE_GETAUXVAL ${HAVE_GETAUXVAL} + +#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..9c0ba18 --- /dev/null +++ b/rcc/include/rcc/Config/rcc-config.h.cmake @@ -0,0 +1,158 @@ +/*===------- 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 + +/* Define if RCC_ENABLE_DUMP is enabled */ +#cmakedefine RCC_ENABLE_DUMP + +/* 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}" + +/* Define if threads enabled */ +#cmakedefine01 RCC_ENABLE_THREADS + +/* Has gcc/MSVC atomic intrinsics */ +#cmakedefine01 RCC_HAS_ATOMICS + +/* 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 this is Unixish platform */ +#cmakedefine RCC_ON_UNIX ${RCC_ON_UNIX} + +/* 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}" + +/* Whether RCC records statistics for use with GetStatistics(), + * PrintStatistics() or PrintStatisticsJSON() + */ +#cmakedefine01 RCC_FORCE_ENABLE_STATS + +/* Define if we have z3 and want to build it */ +#cmakedefine RCC_WITH_Z3 ${RCC_WITH_Z3} + +/* Define if we have curl and want to use it */ +#cmakedefine RCC_ENABLE_CURL ${RCC_ENABLE_CURL} + +/* Define if we have cpp-httplib and want to use it */ +#cmakedefine RCC_ENABLE_HTTPLIB ${RCC_ENABLE_HTTPLIB} + +/* Define if zlib compression is available */ +#cmakedefine01 RCC_ENABLE_ZLIB + +/* Define if zstd compression is available */ +#cmakedefine01 RCC_ENABLE_ZSTD + +/* Define if xz (lzma) decompression is available */ +#cmakedefine01 RCC_ENABLE_LZMA + +/* 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 to 1 if you have the <sysexits.h> header file. */ +#cmakedefine HAVE_SYSEXITS_H ${HAVE_SYSEXITS_H} + +/* Define if building libRCC shared library */ +#cmakedefine RCC_BUILD_RCC_DYLIB + +/* Define if building RCC with BUILD_SHARED_LIBS */ +#cmakedefine RCC_BUILD_SHARED_LIBS + +/* Define if exporting RCC public interface for shared library */ +#cmakedefine RCC_ENABLE_RCC_EXPORT_ANNOTATIONS + +/* Define if exporting RCC-C public interface for shared library */ +#cmakedefine RCC_ENABLE_RCC_C_EXPORT_ANNOTATIONS + +/* Define if building RCC with RCC_FORCE_USE_OLD_TOOLCHAIN_LIBS */ +#cmakedefine RCC_FORCE_USE_OLD_TOOLCHAIN ${RCC_FORCE_USE_OLD_TOOLCHAIN} + +/* Define if rcc_unreachable should be optimized with undefined behavior + * in non assert builds */ +#cmakedefine01 RCC_UNREACHABLE_OPTIMIZE + +/* Define if building RCC with RCC_ENABLE_IO_SANDBOX */ +#cmakedefine01 RCC_ENABLE_IO_SANDBOX + +/* 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 logf128 is available */ +#cmakedefine RCC_HAS_LOGF128 + +/* 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 + +/* Define to 1 to enable RCC OnDisk Content Addressable Storage */ +#cmakedefine01 RCC_ENABLE_ONDISK_CAS + +#endif diff --git a/rcc/include/rcc/DataTypes.h b/rcc/include/rcc/DataTypes.h new file mode 100644 index 0000000..59e9b75 --- /dev/null +++ b/rcc/include/rcc/DataTypes.h @@ -0,0 +1,105 @@ +/* + 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, 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/>. +*/ + +// This implementation is based on include/llvm-c/DataTypes.h +/*===-- include/llvm-c/DataTypes.h - Define fixed size types ------*- 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 contains definitions to figure out the size of _HOST_ data + types.*| + |* This file is important because different host OS's define different + macros,*| + |* which makes portability tough. This file exports the following *| + |* definitions: *| + |* *| + |* [u]int(32|64)_t : typedefs for signed and unsigned 32/64 bit system + types*| + |* [U]INT(8|16|32|64)_(MIN|MAX) : Constants for the min and max values. *| + |* *| + |* No library is required when using these functions. *| + |* *| + |*===----------------------------------------------------------------------===*/ + +/* Please leave this file C-compatible. */ +#ifndef RCC_DATATYPES_H +#define RCC_DATATYPES_H + +#include <inttypes.h> +#include <stdint.h> + +#ifndef _MSC_VER + +#if !defined(UINT32_MAX) +#error "The standard header <cstdint> is not C++11 compliant. Must #define "\ + "__STDC_LIMIT_MACROS before #including llvm-c/DataTypes.h" +#endif + +#if !defined(UINT32_C) +#error "The standard header <cstdint> is not C++11 compliant. Must #define "\ + "__STDC_CONSTANT_MACROS before #including llvm-c/DataTypes.h" +#endif + +/* Note that <inttypes.h> includes <stdint.h>, if this is a C99 system. */ +#include <sys/types.h> + +#ifdef _AIX +// GCC is strict about defining large constants: they must have LL modifier. +#undef INT64_MAX +#undef INT64_MIN +#endif + +#else /* _MSC_VER */ +#ifdef __cplusplus +#include <cstddef> +#include <cstdlib> +#else +#include <stddef.h> +#include <stdlib.h> +#endif +#include <sys/types.h> + +#if defined(_WIN64) +typedef signed __int64 ssize_t; +#else +typedef signed int ssize_t; +#endif /* _WIN64 */ + +#endif /* _MSC_VER */ + +/* Set defaults for constants which we cannot find. */ +#if !defined(INT64_MAX) +#define INT64_MAX 9223372036854775807LL +#endif +#if !defined(INT64_MIN) +#define INT64_MIN ((-INT64_MAX) - 1) +#endif +#if !defined(UINT64_MAX) +#define UINT64_MAX 0xffffffffffffffffULL +#endif + +#endif diff --git a/rcc/include/rcc/Support/Compiler.h b/rcc/include/rcc/Support/Compiler.h new file mode 100644 index 0000000..511c4cd --- /dev/null +++ b/rcc/include/rcc/Support/Compiler.h @@ -0,0 +1,853 @@ +/* + 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/>. +*/ + +//===-- llvm/Support/Compiler.h - Compiler abstraction 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 +// +//===----------------------------------------------------------------------===// +// +// This file defines several macros, based on the current compiler. This allows +// use of compiler-specific features in a way that remains portable. This header +// can be included from either C or C++. +// +//===----------------------------------------------------------------------===// + +#ifndef RCC_SUPPORT_COMPILER_H +#define RCC_SUPPORT_COMPILER_H + +#include "rcc/Config/rcc-config.h" + +#include <stddef.h> + +#if defined(_MSC_VER) +#include <sal.h> +#endif + +#ifndef __has_feature +#define __has_feature(x) 0 +#endif + +#ifndef __has_extension +#define __has_extension(x) 0 +#endif + +#ifndef __has_attribute +#define __has_attribute(x) 0 +#endif + +#ifndef __has_builtin +#define __has_builtin(x) 0 +#endif + +#ifndef __has_warning +#define __has_warning(x) 0 +#endif + +// Only use __has_cpp_attribute in C++ mode. GCC defines __has_cpp_attribute in +// C mode, but the :: in __has_cpp_attribute(scoped::attribute) is invalid. +#ifndef RCC_HAS_CPP_ATTRIBUTE +#if defined(__cplusplus) && defined(__has_cpp_attribute) +#define RCC_HAS_CPP_ATTRIBUTE(x) __has_cpp_attribute (x) +#else +#define RCC_HAS_CPP_ATTRIBUTE(x) 0 +#endif +#endif + +/// \macro RCC_GNUC_PREREQ +/// Extend the default __GNUC_PREREQ even if glibc's features.h isn't +/// available. +#ifndef RCC_GNUC_PREREQ +#if defined(__GNUC__) && defined(__GNUC_MINOR__) && defined(__GNUC_PATCHLEVEL__) +#define RCC_GNUC_PREREQ(maj, min, patch) \ + ((__GNUC__ << 20) + (__GNUC_MINOR__ << 10) + __GNUC_PATCHLEVEL__ >= \ + ((maj) << 20) + ((min) << 10) + (patch)) +#elif defined(__GNUC__) && defined(__GNUC_MINOR__) +#define RCC_GNUC_PREREQ(maj, min, patch) \ + ((__GNUC__ << 20) + (__GNUC_MINOR__ << 10) >= ((maj) << 20) + ((min) << 10)) +#else +#define RCC_GNUC_PREREQ(maj, min, patch) 0 +#endif +#endif + +/// \macro RCC_MSC_PREREQ +/// Is the compiler MSVC of at least the specified version? +/// The common \param version values to check for are: +/// * 1910: VS2017, version 15.1 & 15.2 +/// * 1911: VS2017, version 15.3 & 15.4 +/// * 1912: VS2017, version 15.5 +/// * 1913: VS2017, version 15.6 +/// * 1914: VS2017, version 15.7 +/// * 1915: VS2017, version 15.8 +/// * 1916: VS2017, version 15.9 +/// * 1920: VS2019, version 16.0 +/// * 1921: VS2019, version 16.1 +/// * 1922: VS2019, version 16.2 +/// * 1923: VS2019, version 16.3 +/// * 1924: VS2019, version 16.4 +/// * 1925: VS2019, version 16.5 +/// * 1926: VS2019, version 16.6 +/// * 1927: VS2019, version 16.7 +/// * 1928: VS2019, version 16.8 + 16.9 +/// * 1929: VS2019, version 16.10 + 16.11 +/// * 1930: VS2022, version 17.0 +#ifdef _MSC_VER +#define RCC_MSC_PREREQ(version) (_MSC_VER >= (version)) + +// We require at least VS 2019. +#if !defined(RCC_FORCE_USE_OLD_TOOLCHAIN) +#if !RCC_MSC_PREREQ(1920) +#error RCC requires at least VS 2019. +#endif +#endif + +#else +#define RCC_MSC_PREREQ(version) 0 +#endif + +/// RCC_LIBRARY_VISIBILITY - If a class marked with this attribute is linked +/// into a shared library, then the class should be private to the library and +/// not accessible from outside it. Can also be used to mark variables and +/// functions, making them private to any shared library they are linked into. +/// On PE/COFF targets, library visibility is the default, so this isn't needed. +/// +/// RCC_EXTERNAL_VISIBILITY - classes, functions, and variables marked with +/// this attribute will be made public and visible outside of any shared library +/// they are linked in to. + +#if RCC_HAS_CPP_ATTRIBUTE(gnu::visibility) && defined(__GNUC__) && \ + !defined(__clang__) +#define RCC_ATTRIBUTE_VISIBILITY_HIDDEN [[gnu::visibility ("hidden")]] +#define RCC_ATTRIBUTE_VISIBILITY_DEFAULT [[gnu::visibility ("default")]] +#elif __has_attribute(visibility) +#define RCC_ATTRIBUTE_VISIBILITY_HIDDEN __attribute__ ((visibility ("hidden"))) +#define RCC_ATTRIBUTE_VISIBILITY_DEFAULT \ + __attribute__ ((visibility ("default"))) +#else +#define RCC_ATTRIBUTE_VISIBILITY_HIDDEN +#define RCC_ATTRIBUTE_VISIBILITY_DEFAULT +#endif + +#if defined(RCC_BUILD_RCC_DYLIB) || defined(RCC_BUILD_SHARED_LIBS) +#define RCC_EXTERNAL_VISIBILITY RCC_ATTRIBUTE_VISIBILITY_DEFAULT +#else +#define RCC_EXTERNAL_VISIBILITY +#endif + +#if (!(defined(_WIN32) || defined(__CYGWIN__)) || \ + ((defined(__MINGW32__) || defined(__CYGWIN__)) && defined(__clang__))) +#define RCC_LIBRARY_VISIBILITY RCC_ATTRIBUTE_VISIBILITY_HIDDEN +// Clang compilers older then 15 do not support gnu style attributes on +// namespaces. +#if defined(__clang__) && __clang_major__ < 15 +#define RCC_LIBRARY_VISIBILITY_NAMESPACE [[gnu::visibility ("hidden")]] +#else +#define RCC_LIBRARY_VISIBILITY_NAMESPACE RCC_ATTRIBUTE_VISIBILITY_HIDDEN +#endif +#define RCC_ALWAYS_EXPORT RCC_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/visibility macro to mark something as explicitly +/// exported when rcc is built as a shared library with everything else that is +/// unannotated will have internal visibility. +/// +/// RCC_ABI_EXPORT is for the special case for things like plugin symbol +/// declarations or definitions where we don't want the macro to be switching +/// between dllexport and dllimport on windows based on what codebase is being +/// built, it will only be dllexport. For non windows platforms this macro +/// behaves the same as RCC_ABI. +/// +/// RCC_EXPORT_TEMPLATE is used on explicit template instantiations in source +/// files that were declared extern in a header. This macro is only set as a +/// compiler export attribute on windows, on other platforms it does nothing. +/// +/// RCC_TEMPLATE_ABI is for annotating extern template declarations in headers +/// for both functions and classes. On windows its turned in to dllimport for +/// library consumers, for other platforms its a default visibility attribute. +/// +/// RCC_ABI_FOR_TEST is for annotating symbols that are exported from a +/// library-internal header solely so that unit tests can link against them. +/// Symbols in RCC's public headers are part of the RCC public interface and +/// should use RCC_ABI. RCC_ABI_FOR_TEST is reserved for internal headers, +/// whose symbols could be conditionally excluded when not building tests in the +/// future. +/// +#ifndef RCC_ABI_GENERATING_ANNOTATIONS +// Marker to add to classes or functions in public headers that should not have +// export macros added to them by the clang tool +#define RCC_ABI_NOT_EXPORTED +// TODO(https://github.com/rcc/rcc-project/issues/145406): eliminate need for +// two preprocessor definitions to gate RCC_ABI macro definitions. +#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 __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 __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 + +#if defined(__GNUC__) +#define RCC_PREFETCH(addr, rw, locality) __builtin_prefetch (addr, rw, locality) +#else +#define RCC_PREFETCH(addr, rw, locality) +#endif + +#if __has_attribute(uninitialized) +#define RCC_ATTRIBUTE_UNINITIALIZED __attribute__ ((uninitialized)) +#else +#define RCC_ATTRIBUTE_UNINITIALIZED +#endif + +#if __has_attribute(used) +#define RCC_ATTRIBUTE_USED __attribute__ ((__used__)) +#else +#define RCC_ATTRIBUTE_USED +#endif + +#if __has_attribute(warn_unused) +#define RCC_ATTRIBUTE_WARN_UNUSED __attribute__ ((warn_unused)) +#else +#define RCC_ATTRIBUTE_WARN_UNUSED +#endif + +// Only enabled for clang: +// See https://gcc.gnu.org/bugzilla/show_bug.cgi?id=99587 +// GCC may produce "warning: 'retain' attribute ignored" (despite +// __has_attribute(retain) being 1). +#if defined(__clang__) && __has_attribute(retain) +#define RCC_ATTRIBUTE_RETAIN __attribute__ ((__retain__)) +#else +#define RCC_ATTRIBUTE_RETAIN +#endif + +// For deprecations that are not a simple rename, use [[deprecated(MSG)]] +// instead. +#if defined(__clang__) +#define RCC_DEPRECATED_WITH_FIXIT(MSG, FIX) \ + __attribute__ ((deprecated (MSG, FIX))) +#else +#define RCC_DEPRECATED_WITH_FIXIT(MSG, FIX) [[deprecated (MSG)]] +#endif + +// clang-format off +#if defined(__clang__) || defined(__GNUC__) +#define RCC_SUPPRESS_DEPRECATED_DECLARATIONS_PUSH \ + _Pragma("GCC diagnostic push") \ + _Pragma("GCC diagnostic ignored \"-Wdeprecated-declarations\"") +#define RCC_SUPPRESS_DEPRECATED_DECLARATIONS_POP \ + _Pragma("GCC diagnostic pop") +#elif defined(_MSC_VER) +#define RCC_SUPPRESS_DEPRECATED_DECLARATIONS_PUSH \ + __pragma(warning(push)) \ + __pragma(warning(disable : 4996)) +#define RCC_SUPPRESS_DEPRECATED_DECLARATIONS_POP \ + __pragma(warning(pop)) +#else +#define RCC_SUPPRESS_DEPRECATED_DECLARATIONS_PUSH +#define RCC_SUPPRESS_DEPRECATED_DECLARATIONS_POP +#endif +// clang-format on + +// Indicate that a non-static, non-const C++ member function reinitializes +// the entire object to a known state, independent of the previous state of +// the object. +// +// The clang-tidy check bugprone-use-after-move recognizes this attribute as a +// marker that a moved-from object has left the indeterminate state and can be +// reused. +#if RCC_HAS_CPP_ATTRIBUTE(clang::reinitializes) +#define RCC_ATTRIBUTE_REINITIALIZES [[clang::reinitializes]] +#else +#define RCC_ATTRIBUTE_REINITIALIZES +#endif + +// Some compilers warn about unused functions. When a function is sometimes +// used or not depending on build settings (e.g. a function only called from +// within "assert"), this attribute can be used to suppress such warnings. +// +// However, it shouldn't be used for unused *variables*, as those have a much +// more portable solution: +// (void)unused_var_name; +// Prefer cast-to-void wherever it is sufficient. +#if __has_attribute(unused) +#define RCC_ATTRIBUTE_UNUSED __attribute__ ((__unused__)) +#else +#define RCC_ATTRIBUTE_UNUSED +#endif + +// FIXME: Provide this for PE/COFF targets. +#if __has_attribute(weak) && !defined(__MINGW32__) && !defined(__CYGWIN__) && \ + !defined(_WIN32) +#define RCC_HAS_ATTRIBUTE_WEAK 1 +#else +#define RCC_HAS_ATTRIBUTE_WEAK 0 +#endif +#if RCC_HAS_ATTRIBUTE_WEAK +#define RCC_ATTRIBUTE_WEAK __attribute__ ((__weak__)) +#else +#define RCC_ATTRIBUTE_WEAK +#endif + +// Prior to clang 3.2, clang did not accept any spelling of +// __has_attribute(const), so assume it is supported. +#if defined(__clang__) || defined(__GNUC__) +// aka 'CONST' but following RCC Conventions. +#define RCC_READNONE __attribute__ ((__const__)) +#else +#define RCC_READNONE +#endif + +#if __has_attribute(pure) || defined(__GNUC__) +// aka 'PURE' but following RCC Conventions. +#define RCC_READONLY __attribute__ ((__pure__)) +#else +#define RCC_READONLY +#endif + +#if __has_attribute(minsize) +#define RCC_ATTRIBUTE_MINSIZE __attribute__ ((minsize)) +#else +#define RCC_ATTRIBUTE_MINSIZE +#endif + +#if __has_builtin(__builtin_expect) || defined(__GNUC__) +#define RCC_LIKELY(EXPR) __builtin_expect ((bool)(EXPR), true) +#define RCC_UNLIKELY(EXPR) __builtin_expect ((bool)(EXPR), false) +#else +#define RCC_LIKELY(EXPR) (EXPR) +#define RCC_UNLIKELY(EXPR) (EXPR) +#endif + +/// RCC_ATTRIBUTE_NOINLINE - On compilers where we have a directive to do so, +/// mark a method "not for inlining". +#if __has_attribute(noinline) +#define RCC_ATTRIBUTE_NOINLINE __attribute__ ((noinline)) +#elif defined(_MSC_VER) +#define RCC_ATTRIBUTE_NOINLINE __declspec (noinline) +#else +#define RCC_ATTRIBUTE_NOINLINE +#endif + +/// RCC_ATTRIBUTE_ALWAYS_INLINE - On compilers where we have a directive to do +/// so, mark a method "always inline" because it is performance sensitive. +#if __has_attribute(always_inline) +#define RCC_ATTRIBUTE_ALWAYS_INLINE inline __attribute__ ((always_inline)) +#elif defined(_MSC_VER) +#define RCC_ATTRIBUTE_ALWAYS_INLINE __forceinline +#else +#define RCC_ATTRIBUTE_ALWAYS_INLINE inline +#endif + +/// RCC_ATTRIBUTE_ALWAYS_INLINE_UNLESS_DEBUG - Like +/// RCC_ATTRIBUTE_ALWAYS_INLINE but disabled in debug builds to avoid stack +/// overflow with deep recursion. +#if defined(NDEBUG) +#define RCC_ATTRIBUTE_ALWAYS_INLINE_UNLESS_DEBUG RCC_ATTRIBUTE_ALWAYS_INLINE +#else +#define RCC_ATTRIBUTE_ALWAYS_INLINE_UNLESS_DEBUG inline +#endif + +/// RCC_ATTRIBUTE_NO_DEBUG - On compilers where we have a directive to do +/// so, mark a method "no debug" because debug info makes the debugger +/// experience worse. +#if __has_attribute(nodebug) +#define RCC_ATTRIBUTE_NODEBUG __attribute__ ((nodebug)) +#else +#define RCC_ATTRIBUTE_NODEBUG +#endif + +#if __has_attribute(returns_nonnull) +#define RCC_ATTRIBUTE_RETURNS_NONNULL __attribute__ ((returns_nonnull)) +#elif defined(_MSC_VER) +#define RCC_ATTRIBUTE_RETURNS_NONNULL _Ret_notnull_ +#else +#define RCC_ATTRIBUTE_RETURNS_NONNULL +#endif + +/// RCC_ATTRIBUTE_RESTRICT - Annotates a pointer to tell the compiler that +/// it is not aliased in the current scope. +#if defined(__clang__) || defined(__GNUC__) || defined(_MSC_VER) +#define RCC_ATTRIBUTE_RESTRICT __restrict +#else +#define RCC_ATTRIBUTE_RESTRICT +#endif + +/// \macro RCC_ATTRIBUTE_RETURNS_NOALIAS Used to mark a function as returning a +/// pointer that does not alias any other valid pointer. +#ifdef __GNUC__ +#define RCC_ATTRIBUTE_RETURNS_NOALIAS __attribute__ ((__malloc__)) +#elif defined(_MSC_VER) +#define RCC_ATTRIBUTE_RETURNS_NOALIAS __declspec (restrict) +#else +#define RCC_ATTRIBUTE_RETURNS_NOALIAS +#endif + +/// RCC_FALLTHROUGH - Mark fallthrough cases in switch statements. +#if defined(__cplusplus) && __cplusplus > 201402L && \ + RCC_HAS_CPP_ATTRIBUTE(fallthrough) +#define RCC_FALLTHROUGH [[fallthrough]] +#elif RCC_HAS_CPP_ATTRIBUTE(gnu::fallthrough) +#define RCC_FALLTHROUGH [[gnu::fallthrough]] +#elif __has_attribute(fallthrough) +#define RCC_FALLTHROUGH __attribute__ ((fallthrough)) +#elif RCC_HAS_CPP_ATTRIBUTE(clang::fallthrough) +#define RCC_FALLTHROUGH [[clang::fallthrough]] +#else +#define RCC_FALLTHROUGH +#endif + +/// RCC_REQUIRE_CONSTANT_INITIALIZATION - Apply this to globals to ensure that +/// they are constant initialized. +#if RCC_HAS_CPP_ATTRIBUTE(clang::require_constant_initialization) +#define RCC_REQUIRE_CONSTANT_INITIALIZATION \ + [[clang::require_constant_initialization]] +#else +#define RCC_REQUIRE_CONSTANT_INITIALIZATION +#endif + +/// RCC_GSL_OWNER - Apply this to owning classes like SmallVector to enable +/// lifetime warnings. +#if RCC_HAS_CPP_ATTRIBUTE(gsl::Owner) +#define RCC_GSL_OWNER [[gsl::Owner]] +#else +#define RCC_GSL_OWNER +#endif + +/// RCC_GSL_POINTER - Apply this to non-owning classes like +/// StringRef to enable lifetime warnings. +#if RCC_HAS_CPP_ATTRIBUTE(gsl::Pointer) +#define RCC_GSL_POINTER [[gsl::Pointer]] +#else +#define RCC_GSL_POINTER +#endif + +#if RCC_HAS_CPP_ATTRIBUTE(clang::lifetimebound) +#define RCC_LIFETIME_BOUND [[clang::lifetimebound]] +#else +#define RCC_LIFETIME_BOUND +#endif + +#if RCC_HAS_CPP_ATTRIBUTE(nodiscard) >= 201907L +#define RCC_CTOR_NODISCARD [[nodiscard]] +#else +#define RCC_CTOR_NODISCARD +#endif + +// Macro to suppress the MSVC warning C4848: +// "support for attribute [[msvc::no_unique_address]] in C++17 and earlier +// is a vendor extension". +// This warning is removed in versions >= 19.43. +#if !defined(_MSC_VER) || _MSC_VER >= 1943 || defined(__clang__) +#define RCC_SUPPRESS_MSVC_ATTR_IS_VENDOR_EXT_PUSH +#define RCC_SUPPRESS_MSVC_ATTR_IS_VENDOR_EXT_POP +#else // MSVC < 19.43 +#define RCC_SUPPRESS_MSVC_ATTR_IS_VENDOR_EXT_PUSH \ + __pragma (warning (push)) __pragma (warning (disable : 4848)) +#define RCC_SUPPRESS_MSVC_ATTR_IS_VENDOR_EXT_POP __pragma (warning (pop)) +#endif + +#if RCC_HAS_CPP_ATTRIBUTE(no_unique_address) +#define RCC_NO_UNIQUE_ADDRESS [[no_unique_address]] +#elif RCC_HAS_CPP_ATTRIBUTE(msvc::no_unique_address) +#define RCC_NO_UNIQUE_ADDRESS \ + RCC_SUPPRESS_MSVC_ATTR_IS_VENDOR_EXT_PUSH \ + [[msvc::no_unique_address]] RCC_SUPPRESS_MSVC_ATTR_IS_VENDOR_EXT_POP +#else +#define RCC_NO_UNIQUE_ADDRESS +#endif + +/// RCC_EXTENSION - Support compilers where we have a keyword to suppress +/// pedantic diagnostics. +#ifdef __GNUC__ +#define RCC_EXTENSION __extension__ +#else +#define RCC_EXTENSION +#endif + +/// RCC_BUILTIN_UNREACHABLE - On compilers which support it, expands +/// to an expression which states that it is undefined behavior for the +/// compiler to reach this point. Otherwise is not defined. +/// +/// '#else' is intentionally left out so that other macro logic (e.g., +/// RCC_ASSUME_ALIGNED and rcc_unreachable()) can detect whether +/// RCC_BUILTIN_UNREACHABLE has a definition. +#if __has_builtin(__builtin_unreachable) || defined(__GNUC__) +#define RCC_BUILTIN_UNREACHABLE __builtin_unreachable () +#elif defined(_MSC_VER) +#define RCC_BUILTIN_UNREACHABLE __assume (false) +#endif + +/// RCC_BUILTIN_TRAP - On compilers which support it, expands to an expression +/// which causes the program to exit abnormally. +#if __has_builtin(__builtin_trap) || defined(__GNUC__) +#define RCC_BUILTIN_TRAP __builtin_trap () +#elif defined(_MSC_VER) +// The __debugbreak intrinsic is supported by MSVC, does not require forward +// declarations involving platform-specific typedefs (unlike RaiseException), +// results in a call to vectored exception handlers, and encodes to a short +// instruction that still causes the trapping behavior we want. +#define RCC_BUILTIN_TRAP __debugbreak () +#else +#define RCC_BUILTIN_TRAP *(volatile int *)0x11 = 0 +#endif + +/// RCC_BUILTIN_DEBUGTRAP - On compilers which support it, expands to +/// an expression which causes the program to break while running +/// under a debugger. +#if __has_builtin(__builtin_debugtrap) +#define RCC_BUILTIN_DEBUGTRAP __builtin_debugtrap () +#elif defined(_MSC_VER) +// The __debugbreak intrinsic is supported by MSVC and breaks while +// running under the debugger, and also supports invoking a debugger +// when the OS is configured appropriately. +#define RCC_BUILTIN_DEBUGTRAP __debugbreak () +#else +// Just continue execution when built with compilers that have no +// support. This is a debugging aid and not intended to force the +// program to abort if encountered. +#define RCC_BUILTIN_DEBUGTRAP +#endif + +/// \macro RCC_ASSUME_ALIGNED +/// Returns a pointer with an assumed alignment. +#if __has_builtin(__builtin_assume_aligned) || defined(__GNUC__) +#define RCC_ASSUME_ALIGNED(p, a) __builtin_assume_aligned (p, a) +#elif defined(RCC_BUILTIN_UNREACHABLE) +#define RCC_ASSUME_ALIGNED(p, a) \ + (((uintptr_t (p) % (a)) == 0) ? (p) : (RCC_BUILTIN_UNREACHABLE, (p))) +#else +#define RCC_ASSUME_ALIGNED(p, a) (p) +#endif + +/// \macro RCC_PACKED +/// Used to specify a packed structure. +/// RCC_PACKED( +/// struct A { +/// int i; +/// int j; +/// int k; +/// long long l; +/// }); +/// +/// RCC_PACKED_START +/// struct B { +/// int i; +/// int j; +/// int k; +/// long long l; +/// }; +/// RCC_PACKED_END +#ifdef _MSC_VER +#define RCC_PACKED(d) __pragma (pack (push, 1)) d __pragma (pack (pop)) +#define RCC_PACKED_START __pragma (pack (push, 1)) +#define RCC_PACKED_END __pragma (pack (pop)) +#else +#define RCC_PACKED(d) d __attribute__ ((packed)) +#define RCC_PACKED_START _Pragma ("pack(push, 1)") +#define RCC_PACKED_END _Pragma ("pack(pop)") +#endif + +/// \macro RCC_MEMORY_SANITIZER_BUILD +/// Whether RCC itself is built with MemorySanitizer instrumentation. +#if __has_feature(memory_sanitizer) +#define RCC_MEMORY_SANITIZER_BUILD 1 +#include <sanitizer/msan_interface.h> +#define RCC_NO_SANITIZE_MEMORY_ATTRIBUTE __attribute__ ((no_sanitize_memory)) +#else +#define RCC_MEMORY_SANITIZER_BUILD 0 +#define __msan_allocated_memory(p, size) +#define __msan_unpoison(p, size) +#define RCC_NO_SANITIZE_MEMORY_ATTRIBUTE +#endif + +/// \macro RCC_ADDRESS_SANITIZER_BUILD +/// Whether RCC itself is built with AddressSanitizer instrumentation. +#if __has_feature(address_sanitizer) || defined(__SANITIZE_ADDRESS__) +#define RCC_ADDRESS_SANITIZER_BUILD 1 +#if __has_include(<sanitizer/asan_interface.h>) +#include <sanitizer/asan_interface.h> +#else +// These declarations exist to support ASan with MSVC. If MSVC eventually ships +// asan_interface.h in their headers, then we can remove this. +#ifdef __cplusplus +extern "C" +{ +#endif + void + __asan_poison_memory_region (void const volatile *addr, size_t size); + void + __asan_unpoison_memory_region (void const volatile *addr, size_t size); +#ifdef __cplusplus +} // extern "C" +#endif +#endif +#else +#define RCC_ADDRESS_SANITIZER_BUILD 0 +#define __asan_poison_memory_region(p, size) +#define __asan_unpoison_memory_region(p, size) +#endif + +/// \macro RCC_HWADDRESS_SANITIZER_BUILD +/// Whether RCC itself is built with HWAddressSanitizer instrumentation. +#if __has_feature(hwaddress_sanitizer) +#define RCC_HWADDRESS_SANITIZER_BUILD 1 +#else +#define RCC_HWADDRESS_SANITIZER_BUILD 0 +#endif + +/// \macro RCC_THREAD_SANITIZER_BUILD +/// Whether RCC itself is built with ThreadSanitizer instrumentation. +#if __has_feature(thread_sanitizer) || defined(__SANITIZE_THREAD__) +#define RCC_THREAD_SANITIZER_BUILD 1 +#else +#define RCC_THREAD_SANITIZER_BUILD 0 +#endif + +#if RCC_THREAD_SANITIZER_BUILD +// Thread Sanitizer is a tool that finds races in code. +// See http://code.google.com/p/data-race-test/wiki/DynamicAnnotations . +// tsan detects these exact functions by name. +#ifdef __cplusplus +extern "C" +{ +#endif + void + AnnotateHappensAfter (const char *file, int line, const volatile void *cv); + void + AnnotateHappensBefore (const char *file, int line, const volatile void *cv); + void + AnnotateIgnoreWritesBegin (const char *file, int line); + void + AnnotateIgnoreWritesEnd (const char *file, int line); +#ifdef __cplusplus +} +#endif + +// This marker is used to define a happens-before arc. The race detector will +// infer an arc from the begin to the end when they share the same pointer +// argument. +#define TsanHappensBefore(cv) AnnotateHappensBefore (__FILE__, __LINE__, cv) + +// This marker defines the destination of a happens-before arc. +#define TsanHappensAfter(cv) AnnotateHappensAfter (__FILE__, __LINE__, cv) + +// Ignore any races on writes between here and the next TsanIgnoreWritesEnd. +#define TsanIgnoreWritesBegin() AnnotateIgnoreWritesBegin (__FILE__, __LINE__) + +// Resume checking for racy writes. +#define TsanIgnoreWritesEnd() AnnotateIgnoreWritesEnd (__FILE__, __LINE__) +#else +#define TsanHappensBefore(cv) +#define TsanHappensAfter(cv) +#define TsanIgnoreWritesBegin() +#define TsanIgnoreWritesEnd() +#endif + +/// \macro RCC_NO_SANITIZE +/// Disable a particular sanitizer for a function. +#if __has_attribute(no_sanitize) +#define RCC_NO_SANITIZE(KIND) __attribute__ ((no_sanitize (KIND))) +#else +#define RCC_NO_SANITIZE(KIND) +#endif + +/// Mark debug helper function definitions like dump() that should not be +/// stripped from debug builds. +/// Note that you should also surround dump() functions with +/// `#if !defined(NDEBUG) || defined(RCC_ENABLE_DUMP)` so they do always +/// get stripped in release builds. +// FIXME: Move this to a private config.h as it's not usable in public headers. +#if !defined(NDEBUG) || defined(RCC_ENABLE_DUMP) +#define RCC_DUMP_METHOD \ + RCC_ATTRIBUTE_NOINLINE RCC_ATTRIBUTE_USED RCC_ATTRIBUTE_RETAIN +#else +#define RCC_DUMP_METHOD RCC_ATTRIBUTE_NOINLINE +#endif + +/// \macro RCC_PRETTY_FUNCTION +/// Gets a user-friendly looking function signature for the current scope +/// using the best available method on each platform. The exact format of the +/// resulting string is implementation specific and non-portable, so this should +/// only be used, for example, for logging or diagnostics. +#if defined(_MSC_VER) +#define RCC_PRETTY_FUNCTION __FUNCSIG__ +#elif defined(__GNUC__) || defined(__clang__) +#define RCC_PRETTY_FUNCTION __PRETTY_FUNCTION__ +#else +#define RCC_PRETTY_FUNCTION __func__ +#endif + +/// \macro RCC_THREAD_LOCAL +/// A thread-local storage specifier which can be used with globals, +/// extern globals, and static globals. +/// +/// This is essentially an extremely restricted analog to C++11's thread_local +/// support. It uses thread_local if available, falling back on gcc __thread +/// if not. __thread doesn't support many of the C++11 thread_local's +/// features. You should only use this for PODs that you can statically +/// initialize to some constant value. In almost all circumstances this is most +/// appropriate for use with a pointer, integer, or small aggregation of +/// pointers and integers. +#if RCC_ENABLE_THREADS +#if __has_feature(cxx_thread_local) || defined(_MSC_VER) +#define RCC_THREAD_LOCAL thread_local +#else +// Clang, GCC, and other compatible compilers used __thread prior to C++11 and +// we only need the restricted functionality that provides. +#define RCC_THREAD_LOCAL __thread +#endif +#else // !RCC_ENABLE_THREADS +// If threading is disabled entirely, this compiles to nothing and you get +// a normal global variable. +#define RCC_THREAD_LOCAL +#endif + +/// \macro RCC_ENABLE_EXCEPTIONS +/// Whether RCC is built with exception support. +#if __has_feature(cxx_exceptions) +#define RCC_ENABLE_EXCEPTIONS 1 +#elif defined(__GNUC__) && defined(__EXCEPTIONS) +#define RCC_ENABLE_EXCEPTIONS 1 +#elif defined(_MSC_VER) && defined(_CPPUNWIND) +#define RCC_ENABLE_EXCEPTIONS 1 +#endif + +/// \macro RCC_NO_PROFILE_INSTRUMENT_FUNCTION +/// Disable the profile instrument for a function. +#if __has_attribute(no_profile_instrument_function) +#define RCC_NO_PROFILE_INSTRUMENT_FUNCTION \ + __attribute__ ((no_profile_instrument_function)) +#else +#define RCC_NO_PROFILE_INSTRUMENT_FUNCTION +#endif + +/// \macro RCC_PREFERRED_TYPE +/// Adjust type of bit-field in debug info. +#if __has_attribute(preferred_type) +#define RCC_PREFERRED_TYPE(T) __attribute__ ((preferred_type (T))) +#else +#define RCC_PREFERRED_TYPE(T) +#endif + +#if RCC_HAS_CPP_ATTRIBUTE(clang::ptrauth_vtable_pointer) && \ + (defined(__PTRAUTH__) || __has_feature(ptrauth_calls)) +#define RCC_MOVABLE_POLYMORPHIC_TYPE \ + [[clang::ptrauth_vtable_pointer (default_key, no_address_discrimination, \ + default_extra_discrimination)]] +#else +#define RCC_MOVABLE_POLYMORPHIC_TYPE +#endif + +/// \macro RCC_VIRTUAL_ANCHOR_FUNCTION +/// This macro is used to adhere to RCC's policy that each class with a vtable +/// must have at least one out-of-line virtual function. This macro allows us +/// to declare such a function in `final` classes without triggering a warning. +// clang-format off +// Autoformatting makes this look awful. +#if defined(__clang__) + // Make sure this is only parsed if __clang__ is defined + #if __has_warning("-Wunnecessary-virtual-specifier") + #define RCC_DECLARE_VIRTUAL_ANCHOR_FUNCTION() \ + _Pragma("clang diagnostic push") \ + _Pragma("clang diagnostic ignored \"-Wunnecessary-virtual-specifier\"") \ + virtual void anchor() \ + _Pragma("clang diagnostic pop") + #else // __has_warning + #define RCC_DECLARE_VIRTUAL_ANCHOR_FUNCTION() \ + virtual void anchor() + #endif +#else // defined(__clang__) + #define RCC_DECLARE_VIRTUAL_ANCHOR_FUNCTION() \ + virtual void anchor() +#endif +// clang-format on + +/// \macro RCC_IS_X86 +/// Whether the target architecture is x86 / x86-64. +#if defined(__x86_64__) || defined(__i386__) +#define RCC_IS_X86 1 +#else +#define RCC_IS_X86 0 +#endif + +/// \macro RCC_TARGET_SSE42 +/// Function attribute to compile a function with SSE4.2 enabled. +#if defined(__has_attribute) && __has_attribute(target) +#define RCC_TARGET_SSE42 __attribute__ ((target ("sse4.2"))) +#else +#define RCC_TARGET_SSE42 +#endif + +#if __has_builtin(__builtin_cpu_supports) && \ + (defined(__linux__) || defined(__APPLE__)) +#define RCC_CPU_SUPPORTS(feature) __builtin_cpu_supports (feature) +#else +#define RCC_CPU_SUPPORTS(feature) 0 +#endif + +/// \macro RCC_CPU_SUPPORTS_SSE42 +/// Expands to true if the runtime cpu supports SSE4.2, or if compiled with +/// SSE4.2 enabled. +#if defined(__SSE4_2__) +#define RCC_CPU_SUPPORTS_SSE42 1 +#else +#define RCC_CPU_SUPPORTS_SSE42 RCC_CPU_SUPPORTS ("sse4.2") +#endif + +#endif diff --git a/rcc/include/rcc/Support/Errno.h b/rcc/include/rcc/Support/Errno.h new file mode 100644 index 0000000..21d8d78 --- /dev/null +++ b/rcc/include/rcc/Support/Errno.h @@ -0,0 +1,64 @@ +/* + 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/>. +*/ + +// This implementation is based on llvm/include/llvm/Support/Errno.h. + +#ifndef RCC_SUPPORT_ERRNO_H +#define RCC_SUPPORT_ERRNO_H + +#include "rcc/Support/Compiler.h" + +#include <cerrno> +#include <string> + +namespace rcc +{ +namespace sys +{ + +/// Returns a string representation of the errno value, using whatever +/// thread-safe variant of strerror() is available. Be sure to call this +/// immediately after the function that set errno, or errno may have been +/// overwritten by an intervening call. +RCC_ABI std::string +StrError (); + +/// Like the no-argument version above, but uses \p errnum instead of errno. +RCC_ABI std::string +StrError (int errnum); + +template <typename FailT, typename Fun, typename... Args> +inline decltype (auto) +RetryAfterSignal (const FailT &Fail, const Fun &F, const Args &...As) +{ + decltype (F (As...)) Res; + do + { + errno = 0; + Res = F (As...); + } while (Res == Fail && errno == EINTR); + return Res; +} + +} // namespace sys +} // namespace rcc + +#endif // RCC_SUPPORT_ERRNO_H diff --git a/rcc/include/rcc/Support/ErrorOr.h b/rcc/include/rcc/Support/ErrorOr.h new file mode 100644 index 0000000..0e17822 --- /dev/null +++ b/rcc/include/rcc/Support/ErrorOr.h @@ -0,0 +1,378 @@ +/* + 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/>. +*/ + +//===- llvm/Support/ErrorOr.h - Error Smart Pointer -------------*- 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 +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// +/// Provides ErrorOr<T> smart pointer. +/// +//===----------------------------------------------------------------------===// + +#ifndef RCC_SUPPORT_ERROROR_H +#define RCC_SUPPORT_ERROROR_H + +#include <cassert> +#include <functional> // for std::reference_wrapper +#include <system_error> +#include <type_traits> +#include <utility> + +namespace rcc +{ + +/// Represents either an error or a value T. +/// +/// ErrorOr<T> is a pointer-like class that represents the result of an +/// operation. The result is either an error, or a value of type T. This is +/// designed to emulate the usage of returning a pointer where nullptr indicates +/// failure. However instead of just knowing that the operation failed, we also +/// have an error_code that describes why it failed. +/// +/// It is used like the following. +/// \code +/// ErrorOr<Buffer> getBuffer(); +/// +/// auto buffer = getBuffer(); +/// if (error_code ec = buffer.getError()) +/// return ec; +/// buffer->write("adena"); +/// \endcode +/// +/// +/// Implicit conversion to bool returns true if there is a usable value. The +/// unary * and -> operators provide pointer like access to the value. Accessing +/// the value when there is an error has undefined behavior. +/// +/// When T is a reference type the behavior is slightly different. The reference +/// is held in a std::reference_wrapper<std::remove_reference<T>::type>, and +/// there is special handling to make operator -> work as if T was not a +/// reference. +/// +/// T cannot be a rvalue reference. +template <class T> class ErrorOr +{ + template <class OtherT> friend class ErrorOr; + + static constexpr bool isRef = std::is_reference_v<T>; + + using wrap = std::reference_wrapper<std::remove_reference_t<T>>; + +public: + using storage_type = std::conditional_t<isRef, wrap, T>; + +private: + using reference = std::remove_reference_t<T> &; + using const_reference = const std::remove_reference_t<T> &; + using pointer = std::remove_reference_t<T> *; + using const_pointer = const std::remove_reference_t<T> *; + +public: + template <class E> + ErrorOr (E ErrorCode, + std::enable_if_t<std::is_error_code_enum<E>::value || + std::is_error_condition_enum<E>::value, + void *> = nullptr) + : HasError (true) + { + new (getErrorStorage ()) std::error_code (make_error_code (ErrorCode)); + } + + ErrorOr (std::error_code EC) : HasError (true) + { + new (getErrorStorage ()) std::error_code (EC); + } + + template <class OtherT> + ErrorOr (OtherT &&Val, + std::enable_if_t<std::is_convertible_v<OtherT, T>> * = nullptr) + : HasError (false) + { + new (getStorage ()) storage_type (std::forward<OtherT> (Val)); + } + + ErrorOr (const ErrorOr &Other) + { + copyConstruct (Other); + } + + template <class OtherT> + ErrorOr (const ErrorOr<OtherT> &Other, + std::enable_if_t<std::is_convertible_v<OtherT, T>> * = nullptr) + { + copyConstruct (Other); + } + + template <class OtherT> + explicit ErrorOr ( + const ErrorOr<OtherT> &Other, + std::enable_if_t<!std::is_convertible_v<OtherT, const T &>> * = nullptr) + { + copyConstruct (Other); + } + + ErrorOr (ErrorOr &&Other) + { + moveConstruct (std::move (Other)); + } + + template <class OtherT> + ErrorOr (ErrorOr<OtherT> &&Other, + std::enable_if_t<std::is_convertible_v<OtherT, T>> * = nullptr) + { + moveConstruct (std::move (Other)); + } + + // This might eventually need SFINAE but it's more complex than is_convertible + // & I'm too lazy to write it right now. + template <class OtherT> + explicit ErrorOr ( + ErrorOr<OtherT> &&Other, + std::enable_if_t<!std::is_convertible_v<OtherT, T>> * = nullptr) + { + moveConstruct (std::move (Other)); + } + + ErrorOr & + operator= (const ErrorOr &Other) + { + copyAssign (Other); + return *this; + } + + ErrorOr & + operator= (ErrorOr &&Other) + { + moveAssign (std::move (Other)); + return *this; + } + + ~ErrorOr () + { + if (!HasError) + { + getStorage ()->~storage_type (); + } + } + + /// Return false if there is an error. + explicit + operator bool () const + { + return !HasError; + } + + reference + get () + { + return *getStorage (); + } + const_reference + get () const + { + return const_cast<ErrorOr<T> *> (this)->get (); + } + + std::error_code + getError () const + { + return HasError ? *getErrorStorage () : std::error_code (); + } + + pointer + operator->() + { + return toPointer (getStorage ()); + } + + const_pointer + operator->() const + { + return toPointer (getStorage ()); + } + + reference + operator* () + { + return *getStorage (); + } + + const_reference + operator* () const + { + return *getStorage (); + } + +private: + template <class OtherT> + void + copyConstruct (const ErrorOr<OtherT> &Other) + { + if (!Other.HasError) + { + // Get the other value. + HasError = false; + new (getStorage ()) storage_type (*Other.getStorage ()); + } + else + { + // Get other's error. + HasError = true; + new (getErrorStorage ()) std::error_code (Other.getError ()); + } + } + + template <class T1> + static bool + compareThisIfSameType (const T1 &a, const T1 &b) + { + return &a == &b; + } + + template <class T1, class T2> + static bool + compareThisIfSameType (const T1 &, const T2 &) + { + return false; + } + + template <class OtherT> + void + copyAssign (const ErrorOr<OtherT> &Other) + { + if (compareThisIfSameType (*this, Other)) + { + return; + } + + this->~ErrorOr (); + new (this) ErrorOr (Other); + } + + template <class OtherT> + void + moveConstruct (ErrorOr<OtherT> &&Other) + { + if (!Other.HasError) + { + // Get the other value. + HasError = false; + new (getStorage ()) storage_type (std::move (*Other.getStorage ())); + } + else + { + // Get other's error. + HasError = true; + new (getErrorStorage ()) std::error_code (Other.getError ()); + } + } + + template <class OtherT> + void + moveAssign (ErrorOr<OtherT> &&Other) + { + if (compareThisIfSameType (*this, Other)) + { + return; + } + + this->~ErrorOr (); + new (this) ErrorOr (std::move (Other)); + } + + pointer + toPointer (pointer Val) + { + return Val; + } + + const_pointer + toPointer (const_pointer Val) const + { + return Val; + } + + pointer + toPointer (wrap *Val) + { + return &Val->get (); + } + + const_pointer + toPointer (const wrap *Val) const + { + return &Val->get (); + } + + storage_type * + getStorage () + { + assert (!HasError && "Cannot get value when an error exists!"); + return &TStorage; + } + + const storage_type * + getStorage () const + { + assert (!HasError && "Cannot get value when an error exists!"); + return &TStorage; + } + + std::error_code * + getErrorStorage () + { + assert (HasError && "Cannot get error when a value exists!"); + return &ErrorStorage; + } + + const std::error_code * + getErrorStorage () const + { + assert (HasError && "Cannot get error when a value exists!"); + return &ErrorStorage; + } + + union + { + storage_type TStorage; + std::error_code ErrorStorage; + }; + bool HasError : 1; +}; + +template <class T, class E> +std::enable_if_t<std::is_error_code_enum<E>::value || + std::is_error_condition_enum<E>::value, + bool> +operator== (const ErrorOr<T> &Err, E Code) +{ + return Err.getError () == Code; +} + +} // end namespace rcc + +#endif // RCC_SUPPORT_ERROROR_H diff --git a/rcc/include/rcc/Support/MathExtras.h b/rcc/include/rcc/Support/MathExtras.h new file mode 100644 index 0000000..6ea2a1f --- /dev/null +++ b/rcc/include/rcc/Support/MathExtras.h @@ -0,0 +1,1050 @@ +/* + 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/>. +*/ + +//===-- llvm/Support/MathExtras.h - Useful math functions -------*- 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 contains some functions that are useful for math stuff. +// +//===----------------------------------------------------------------------===// + +#ifndef RCC_SUPPORT_MATHEXTRAS_H +#define RCC_SUPPORT_MATHEXTRAS_H + +#include "rcc/Support/Compiler.h" +#include <bit> +#include <cassert> +#include <climits> +#include <cstdint> +#include <cstring> +#include <limits> +#include <numbers> +#include <type_traits> +#include <utility> + +namespace rcc +{ +/// Some template parameter helpers to optimize for bitwidth, for functions that +/// take multiple arguments. + +// We can't verify signedness, since callers rely on implicit coercions to +// signed/unsigned. +template <typename T, typename U> +using enableif_int = + std::enable_if_t<std::is_integral_v<T> && std::is_integral_v<U>>; + +// Use std::common_type_t to widen only up to the widest argument. +template <typename T, typename U, typename = enableif_int<T, U>> +using common_uint = + std::common_type_t<std::make_unsigned_t<T>, std::make_unsigned_t<U>>; +template <typename T, typename U, typename = enableif_int<T, U>> +using common_sint = + std::common_type_t<std::make_signed_t<T>, std::make_signed_t<U>>; + +/// Mathematical constants. +namespace numbers +{ +// clang-format off +inline constexpr float ef = std::numbers::e_v<float>; +inline constexpr float egammaf = std::numbers::egamma_v<float>; +inline constexpr float ln2f = std::numbers::ln2_v<float>; +inline constexpr float ln10f = std::numbers::ln10_v<float>; +inline constexpr float log2ef = std::numbers::log2e_v<float>; +inline constexpr float log10ef = std::numbers::log10e_v<float>; +inline constexpr float pif = std::numbers::pi_v<float>; +inline constexpr float inv_pif = std::numbers::inv_pi_v<float>; +inline constexpr float inv_sqrtpif = std::numbers::inv_sqrtpi_v<float>; +inline constexpr float sqrt2f = std::numbers::sqrt2_v<float>; +inline constexpr float inv_sqrt2f = 1.0f / std::numbers::sqrt2_v<float>; +inline constexpr float sqrt3f = std::numbers::sqrt3_v<float>; +inline constexpr float inv_sqrt3f = std::numbers::inv_sqrt3_v<float>; +inline constexpr float phif = std::numbers::phi_v<float>; + +// sqrtpi is not in C++20 std::numbers. +template <typename T, typename = std::enable_if_t<std::is_floating_point_v<T>>> +inline constexpr T sqrtpi_v = T(0x1.c5bf891b4ef6bP+0); // (1.7724538509055160273) https://oeis.org/A002161 +inline constexpr double sqrtpi = sqrtpi_v<double>; +inline constexpr float sqrtpif = sqrtpi_v<float>; + +// These string literals are taken from below: +// https://github.com/bminor/glibc/blob/8543577b04ded6d979ffcc5a818930e4d74d0645/math/math.h#L1215-L1229 +constexpr const char *pis = "3.141592653589793238462643383279502884", + *inv_pis = "0.318309886183790671537767526745028724"; +// clang-format on +} // namespace numbers + +/// Create a bitmask with the N right-most bits set to 1, and all other +/// bits set to 0. Only unsigned types are allowed. +template <typename T> +constexpr T +maskTrailingOnes (unsigned N) +{ + static_assert (std::is_unsigned_v<T>, "Invalid type!"); + const unsigned Bits = CHAR_BIT * sizeof (T); + assert (N <= Bits && "Invalid bit index"); + if (N == 0) + { + return 0; + } + return T (-1) >> (Bits - N); +} + +/// Create a bitmask with the N left-most bits set to 1, and all other +/// bits set to 0. Only unsigned types are allowed. +template <typename T> +constexpr T +maskLeadingOnes (unsigned N) +{ + return ~maskTrailingOnes<T> (CHAR_BIT * sizeof (T) - N); +} + +/// Create a bitmask with the N right-most bits set to 0, and all other +/// bits set to 1. Only unsigned types are allowed. +template <typename T> +constexpr T +maskTrailingZeros (unsigned N) +{ + return maskLeadingOnes<T> (CHAR_BIT * sizeof (T) - N); +} + +/// Create a bitmask with the N left-most bits set to 0, and all other +/// bits set to 1. Only unsigned types are allowed. +template <typename T> +constexpr T +maskLeadingZeros (unsigned N) +{ + return maskTrailingOnes<T> (CHAR_BIT * sizeof (T) - N); +} + +/// Macro compressed bit reversal table for 256 bits. +/// +/// http://graphics.stanford.edu/~seander/bithacks.html#BitReverseTable +static const unsigned char BitReverseTable256[256] = { +#define R2(n) n, n + 2 * 64, n + 1 * 64, n + 3 * 64 +#define R4(n) R2 (n), R2 (n + 2 * 16), R2 (n + 1 * 16), R2 (n + 3 * 16) +#define R6(n) R4 (n), R4 (n + 2 * 4), R4 (n + 1 * 4), R4 (n + 3 * 4) + R6 (0), R6 (2), R6 (1), R6 (3) +#undef R2 +#undef R4 +#undef R6 +}; + +/// Reverse the bits in \p Val. +template <typename T> +constexpr T +reverseBits (T Val) +{ +#if __has_builtin(__builtin_bitreverse8) + if constexpr (std::is_same_v<T, uint8_t>) + { + return __builtin_bitreverse8 (Val); + } +#endif +#if __has_builtin(__builtin_bitreverse16) + if constexpr (std::is_same_v<T, uint16_t>) + { + return __builtin_bitreverse16 (Val); + } +#endif +#if __has_builtin(__builtin_bitreverse32) + if constexpr (std::is_same_v<T, uint32_t>) + { + return __builtin_bitreverse32 (Val); + } +#endif +#if __has_builtin(__builtin_bitreverse64) + if constexpr (std::is_same_v<T, uint64_t>) + { + return __builtin_bitreverse64 (Val); + } +#endif + + unsigned char in[sizeof (Val)]; + unsigned char out[sizeof (Val)]; + std::memcpy (in, &Val, sizeof (Val)); + for (unsigned i = 0; i < sizeof (Val); ++i) + { + out[(sizeof (Val) - i) - 1] = BitReverseTable256[in[i]]; + } + std::memcpy (&Val, out, sizeof (Val)); + return Val; +} + +// NOTE: The following support functions use the _32/_64 extensions instead of +// type overloading so that signed and unsigned integers can be used without +// ambiguity. + +/// Return the high 32 bits of a 64 bit value. +constexpr uint32_t +Hi_32 (uint64_t Value) +{ + return static_cast<uint32_t> (Value >> 32); +} + +/// Return the low 32 bits of a 64 bit value. +constexpr uint32_t +Lo_32 (uint64_t Value) +{ + return static_cast<uint32_t> (Value); +} + +/// Make a 64-bit integer from a high / low pair of 32-bit integers. +constexpr uint64_t +Make_64 (uint32_t High, uint32_t Low) +{ + return ((uint64_t)High << 32) | (uint64_t)Low; +} + +/// Checks if an integer fits into the given bit width. +template <unsigned N> +constexpr bool +isInt (int64_t x) +{ + if constexpr (N == 0) + { + return 0 == x; + } + if constexpr (N == 8) + { + return static_cast<int8_t> (x) == x; + } + if constexpr (N == 16) + { + return static_cast<int16_t> (x) == x; + } + if constexpr (N == 32) + { + return static_cast<int32_t> (x) == x; + } + if constexpr (N < 64) + { + return -(INT64_C (1) << (N - 1)) <= x && x < (INT64_C (1) << (N - 1)); + } + (void)x; // MSVC v19.25 warns that x is unused. + return true; +} + +/// Checks if a signed integer is an N bit number shifted left by S. +template <unsigned N, unsigned S> +constexpr bool +isShiftedInt (int64_t x) +{ + static_assert (S < 64, "isShiftedInt<N, S> with S >= 64 is too much."); + static_assert (N + S <= 64, + "isShiftedInt<N, S> with N + S > 64 is too wide."); + return isInt<N + S> (x) && (x % (UINT64_C (1) << S) == 0); +} + +/// Checks if an unsigned integer fits into the given bit width. +template <unsigned N> +constexpr bool +isUInt (uint64_t x) +{ + if constexpr (N < 64) + { + return (x >> N) == 0; + } + (void)x; // MSVC v19.25 warns that x is unused. + return true; +} + +/// Checks if a unsigned integer is an N bit number shifted left by S. +template <unsigned N, unsigned S> +constexpr bool +isShiftedUInt (uint64_t x) +{ + static_assert (S < 64, "isShiftedUInt<N, S> with S >= 64 is too much."); + static_assert (N + S <= 64, + "isShiftedUInt<N, S> with N + S > 64 is too wide."); + // S must be strictly less than 64. So 1 << S is not undefined behavior. + return isUInt<N + S> (x) && (x % (UINT64_C (1) << S) == 0); +} + +/// Gets the maximum value for a N-bit unsigned integer. +inline constexpr uint64_t +maxUIntN (uint64_t N) +{ + assert (N <= 64 && "integer width out of range"); + + // uint64_t(1) << 64 is undefined behavior, so we can't do + // (uint64_t(1) << N) - 1 + // without checking first that N != 64. But this works and doesn't have a + // branch for N != 0. + // Unfortunately, shifting a uint64_t right by 64 bit is undefined + // behavior, so the condition on N == 0 is necessary. Fortunately, most + // optimizers do not emit branches for this check. + if (N == 0) + { + return 0; + } + return UINT64_MAX >> (64 - N); +} + +/// Gets the minimum value for a N-bit signed integer. +inline constexpr int64_t +minIntN (int64_t N) +{ + assert (N <= 64 && "integer width out of range"); + + if (N == 0) + { + return 0; + } + return UINT64_MAX << (N - 1); +} + +/// Gets the maximum value for a N-bit signed integer. +inline constexpr int64_t +maxIntN (int64_t N) +{ + assert (N <= 64 && "integer width out of range"); + + // This relies on two's complement wraparound when N == 64, so we convert to + // int64_t only at the very end to avoid UB. + if (N == 0) + { + return 0; + } + return (UINT64_C (1) << (N - 1)) - 1; +} + +/// Checks if an unsigned integer fits into the given (dynamic) bit width. +inline constexpr bool +isUIntN (unsigned N, uint64_t x) +{ + return N >= 64 || (x >> N) == 0; +} + +/// Checks if an signed integer fits into the given (dynamic) bit width. +inline constexpr bool +isIntN (unsigned N, int64_t x) +{ + return N >= 64 || (minIntN (N) <= x && x <= maxIntN (N)); +} + +/// Return true if the argument is a non-empty sequence of ones starting at the +/// least significant bit with the remainder zero (32 bit version). +/// Ex. isMask_32(0x0000FFFFU) == true. +constexpr bool +isMask_32 (uint32_t Value) +{ + return Value && ((Value + 1) & Value) == 0; +} + +/// Return true if the argument is a non-empty sequence of ones starting at the +/// least significant bit with the remainder zero (64 bit version). +constexpr bool +isMask_64 (uint64_t Value) +{ + return Value && ((Value + 1) & Value) == 0; +} + +/// Return true if the argument contains a non-empty sequence of ones with the +/// remainder zero (32 bit version.) Ex. isShiftedMask_32(0x0000FF00U) == true. +constexpr bool +isShiftedMask_32 (uint32_t Value) +{ + return Value && isMask_32 ((Value - 1) | Value); +} + +/// Return true if the argument contains a non-empty sequence of ones with the +/// remainder zero (64 bit version.) +constexpr bool +isShiftedMask_64 (uint64_t Value) +{ + return Value && isMask_64 ((Value - 1) | Value); +} + +/// Return true if the argument is a power of two > 0. +/// Ex. isPowerOf2_32(0x00100000U) == true (32 bit edition.) +constexpr bool +isPowerOf2_32 (uint32_t Value) +{ + return std::has_single_bit (Value); +} + +/// Return true if the argument is a power of two > 0 (64 bit edition.) +constexpr bool +isPowerOf2_64 (uint64_t Value) +{ + return std::has_single_bit (Value); +} + +/// Return true if the argument contains a non-empty sequence of ones with the +/// remainder zero (32 bit version.) Ex. isShiftedMask_32(0x0000FF00U) == true. +/// If true, \p MaskIdx will specify the index of the lowest set bit and \p +/// MaskLen is updated to specify the length of the mask, else neither are +/// updated. +inline bool +isShiftedMask_32 (uint32_t Value, unsigned &MaskIdx, unsigned &MaskLen) +{ + if (!isShiftedMask_32 (Value)) + { + return false; + } + MaskIdx = std::countr_zero (Value); + MaskLen = std::popcount (Value); + return true; +} + +/// Return true if the argument contains a non-empty sequence of ones with the +/// remainder zero (64 bit version.) If true, \p MaskIdx will specify the index +/// of the lowest set bit and \p MaskLen is updated to specify the length of the +/// mask, else neither are updated. +inline bool +isShiftedMask_64 (uint64_t Value, unsigned &MaskIdx, unsigned &MaskLen) +{ + if (!isShiftedMask_64 (Value)) + { + return false; + } + MaskIdx = std::countr_zero (Value); + MaskLen = std::popcount (Value); + return true; +} + +/// Compile time Log2. +/// Valid only for positive powers of two. +template <size_t kValue> +constexpr size_t +ConstantLog2 () +{ + static_assert (rcc::isPowerOf2_64 (kValue), + "Value is not a valid power of 2"); + return std::countr_zero (kValue); +} + +/// Return the floor log base 2 of the specified value, -1 if the value is zero. +/// (32 bit edition.) +/// Ex. Log2_32(32) == 5, Log2_32(1) == 0, Log2_32(0) == -1, Log2_32(6) == 2 +inline unsigned +Log2_32 (uint32_t Value) +{ + return 31 - std::countl_zero (Value); +} + +/// Return the floor log base 2 of the specified value, -1 if the value is zero. +/// (64 bit edition.) +inline unsigned +Log2_64 (uint64_t Value) +{ + return 63 - std::countl_zero (Value); +} + +/// Return the ceil log base 2 of the specified value, 32 if the value is zero. +/// (32 bit edition). +/// Ex. Log2_32_Ceil(32) == 5, Log2_32_Ceil(1) == 0, Log2_32_Ceil(6) == 3 +inline unsigned +Log2_32_Ceil (uint32_t Value) +{ + return 32 - std::countl_zero (Value - 1); +} + +/// Return the ceil log base 2 of the specified value, 64 if the value is zero. +/// (64 bit edition.) +inline unsigned +Log2_64_Ceil (uint64_t Value) +{ + return 64 - std::countl_zero (Value - 1); +} + +/// A and B are either alignments or offsets. Return the minimum alignment that +/// may be assumed after adding the two together. +template <typename U, typename V, typename T = common_uint<U, V>> +constexpr T +MinAlign (U A, V B) +{ + // The largest power of 2 that divides both A and B. + // + // Replace "-Value" by "1+~Value" in the following commented code to avoid + // MSVC warning C4146 + // return (A | B) & -(A | B); + return (A | B) & (1 + ~(A | B)); +} + +/// Fallback when arguments aren't integral. +constexpr uint64_t +MinAlign (uint64_t A, uint64_t B) +{ + return (A | B) & (1 + ~(A | B)); +} + +/// Returns the next power of two (in 64-bits) that is strictly greater than A. +/// Returns zero on overflow. +constexpr uint64_t +NextPowerOf2 (uint64_t A) +{ + A |= (A >> 1); + A |= (A >> 2); + A |= (A >> 4); + A |= (A >> 8); + A |= (A >> 16); + A |= (A >> 32); + return A + 1; +} + +/// Returns the power of two which is greater than or equal to the given value. +/// Essentially, it is a ceil operation across the domain of powers of two. +inline uint64_t +PowerOf2Ceil (uint64_t A) +{ + if (!A || A > UINT64_MAX / 2) + { + return 0; + } + return UINT64_C (1) << Log2_64_Ceil (A); +} + +/// Returns the integer ceil(Numerator / Denominator). Unsigned version. +/// Guaranteed to never overflow. +template <typename U, typename V, typename T = common_uint<U, V>> +constexpr T +divideCeil (U Numerator, V Denominator) +{ + assert (Denominator && "Division by zero"); + T Bias = (Numerator != 0); + return (Numerator - Bias) / Denominator + Bias; +} + +/// Fallback when arguments aren't integral. +constexpr uint64_t +divideCeil (uint64_t Numerator, uint64_t Denominator) +{ + assert (Denominator && "Division by zero"); + uint64_t Bias = (Numerator != 0); + return (Numerator - Bias) / Denominator + Bias; +} + +// Check whether divideCeilSigned or divideFloorSigned would overflow. This +// happens only when Numerator = INT_MIN and Denominator = -1. +template <typename U, typename V> +constexpr bool +divideSignedWouldOverflow (U Numerator, V Denominator) +{ + return Numerator == std::numeric_limits<U>::min () && Denominator == -1; +} + +/// Returns the integer ceil(Numerator / Denominator). Signed version. +/// Overflow is explicitly forbidden with an assert. +template <typename U, typename V, typename T = common_sint<U, V>> +constexpr T +divideCeilSigned (U Numerator, V Denominator) +{ + assert (Denominator && "Division by zero"); + assert (!divideSignedWouldOverflow (Numerator, Denominator) && + "Divide would overflow"); + if (!Numerator) + { + return 0; + } + // C's integer division rounds towards 0. + T Bias = Denominator >= 0 ? 1 : -1; + bool SameSign = (Numerator >= 0) == (Denominator >= 0); + return SameSign ? (Numerator - Bias) / Denominator + 1 + : Numerator / Denominator; +} + +/// Returns the integer floor(Numerator / Denominator). Signed version. +/// Overflow is explicitly forbidden with an assert. +template <typename U, typename V, typename T = common_sint<U, V>> +constexpr T +divideFloorSigned (U Numerator, V Denominator) +{ + assert (Denominator && "Division by zero"); + assert (!divideSignedWouldOverflow (Numerator, Denominator) && + "Divide would overflow"); + if (!Numerator) + { + return 0; + } + // C's integer division rounds towards 0. + T Bias = Denominator >= 0 ? -1 : 1; + bool SameSign = (Numerator >= 0) == (Denominator >= 0); + return SameSign ? Numerator / Denominator + : (Numerator - Bias) / Denominator - 1; +} + +/// Returns the remainder of the Euclidean division of LHS by RHS. Result is +/// always non-negative. +template <typename U, typename V, typename T = common_sint<U, V>> +constexpr T +mod (U Numerator, V Denominator) +{ + assert (Denominator >= 1 && "Mod by non-positive number"); + T Mod = Numerator % Denominator; + return Mod < 0 ? Mod + Denominator : Mod; +} + +/// Returns (Numerator / Denominator) rounded by round-half-up. Guaranteed to +/// never overflow. +template <typename U, typename V, typename T = common_uint<U, V>> +constexpr T +divideNearest (U Numerator, V Denominator) +{ + assert (Denominator && "Division by zero"); + T Mod = Numerator % Denominator; + return (Numerator / Denominator) + + (Mod > (static_cast<T> (Denominator) - 1) / 2); +} + +/// Returns the next integer (mod 2**nbits) that is greater than or equal to +/// \p Value and is a multiple of \p Align. \p Align must be non-zero. +/// +/// Examples: +/// \code +/// alignTo(5, 8) = 8 +/// alignTo(17, 8) = 24 +/// alignTo(~0LL, 8) = 0 +/// alignTo(321, 255) = 510 +/// \endcode +/// +/// Will overflow only if result is not representable in T. +template <typename U, typename V, typename T = common_uint<U, V>> +constexpr T +alignTo (U Value, V Align) +{ + assert (Align != 0u && "Align can't be 0."); + T CeilDiv = divideCeil (Value, Align); + return CeilDiv * Align; +} + +/// Fallback when arguments aren't integral. +constexpr uint64_t +alignTo (uint64_t Value, uint64_t Align) +{ + assert (Align != 0u && "Align can't be 0."); + uint64_t CeilDiv = divideCeil (Value, Align); + return CeilDiv * Align; +} + +/// Will overflow only if result is not representable in T. +template <typename U, typename V, typename T = common_uint<U, V>> +constexpr T +alignToPowerOf2 (U Value, V Align) +{ + assert (Align != 0 && (Align & (Align - 1)) == 0 && + "Align must be a power of 2"); + T NegAlign = static_cast<T> (0) - Align; + return (Value + (Align - 1)) & NegAlign; +} + +/// Fallback when arguments aren't integral. +constexpr uint64_t +alignToPowerOf2 (uint64_t Value, uint64_t Align) +{ + assert (Align != 0 && (Align & (Align - 1)) == 0 && + "Align must be a power of 2"); + uint64_t NegAlign = 0 - Align; + return (Value + (Align - 1)) & NegAlign; +} + +/// If non-zero \p Skew is specified, the return value will be a minimal integer +/// that is greater than or equal to \p Size and equal to \p A * N + \p Skew for +/// some integer N. If \p Skew is larger than \p A, its value is adjusted to '\p +/// Skew mod \p A'. \p Align must be non-zero. +/// +/// Examples: +/// \code +/// alignTo(5, 8, 7) = 7 +/// alignTo(17, 8, 1) = 17 +/// alignTo(~0LL, 8, 3) = 3 +/// alignTo(321, 255, 42) = 552 +/// \endcode +/// +/// May overflow. +template <typename U, typename V, typename W, + typename T = common_uint<common_uint<U, V>, W>> +constexpr T +alignTo (U Value, V Align, W Skew) +{ + assert (Align != 0u && "Align can't be 0."); + Skew %= Align; + return alignTo (Value - Skew, Align) + Skew; +} + +/// Returns the next integer (mod 2**nbits) that is greater than or equal to +/// \p Value and is a multiple of \c Align. \c Align must be non-zero. +/// +/// Will overflow only if result is not representable in T. +template <auto Align, typename V, typename T = common_uint<decltype (Align), V>> +constexpr T +alignTo (V Value) +{ + static_assert (Align != 0u, "Align must be non-zero"); + T CeilDiv = divideCeil (Value, Align); + return CeilDiv * Align; +} + +/// Returns the largest unsigned integer less than or equal to \p Value and is +/// \p Skew mod \p Align. \p Align must be non-zero. Guaranteed to never +/// overflow. +template <typename U, typename V, typename W = uint8_t, + typename T = common_uint<common_uint<U, V>, W>> +constexpr T +alignDown (U Value, V Align, W Skew = 0) +{ + assert (Align != 0u && "Align can't be 0."); + Skew %= Align; + return (Value - Skew) / Align * Align + Skew; +} + +/// Sign-extend the number in the bottom B bits of X to a 32-bit integer. +/// Requires B <= 32. +template <unsigned B> +constexpr int32_t +SignExtend32 (uint32_t X) +{ + static_assert (B <= 32, "Bit width out of range."); + if constexpr (B == 0) + { + return 0; + } + return int32_t (X << (32 - B)) >> (32 - B); +} + +/// Sign-extend the number in the bottom B bits of X to a 32-bit integer. +/// Requires B <= 32. +inline int32_t +SignExtend32 (uint32_t X, unsigned B) +{ + assert (B <= 32 && "Bit width out of range."); + if (B == 0) + { + return 0; + } + return int32_t (X << (32 - B)) >> (32 - B); +} + +/// Sign-extend the number in the bottom B bits of X to a 64-bit integer. +/// Requires B <= 64. +template <unsigned B> +constexpr int64_t +SignExtend64 (uint64_t x) +{ + static_assert (B <= 64, "Bit width out of range."); + if constexpr (B == 0) + { + return 0; + } + return int64_t (x << (64 - B)) >> (64 - B); +} + +/// Sign-extend the number in the bottom B bits of X to a 64-bit integer. +/// Requires B <= 64. +inline int64_t +SignExtend64 (uint64_t X, unsigned B) +{ + assert (B <= 64 && "Bit width out of range."); + if (B == 0) + { + return 0; + } + return int64_t (X << (64 - B)) >> (64 - B); +} + +/// Return the absolute value of a signed integer, converted to the +/// corresponding unsigned integer type. Avoids undefined behavior in std::abs +/// when you pass it INT_MIN or similar. +template <typename T, typename U = std::make_unsigned_t<T>> +constexpr U +AbsoluteValue (T X) +{ + // If X is negative, cast it to the unsigned type _before_ negating it. + return X < 0 ? -static_cast<U> (X) : X; +} + +/// Subtract two unsigned integers, X and Y, of type T and return the absolute +/// value of the result. +template <typename U, typename V, typename T = common_uint<U, V>> +constexpr T +AbsoluteDifference (U X, V Y) +{ + return X > Y ? (X - Y) : (Y - X); +} + +/// Add two unsigned integers, X and Y, of type T. Clamp the result to the +/// maximum representable value of T on overflow. ResultOverflowed indicates if +/// the result is larger than the maximum representable value of type T. +template <typename T> +std::enable_if_t<std::is_unsigned_v<T>, T> +SaturatingAdd (T X, T Y, bool *ResultOverflowed = nullptr) +{ + bool Dummy; + bool &Overflowed = ResultOverflowed ? *ResultOverflowed : Dummy; + // Hacker's Delight, p. 29 + T Z = X + Y; + Overflowed = (Z < X || Z < Y); + if (Overflowed) + { + return std::numeric_limits<T>::max (); + } + else + { + return Z; + } +} + +/// Add multiple unsigned integers of type T. Clamp the result to the +/// maximum representable value of T on overflow. +template <class T, class... Ts> +std::enable_if_t<std::is_unsigned_v<T>, T> +SaturatingAdd (T X, T Y, T Z, Ts... Args) +{ + bool Overflowed = false; + T XY = SaturatingAdd (X, Y, &Overflowed); + if (Overflowed) + { + return SaturatingAdd (std::numeric_limits<T>::max (), T (1), Args...); + } + return SaturatingAdd (XY, Z, Args...); +} + +/// Multiply two unsigned integers, X and Y, of type T. Clamp the result to the +/// maximum representable value of T on overflow. ResultOverflowed indicates if +/// the result is larger than the maximum representable value of type T. +template <typename T> +std::enable_if_t<std::is_unsigned_v<T>, T> +SaturatingMultiply (T X, T Y, bool *ResultOverflowed = nullptr) +{ + bool Dummy; + bool &Overflowed = ResultOverflowed ? *ResultOverflowed : Dummy; + + // Hacker's Delight, p. 30 has a different algorithm, but we don't use that + // because it fails for uint16_t (where multiplication can have undefined + // behavior due to promotion to int), and requires a division in addition + // to the multiplication. + + Overflowed = false; + + // Log2(Z) would be either Log2Z or Log2Z + 1. + // Special case: if X or Y is 0, Log2_64 gives -1, and Log2Z + // will necessarily be less than Log2Max as desired. + int Log2Z = Log2_64 (X) + Log2_64 (Y); + const T Max = std::numeric_limits<T>::max (); + int Log2Max = Log2_64 (Max); + if (Log2Z < Log2Max) + { + return X * Y; + } + if (Log2Z > Log2Max) + { + Overflowed = true; + return Max; + } + + // We're going to use the top bit, and maybe overflow one + // bit past it. Multiply all but the bottom bit then add + // that on at the end. + T Z = (X >> 1) * Y; + if (Z & ~(Max >> 1)) + { + Overflowed = true; + return Max; + } + Z <<= 1; + if (X & 1) + { + return SaturatingAdd (Z, Y, ResultOverflowed); + } + + return Z; +} + +/// Multiply two unsigned integers, X and Y, and add the unsigned integer, A to +/// the product. Clamp the result to the maximum representable value of T on +/// overflow. ResultOverflowed indicates if the result is larger than the +/// maximum representable value of type T. +template <typename T> +std::enable_if_t<std::is_unsigned_v<T>, T> +SaturatingMultiplyAdd (T X, T Y, T A, bool *ResultOverflowed = nullptr) +{ + bool Dummy; + bool &Overflowed = ResultOverflowed ? *ResultOverflowed : Dummy; + + T Product = SaturatingMultiply (X, Y, &Overflowed); + if (Overflowed) + { + return Product; + } + + return SaturatingAdd (A, Product, &Overflowed); +} + +/// Use this rather than HUGE_VALF; the latter causes warnings on MSVC. +RCC_ABI extern const float huge_valf; + +/// Add two signed integers, computing the two's complement truncated result, +/// returning a pair {result, overflow}, where "overflow" is a boolean value +/// indicating whether an overflow occurred. +template <typename T> +constexpr std::enable_if_t<std::is_signed_v<T>, std::pair<T, bool>> +AddOverflow (T X, T Y) +{ + // Perform the unsigned addition. + using U = std::make_unsigned_t<T>; + const U UX = static_cast<U> (X); + const U UY = static_cast<U> (Y); + const U UResult = UX + UY; + + // Convert to signed. + auto Result = static_cast<T> (UResult); + + // Adding two positive numbers should result in a positive number. + if (X > 0 && Y > 0) + { + return {Result, Result <= 0}; + } + // Adding two negatives should result in a negative number. + if (X < 0 && Y < 0) + { + return {Result, Result >= 0}; + } + return {Result, false}; +} + +/// Add two signed integers, computing the two's complement truncated result, +/// returning true if overflow occurred. +template <typename T> +std::enable_if_t<std::is_signed_v<T>, bool> +AddOverflow (T X, T Y, T &Result) +{ +#if __has_builtin(__builtin_add_overflow) + return __builtin_add_overflow (X, Y, &Result); +#else + auto [Res, Ovf] = AddOverflow (X, Y); + Result = Res; + return Ovf; +#endif +} + +/// Subtract two signed integers, computing the two's complement truncated +/// result, returning a pair {result, overflow}, where "overflow" is a +/// boolean value indicating whether an overflow occurred. +template <typename T> +constexpr std::enable_if_t<std::is_signed_v<T>, std::pair<T, bool>> +SubOverflow (T X, T Y) +{ + // Perform the unsigned addition. + using U = std::make_unsigned_t<T>; + const U UX = static_cast<U> (X); + const U UY = static_cast<U> (Y); + const U UResult = UX - UY; + + // Convert to signed. + auto Result = static_cast<T> (UResult); + + // Subtracting a positive number from a negative results in a negative number. + if (X <= 0 && Y > 0) + { + return {Result, Result >= 0}; + } + // Subtracting a negative number from a positive results in a positive number. + if (X >= 0 && Y < 0) + { + return {Result, Result <= 0}; + } + return {Result, false}; +} + +/// Subtract two signed integers, computing the two's complement truncated +/// result, returning true if an overflow occurred. +template <typename T> +std::enable_if_t<std::is_signed_v<T>, bool> +SubOverflow (T X, T Y, T &Result) +{ +#if __has_builtin(__builtin_sub_overflow) + return __builtin_sub_overflow (X, Y, &Result); +#else + auto [Res, Ovf] = SubOverflow (X, Y); + Result = Res; + return Ovf; +#endif +} + +/// Multiply two signed integers, computing the two's complement truncated +/// result, returning a pair {result, overflow}, where "overflow" is a +/// boolean value indicating whether an overflow occurred. +template <typename T> +constexpr std::enable_if_t<std::is_signed_v<T>, std::pair<T, bool>> +MulOverflow (T X, T Y) +{ + // Perform the unsigned multiplication on absolute values. + using U = std::make_unsigned_t<T>; + const U UX = X < 0 ? (0 - static_cast<U> (X)) : static_cast<U> (X); + const U UY = Y < 0 ? (0 - static_cast<U> (Y)) : static_cast<U> (Y); + const U UResult = UX * UY; + + // Convert to signed. + const bool IsNegative = (X < 0) ^ (Y < 0); + auto Result = IsNegative ? (0 - UResult) : UResult; + + // If any of the args was 0, result is 0 and no overflow occurs. + if (UX == 0 || UY == 0) + { + return {Result, false}; + } + + // UX and UY are in [1, 2^n], where n is the number of digits. + // Check how the max allowed absolute value (2^n for negative, 2^(n-1) for + // positive) divided by an argument compares to the other. + bool Overflow = + IsNegative + ? UX > (static_cast<U> (std::numeric_limits<T>::max ()) + U (1)) / UY + : UX > (static_cast<U> (std::numeric_limits<T>::max ())) / UY; + + return {Result, Overflow}; +} + +/// Multiply two signed integers, computing the two's complement truncated +/// result, returning true if an overflow occurred. +template <typename T> +std::enable_if_t<std::is_signed_v<T>, bool> +MulOverflow (T X, T Y, T &Result) +{ +#if __has_builtin(__builtin_mul_overflow) + return __builtin_mul_overflow (X, Y, &Result); +#else + auto [Res, Ovf] = MulOverflow (X, Y); + Result = Res; + return Ovf; +#endif +} + +/// Type to force float point values onto the stack, so that x86 doesn't add +/// hidden precision, avoiding rounding differences on various platforms. +#if defined(__i386__) || defined(_M_IX86) +using stack_float_t = volatile float; +#else +using stack_float_t = float; +#endif + +/// Returns the number of digits in the given integer. +RCC_ABI int +NumDigitsBase10 (uint64_t X); + +} // namespace rcc + +#endif diff --git a/rcc/lib/CMakeLists.txt b/rcc/lib/CMakeLists.txt new file mode 100644 index 0000000..6ab18c4 --- /dev/null +++ b/rcc/lib/CMakeLists.txt @@ -0,0 +1,7 @@ +# SPDX-License-Identifier: GPL-3.0-or-later + +add_rcc_library(RCCSupport + STATIC + Support/Errno.cc + Support/MathExtras.cc +) diff --git a/rcc/lib/Support/Errno.cc b/rcc/lib/Support/Errno.cc new file mode 100644 index 0000000..f0161a6 --- /dev/null +++ b/rcc/lib/Support/Errno.cc @@ -0,0 +1,79 @@ +/* + 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/>. +*/ + +// This implementation is based on llvm/lib/Support/Errno.cpp. + +#include "rcc/Support/Errno.h" +#include "rcc/Config/config.h" + +#include <cstring> +#include <errno.h> + +namespace rcc +{ +namespace sys +{ + +std::string +StrError () +{ + return StrError (errno); +} + +std::string +StrError (int errnum) +{ + std::string str; + if (errnum == 0) + { + return str; + } + +#if defined(HAVE_STRERROR_R) || HAVE_DECL_STRERROR_S + const int maxErrStrLen = 2000; + char buffer[maxErrStrLen]; + buffer[0] = '\0'; +#endif + +#ifdef HAVE_STRERROR_R + // strerror_r is thread-safe. +#if defined(__GLIBC__) && defined(_GNU_SOURCE) + // glibc defines its own incompatible version of strerror_r which may not + // use the buffer supplied. + str = strerror_r (errnum, buffer, maxErrStrLen - 1); +#else + strerror_r (errnum, buffer, maxErrStrLen - 1); + str = buffer; +#endif +#elif HAVE_DECL_STRERROR_S + // Windows Secure API. + strerror_s (buffer, maxErrStrLen - 1, errnum); + str = buffer; +#else + // Copy the thread-unsafe result of strerror into the buffer as fast as + // possible to minimize the impact of collisions in multiple threads. + str = strerror (errnum); +#endif + return str; +} + +} // namespace sys +} // namespace rcc diff --git a/rcc/lib/Support/MathExtras.cc b/rcc/lib/Support/MathExtras.cc new file mode 100644 index 0000000..f0db3a4 --- /dev/null +++ b/rcc/lib/Support/MathExtras.cc @@ -0,0 +1,97 @@ +/* + 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/>. +*/ + +//===-- MathExtras.cpp - Implement the MathExtras header --------------===// +// +// 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 implements the MathExtras.h header +// +//===----------------------------------------------------------------------===// + +#include "rcc/Support/MathExtras.h" + +#ifdef _MSC_VER +#include <limits> +#else +#include <cmath> +#endif + +namespace rcc +{ + +#if defined(_MSC_VER) +// Visual Studio defines the HUGE_VAL class of macros using purposeful +// constant arithmetic overflow, which it then warns on when encountered. +const float huge_valf = std::numeric_limits<float>::infinity (); +#else +const float huge_valf = HUGE_VALF; +#endif + +/// Returns the number of digits in the given integer. +int +NumDigitsBase10 (uint64_t X) +{ + static constexpr struct ConstexprData + { + uint8_t AtLeast[65] = {}; + uint64_t Boundaries[20] = {}; + static constexpr int + NumDigitsConstexpr (uint64_t N) + { + int res = 1; + while (N >= 10) + { + res++; + N /= 10; + } + return res; + } + constexpr ConstexprData () + { + uint64_t Val = ~0ull; + for (uint64_t i = 0; i <= 64; i++) + { + uint64_t Digits = NumDigitsConstexpr (Val) - 1; + AtLeast[i] = Digits; + Val >>= 1; + } + // Special case because X=0 should return 1 and not 0 + Boundaries[0] = 0; + Val = 10; + for (uint64_t i = 1; i < 20; i++) + { + Boundaries[i] = Val; + Val *= 10; + } + } + } Data; + + uint64_t Base2 = X ? std::countl_zero (X) : 64; + uint64_t Digits = Data.AtLeast[Base2]; + return Digits + (X >= Data.Boundaries[Digits]); +} + +} // namespace rcc 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) |
