Tomilov Anatoliy
Tomilov Anatoliy

Reputation: 16731

cmake detect which library libc++ or libstdc++ is configured to be used against g++ or clang++

I wrote an CMakeLists.txt to build a project with either g++ or clang++.

To catch as many as possible bugs I use both libc++ with -D_LIBCPP_DEBUG2=2 (for clang++) and libstdc++ with -D_GLIBCXX_DEBUG (for both g++ and clang++).

set(CMAKE_CXX_FLAGS_DEBUG "-ggdb -fno-inline -DDEBUG=1 -march=x86-64 -mtune=generic")
#[[
if("${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang")
    set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -D_LIBCPP_DEBUG2=2")
elseif("${CMAKE_CXX_COMPILER_ID}" MATCHES "GNU")
    set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -D_GLIBCXX_DEBUG")
endif()
]]
if(CMAKE_BUILD_TYPE STREQUAL "Debug")
elseif(CMAKE_BUILD_TYPE STREQUAL "Release")
    set(CMAKE_CXX_FLAGS_RELEASE "-O3 -DNDEBUG=1 -march=native")
elseif(CMAKE_BUILD_TYPE STREQUAL "RelWithDebInfo")
    set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "-fno-omit-frame-pointer -DNDEBUG=1 -march=native")
    if("${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang")
        set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "${CMAKE_CXX_FLAGS_RELWITHDEBINFO} -O3 -gline-tables-only")
    elseif("${CMAKE_CXX_COMPILER_ID}" MATCHES "GNU")
        set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "${CMAKE_CXX_FLAGS_RELWITHDEBINFO} -Og -ggdb")
    endif()
elseif(CMAKE_BUILD_TYPE STREQUAL "MinSizeRel")
    set(CMAKE_CXX_FLAGS_MINSIZEREL "-Os -DNDEBUG=1 -march=native")
else()
    message(STATUS "Wrong build type selected, defaulted to Debug.")
    set(CMAKE_BUILD_TYPE "Debug")
endif()

Commented out code is the point where I should to know which library currently will be used with current compiler.

How to achieve this? I know, that libstdc++ defines __GLIBCXX__ and libc++ defines _LIBCPP_VERSION, but how to detect them?

Upvotes: 5

Views: 2114

Answers (3)

Vincent X
Vincent X

Reputation: 372

With the assistance of CMake's CheckCXXSymbolExists module, you can determine whether libc++ or libstdc++ is configured to be used in your project. Here's how you can do it:

include(CheckCXXSymbolExists)

if(cxx_std_20 IN_LIST CMAKE_CXX_COMPILE_FEATURES)
  set(header version)
else()
  set(header ciso646)
endif()

check_cxx_symbol_exists(_LIBCPP_VERSION ${header} LIBCPP)
if(LIBCPP)
  # Logic for libc++
endif()

check_cxx_symbol_exists(__GLIBCXX__ ${header} GLIBCXX)
if(GLIBCXX)
  # Logic for libstdc++
endif()

In this implementation, we use the <version> header if the compiler supports C++20. Otherwise, we resort to the <ciso646> header. Note that while <ciso646> is removed in C++20, it may still exist in implementations for backward compatibility purposes.

Upvotes: 2

Gelldur
Gelldur

Reputation: 11558

In source code you may:

#define STR_HELPER(x) #x
#define STR(x) STR_HELPER(x)

#if defined(__cplusplus) && defined(_LIBCPP_VERSION)
// This indicates that libc++ is being used
#   pragma message("libc++ version: " STR(_LIBCPP_VERSION))
#endif

#ifdef __GLIBCXX__
// This indicates that libstdc++ is being used
#   pragma message("libstdc++ version: " STR(__GLIBCXX__))
#endif

When you build you should see:

main.cpp:38:10: warning: libstdc++ version: 20230801 [-W#pragma-messages]

Checkout also: https://iq.opengenus.org/find-glibc-version/

Upvotes: 1

arrowd
arrowd

Reputation: 34411

I think you can safely just pass both defines for each library.

But if you really want to do this conditionally, I'd recommend using CheckCXXSourceCompiles module with following code:

#include <iostream>

int a =
#ifdef __GLIBCXX__
    1;
#else
    fgsfds;
#endif

int main(int argc, char* argv[])
{
return 0;
}

If that code compiles, then you are using libstdc++.

Upvotes: 2

Related Questions