libcpr / cpr

C++ Requests: Curl for People, a spiritual port of Python Requests.
https://docs.libcpr.org/
Other
6.61k stars 942 forks source link

Unable to statically link with CPM #1137

Open Sightem opened 2 weeks ago

Sightem commented 2 weeks ago

Description

It appears that the library has trouble with static linking (specifically in debug mode) when included using CPM.

I cannot reproduce this when building in release or release with debug info modes.

This problem appears to have been caused by a change in v1.11.0, as I cannot reproduce this using v1.9.8

[1/2] Building CXX object CMakeFiles/cpr_static_repro.dir/src/main.cpp.obj
[2/2] Linking CXX executable cpr_static_repro.exe
FAILED: cpr_static_repro.exe 
cmd.exe /C "cd . && C:\msys64\mingw64\bin\c++.exe -g -static -static-libgcc -static-libstdc++ CMakeFiles/cpr_static_repro.dir/src/main.cpp.obj -o cpr_static_repro.exe -Wl,--out-implib,libcpr_static_repro.dll.a -Wl,--major-image-version,0,--minor-image-version,0  _deps/cpr-build/cpr/libcpr.a  _deps/curl-build/lib/libcurl-d.a  C:/msys64/mingw64/lib/libidn2.dll.a  -lws2_32  -lbcrypt  C:/msys64/mingw64/lib/libz.dll.a  C:/msys64/mingw64/lib/libpsl.dll.a  C:/msys64/mingw64/lib/libssh2.dll.a  -ladvapi32  -lcrypt32  -lkernel32 -luser32 -lgdi32 -lwinspool -lshell32 -lole32 -loleaut32 -luuid -lcomdlg32 -ladvapi32 && cd ."
C:/msys64/mingw64/bin/../lib/gcc/x86_64-w64-mingw32/14.2.0/../../../../x86_64-w64-mingw32/bin/ld.exe: C:/msys64/mingw64/bin/../lib/gcc/x86_64-w64-mingw32/14.2.0/../../../../lib\libstdc++.a(tinfo.o):(.text$_ZNKSt9type_infoeqERKS_+0x0): multiple definition of `std::type_info::operator==(std::type_info const&) const'; _deps/cpr-build/cpr/libcpr.a(session.cpp.obj):C:/msys64/mingw64/include/c++/14.2.0/typeinfo:193: first defined here
collect2.exe: error: ld returned 1 exit status
ninja: build stopped: subcommand failed.

Example/How to Reproduce

// Directory structure:
//
// minimal-repro/
// ├── CMakeLists.txt
// ├── cmake/
// │   └── CPM.cmake
// └── src/
//     └── main.cpp
# CMakeLists.txt
cmake_minimum_required(VERSION 3.26)
project(cpr_static_repro)

set(CMAKE_CXX_STANDARD 23)
set(CMAKE_INCLUDE_DIRECTORIES_BEFORE ON)

include(cmake/CPM.cmake)

CPMAddPackage(
    NAME cpr
    GITHUB_REPOSITORY libcpr/cpr
    GIT_TAG bb01c8db702fb41e5497aee9c0559ddf4bf13749
    OPTIONS
    "BUILD_SHARED_LIBS OFF"
    "CPR_FORCE_USE_SYSTEM_CURL OFF"
    "CPR_BUILD_TESTS OFF"
    "CPR_ENABLE_SSL ON"
)

add_executable(cpr_static_repro src/main.cpp)
target_link_options(cpr_static_repro PRIVATE -static -static-libgcc -static-libstdc++)
target_link_libraries(cpr_static_repro PRIVATE cpr::cpr)
// src/main.cpp
#include <cpr/cpr.h>
#include <iostream>

int main() {
    auto r = cpr::Get(cpr::Url{"https://api.github.com/repos/libcpr/cpr"});
    std::cout << "Status code: " << r.status_code << std::endl;
    return 0;
}

// To reproduce:
// 1. Create the directory structure above
// 2. Download CPM.cmake from https://github.com/cpm-cmake/CPM.cmake/releases
// 3. Run:
//   mkdir build
//    cd build
//    cmake -DCMAKE_BUILD_TYPE=Debug ..
//    cmake --build . --config Debug

Possible Fix

No response

Where did you get it from?

Other (specify in "Additional Context/Your Environment")

Additional Context/Your Environment

COM8 commented 2 weeks ago

@Sightem thanks for reporting!

When I try building both 1.11.0 and 1.9.8 with the following CMake config based on yours

cmake_minimum_required(VERSION 3.20)
project(cpr_example)

set(CMAKE_CXX_STANDARD 17)

# Set to C++ 11 if you are using cpr <= 1.9.x
# More: https://github.com/libcpr/cpr#supported-releases
# set(CMAKE_CXX_STANDARD 11)

# Set a default build type if none was specified
# Based on: https://github.com/openchemistry/tomviz/blob/master/cmake/BuildType.cmake
set(DEFAULT_BUILD_TYPE "Release")

if(EXISTS "${CMAKE_SOURCE_DIR}/.git")
  set(DEFAULT_BUILD_TYPE "Debug")
endif()

if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES)
  message(STATUS "Setting build type to '${DEFAULT_BUILD_TYPE}' as none was specified.")
  set(CMAKE_BUILD_TYPE "${DEFAULT_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()

add_executable(cpr_example main.cpp)

if(WIN32) # Install dlls in the same directory as the executable on Windows
  set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR})
  set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR})
endif()

set(BUILD_SHARED_LIBS OFF CACHE INTERNAL "" FORCE)
set(CPR_FORCE_USE_SYSTEM_CURL OFF CACHE INTERNAL "" FORCE)
set(CPR_BUILD_TESTS OFF CACHE INTERNAL "" FORCE)
set(CPR_ENABLE_SSL OFF CACHE INTERNAL "" FORCE)
set(CURL_ZLIB OFF CACHE INTERNAL "" FORCE)
target_link_options(cpr_example PUBLIC -static -static-libgcc -static-libstdc++)

include(FetchContent)
FetchContent_Declare(cpr GIT_REPOSITORY https://github.com/libcpr/cpr.git
  GIT_TAG 1.11.0)
FetchContent_MakeAvailable(cpr)

target_link_libraries(cpr_example PRIVATE cpr::cpr)

I run into a different problem:

[build] [29/31  90% :: 4.080] Building CXX object _deps/cpr-build/cpr/CMakeFiles/cpr.dir/multiperform.cpp.o
[build] [29/31  93% :: 5.592] Building CXX object _deps/cpr-build/cpr/CMakeFiles/cpr.dir/session.cpp.o
[build] [30/31  96% :: 5.626] Linking CXX static library _deps/cpr-build/cpr/libcpr.a
[build] [31/31 100% :: 5.633] Linking CXX executable cpr_example
[build] FAILED: cpr_example 
[build] : && /usr/lib64/ccache/g++ -O3 -DNDEBUG -static -static-libgcc -static-libstdc++ CMakeFiles/cpr_example.dir/main.cpp.o -o cpr_example  _deps/cpr-build/cpr/libcpr.a  /usr/lib64/libcurl.so && :
[build] /usr/bin/ld: attempted static link of dynamic object `/usr/lib64/libcurl.so'
[build] collect2: error: ld returned 1 exit status

If I remove the -static flag it works, but the executable links against some shared stuff.

ldd build/cpr_example 
    linux-vdso.so.1 (0x00007ff8e9ca2000)
    libnghttp2.so.14 => /lib64/libnghttp2.so.14 (0x00007ff8e9c55000)
    libpsl.so.5 => /lib64/libpsl.so.5 (0x00007ff8e9c40000)
    libm.so.6 => /lib64/libm.so.6 (0x00007ff8e9b5c000)
    libc.so.6 => /lib64/libc.so.6 (0x00007ff8e996b000)
    /lib64/ld-linux-x86-64.so.2 (0x00007ff8e9ca4000)
    libunistring.so.5 => /lib64/libunistring.so.5 (0x00007ff8e97be000)
    libidn2.so.0 => /lib64/libidn2.so.0 (0x00007ff8e979a000)

I suspect curl linkage is not purely static and relies on some shared dependencies that can be build and then used.

Sightem commented 2 weeks ago

Oddly enough, from my own example I get these results. This is with the -static flag still included.

$ ldd C:\\Users\\Sightem\\CLionProjects\\minimal-repro\\cmake-build-debug\\cpr_static_repro.exe
        ntdll.dll => /c/WINDOWS/SYSTEM32/ntdll.dll (0x7ff8b9fb0000)
        KERNEL32.DLL => /c/WINDOWS/System32/KERNEL32.DLL (0x7ff8b93e0000)
        KERNELBASE.dll => /c/WINDOWS/System32/KERNELBASE.dll (0x7ff8b7790000)
        ADVAPI32.dll => /c/WINDOWS/System32/ADVAPI32.dll (0x7ff8b9e30000)
        msvcrt.dll => /c/WINDOWS/System32/msvcrt.dll (0x7ff8b8ba0000)
        sechost.dll => /c/WINDOWS/System32/sechost.dll (0x7ff8b92e0000)
        RPCRT4.dll => /c/WINDOWS/System32/RPCRT4.dll (0x7ff8b8a70000)
        bcrypt.dll => /c/WINDOWS/System32/bcrypt.dll (0x7ff8b7c90000)
        CRYPT32.dll => /c/WINDOWS/System32/CRYPT32.dll (0x7ff8b7b30000)
        ucrtbase.dll => /c/WINDOWS/System32/ucrtbase.dll (0x7ff8b7cc0000)
        WS2_32.dll => /c/WINDOWS/System32/WS2_32.dll (0x7ff8b9dc0000)
        libidn2-0.dll => not found
        libpsl-5.dll => not found
        libssh2-1.dll => not found
        zlib1.dll => not found

Below is the output without the -static flag:

$ ldd C:\\Users\\Sightem\\CLionProjects\\minimal-repro\\cmake-build-debug\\cpr_static_repro.exe
        ntdll.dll => /c/WINDOWS/SYSTEM32/ntdll.dll (0x7ff8b9fb0000)
        KERNEL32.DLL => /c/WINDOWS/System32/KERNEL32.DLL (0x7ff8b93e0000)
        KERNELBASE.dll => /c/WINDOWS/System32/KERNELBASE.dll (0x7ff8b7790000)
        ADVAPI32.dll => /c/WINDOWS/System32/ADVAPI32.dll (0x7ff8b9e30000)
        msvcrt.dll => /c/WINDOWS/System32/msvcrt.dll (0x7ff8b8ba0000)
        sechost.dll => /c/WINDOWS/System32/sechost.dll (0x7ff8b92e0000)
        RPCRT4.dll => /c/WINDOWS/System32/RPCRT4.dll (0x7ff8b8a70000)
        bcrypt.dll => /c/WINDOWS/System32/bcrypt.dll (0x7ff8b7c90000)
        CRYPT32.dll => /c/WINDOWS/System32/CRYPT32.dll (0x7ff8b7b30000)
        ucrtbase.dll => /c/WINDOWS/System32/ucrtbase.dll (0x7ff8b7cc0000)
        WS2_32.dll => /c/WINDOWS/System32/WS2_32.dll (0x7ff8b9dc0000)
        libwinpthread-1.dll => /home/Sightem/libwinpthread-1.dll (0x75c50000)
        libidn2-0.dll => not found
        libpsl-5.dll => not found
        libssh2-1.dll => not found
        zlib1.dll => not found

Even weirder, your example works perfectly somehow with no linking errors, Though ldd still yields some strange results:

$ ldd C:\\Users\\Sightem\\CLionProjects\\minimal-repro\\cmake-build-debug\\cpr_example.exe
        ntdll.dll => /c/WINDOWS/SYSTEM32/ntdll.dll (0x7ff8b9fb0000)
        KERNEL32.DLL => /c/WINDOWS/System32/KERNEL32.DLL (0x7ff8b93e0000)
        KERNELBASE.dll => /c/WINDOWS/System32/KERNELBASE.dll (0x7ff8b7790000)
        ADVAPI32.dll => /c/WINDOWS/System32/ADVAPI32.dll (0x7ff8b9e30000)
        msvcrt.dll => /c/WINDOWS/System32/msvcrt.dll (0x7ff8b8ba0000)
        sechost.dll => /c/WINDOWS/System32/sechost.dll (0x7ff8b92e0000)
        RPCRT4.dll => /c/WINDOWS/System32/RPCRT4.dll (0x7ff8b8a70000)
        bcrypt.dll => /c/WINDOWS/System32/bcrypt.dll (0x7ff8b7c90000)
        WS2_32.dll => /c/WINDOWS/System32/WS2_32.dll (0x7ff8b9dc0000)
        libidn2-0.dll => not found
        libpsl-5.dll => not found
        libssh2-1.dll => not found

Going back to the example you provided, it appears that setting CMAKE_CXX_STANDARD to 23 makes the build break. 20 and 17 apparently build and execute just fine (ldd still shows some strange results). I can verify that this happens with CPM too so clearly that is not the issue here.

I cannot seem to reproduce the exact problem you are having however