# Copyright (C) 2011-2021 ycmd contributors # # This file is part of YouCompleteMe. # # YouCompleteMe 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. # # YouCompleteMe 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 YouCompleteMe. If not, see . cmake_minimum_required( VERSION 3.14 ) project( ycm_core ) option( USE_DEV_FLAGS "Use compilation flags meant for YCM developers" OFF ) option( USE_CLANG_COMPLETER "Use Clang semantic completer for C/C++/ObjC" OFF ) option( USE_SYSTEM_LIBCLANG "Set to ON to use the system libclang library" OFF ) set( PATH_TO_LLVM_ROOT "" CACHE PATH "Path to the root of a LLVM+Clang binary distribution" ) set( EXTERNAL_LIBCLANG_PATH "" CACHE PATH "Path to the libclang library to use" ) if ( USE_CLANG_COMPLETER AND NOT USE_SYSTEM_LIBCLANG AND NOT PATH_TO_LLVM_ROOT AND NOT EXTERNAL_LIBCLANG_PATH ) set( CLANG_VERSION 15.0.1 ) if ( APPLE ) if ( "${CMAKE_SYSTEM_PROCESSOR}" STREQUAL "arm64" ) set( LIBCLANG_DIRNAME "libclang-${CLANG_VERSION}-arm64-apple-darwin" ) set( LIBCLANG_SHA256 "91d0c94cea43abe36d1fc954f78bc44dc77773fdf4680dd0958f05a8c859fcae" ) else() set( LIBCLANG_DIRNAME "libclang-${CLANG_VERSION}-x86_64-apple-darwin" ) set( LIBCLANG_SHA256 "f004735cb8bfd56a8ea3c0ed21d1c8c02127ea23ce62eda38143270650a902fe" ) endif() elseif ( WIN32 ) if( 64_BIT_PLATFORM ) set( LIBCLANG_DIRNAME "libclang-${CLANG_VERSION}-win64" ) set( LIBCLANG_SHA256 "6e3196a4b92b5313898e51745c51d95410efd08fb2f1fabeba7205537ef7fed8" ) else() set( LIBCLANG_DIRNAME "libclang-${CLANG_VERSION}-win32" ) set( LIBCLANG_SHA256 "3e0f81aeb47ad1f748abc51a5b37cdb5086a9aaac03955c1fdd0930b41887e11" ) endif() # FreeBSD binaries are not yet available for llvm 15.0,1 # # elseif ( SYSTEM_IS_FREEBSD ) # if ( 64_BIT_PLATFORM ) # set( LIBCLANG_DIRNAME # "libclang-${CLANG_VERSION}-amd64-unknown-freebsd13" ) # set( LIBCLANG_SHA256 # "" ) # else() # set( LIBCLANG_DIRNAME # "libclang-${CLANG_VERSION}-i386-unknown-freebsd13" ) # set( LIBCLANG_SHA256 # "" ) # endif() elseif ( CMAKE_SYSTEM_PROCESSOR MATCHES "^(aarch64.*|AARCH64.*)" ) set( LIBCLANG_DIRNAME "libclang-${CLANG_VERSION}-aarch64-linux-gnu" ) set( LIBCLANG_SHA256 "294ffae61aab3198dc26b8cb891475ab4c08ab4e20f826c687260730350a3eaf" ) elseif ( CMAKE_SYSTEM_PROCESSOR MATCHES "^(arm.*|ARM.*)" ) set( LIBCLANG_DIRNAME "libclang-${CLANG_VERSION}-armv7a-linux-gnueabihf" ) set( LIBCLANG_SHA256 "e8ebe03b6fc558699f8ce389dd3693a9ac8cf4170b436cc0123fd923eba057d6" ) elseif ( CMAKE_SYSTEM_PROCESSOR MATCHES "^(x86_64)" ) set( LIBCLANG_DIRNAME "libclang-${CLANG_VERSION}-x86_64-unknown-linux-gnu" ) set( LIBCLANG_SHA256 "89e8fdb6e7b694226405c6c2b8f5ef26f7683b4328bca7d7e611314189a75aa9" ) else() message( FATAL_ERROR "No prebuilt Clang ${CLANG_VERSION} binaries for this system. " "You'll have to compile Clang ${CLANG_VERSION} from source " "or use your system libclang. " "See the YCM docs for details on how to use a user-compiled libclang." ) endif() set( LIBCLANG_FILENAME "${LIBCLANG_DIRNAME}.tar.bz2" ) set( LIBCLANG_DOWNLOAD ON ) set( LIBCLANG_URL "https://github.com/ycm-core/llvm/releases/download/${CLANG_VERSION}/${LIBCLANG_FILENAME}" ) # Check if the Clang archive is already downloaded and its checksum is # correct. If this is not the case, remove it if needed and download it. set( LIBCLANG_LOCAL_FILE "${CMAKE_SOURCE_DIR}/../clang_archives/${LIBCLANG_FILENAME}" ) if( EXISTS "${LIBCLANG_LOCAL_FILE}" ) file( SHA256 "${LIBCLANG_LOCAL_FILE}" LIBCLANG_LOCAL_SHA256 ) if( "${LIBCLANG_LOCAL_SHA256}" STREQUAL "${LIBCLANG_SHA256}" ) set( LIBCLANG_DOWNLOAD OFF ) else() file( REMOVE "${LIBCLANG_LOCAL_FILE}" ) endif() endif() if( LIBCLANG_DOWNLOAD ) message( STATUS "Downloading libclang ${CLANG_VERSION} from ${LIBCLANG_URL}" ) file( DOWNLOAD "${LIBCLANG_URL}" "${LIBCLANG_LOCAL_FILE}" SHOW_PROGRESS EXPECTED_HASH SHA256=${LIBCLANG_SHA256} ) else() message( STATUS "Using libclang archive: ${LIBCLANG_LOCAL_FILE}" ) endif() # Copy and extract the Clang archive in the building directory. file( COPY "${LIBCLANG_LOCAL_FILE}" DESTINATION "${CMAKE_CURRENT_BINARY_DIR}/../" ) execute_process( COMMAND ${CMAKE_COMMAND} -E tar -xjf ${LIBCLANG_FILENAME} ) # We determine PATH_TO_LLVM_ROOT by searching the libclang library path in # CMake build folder. file( GLOB_RECURSE PATH_TO_LIBCLANG_WITH_SYMLINKS ${CMAKE_BINARY_DIR}/libclang.* ) if ( NOT PATH_TO_LIBCLANG_WITH_SYMLINKS ) message( FATAL_ERROR "Cannot find path to libclang in prebuilt binaries" ) endif() # file( GLOB_RECURSE ... ) returns a list of files. Take the first one. list( GET PATH_TO_LIBCLANG_WITH_SYMLINKS 0 PATH_TO_LIBCLANG ) # We know that LLVM root is parent to the directory containing libclang so we # need to go up two directories: # # /path/to/llvm/root/lib/libclang.so/../.. = /path/to/llvm/root/ # get_filename_component( PATH_TO_LLVM_ROOT "${PATH_TO_LIBCLANG}/../.." ABSOLUTE ) endif() if ( PATH_TO_LLVM_ROOT OR USE_SYSTEM_LIBCLANG OR EXTERNAL_LIBCLANG_PATH ) set( USE_CLANG_COMPLETER TRUE ) endif() if ( USE_CLANG_COMPLETER AND NOT PATH_TO_LLVM_ROOT AND NOT USE_SYSTEM_LIBCLANG AND NOT EXTERNAL_LIBCLANG_PATH ) message( FATAL_ERROR "You have not specified which libclang to use. You have several options:\n" " 1. Set PATH_TO_LLVM_ROOT to a path to the root of a LLVM+Clang binary " "distribution. You can download such a binary distro from llvm.org. This " "is the recommended approach.\n" " 2. Set USE_SYSTEM_LIBCLANG to ON; this makes YCM search for the system " "version of libclang.\n" " 3. Set EXTERNAL_LIBCLANG_PATH to a path to whatever " "libclang.[so|dylib|dll] you wish to use.\n" "You HAVE to pick one option. See the docs for more information.") endif() if ( USE_CLANG_COMPLETER ) message( STATUS "Using libclang to provide semantic completion for C/C++/ObjC" ) else() message( STATUS "Not using libclang for C/C++/ObjC." ) endif() if ( NOT LIBCLANG_FILENAME AND PATH_TO_LLVM_ROOT ) set( CLANG_INCLUDES_DIR "${PATH_TO_LLVM_ROOT}/include" ) else() set( CLANG_INCLUDES_DIR "${CMAKE_SOURCE_DIR}/llvm/include" ) endif() if ( NOT IS_ABSOLUTE "${CLANG_INCLUDES_DIR}" ) get_filename_component(CLANG_INCLUDES_DIR "${CMAKE_BINARY_DIR}/${CLANG_INCLUDES_DIR}" ABSOLUTE) endif() if ( NOT EXTERNAL_LIBCLANG_PATH AND PATH_TO_LLVM_ROOT ) if ( MINGW ) set( LIBCLANG_SEARCH_PATH "${PATH_TO_LLVM_ROOT}/bin" ) else() set( LIBCLANG_SEARCH_PATH "${PATH_TO_LLVM_ROOT}/lib" ) endif() # Need TEMP because find_library does not work with an option variable find_library( TEMP NAMES clang libclang.so.1 PATHS ${LIBCLANG_SEARCH_PATH} NO_DEFAULT_PATH ) set( EXTERNAL_LIBCLANG_PATH ${TEMP} ) endif() set( PYBIND11_INCLUDES_DIR "${CMAKE_SOURCE_DIR}/pybind11" ) file( GLOB SERVER_SOURCES *.h *.cpp ) include_directories( ${CMAKE_CURRENT_SOURCE_DIR} ) if ( USE_CLANG_COMPLETER ) include_directories( "${CMAKE_CURRENT_SOURCE_DIR}/ClangCompleter" ) add_definitions( -DUSE_CLANG_COMPLETER ) file( GLOB add_clang ClangCompleter/*.h ClangCompleter/*.cpp ) list( APPEND SERVER_SOURCES ${add_clang} ) endif() ############################################################################# # One can use the system libclang.[so|dylib] like so: # cmake -DUSE_SYSTEM_LIBCLANG=1 [...] # One can also explicitly pick the external libclang.[so|dylib] for use like so: # cmake -DEXTERNAL_LIBCLANG_PATH=/path/to/libclang.so [...] # The final .so we build will then first look in the same dir in which it is # located for libclang.so. This is provided by the rpath = $ORIGIN feature. if ( EXTERNAL_LIBCLANG_PATH OR USE_SYSTEM_LIBCLANG ) if ( USE_SYSTEM_LIBCLANG ) if ( APPLE ) set( ENV_LIB_PATHS ENV DYLD_LIBRARY_PATH ) elseif ( UNIX ) set( ENV_LIB_PATHS ENV LD_LIBRARY_PATH ) elseif ( WIN32 ) set( ENV_LIB_PATHS ENV PATH ) else () set( ENV_LIB_PATHS "" ) endif() find_program( LLVM_CONFIG_EXECUTABLE NAMES llvm-config ) if ( LLVM_CONFIG_EXECUTABLE ) execute_process( COMMAND ${LLVM_CONFIG_EXECUTABLE} --libdir OUTPUT_VARIABLE LLVM_CONFIG_PATH OUTPUT_STRIP_TRAILING_WHITESPACE ) endif() # On Debian-based systems, llvm installs into /usr/lib/llvm-x.y. file( GLOB SYS_LLVM_PATHS "/usr/lib/llvm*/lib" ) # On FreeBSD , llvm install into /usr/local/llvm-xy file ( GLOB FREEBSD_LLVM_PATHS "/usr/local/llvm*/lib") # Need TEMP because find_library does not work with an option variable # On Debian-based systems only a symlink to libclang.so.1 is created find_library( TEMP NAMES clang libclang.so.1 PATHS ${ENV_LIB_PATHS} ${LLVM_CONFIG_PATH} /usr/lib /usr/lib/llvm ${SYS_LLVM_PATHS} ${FREEBSD_LLVM_PATHS} /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib /Library/Developer/CommandLineTools/usr/lib ) set( EXTERNAL_LIBCLANG_PATH ${TEMP} ) else() if ( NOT APPLE AND NOT MSVC ) # Setting this to true makes sure that libraries we build will have our # rpath set even without having to do "make install" set( CMAKE_BUILD_WITH_INSTALL_RPATH TRUE ) set( CMAKE_INSTALL_RPATH "\$ORIGIN" ) # Add directories from all libraries outside the build tree to the rpath. # This makes the dynamic linker able to find non system libraries that # our libraries require, in particular the Python one (from pyenv for # instance). set( CMAKE_INSTALL_RPATH_USE_LINK_PATH TRUE ) endif() endif() # On Linux, the library target is a symlink of the soversion one. Since it # will be copied in the project folder, we need the symlinked library. get_filename_component( LIBCLANG_TARGET "${EXTERNAL_LIBCLANG_PATH}" REALPATH ) message( STATUS "Using external libclang: ${LIBCLANG_TARGET}" ) else() set( LIBCLANG_TARGET ) endif() if ( EXTRA_RPATH ) set( CMAKE_INSTALL_RPATH "${EXTRA_RPATH}:${CMAKE_INSTALL_RPATH}" ) endif() # Needed on Linux machines, but not on Macs and OpenBSD if ( UNIX AND NOT ( APPLE OR SYSTEM_IS_OPENBSD OR HAIKU ) ) set( EXTRA_LIBS rt ) endif() # Check if we need to add -lstdc++fs or -lc++fs or nothing file( WRITE ${CMAKE_CURRENT_BINARY_DIR}/main.cpp "#include \nint main( int argc, char ** argv ) {\n std::filesystem::path p( argv[ 0 ] );\n return p.string().length();\n}" ) try_compile( STD_FS_NO_LIB_NEEDED ${CMAKE_CURRENT_BINARY_DIR} SOURCES ${CMAKE_CURRENT_BINARY_DIR}/main.cpp CXX_STANDARD 17 CXX_STANDARD_REQUIRED TRUE CXX_EXTENSIONS ${NEEDS_EXTENSIONS} ) try_compile( STD_FS_NEEDS_STDCXXFS ${CMAKE_CURRENT_BINARY_DIR} SOURCES ${CMAKE_CURRENT_BINARY_DIR}/main.cpp CXX_STANDARD 17 CXX_STANDARD_REQUIRED TRUE CXX_EXTENSIONS ${NEEDS_EXTENSIONS} LINK_LIBRARIES stdc++fs ) try_compile( STD_FS_NEEDS_CXXFS ${CMAKE_CURRENT_BINARY_DIR} SOURCES ${CMAKE_CURRENT_BINARY_DIR}/main.cpp CXX_STANDARD 17 CXX_STANDARD_REQUIRED TRUE CXX_EXTENSIONS ${NEEDS_EXTENSIONS} LINK_LIBRARIES c++fs ) file( REMOVE ${CMAKE_CURRENT_BINARY_DIR}/main.cpp ) if( ${STD_FS_NEEDS_STDCXXFS} ) set( STD_FS_LIB stdc++fs ) elseif( ${STD_FS_NEEDS_CXXFS} ) set( STD_FS_LIB c++fs ) elseif( ${STD_FS_NO_LIB_NEEDED} ) set( STD_FS_LIB "" ) else() message( FATAL_ERROR "Unknown compiler - C++17 filesystem library missing" ) endif() # Check if Abseil supports this environment file( WRITE ${CMAKE_CURRENT_BINARY_DIR}/main.cpp "#include \nint main() {}" ) try_compile( ABSL_SUPPORTED ${CMAKE_CURRENT_BINARY_DIR} SOURCES ${CMAKE_CURRENT_BINARY_DIR}/main.cpp CMAKE_FLAGS -DINCLUDE_DIRECTORIES=${CMAKE_SOURCE_DIR}/absl CXX_STANDARD 17 CXX_STANDARD_REQUIRED TRUE CXX_EXTENSIONS ${NEEDS_EXTENSIONS} ) file( REMOVE ${CMAKE_CURRENT_BINARY_DIR}/main.cpp ) if( ${ABSL_SUPPORTED} ) message( STATUS "Using Abseil hash tables" ) set( EXTRA_LIBS ${EXTRA_LIBS} absl::flat_hash_map absl::flat_hash_set ) else() message( STATUS "Using STL hash tables" ) endif() ############################################################################# add_library( ${PROJECT_NAME} SHARED ${SERVER_SOURCES}) if ( ${ABSL_SUPPORTED} ) target_compile_definitions( ${PROJECT_NAME} PUBLIC YCM_ABSEIL_SUPPORTED ) endif() target_include_directories( ${PROJECT_NAME} SYSTEM PUBLIC ${PYBIND11_INCLUDES_DIR} PUBLIC ${Python3_INCLUDE_DIRS} PUBLIC ${CLANG_INCLUDES_DIR} PUBLIC ${CMAKE_SOURCE_DIR}/absl ) if ( USE_CLANG_COMPLETER AND NOT LIBCLANG_TARGET ) message( FATAL_ERROR "Using Clang completer, but no libclang found. " "Try setting EXTERNAL_LIBCLANG_PATH or revise " "your configuration" ) endif() target_link_libraries( ${PROJECT_NAME} PUBLIC ${Python3_LIBRARIES} PUBLIC ${LIBCLANG_TARGET} PUBLIC ${STD_FS_LIB} PUBLIC ${EXTRA_LIBS} ) ############################################################################# # We don't want the "lib" prefix, it can screw up python when it tries to search # for our module execute_process( COMMAND "${Python3_EXECUTABLE}" "-c" "print(__import__('sysconfig').get_config_var('EXT_SUFFIX'))" OUTPUT_VARIABLE PYTHON_EXTENSION OUTPUT_STRIP_TRAILING_WHITESPACE ) set_target_properties( ${PROJECT_NAME} PROPERTIES PREFIX "" SUFFIX "${PYTHON_EXTENSION}" ) if ( WIN32 OR CYGWIN OR MSYS ) # DLL platforms put dlls in the RUNTIME_OUTPUT_DIRECTORY # First for the generic no-config case (e.g. with mingw) set_target_properties( ${PROJECT_NAME} PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${PROJECT_SOURCE_DIR}/../.. ) # Second, for multi-config builds (e.g. msvc) foreach( OUTPUTCONFIG ${CMAKE_CONFIGURATION_TYPES} ) string( TOUPPER ${OUTPUTCONFIG} OUTPUTCONFIG ) set_target_properties( ${PROJECT_NAME} PROPERTIES RUNTIME_OUTPUT_DIRECTORY_${OUTPUTCONFIG} ${PROJECT_SOURCE_DIR}/../.. ) endforeach() endif() set_target_properties( ${PROJECT_NAME} PROPERTIES LIBRARY_OUTPUT_DIRECTORY ${PROJECT_SOURCE_DIR}/../.. ) ############################################################################# if ( USE_DEV_FLAGS AND ( CMAKE_COMPILER_IS_GNUCXX OR COMPILER_IS_CLANG ) ) # We want all warnings, and warnings should be treated as errors target_compile_options( ${PROJECT_NAME} PUBLIC "-Wall" "-Wextra" "-Werror" ) endif() ############################################################################# if( SYSTEM_IS_SUNOS ) # SunOS needs this setting for thread support target_compile_options( ${PROJECT_NAME} PUBLIC "-pthreads" ) endif() if( SYSTEM_IS_OPENBSD OR SYSTEM_IS_FREEBSD ) target_compile_options( ${PROJECT_NAME} PUBLIC "-pthread" ) endif() if ( DEFINED ENV{YCM_TESTRUN} ) add_subdirectory( tests ) endif() if ( DEFINED ENV{YCM_BENCHMARK} ) add_subdirectory( benchmarks ) endif() ############################################################################### if( USE_CLANG_TIDY ) if( NOT APPLE ) find_program( CLANG_TIDY NAMES clang-tidy ) else() execute_process( COMMAND brew --prefix llvm OUTPUT_VARIABLE LLVM_ROOT ) string( STRIP ${LLVM_ROOT} LLVM_ROOT ) message( STATUS "${LLVM_ROOT}/bin" ) find_program( CLANG_TIDY NAMES clang-tidy PATHS "${LLVM_ROOT}/bin" ) endif() if ( CLANG_TIDY ) message( STATUS "clang-tidy executable found: ${CLANG_TIDY}" ) set( CLANG_TIDY_ARGS "${CLANG_TIDY}" ) set_target_properties( ycm_core PROPERTIES CXX_CLANG_TIDY "${CLANG_TIDY_ARGS}" ) else() message( STATUS "clang-tidy not found" ) endif() endif() if ( CMAKE_COMPILER_IS_GNUCXX ) set_property(SOURCE "${CMAKE_CURRENT_SOURCE_DIR}/CodePoint.cpp" APPEND PROPERTY COMPILE_FLAGS "-Wno-bidi-chars") endif()