From ad3f990267dd578e74956fefb2e61685e3c3a98d Mon Sep 17 00:00:00 2001 From: DaanDeMeyer Date: Sat, 17 Mar 2018 20:03:41 +0100 Subject: [PATCH] Add CMake as alternative build system (#526) --- CMakeLists.txt | 254 +++++++++++++++++++++++++++++ cmake/DefaultCMakeBuildType.cmake | 18 ++ cmake/DownloadAndExtract7zip.cmake | 51 ++++++ cmake/DownloadAndExtractLLVM.cmake | 107 ++++++++++++ cmake/FindClang.cmake | 143 ++++++++++++++++ 5 files changed, 573 insertions(+) create mode 100644 CMakeLists.txt create mode 100644 cmake/DefaultCMakeBuildType.cmake create mode 100644 cmake/DownloadAndExtract7zip.cmake create mode 100644 cmake/DownloadAndExtractLLVM.cmake create mode 100644 cmake/FindClang.cmake diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 00000000..c779f5bf --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,254 @@ +cmake_minimum_required(VERSION 3.1) +project(cquery LANGUAGES CXX) + +list(APPEND CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/cmake/) +include(DefaultCMakeBuildType) +include(DownloadAndExtractLLVM) + +set(CLANG_VERSION 6.0.0 CACHE STRING "Downloaded Clang version (6.0.0)") +option(SYSTEM_CLANG "Use system Clang instead of downloading Clang" OFF) +option(ASAN "Compile with address sanitizers" OFF) +option(CLANG_CXX "Build with Clang C++ api required by some cquery \ +features (warning: not available in LLVM Windows downloads)" OFF) + +# Sources for the executable are specified at end of CMakeLists.txt +add_executable(cquery "") + +### Compile options + +# CMake default compile flags: +# MSVC + Clang(Windows): +# debug: /MDd /Zi /Ob0 /Od /RTC1 +# release: /MD /O2 /Ob2 /DNDEBUG +# GCC + Clang(Linux): +# debug: -g +# release: -O3 -DNDEBUG + +# Enable C++14 (Required) +set_property(TARGET cquery PROPERTY CXX_STANDARD 14) +set_property(TARGET cquery PROPERTY CXX_STANDARD_REQUIRED ON) +# Disable gcc extensions +set_property(TARGET cquery PROPERTY CXX_EXTENSIONS OFF) + +if(${CMAKE_SYSTEM_NAME} STREQUAL Windows) + # Common MSVC/Clang(Windows) options + target_compile_options(cquery PRIVATE + /nologo + /EHsc + /W3 # roughly -Wall + /wd4996 # disable loguru unsafe warnings + /wd4722 # ignores warning C4722 + # (destructor never returns) in loguru + /wd4267 # ignores warning C4267 + # (conversion from 'size_t' to 'type'), + # roughly -Wno-sign-compare + /wd4800 + $<$:/FS> + ) +else() + # Common GCC/Clang(Linux) options + target_compile_options(cquery PRIVATE + -Wall + -Wno-sign-compare + ) + + if(${CMAKE_CXX_COMPILER_ID} STREQUAL GNU) + target_compile_options(cquery PRIVATE -Wno-return-type -Wno-unused-result) + endif() + + if(${CMAKE_CXX_COMPILER_ID} STREQUAL Clang) + target_compile_options(cquery PRIVATE + $<$:-fno-limit-debug-info>) + endif() + + if(CLANG_CXX) + # -Wno-comment: include/clang/Format/Format.h error: multi-line comment + # -fno-rtti: # Without -fno-rtti, some Clang C++ functions may report + # `undefined references to typeinfo` + target_compile_options(cquery PRIVATE -Wno-comment -fno-rtti) + endif() + + if(ASAN) + target_compile_options(cquery PRIVATE -fsanitize=address,undefined) + # target_link_libraries also takes linker flags + target_link_libraries(cquery PRIVATE -fsanitize=address,undefined) + endif() +endif() + +### Download Clang if required + +if(NOT SYSTEM_CLANG) + download_and_extract_llvm(${CLANG_VERSION}) + # Used by FindClang + set(CLANG_ROOT ${DOWNLOADED_CLANG_DIR}) +endif() + +### Libraries + +# See cmake/FindClang.cmake +find_package(Clang REQUIRED) +target_link_libraries(cquery PRIVATE Clang::Clang) + +# Enable threading support +set(THREADS_PREFER_PTHREAD_FLAG ON) +find_package(Threads REQUIRED) +target_link_libraries(cquery PRIVATE Threads::Threads) + +if(${CMAKE_SYSTEM_NAME} STREQUAL Linux) + # (either through hardware or OS support) + # loguru calls dladdr + target_link_libraries(cquery PRIVATE ${CMAKE_DL_LIBS}) + +elseif(${CMAKE_SYSTEM_NAME} STREQUAL FreeBSD) + # loguru::stacktrace_as_stdstring calls backtrace_symbols + # sparsepp/spp_memory.h uses libkvm + # src/platform_posix.cc uses libthr + find_package(Backtrace REQUIRED) + target_link_libraries(cquery PRIVATE ${Backtrace_LIBRARIES} kvm thr) + +elseif(${CMAKE_SYSTEM_NAME} STREQUAL Windows) + # sparsepp/spp_memory.h uses LibPsapi + target_link_libraries(cquery PRIVATE Psapi) +endif() + +if(CLANG_CXX) + # Clang C++ api uses ncurses + find_package(Curses REQUIRED) + target_link_libraries(cquery PRIVATE ${CURSES_LIBRARIES}) +endif() + +### Definitions + +target_compile_definitions(cquery PRIVATE + LOGURU_WITH_STREAMS=1 + LOGURU_FILENAME_WIDTH=18 + LOGURU_THREADNAME_WIDTH=13 + DEFAULT_RESOURCE_DIRECTORY="${Clang_RESOURCE_DIR}") + +if(CLANG_CXX) + target_compile_definitions(cquery PRIVATE USE_CLANG_CXX=1 LOGURU_RTTI=0) +endif() + +### Includes + +target_include_directories(cquery PRIVATE + src + third_party + third_party/rapidjson/include + third_party/sparsepp + third_party/loguru + third_party/doctest + third_party/msgpack-c/include) + +### Install + +install(TARGETS cquery RUNTIME DESTINATION bin) + +# We don't need to install libclang on Windows if we are using downloaded LLVM +# since libclang is distributed as a static library on Windows +if(NOT SYSTEM_CLANG AND NOT ${CMAKE_SYSTEM_NAME} STREQUAL Windows) + + if(${CMAKE_SYSTEM_NAME} MATCHES Linux|FreeBSD) + set_property(TARGET cquery APPEND PROPERTY + INSTALL_RPATH $ORIGIN/../lib) + elseif(${CMAKE_SYSTEM_NAME} STREQUAL Darwin) + set_property(TARGET cquery APPEND PROPERTY + INSTALL_RPATH @loader_path/../lib) + endif() + + file(GLOB LIBCLANG_PLUS_SYMLINKS + ${DOWNLOADED_CLANG_DIR}/lib/libclang.[so,dylib]*) + install(FILES ${LIBCLANG_PLUS_SYMLINKS} DESTINATION lib) +endif() + +### Sources + +target_sources(cquery PRIVATE third_party/siphash.cc) + +target_sources(cquery PRIVATE + src/cache_manager.cc + src/clang_complete.cc + src/clang_cursor.cc + src/clang_format.cc + src/clang_index.cc + src/clang_indexer.cc + src/clang_translation_unit.cc + src/clang_utils.cc + src/code_complete_cache.cc + src/command_line.cc + src/diagnostics_engine.cc + src/file_consumer.cc + src/file_contents.cc + src/fuzzy_match.cc + src/iindexer.cc + src/import_manager.cc + src/import_pipeline.cc + src/include_complete.cc + src/ipc.cc + src/lex_utils.cc + src/lsp_diagnostic.cc + src/lsp.cc + src/match.cc + src/message_handler.cc + src/options.cc + src/platform_posix.cc + src/platform_win.cc + src/platform.cc + src/port.cc + src/position.cc + src/project.cc + src/query_utils.cc + src/query.cc + src/queue_manager.cc + src/recorder.cc + src/semantic_highlight_symbol_cache.cc + src/serializer.cc + src/standard_includes.cc + src/task.cc + src/test.cc + src/third_party_impl.cc + src/timer.cc + src/timestamp_manager.cc + src/type_printer.cc + src/utils.cc + src/work_thread.cc + src/working_files.cc) + +target_sources(cquery PRIVATE + src/messages/cquery_base.cc + src/messages/cquery_call_hierarchy.cc + src/messages/cquery_callers.cc + src/messages/cquery_derived.cc + src/messages/cquery_did_view.cc + src/messages/cquery_file_info.cc + src/messages/cquery_freshen_index.cc + src/messages/cquery_index_file.cc + src/messages/cquery_inheritance_hierarchy.cc + src/messages/cquery_member_hierarchy.cc + src/messages/cquery_random.cc + src/messages/cquery_vars.cc + src/messages/cquery_wait.cc + src/messages/exit.cc + src/messages/initialize.cc + src/messages/shutdown.cc + src/messages/text_document_code_action.cc + src/messages/text_document_code_lens.cc + src/messages/text_document_completion.cc + src/messages/text_document_definition.cc + src/messages/text_document_did_change.cc + src/messages/text_document_did_close.cc + src/messages/text_document_did_open.cc + src/messages/text_document_did_save.cc + src/messages/text_document_document_highlight.cc + src/messages/text_document_document_link.cc + src/messages/text_document_document_symbol.cc + src/messages/text_document_formatting.cc + src/messages/text_document_hover.cc + src/messages/text_document_range_formatting.cc + src/messages/text_document_references.cc + src/messages/text_document_rename.cc + src/messages/text_document_signature_help.cc + src/messages/text_document_type_definition.cc + src/messages/workspace_did_change_configuration.cc + src/messages/workspace_did_change_watched_files.cc + src/messages/workspace_symbol.cc) \ No newline at end of file diff --git a/cmake/DefaultCMakeBuildType.cmake b/cmake/DefaultCMakeBuildType.cmake new file mode 100644 index 00000000..fa86f433 --- /dev/null +++ b/cmake/DefaultCMakeBuildType.cmake @@ -0,0 +1,18 @@ +# Set a default build type if none was specified +set(DEFAULT_CMAKE_BUILD_TYPE Release) +if(EXISTS ${CMAKE_SOURCE_DIR}/.git) + set(DEFAULT_CMAKE_BUILD_TYPE Debug) +endif() + +# CMAKE_BUILD_TYPE is not available if a multi-configuration generator is used +# (eg Visual Studio generators) +if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES) + message(STATUS "Setting build type to '${DEFAULT_CMAKE_BUILD_TYPE}' as none \ +was specified.") + set(CMAKE_BUILD_TYPE ${DEFAULT_CMAKE_BUILD_TYPE} + CACHE STRING "Choose the type of build." FORCE) + + # Set the possible values of build type for cmake-gui + set_property(CACHE CMAKE_BUILD_TYPE + PROPERTY STRINGS Debug Release MinSizeRel RelWithDebInfo) +endif() \ No newline at end of file diff --git a/cmake/DownloadAndExtract7zip.cmake b/cmake/DownloadAndExtract7zip.cmake new file mode 100644 index 00000000..f3257e6b --- /dev/null +++ b/cmake/DownloadAndExtract7zip.cmake @@ -0,0 +1,51 @@ +# Downloads and extracts the 7-Zip MSI installer from https://www.7-zip.org/. +# +# Returns the extracted 7-Zip directory in DOWNLOADED_7ZIP_DIR +# +# Downloads 7-Zip to extract LLVM if it isn't available in the PATH +function(download_and_extract_7zip) + +set(7ZIP_VERSION 1801) +set(7ZIP_EXT .msi) +set(7ZIP_NAME 7z${7ZIP_VERSION}-x64) +set(7ZIP_FULL_NAME ${7ZIP_NAME}${7ZIP_EXT}) + +set(7ZIP_FILE ${CMAKE_BINARY_DIR}/${7ZIP_FULL_NAME}) +set(7ZIP_EXTRACT_DIR ${CMAKE_BINARY_DIR}/${7ZIP_NAME}) +set(7ZIP_URL https://www.7-zip.org/a/${7ZIP_FULL_NAME}) + +# msiexec requires Windows path separators (\) +file(TO_NATIVE_PATH ${7ZIP_FILE} 7ZIP_FILE) +file(TO_NATIVE_PATH ${7ZIP_EXTRACT_DIR} 7ZIP_EXTRACT_DIR) + +if(NOT EXISTS ${7ZIP_FILE}) + message(STATUS "Downloading 7-Zip ${7ZIP_VERSION} (${7ZIP_URL}) ...") + file(DOWNLOAD ${7ZIP_URL} ${7ZIP_FILE}) +endif() + +if(NOT EXISTS ${7ZIP_EXTRACT_DIR}) + + find_program(MSIEXEC_EXECUTABLE msiexec) + if(NOT MSIEXEC_EXECUTABLE) + message(FATAL_ERROR "Unable to find msiexec (required to extract 7-Zip msi \ +installer). Install 7-Zip yourself and make sure it is available in the path") + endif() + + message(STATUS "Extracting downloaded 7-Zip ...") + + # msiexec with /a option allows extraction of msi installers without requiring + # admin privileges. We use this to extract the 7-Zip installer without + # requiring any actions from the user + execute_process(COMMAND ${MSIEXEC_EXECUTABLE} /a ${7ZIP_FILE} /qn + TARGETDIR=${7ZIP_EXTRACT_DIR} + OUTPUT_QUIET) +endif() + +# Convert back to CMake separators (/) before returning +file(TO_CMAKE_PATH ${7ZIP_EXTRACT_DIR} 7ZIP_EXTRACT_DIR) + +# Actual directory is nested inside the extract directory. We return the nested +# directory instead of the extract directory +set(DOWNLOADED_7ZIP_DIR ${7ZIP_EXTRACT_DIR}/Files/7-Zip PARENT_SCOPE) + +endfunction() \ No newline at end of file diff --git a/cmake/DownloadAndExtractLLVM.cmake b/cmake/DownloadAndExtractLLVM.cmake new file mode 100644 index 00000000..8fcdda64 --- /dev/null +++ b/cmake/DownloadAndExtractLLVM.cmake @@ -0,0 +1,107 @@ +# Downloads and extracts the LLVM archive for the current system from +# https://releases.llvm.org +# +# Returns the extracted LLVM archive directory in DOWNLOADED_CLANG_DIR +# +# Downloads 7-Zip to extract LLVM if it isn't available in the PATH +function(download_and_extract_llvm CLANG_VERSION) + +include(DownloadAndExtract7zip) + +set(CLANG_ARCHIVE_EXT .tar.xz) + +if(${CMAKE_SYSTEM_NAME} STREQUAL Linux) + + set(CLANG_ARCHIVE_NAME + clang+llvm-${CLANG_VERSION}-x86_64-linux-gnu-ubuntu-14.04) + +elseif(${CMAKE_SYSTEM_NAME} STREQUAL Darwin) + + set(CLANG_ARCHIVE_NAME clang+llvm-${CLANG_VERSION}-x86_64-apple-darwin) + +elseif(${CMAKE_SYSTEM_NAME} STREQUAL Windows) + + set(CLANG_ARCHIVE_NAME LLVM-${CLANG_VERSION}-win64) + set(CLANG_ARCHIVE_EXT .exe) + +elseif(${CMAKE_SYSTEM_NAME} STREQUAL FreeBSD) + + set(CLANG_ARCHIVE_NAME clang+llvm-${CLANG_VERSION}-amd64-unknown-freebsd10) + +endif() + +set(CLANG_ARCHIVE_FULL_NAME ${CLANG_ARCHIVE_NAME}${CLANG_ARCHIVE_EXT}) +set(CLANG_ARCHIVE_FILE ${CMAKE_BINARY_DIR}/${CLANG_ARCHIVE_FULL_NAME}) +set(CLANG_ARCHIVE_EXTRACT_DIR ${CMAKE_BINARY_DIR}/${CLANG_ARCHIVE_NAME}) +set(CLANG_ARCHIVE_URL + https://releases.llvm.org/${CLANG_VERSION}/${CLANG_ARCHIVE_FULL_NAME}) + +if(NOT EXISTS ${CLANG_ARCHIVE_FILE}) + message(STATUS "Downloading LLVM ${CLANG_VERSION} (${CLANG_ARCHIVE_URL}) ...") + file(DOWNLOAD ${CLANG_ARCHIVE_URL} ${CLANG_ARCHIVE_FILE}) +endif() + +if(NOT EXISTS ${CLANG_ARCHIVE_EXTRACT_DIR}) + if(${CLANG_ARCHIVE_EXT} STREQUAL .exe) + find_program(7ZIP_EXECUTABLE 7z) + + if(NOT 7ZIP_EXECUTABLE) + message(STATUS "7-Zip not found in PATH") + download_and_extract_7zip() + find_program(7ZIP_EXECUTABLE 7z NO_DEFAULT_PATH + PATHS ${DOWNLOADED_7ZIP_DIR}) + else() + message(STATUS "7-Zip found in PATH") + endif() + + message(STATUS "Extracting downloaded LLVM with 7-Zip ...") + + # Avoid running the LLVM installer by extracting the exe with 7-Zip + execute_process(COMMAND ${7ZIP_EXECUTABLE} x + -o${CLANG_ARCHIVE_EXTRACT_DIR} + -xr!$PLUGINSDIR ${CLANG_ARCHIVE_FILE} + OUTPUT_QUIET) + elseif(${CLANG_ARCHIVE_EXT} STREQUAL .tar.xz) + message(STATUS "Extracting downloaded LLVM with CMake built-in tar ...") + # CMake has builtin support for tar via the -E flag + execute_process(COMMAND ${CMAKE_COMMAND} -E tar -xf ${CLANG_ARCHIVE_FILE} + OUTPUT_QUIET) + endif() + + # There is a null pointer dereference issue in + # tools/libclang/CXIndexDataConsumer.cpp handleReference. + # https://github.com/cquery-project/cquery/issues/219 + if(${CMAKE_SYSTEM_NAME} STREQUAL Linux AND + ${CLANG_VERSION} MATCHES 4.0.0|5.0.1) + message(STATUS "Patching downloaded LLVM (see \ +https://github.com/cquery-project/cquery/issues/219)") + + if(${CLANG_VERSION} STREQUAL 4.0.0) + # 4289205 = $[0x4172b5] (we use decimals for seek since execute_process + # does not evaluate $[] bash syntax) + execute_process(COMMAND printf \\x4d + COMMAND dd + of=${CLANG_ARCHIVE_EXTRACT_DIR}/lib/libclang.so.4.0 + obs=1 seek=4289205 conv=notrunc + OUTPUT_QUIET) + + elseif(${CLANG_VERSION} STREQUAL 5.0.1) + # 4697806 = $[0x47aece] + execute_process(COMMAND printf \\x4d + COMMAND dd + of=${CLANG_ARCHIVE_EXTRACT_DIR}/lib/libclang.so.5.0 + obs=1 seek=4697806 conv=notrunc + OUTPUT_QUIET) + endif() + endif() +endif() + +# CMake functions have no return values so we just lift our return variable to +# the parent scope +set(DOWNLOADED_CLANG_DIR ${CLANG_ARCHIVE_EXTRACT_DIR} PARENT_SCOPE) + +endfunction() + + + + diff --git a/cmake/FindClang.cmake b/cmake/FindClang.cmake new file mode 100644 index 00000000..57524aca --- /dev/null +++ b/cmake/FindClang.cmake @@ -0,0 +1,143 @@ +#.rst +# FindClang +# --------- +# +# Find Clang and LLVM libraries required by cquery +# +# Results are reported in the following variables:: +# +# Clang_FOUND - True if headers and requested libraries were found +# Clang_EXECUTABLE - Clang executable +# Clang_RESOURCE_DIR - Clang resource directory +# Clang_VERSION - Clang version as reported by Clang executable +# +# The following :prop_tgt:`IMPORTED` targets are also defined:: +# +# Clang::Clang - Target for all required Clang libraries and headers +# +# This module reads hints about which libraries to look for and where to find +# them from the following variables:: +# +# CLANG_CXX - Search for and add Clang C++ libraries +# CLANG_ROOT - If set, only look for Clang components in CLANG_ROOT +# +# Example to link against Clang target:: +# +# target_link_libraries( PRIVATE Clang::Clang) + +### Definitions + +# Wrapper macro's around the find_* macro's from CMake that only search in +# CLANG_ROOT if it is defined + +macro(_Clang_find_library VAR NAME) + # Windows needs lib prefix + if (CLANG_ROOT) + find_library(${VAR} NAMES ${NAME} lib${NAME} + NO_DEFAULT_PATH PATHS ${CLANG_ROOT} PATH_SUFFIXES lib) + else() + find_library(${VAR} NAMES ${NAME} lib${NAME}) + endif() +endmacro() + +macro(_Clang_find_path VAR INCLUDE_FILE) + if (CLANG_ROOT) + find_path(${VAR} ${INCLUDE_FILE} + NO_DEFAULT_PATH PATHS ${CLANG_ROOT} PATH_SUFFIXES include) + else() + find_path(${VAR} ${INCLUDE_FILE}) + endif() +endmacro() + +macro(_Clang_find_program VAR NAME) + if (CLANG_ROOT) + find_program(${VAR} ${NAME} + NO_DEFAULT_PATH PATHS ${CLANG_ROOT} PATH_SUFFIXES bin) + else() + find_program(${VAR} ${NAME}) + endif() +endmacro() + +# Macro to avoid duplicating logic for each Clang C++ library +macro(_Clang_find_and_add_cxx_lib NAME INCLUDE_FILE) + # Find library + _Clang_find_library(Clang_${NAME}_LIBRARY ${NAME}) + list(APPEND _Clang_REQUIRED_VARS Clang_${NAME}_LIBRARY) + list(APPEND _Clang_CXX_LIBRARIES ${Clang_${NAME}_LIBRARY}) + + # Find corresponding include directory + _Clang_find_path(Clang_${NAME}_INCLUDE_DIR ${INCLUDE_FILE}) + list(APPEND _Clang_REQUIRED_VARS Clang_${NAME}_INCLUDE_DIR) + list(APPEND _Clang_CXX_INCLUDE_DIRS ${Clang_${NAME}_INCLUDE_DIR}) +endmacro() + +### Start + +set(_Clang_REQUIRED_VARS Clang_LIBRARY Clang_INCLUDE_DIR Clang_EXECUTABLE + Clang_RESOURCE_DIR Clang_VERSION) + +_Clang_find_library(Clang_LIBRARY clang) +_Clang_find_path(Clang_INCLUDE_DIR clang-c/Index.h) + +if(CLANG_CXX) + # The order is derived by topological sorting LINK_LIBS in + # clang/lib/*/CMakeLists.txt + _Clang_find_and_add_cxx_lib(clangFormat clang/Format/Format.h) + _Clang_find_and_add_cxx_lib(clangToolingCore clang/Tooling/Core/Diagnostic.h) + _Clang_find_and_add_cxx_lib(clangRewrite clang/Rewrite/Core/Rewriter.h) + _Clang_find_and_add_cxx_lib(clangAST clang/AST/AST.h) + _Clang_find_and_add_cxx_lib(clangLex clang/Lex/Lexer.h) + _Clang_find_and_add_cxx_lib(clangBasic clang/Basic/ABI.h) + + # The order is derived from llvm-config --libs core + _Clang_find_and_add_cxx_lib(LLVMCore llvm/Pass.h) + _Clang_find_and_add_cxx_lib(LLVMBinaryFormat llvm/BinaryFormat/Dwarf.h) + _Clang_find_and_add_cxx_lib(LLVMSupport llvm/Support/Error.h) + _Clang_find_and_add_cxx_lib(LLVMDemangle llvm/Demangle/Demangle.h) +endif() + +_Clang_find_program(Clang_EXECUTABLE clang) +if(Clang_EXECUTABLE) + # Find Clang resource directory with Clang executable + # TODO: simplify by using -print-resource-dir once Clang 4 support is dropped + if(${CMAKE_SYSTEM_NAME} STREQUAL Windows) + set(_DEV_NULL NUL) + else() + set(_DEV_NULL /dev/null) + endif() + + # clang "-###" -xc /dev/null + execute_process(COMMAND ${Clang_EXECUTABLE} "-###" -xc ${_DEV_NULL} + ERROR_VARIABLE Clang_RESOURCE_DIR OUTPUT_QUIET) + # Strip everything except '"-resource-dir" ""' + string(REGEX MATCH "\"-resource-dir\" \"([^\"]*)\"" + Clang_RESOURCE_DIR ${Clang_RESOURCE_DIR}) + # Strip quotes + string(REPLACE "\"" "" Clang_RESOURCE_DIR ${Clang_RESOURCE_DIR}) + # Strip '-resource-dir ' + string(REPLACE "-resource-dir " "" Clang_RESOURCE_DIR ${Clang_RESOURCE_DIR}) + + # Find Clang version + set(_Clang_VERSION_REGEX "([0-9]+)\\.([0-9]+)\\.([0-9]+)") + execute_process(COMMAND ${Clang_EXECUTABLE} --version + OUTPUT_VARIABLE Clang_VERSION) + string(REGEX MATCH ${_Clang_VERSION_REGEX} Clang_VERSION ${Clang_VERSION}) +endif() + +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args(Clang + FOUND_VAR Clang_FOUND + REQUIRED_VARS ${_Clang_REQUIRED_VARS} + VERSION_VAR Clang_VERSION +) + +if(Clang_FOUND AND NOT TARGET Clang::Clang) + set(_Clang_LIBRARIES ${Clang_LIBRARY} ${_Clang_CXX_LIBRARIES}) + set(_Clang_INCLUDE_DIRS ${Clang_INCLUDE_DIR} ${_Clang_CXX_INCLUDE_DIRS}) + + add_library(Clang::Clang INTERFACE IMPORTED) + set_property(TARGET Clang::Clang PROPERTY + INTERFACE_LINK_LIBRARIES ${_Clang_LIBRARIES}) + set_property(TARGET Clang::Clang PROPERTY + INTERFACE_INCLUDE_DIRECTORIES ${_Clang_INCLUDE_DIRS}) +endif()