0

I am trying to use libpqxx (and implicitly libpq) in a C++ project.
I use vcpkg as a submodule to get my libs by setting CMAKE_TOOLCHAIN_FILE.

When I try to build, I get the following errors:

/usr/bin/ld: /home/user/work/cxx-playground/third-party/vcpkg/installed/x64-linux/lib/libpq.a(fe-auth.o): in function `pg_fe_sendauth':
fe-auth.c:(.text+0x4fe): undefined reference to `pg_md5_encrypt'
/usr/bin/ld: fe-auth.c:(.text+0x51b): undefined reference to `pg_md5_encrypt'
/usr/bin/ld: /home/user/work/cxx-playground/third-party/vcpkg/installed/x64-linux/lib/libpq.a(fe-auth.o): in function `pg_fe_getauthname':
fe-auth.c:(.text+0x838): undefined reference to `pqGetpwuid'
/usr/bin/ld: fe-auth.c:(.text+0x8a4): undefined reference to `pg_strerror_r'
/usr/bin/ld: /home/user/work/cxx-playground/third-party/vcpkg/installed/x64-linux/lib/libpq.a(fe-auth.o): in function `PQencryptPassword':
fe-auth.c:(.text+0x936): undefined reference to `pg_md5_encrypt'
/usr/bin/ld: /home/user/work/cxx-playground/third-party/vcpkg/installed/x64-linux/lib/libpq.a(fe-auth.o): in function `PQencryptPasswordConn':
fe-auth.c:(.text+0x9ed): undefined reference to `pg_md5_encrypt'
/usr/bin/ld: /home/user/work/cxx-playground/third-party/vcpkg/installed/x64-linux/lib/libpq.a(fe-auth-scram.o): in function `build_client_final_message':
fe-auth-scram.c:(.text+0xdb): undefined reference to `scram_SaltedPassword'
/usr/bin/ld: fe-auth-scram.c:(.text+0xee): undefined reference to `scram_ClientKey'
/usr/bin/ld: fe-auth-scram.c:(.text+0xfe): undefined reference to `scram_H'
/usr/bin/ld: fe-auth-scram.c:(.text+0x113): undefined reference to `scram_HMAC_init'
/usr/bin/ld: fe-auth-scram.c:(.text+0x12d): undefined reference to `scram_HMAC_update'
/usr/bin/ld: fe-auth-scram.c:(.text+0x141): undefined reference to `scram_HMAC_update'
/usr/bin/ld: fe-auth-scram.c:(.text+0x15b): undefined reference to `scram_HMAC_update'
/usr/bin/ld: fe-auth-scram.c:(.text+0x16f): undefined reference to `scram_HMAC_update'
/usr/bin/ld: fe-auth-scram.c:(.text+0x185): undefined reference to `scram_HMAC_update'
/usr/bin/ld: fe-auth-scram.c:(.text+0x195): undefined reference to `scram_HMAC_final'
/usr/bin/ld: fe-auth-scram.c:(.text+0x1e4): undefined reference to `pg_b64_enc_len'
/usr/bin/ld: fe-auth-scram.c:(.text+0x213): undefined reference to `pg_b64_encode'
/usr/bin/ld: fe-auth-scram.c:(.text+0x344): undefined reference to `pg_b64_enc_len'
/usr/bin/ld: fe-auth-scram.c:(.text+0x368): undefined reference to `pg_b64_encode'
...

I have broken down the linking command here:

/usr/bin/cmake -E cmake_link_script CMakeFiles/hello-pq.dir/link.txt --verbose=1
/usr/bin/c++     CMakeFiles/hello-pq.dir/CMakeFiles/3.16.3/CompilerIdCXX/CMakeCXXCompilerId.cpp.o CMakeFiles/hello-pq.dir/main.cpp.o  
-o ../bin/hello-pq  
/home/user/work/cxx-playground/third-party/vcpkg/installed/x64-linux/debug/lib/libssl.a 
/home/user/work/cxx-playground/third-party/vcpkg/installed/x64-linux/debug/lib/libcrypto.a 
/home/user/work/cxx-playground/third-party/vcpkg/installed/x64-linux/debug/lib/libpqxx-7.3.a 
-lpthread 
/home/user/work/cxx-playground/third-party/vcpkg/installed/x64-linux/lib/libpq.a -ldl 

My CMakeLists.txt looks like this:

cmake_minimum_required(VERSION 3.16)

set(CMAKE_TOOLCHAIN_FILE ${CMAKE_CURRENT_SOURCE_DIR}/../../third-party/vcpkg/scripts/buildsystems/vcpkg.cmake
        CACHE STRING "Vcpkg toolchain file")

project(hello-pq)

set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD_REQUIRED ON)

set(EXECUTABLE_OUTPUT_PATH ${CMAKE_CURRENT_SOURCE_DIR}/bin)

find_package(OpenSSL REQUIRED) 
find_package(libpqxx CONFIG REQUIRED)

file(GLOB_RECURSE PROJECT_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/src/*.cpp)
add_executable(${PROJECT_NAME} ${PROJECT_SOURCES})

target_link_libraries(${PROJECT_NAME}
                        PRIVATE OpenSSL::SSL 
                        PRIVATE OpenSSL::Crypto
                        PRIVATE libpqxx::pqxx)

I looked at the libpq.a from vcpkg installed dir with nm and I can see that for example, pg_md5_encrypt appears as undefined:

U pg_md5_encrypt

I don't get it, where are these missing functions like pg_md5_encrypt?
Is there another libpq... that I need to link or maybe a different version?

codentary
  • 993
  • 1
  • 14
  • 33
  • 1
    The order of the libraries etc may affect things. Put the ones required by others before the ones that require them and your own code last. But you still have two `main`s there also – Sami Kuhmonen Mar 28 '21 at 08:52
  • 1
    `GLOB_RECURSE` seems to be picking up `CMakeCXXCompilerId.cpp` from your build directory, its probably not a good idea to use `GLOB_RECURSE` in the root of your project – Alan Birtles Mar 28 '21 at 08:53
  • Yes, you are right, I will fix that now and update in order to get that error out of the way. – codentary Mar 28 '21 at 09:01
  • @SamiKuhmonen I have tried already to put libpq before libpqxx and I am getting `undefined reference` errors for `libpqxx`, e.g.: in function pqxx::encrypt_password[abi:cxx11](char const*, char const*): .../connection.cxx:98: undefined reference to PQencryptPassword – codentary Mar 28 '21 at 09:12
  • I think I am missing some lib that I need to link as I am linking statically, but what throws me off is the fact that some `undefined reference` errors are for functions like `pg_md5_encrypt` which start with `pg_` so I assume they should be part of the `libpq` or some other `postgres` lib and not some external lib that I might be missing. – codentary Mar 28 '21 at 09:17

1 Answers1

1

After some more digging around and testing I found that there are 2 more postgres static libraries that I need to link:

  • libpqcommon.a
  • libpgport.a

Additionally, the link order is also important and altough I was expecting to have to pass libs that are needed before the libs that need them, this is not the case here and it feels backwards.
Here is a working linker command:

usr/bin/c++ CMakeFiles/hello-pq.dir/src/main.cpp.o \
-o ../bin/hello-pq \
/home/user/work/cxx-playground/third-party/vcpkg/installed/x64-linux/debug/lib/libpqxx-7.3.a \
/home/user/work/cxx-playground/third-party/vcpkg/installed/x64-linux/lib/libpq.a \
/home/user/work/cxx-playground/third-party/vcpkg/installed/x64-linux/lib/libpgcommon.a \
/home/user/work/cxx-playground/third-party/vcpkg/installed/x64-linux/lib/libpgport.a \
/home/user/work/cxx-playground/third-party/vcpkg/installed/x64-linux/debug/lib/libssl.a \
/home/user/work/cxx-playground/third-party/vcpkg/installed/x64-linux/debug/lib/libcrypto.a \
-lpthread \
-ldl

In order to link the extra 2 static libs and also to achieve the order above, I updated the CMakeLists.txt like so:

cmake_minimum_required(VERSION 3.16)

set(CMAKE_TOOLCHAIN_FILE ${CMAKE_CURRENT_SOURCE_DIR}/../../third-party/vcpkg/scripts/buildsystems/vcpkg.cmake
        CACHE STRING "Vcpkg toolchain file")

project(hello-pq)

set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD_REQUIRED ON)

set(EXECUTABLE_OUTPUT_PATH ${CMAKE_CURRENT_SOURCE_DIR}/bin)

find_package(OpenSSL REQUIRED) 
#find_package(PostgreSQL REQUIRED) # Not needed (it will duplicate the libs)
find_package(libpqxx CONFIG REQUIRED)

file(GLOB_RECURSE PROJECT_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/src/*.cpp)
add_executable(${PROJECT_NAME} ${PROJECT_SOURCES})

target_link_libraries(${PROJECT_NAME}
                        PRIVATE libpqxx::pqxx
                        PRIVATE PostgreSQL::PostgreSQL
                        PRIVATE OpenSSL::SSL 
                        PRIVATE OpenSSL::Crypto)

This translates into the following linker command:

/usr/bin/c++ \
CMakeFiles/hello-pq.dir/src/main.cpp.o \
-o ../bin/hello-pq \
/home/user/work/cxx-playground/third-party/vcpkg/installed/x64-linux/debug/lib/libpqxx-7.3.a \
/home/user/work/cxx-playground/third-party/vcpkg/installed/x64-linux/debug/lib/libpq.a \
/home/user/work/cxx-playground/third-party/vcpkg/installed/x64-linux/debug/lib/libssl.a \
/home/user/work/cxx-playground/third-party/vcpkg/installed/x64-linux/debug/lib/libcrypto.a \
/home/user/work/cxx-playground/third-party/vcpkg/installed/x64-linux/lib/libpq.a \
/home/user/work/cxx-playground/third-party/vcpkg/installed/x64-linux/lib/libpgport.a \
/home/user/work/cxx-playground/third-party/vcpkg/installed/x64-linux/lib/libpgcommon.a \
-ldl \
-lpthread

We can see that libpq.a appears twice in the link arguments:

  • once due to libpqxx::pqxx target (I assume)
  • once due to PostgreSQL::PostgreSQL target which also contains the 2 missing static libs that need to be linked (libpgport.a and libpgcommon.a).

My conclusion is that this is a libpqxx bug, which should also link those 2 static libs with target_link_libraries(PUBLIC...) in the only target that it exposes (libpqxx::pqxx)

codentary
  • 993
  • 1
  • 14
  • 33
  • libpqxx is probably not linking against the target PostgreSQL::PostgreSQL but just the library libpq. – Alexander Neumann Mar 30 '21 at 15:29
  • Ah it is using ${PostgreSQL_LIBRARIES}. looking at the cmakelists.txt this is a bug in vcpkg-cmake-wrapper.cmake which does adjust the target but does not fix the PostgreSQL_LIBRARIES variable – Alexander Neumann Mar 30 '21 at 15:31