diff --git a/docker-compose.yml b/docker-compose.yml index 38602127..3e658085 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -128,4 +128,11 @@ services: build: context: . dockerfile: ./docker/Dockerfile.arm-bare-metal - target: build \ No newline at end of file + target: build + build-gcc-with-custom-linker: + build: + context: . + dockerfile: ./docker/Dockerfile + args: + compiler: gcc + target: build-with-custom-linker \ No newline at end of file diff --git a/docker/Dockerfile b/docker/Dockerfile index e9a764b7..be2ac7f9 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -16,6 +16,14 @@ RUN setup-cpp --compiler $compiler --llvm true --cmake true --doxygen true --nin COPY ./docker/entrypoint.sh /docker-entrypoint.sh ENTRYPOINT [ "/docker-entrypoint.sh" ] +FROM setup AS setup-custom-linker + +# install linker +RUN apt-get update && apt-get install -y \ + binutils \ + mold \ + lld \ + && rm -rf /var/lib/apt/lists/* FROM setup AS build COPY . /home/project_options @@ -30,3 +38,9 @@ WORKDIR /home/project_options RUN git submodule update --init CMD ["/bin/bash", "-c", "task myproj:test"] + +FROM setup-custom-linker AS build-with-custom-linker +COPY . /home/project_options +WORKDIR /home/project_options +RUN git submodule update --init +CMD ["/bin/bash", "-c", "task myproj:build"] \ No newline at end of file diff --git a/docker/Taskfile.yml b/docker/Taskfile.yml index 3330e0f6..b60a2699 100644 --- a/docker/Taskfile.yml +++ b/docker/Taskfile.yml @@ -6,6 +6,10 @@ tasks: - docker-compose up --build test-gcc - docker-compose down + gcc.custom-linker: + - docker-compose up --build build-gcc-with-custom-linker + - docker-compose down + llvm: - docker-compose up --build build-llvm - docker-compose up --build test-llvm diff --git a/src/Linker.cmake b/src/Linker.cmake index 06fe2af3..9ab2a1f2 100644 --- a/src/Linker.cmake +++ b/src/Linker.cmake @@ -1,5 +1,22 @@ include_guard() +# Test linker option support +function(test_linker_option output_linker_test linker) + set(output_linker_test OFF PARENT_SCOPE) + if(NOT "${linker}" STREQUAL "") + include(CheckCXXCompilerFlag) + + set(_linker_flag "-fuse-ld=${linker}") + + check_cxx_compiler_flag(${_linker_flag} _cxx_supports_linker) + if("${_cxx_supports_linker}" STREQUAL "1") + set(${output_linker_test} ON PARENT_SCOPE) + else() + set(${output_linker_test} OFF PARENT_SCOPE) + endif() + endif() +endfunction() + # Set the linker to use for the linking phase macro(configure_linker _project_name _linker) if(NOT "${_linker}" STREQUAL "") @@ -8,8 +25,8 @@ macro(configure_linker _project_name _linker) set(_linker_flag "-fuse-ld=${_linker}") - check_cxx_compiler_flag(${_linker_flag} _cxx_supports_linker) - if("${_cxx_supports_linker}" STREQUAL "1") + test_linker_option(_cxx_supports_linker ${_linker}) + if(_cxx_supports_linker) message(TRACE "Using ${_linker} as the linker for ${_project_name}") target_link_options(${_project_name} INTERFACE ${_linker_flag}) else() @@ -19,3 +36,100 @@ macro(configure_linker _project_name _linker) endif() endif() endmacro() + +#[[.rst: + +``find_linker`` +=============== + +Find a linker prefering the linkers in this order: sold, mold, lld, gold, the system linker + +Output variables: + +- ``LINKER``: the linker to use + +]] +function(find_linker LINKER) + find_sold(_PROGRAM_sold) + if(_PROGRAM_sold) + set(${LINKER} "sold" PARENT_SCOPE) + return() + endif() + + find_mold(_PROGRAM_mold) + if(_PROGRAM_mold) + set(${LINKER} "mold" PARENT_SCOPE) + return() + endif() + + find_lld(_PROGRAM_lld) + if(_PROGRAM_lld) + set(${LINKER} "lld" PARENT_SCOPE) + return() + endif() + + find_gold(_PROGRAM_gold) + if(_PROGRAM_gold) + set(${LINKER} "gold" PARENT_SCOPE) + return() + endif() + + # else, use the default linker + set(${LINKER} "" PARENT_SCOPE) +endfunction() + +function(find_sold PROGRAM_sold) + if(UNIX AND NOT WIN32) + find_program(_PROGRAM_sold NAMES "sold") + if(EXISTS ${_PROGRAM_sold}) + test_linker_option(SUPPORTS_SOLD "sold") + if(SUPPORTS_SOLD) + set(${PROGRAM_sold} ${_PROGRAM_sold} PARENT_SCOPE) + return() + endif() + endif() + endif() + set(${PROGRAM_sold} OFF PARENT_SCOPE) +endfunction() + +function(find_mold PROGRAM_mold) + if(UNIX AND NOT WIN32) + find_program(_PROGRAM_MOLD NAMES "mold") + if(EXISTS ${_PROGRAM_MOLD}) + test_linker_option(SUPPORTS_MOLD "mold") + if(SUPPORTS_MOLD) + set(${PROGRAM_mold} ${_PROGRAM_MOLD} PARENT_SCOPE) + return() + endif() + endif() + endif() + set(${PROGRAM_mold} OFF PARENT_SCOPE) +endfunction() + +function(find_lld PROGRAM_lld) + if((CMAKE_CXX_COMPILER_ID MATCHES ".*Clang.*")) + find_program(_PROGRAM_LLD NAMES "lld") + if(EXISTS ${_PROGRAM_LLD}) + test_linker_option(SUPPORTS_LLD "lld") + if(SUPPORTS_LLD) + set(${PROGRAM_lld} ${_PROGRAM_LLD} PARENT_SCOPE) + return() + endif() + endif() + endif() + set(${PROGRAM_lld} OFF PARENT_SCOPE) +endfunction() + +function(find_gold PROGRAM_gold) + if((CMAKE_CXX_COMPILER_ID MATCHES ".*GNU.*") AND NOT WIN32) + find_program(_PROGRAM_GOLD NAMES "gold") + if(EXISTS ${_PROGRAM_GOLD}) + test_linker_option(SUPPORTS_GOLD "gold") + if(SUPPORTS_GOLD) + set(${PROGRAM_gold} ${_PROGRAM_GOLD} PARENT_SCOPE) + return() + endif() + endif() + endif() + set(${PROGRAM_gold} OFF PARENT_SCOPE) +endfunction() diff --git a/src/Sanitizers.cmake b/src/Sanitizers.cmake index e965af51..385c8400 100644 --- a/src/Sanitizers.cmake +++ b/src/Sanitizers.cmake @@ -16,11 +16,13 @@ function( if(${ENABLE_SANITIZER_ADDRESS}) list(APPEND SANITIZERS "address") - if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU" AND CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL 8) + if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU" AND CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL + 8 + ) list(APPEND SANITIZERS "pointer-compare" "pointer-subtract") message( STATUS - "To enable invalid pointer pairs detection, add detect_invalid_pointer_pairs=2 to the environment variable ASAN_OPTIONS." + "To enable invalid pointer pairs detection, add detect_invalid_pointer_pairs=2 to the environment variable ASAN_OPTIONS." ) endif() endif() @@ -91,3 +93,52 @@ function( endif() endfunction() + +# detect sanitizers support for compiler +function( + check_sanitizers_support + ENABLE_SANITIZER_ADDRESS + ENABLE_SANITIZER_UNDEFINED_BEHAVIOR + ENABLE_SANITIZER_LEAK + ENABLE_SANITIZER_THREAD + ENABLE_SANITIZER_MEMORY +) + set(SANITIZERS "") + if(NOT "${CMAKE_SYSTEM_NAME}" STREQUAL "Windows") + list(APPEND SANITIZERS "address") + list(APPEND SANITIZERS "undefined") + list(APPEND SANITIZERS "leak") + list(APPEND SANITIZERS "thread") + list(APPEND SANITIZERS "memory") + elseif(MSVC) + # or it is MSVC and has run vcvarsall + string(FIND "$ENV{PATH}" "$ENV{VSINSTALLDIR}" index_of_vs_install_dir) + if("${index_of_vs_install_dir}" STREQUAL "-1") + list(APPEND SANITIZERS "address") + endif() + endif() + + list(JOIN SANITIZERS "," LIST_OF_SANITIZERS) + + if(LIST_OF_SANITIZERS) + if(NOT "${LIST_OF_SANITIZERS}" STREQUAL "") + if("address" IN_LIST SANITIZERS) + set(${ENABLE_SANITIZER_ADDRESS} "ENABLE_SANITIZER_ADDRESS" PARENT_SCOPE) + endif() + if("undefined" IN_LIST SANITIZERS) + set(${ENABLE_SANITIZER_UNDEFINED_BEHAVIOR} "ENABLE_SANITIZER_UNDEFINED_BEHAVIOR" + PARENT_SCOPE + ) + endif() + if("leak" IN_LIST SANITIZERS) + set(${ENABLE_SANITIZER_LEAK} "ENABLE_SANITIZER_LEAK" PARENT_SCOPE) + endif() + if("thread" IN_LIST SANITIZERS) + set(${ENABLE_SANITIZER_THREAD} "ENABLE_SANITIZER_THREAD" PARENT_SCOPE) + endif() + if("memory" IN_LIST SANITIZERS) + set(${ENABLE_SANITIZER_MEMORY} "ENABLE_SANITIZER_MEMORY" PARENT_SCOPE) + endif() + endif() + endif() +endfunction() diff --git a/src/Utilities.cmake b/src/Utilities.cmake index 022c384a..d450fd35 100644 --- a/src/Utilities.cmake +++ b/src/Utilities.cmake @@ -124,48 +124,3 @@ function(detect_architecture arch) set(${arch} x64 PARENT_SCOPE) endif() endfunction() - -# detect sanitizers support for compiler -function(check_sanitizers_support - ENABLE_SANITIZER_ADDRESS - ENABLE_SANITIZER_UNDEFINED_BEHAVIOR - ENABLE_SANITIZER_LEAK - ENABLE_SANITIZER_THREAD - ENABLE_SANITIZER_MEMORY) - set(SANITIZERS "") - if(NOT "${CMAKE_SYSTEM_NAME}" STREQUAL "Windows") - list(APPEND SANITIZERS "address") - list(APPEND SANITIZERS "undefined") - list(APPEND SANITIZERS "leak") - list(APPEND SANITIZERS "thread") - list(APPEND SANITIZERS "memory") - elseif(MSVC) - # or it is MSVC and has run vcvarsall - string(FIND "$ENV{PATH}" "$ENV{VSINSTALLDIR}" index_of_vs_install_dir) - if("${index_of_vs_install_dir}" STREQUAL "-1") - list(APPEND SANITIZERS "address") - endif() - endif() - - list(JOIN SANITIZERS "," LIST_OF_SANITIZERS) - - if(LIST_OF_SANITIZERS) - if(NOT "${LIST_OF_SANITIZERS}" STREQUAL "") - if("address" IN_LIST SANITIZERS) - set(${ENABLE_SANITIZER_ADDRESS} "ENABLE_SANITIZER_ADDRESS" PARENT_SCOPE) - endif() - if("undefined" IN_LIST SANITIZERS) - set(${ENABLE_SANITIZER_UNDEFINED_BEHAVIOR} "ENABLE_SANITIZER_UNDEFINED_BEHAVIOR" PARENT_SCOPE) - endif() - if("leak" IN_LIST SANITIZERS) - set(${ENABLE_SANITIZER_LEAK} "ENABLE_SANITIZER_LEAK" PARENT_SCOPE) - endif() - if("thread" IN_LIST SANITIZERS) - set(${ENABLE_SANITIZER_THREAD} "ENABLE_SANITIZER_THREAD" PARENT_SCOPE) - endif() - if("memory" IN_LIST SANITIZERS) - set(${ENABLE_SANITIZER_MEMORY} "ENABLE_SANITIZER_MEMORY" PARENT_SCOPE) - endif() - endif() - endif() -endfunction() \ No newline at end of file diff --git a/tests/myproj/CMakeLists.txt b/tests/myproj/CMakeLists.txt index 24c8e187..071c6331 100644 --- a/tests/myproj/CMakeLists.txt +++ b/tests/myproj/CMakeLists.txt @@ -39,6 +39,12 @@ check_sanitizers_support(ENABLE_SANITIZER_ADDRESS ENABLE_SANITIZER_THREAD ENABLE_SANITIZER_MEMORY) +# Detect custom linker +if(NOT MSVC) + find_linker(LINKER) + #message(STATUS "Detect Linker: ${LINKER}") +endif() + # Initialize project_options # uncomment the options to enable them project_options( @@ -66,6 +72,7 @@ project_options( # ${ENABLE_SANITIZER_THREAD} # ${ENABLE_SANITIZER_MEMORY} # CLANG_WARNINGS "-Weverything" + LINKER "${LINKER}" ) # NOTE: project_options and project_warnings are defined inside project_options