mercredi 22 septembre 2021

CMake: Library order in CMAKE_REQUIRED_LIBRARIES to test a minimal program while configuring

I wrote this little code to ensure that my software links to libatomic if necessary. Usually linking to libatomic is only necessary on Raspberry Pi. Currently, I'm using a Raspberry Pi 4, with Raspbian Bullseye 64-bit.

Here's the cmake code:

set(ATOMIC32_TEST_CODE "
    #include <atomic>
    #include <stdint.h>
    int main() {
        std::atomic<int32_t> x;
        x.store(1);
        x--;
        return x.load();
    }")

set(ATOMIC64_TEST_CODE "
    #include <atomic>
    #include <stdint.h>
    int main() {
        std::atomic<int64_t> x;
        x.store(1);
        x--;
        return x.load();
    }")

macro(ATOMIC_CHECK)

    # test whether atomic works
    check_cxx_source_compiles("${ATOMIC32_TEST_CODE}" atomic32_test)
    check_cxx_source_compiles("${ATOMIC64_TEST_CODE}" atomic64_test)

    # if doesn't work, attempt to find the atomic library, link with it and try again
    if(NOT atomic32_test OR NOT atomic64_test)
        find_library(ATOMIC NAMES libatomic.so.1
        HINTS
          $ENV{HOME}/local/lib64
          $ENV{HOME}/local/lib
          /usr/local/lib64
          /usr/local/lib
          /opt/local/lib64
          /opt/local/lib
          /usr/lib64
          /usr/lib
          /lib64
          /lib
          /usr/lib/arm-linux-gnueabihf
        )

        if(ATOMIC)
            set(LIBATOMIC ${ATOMIC})
            message(STATUS "Found libatomic: ${LIBATOMIC}")
            message(STATUS "Attempting to test atomic with atomic library linked")

            get_filename_component(atomic_lib_dir ${LIBATOMIC} DIRECTORY)

            # Before setting CMAKE_REQUIRED_FLAGS, we preserve the current state
            cmake_push_check_state()

            set(CMAKE_REQUIRED_LIBRARIES "-L${atomic_lib_dir} -latomic")
            check_cxx_source_compiles("${ATOMIC32_TEST_CODE}" atomic32_test_with_atomic_linking)
            check_cxx_source_compiles("${ATOMIC64_TEST_CODE}" atomic64_test_with_atomic_linking)

            cmake_pop_check_state()

            if(NOT atomic32_test_with_atomic_linking)
                message(FATAL_ERROR "Even after linking with the atomic library, atomic 32-bit compilation failed.")
            endif()

            if(NOT atomic64_test_with_atomic_linking)
                message(FATAL_ERROR "Even after linking with the atomic library, atomic 64-bit compilation failed.")
            endif()

            set(ATOMIC_LINKER_LIBS "-L${atomic_lib_dir} -latomic")
        else()
            message(FATAL_ERROR "Failed to find libatomic even though it seems to be required")
        endif()
    endif()

endmacro()

ATOMIC_CHECK()

What this code does, is the following:

  1. Attempt to compile both the 32-bit and 64-bit versions of the code and ensure that they return 0 on exit.
  2. If any of them doesn't compile, find libatomic, if not found, error.
  3. If found, attempt to link to it and compile the same small sources again
  4. If they succeed, great, we use the new found libraries as base for compiling everything else. If not, cmake configuration stops.

Recently I started to get linking errors on my Pi out of the blue. So I investigated, and that lead me to this problem. In CMakeError.log, I see this linking error:

/usr/bin/c++ -Wall -Wextra -Wno-unused-variable -Wno-unused-function -Wno-unused-private-field -Wno-class-memacces>
/usr/bin/ld: CMakeFiles/cmTC_7285b.dir/src.cxx.o: in function `main':
src.cxx:(.text+0x40): undefined reference to `__atomic_store_8'
/usr/bin/ld: src.cxx:(.text+0x80): undefined reference to `__atomic_load_8'
/usr/bin/ld: CMakeFiles/cmTC_7285b.dir/src.cxx.o: in function `std::__atomic_base<long long>::operator--(int)':
src.cxx:(.text._ZNSt13__atomic_baseIxEmmEi[_ZNSt13__atomic_baseIxEmmEi]+0x40): undefined reference to `__atomic_fetch_s>
collect2: error: ld returned 1 exit status

Eventually, I figured that the only issue is the linking order. All cmake has to do to get this to work, is to put -latomic after the source file it's trying to compile.

How can I tell cmake to put the linking commands after the source file, and not before it?

Aucun commentaire:

Enregistrer un commentaire