How to link .pdbs to a project using CMake? - c++

I've run into a problem that I cannot seem to find anywhere else.
I am compiling a program that Visual Studio complains about not finding the corresponding .pdb file:
foo.lib(func.obj) : Warning LNK4099: PDB 'foo.pdb' was not found with 'foo.lib(func.obj)' or at '<Source_DIR>\build\Debug\foo.pdb'; linking object as if no debug info.
So I found a helpful link here describing that installing the .pdb files is as simple as:
install(FILES $<TARGET_PDB_FILE:foo> DESTINATION ${CMAKE_INSTALL_BINDIR}/$<CONFIG> OPTIONAL)
So in my foo lib I have my CMakeLists.txt file set up as such:
cmake_minimum_required(VERSION 3.18)
project(foo)
include(GNUInstallDirs)
include(CMakePackageConfigHelpers)
set(version 1)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED True)
add_library(foo SHARED foo.hpp foo.cpp)
set_target_properties(foo PROPERTIES DEBUG_POSTFIX "d")
target_include_directories(foo
PUBLIC
"$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>"
"$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>"
)
add_library(foo::foo ALIAS foo)
install(TARGETS foo
EXPORT fooTargets
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}/$<CONFIG>
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}/$<CONFIG>
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}/$<CONFIG>
INCLUDES DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}
)
install(FILES foo.hpp DESTINATION ${CMAKE_INSTALL_INCLUDEDIR})
install(FILES $<TARGET_PDB_FILE:foo> DESTINATION ${CMAKE_INSTALL_BINDIR}/$<CONFIG>
OPTIONAL)
install(EXPORT fooTargets
FILE footargets.cmake
NAMESPACE foo::
DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake
)
# generate the version file for the config file
write_basic_package_version_file(
"${CMAKE_CURRENT_BINARY_DIR}/fooConfigVersion.cmake"
VERSION "${version}"
COMPATIBILITY AnyNewerVersion
)
# create config file
configure_package_config_file(${CMAKE_CURRENT_SOURCE_DIR}/Config.cmake.in
"${CMAKE_CURRENT_BINARY_DIR}/fooConfig.cmake"
INSTALL_DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake
)
# install config files
install(FILES
"${CMAKE_CURRENT_BINARY_DIR}/fooConfig.cmake"
"${CMAKE_CURRENT_BINARY_DIR}/fooConfigVersion.cmake"
DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake
)
# generate the export targets for the build tree
export(EXPORT fooTargets
FILE "${CMAKE_CURRENT_BINARY_DIR}/cmake/footargets.cmake"
NAMESPACE foo::
)
Which installs everything nicely into something like this:
InstallDir
-- include
-- bin
-- lib
However, when I link against foo in my CMakeLists.txt I get the warning that is provided above on how it cannot find the .pdb file. However, the .pdb file is living in the InstallDir -> bin -> food.pdb.
Is there a way to link to this using CMake? Or do I have to manually copy over the .pdb file into the Debug directory even before it exists? This is where I'm stuck and I'm not sure how to proceed. This is the current CMake file:
cmake_minimum_required(VERSION 3.18)
project(Test_foo)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED True)
set(foo_DIR ${CMAKE_CURRENT_LIST_DIR}/foo/lib/cmake)
find_package(foo 1 EXACT REQUIRED)
add_executable(Test_foo main.cpp)
target_link_libraries(Test_foo PUBLIC foo::foo)
set_property(TARGET Test_foo PROPERTY NO_SYSTEM_FROM_IMPORTED 1)
This will link happily. However, when I build I get the warning as described from above. I want to be able to point it to the .pdb file that is installed. However I am not sure how to do that. Any tips?

The linker expects the .pdb to be next to the .lib.
When you link in a static library, its debug info is merged into the thing you're building. So you should put the .pdb next to the lib so the linker can find it.
When you link in a shared library, its debug info is not touched by the linker and should be next to the .dll, where the debugger will find it when debugging. So in this case it is better to have the .pdb in your bin folder – next to the dll.

Related

Deplying a C++ application on Linux- linking everything statically to simplify deployment?

I am building a C++ project from Github and want to deploy the code to a remote Linux machine. This is all new to me.
The project has a main.cpp, which includes the various headers/sources like a library.
The CMake outputs an executable (to represent main.cpp) AND a separate static library. The project also uses OpenSSL, which I have linked statically.
I presume the OpenSSL functions are included within the static library? So when I deploy, I don't need to copy-over or install any OpenSSL on the remote machine?
Is it possible to modify the CMake so the application and the library are merged in to one file?
I am trying to make deployment as simple as copying over a single file, if this is possible.
Any additional advice/references are most-welcome.
UPDATE the CMake script:
cmake_minimum_required(VERSION 3.20)
set(CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/CMake;${CMAKE_MODULE_PATH}")
project(helloworld C CXX)
set (CMAKE_CXX_STANDARD 20)
set (CMAKE_BUILD_TYPE Release)
set (BUILD_MAIN TRUE)
set (BUILD_SHARED_LIBS FALSE)
set (OPENSSL_USE_STATIC_LIBS TRUE)
set(CMAKE_POSITION_INDEPENDENT_CODE ON)
set( HELLOWORLD_HEADERS helloworld/File1.h helloworld/File2.h )
set( HELLOWORLD_SOURCES helloworld/File1.cpp helloworld/File2.cpp )
# Static library
add_library( helloworld ${HELLOWORLD_SOURCES} ${HELLOWORLD_HEADERS} )
# Rapidjson
include_directories(/tmp/rapidjson/include/)
# OpenSSL
if (NOT OPENSSL_FOUND)
find_package(OpenSSL REQUIRED)
endif()
add_definitions(${OPENSSL_DEFINITIONS})
target_include_directories(helloworld PUBLIC $<BUILD_INTERFACE:${OPENSSL_INCLUDE_DIR}>)
target_link_libraries(helloworld PRIVATE ${OPENSSL_LIBRARIES})
set( HELLOWORLD_INCLUDE_DIRS ${CMAKE_CURRENT_SOURCE_DIR})
include(GNUInstallDirs)
target_include_directories(helloworld PUBLIC
$<BUILD_INTERFACE:${HELLOWORLD_INCLUDE_DIRS}/>
$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}/helloworld>
)
set_target_properties(helloworld PROPERTIES PUBLIC_HEADER "${HELLOWORLD_HEADERS}")
add_library(helloworld::helloworld ALIAS helloworld)
option(HELLOWORLD_INSTALL "Install HelloWorld" TRUE)
if (HELLOWORLD_INSTALL)
install(TARGETS helloworld
EXPORT helloworld
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
PUBLIC_HEADER DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/helloworld/
)
configure_file("${CMAKE_CURRENT_LIST_DIR}/helloworld-config.cmake.in" "${CMAKE_BINARY_DIR}/helloworld-config.cmake" #ONLY)
install(FILES "${CMAKE_BINARY_DIR}/helloworld-config.cmake" DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/helloworld")
install(EXPORT helloworld
FILE helloworld-targets.cmake
NAMESPACE helloworld::
DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/helloworld
)
endif()
if (BUILD_MAIN)
add_executable(main main.cpp)
target_link_libraries(main helloworld)
endif()
ITNOA
I it is very helpful to make URL of your GitHub's project, but I write some public notes about that
In generally in CMake for static linking your library to your executable, you can write simple like below (from official CMake example)
add_library(archive archive.cpp zip.cpp lzma.cpp)
add_executable(zipapp zipapp.cpp)
target_link_libraries(zipapp archive)
In above example your executable file is just work without needing .a library file and you can simple copy single file.
if you want to make all of thing static, you make sure all dependencies make static link to your project, like CMake: how to produce binaries "as static as possible"
if you want to prevent library creation, Probably in your CMake file, you can find add_library command, and add_executable command. you can remove add_library command and add all sources to add_executable command.
for example add_executable(a.out main.cpp lib.cpp)

cmake to link to dll without lib

I've been given a dll without libs.
The dll comes with hpp and h files.
I used dumpbin to create an exports.def file and lib to create a library.
I'm using the following CMakeLists.txt
cmake_minimum_required ( VERSION 3.22 )
project ( mytest )
include_directories("${PROJECT_SOURCE_DIR}/libincludedir")
add_executable ( mytest main.cpp)
target_link_libraries ( mytest LINK_PUBLIC ${PROJECT_SOURCE_DIR}/libincludedir/anewlib.lib )
The original dll and created lib and h and hpp files are all in the libincludedir. Dll is also copied to the bin dir where the exe would be.
I get no linker errors with the lib, but no functions defined in the include headers have bodies found. Types are Undefined. Classes are incomplete.
How do I fix this? Can I progress with what I was given or should I ask for more?
Assuming that you created the .lib correctly, this is how you would set up something linkable:
cmake_minimum_required(VERSION 3.22)
project(example)
add_library(anewlib::anewlib SHARED IMPORTED)
set_target_properties(
anewlib::anewlib
PROPERTIES
IMPORTED_IMPLIB "${PROJECT_SOURCE_DIR}/libincludedir/anewlib.lib"
IMPORTED_LOCATION "${PROJECT_SOURCE_DIR}/libincludedir/anewlib.dll"
IMPORTED_NO_SONAME "TRUE"
INTERFACE_INCLUDE_DIRECTORIES "${PROJECT_SOURCE_DIR}/libincludedir"
)
add_executable(mytest main.cpp)
target_link_libraries(mytest PRIVATE anewlib::anewlib)
Always link to targets since they provide CMake with much more information than a raw library file. Here, we're telling CMake about the locations of both the .lib and the .dll, that it doesn't have a SONAME (a *nix only thing, anyway, but good to include), and the include path that linkees should use to find its associated headers.
Then when you link mytest to it, mytest will have a correct link line constructed and it will see the right headers.

cmake - Header files of shared library not found

I am making a custom library that I want to be installable for users. However, when I try to use my own library in a cmake executable, I get a build error saying that the library headers were not found.
The library CMakeLists.txt:
cmake_minimum_required(VERSION 3.16)
project(mylibrary)
include(GNUInstallDirs)
set(CMAKE_CXX_STANDARD 14)
# Register a library - This will created lib[xxx].so
add_library(mylibrary SHARED src/library.cpp)
configure_file(mylibrary.pc.in mylibrary.pc #ONLY)
# List the /include directory
target_include_directories(mylibrary PUBLIC
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
$<INSTALL_INTERFACE:include>)
install(TARGETS mylibrary
EXPORT mylibraryConfig
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR})
export(TARGETS mylibrary
FILE "${CMAKE_CURRENT_BINARY_DIR}/mylibraryConfig.cmake")
install(EXPORT mylibraryConfig
DESTINATION "${CMAKE_INSTALL_LIBDIR}/mylibrary/cmake"
NAMESPACE mylibraryConfig::)
install(
DIRECTORY include
DESTINATION ${CMAKE_INSTALL_PREFIX})
install(FILES ${CMAKE_BINARY_DIR}/mylibrary.pc
DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/pkgconfig)
Which I successfully build and install with:
$ cmake .. -DCMAKE_INSTALL_PREFIX=~/libraries/local # Use non-standard destination
$ make && make install
The executableCMakeLists.txt:
cmake_minimum_required(VERSION 3.16)
project(myexecutable)
set(CMAKE_CXX_STANDARD 14)
find_package(mylibrary REQUIRED)
add_executable(myexecutable src/main.cpp)
target_link_libraries(myexecutable PUBLIC mylibrary)
target_include_directories(myexecutable PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/include)
I can prepare cmake for this project:
$ cmake .. -DCMAKE_PREFIX_PATH=~/libraries/local # Use non-standard location
However, building it fails:
$ make
fatal error: mylibrary/library.h: No such file or directory
2 | #include <mylibrary/library.h>
To my understanding the location of the library (binaries and headers) is embedded in the installed package. And through find_package() that information retrieved, so why isn't it working here?
Similar questions:
I largely based my library cmake on: How to create a shared library with cmake?
Same problem but I am already using target_include_directories: Cmake Linking Shared Library: "No such file or directory" when include a header file from library
When a shared library target is namespaced in the config file you need to reference it with the full name in the downstream packages when using find_package, i.e. you need to use
target_link_libraries(myexecutable PUBLIC mylibraryConfig::mylibrary)
Alternatively, remove the namespace from the install by replacing
install(EXPORT mylibraryConfig
DESTINATION "${CMAKE_INSTALL_LIBDIR}/mylibrary/cmake"
NAMESPACE mylibraryConfig::)
...with:
install(EXPORT mylibraryConfig
DESTINATION "${CMAKE_INSTALL_LIBDIR}/mylibrary/cmake")

CMake import local shared library and how to include it in a header

I'm stuck to import a shared library in another project with CMake.
my-utils-lib
My lib files are generated in my-utils-lib project:
CMakeLists.txt
cmake_minimum_required(VERSION 3.16)
project(my-utils-lib VERSION 1.0.0 DESCRIPTION "Utils for C++.")
set(CMAKE_CXX_STANDARD 14)
add_library(
my-utils-lib SHARED
./src/string_utils/find_matches.cpp
./src/string_utils/split.cpp
./src/string_utils/format.cpp
./src/vector_utils/print_vector.cpp
)
set_target_properties(
my-utils-lib PROPERTIES
VERSION ${PROJECT_VERSION}
SOVERSION 1
)
target_include_directories(my-utils-lib PRIVATE src)
include(GNUInstallDirs)
install(
TARGETS my-utils-lib
DESTINATION ${CMAKE_INSTALL_LIBDIR}
EXPORT my-utils-lib
)
configure_file(my-utils-lib.pc.in my-utils-lib.pc #ONLY)
install(
FILES ${CMAKE_BINARY_DIR}/my-utils-lib.pc
DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/pkgconfig
)
I run mkdir build && cd ./build/ && cmake .. && sudo make install to generate and install my library files.
Finally, my-utils-lib project tree looks like this:
CMakeLists.txt
my-utils-lib.pc.in
src/
|_main.cpp
|_main.h
|_string_utils/
|_find_matches.cpp
|_find_matches.h
|_format.cpp
|_format.h
|_split.cpp
|_split.h
|_vector_utils/
|_print_vector.cpp
|_print_vector.h
my-project
In another project, I'd like to import "split.h" in one of my local headers without having to enter the full path to the actual file. Something like:
#include "my-utils-lib/string_utils/split.h"
I tried many solution so far, none of them is working. My last attempt is:
CMakeLists.txt
cmake_minimum_required(VERSION 3.16)
project(longer_sub_sequence)
set(CMAKE_CXX_STANDARD 14)
find_package(Qt5Widgets REQUIRED)
find_library(my-utils-lib libmy-utils-lib.1.0.0.dylib)
add_library(my-utils-lib SHARED IMPORTED)
add_executable(
longer_sub_sequence
main.cpp
visualization/draw_matrix.cpp
visualization/draw_matrix.h
)
target_link_libraries(longer_sub_sequence my-utils-lib Qt5::Widgets)
CMake autobuild runs fine (I am using CLion as an IDE) but I can't find how to import any code from my library.
I checked in /usr/local/lib (where CMake generated my lib files), and found the correct lib files :
find . -maxdepth 1 -name "*my-utils-lib*" -print
./libmy-utils-lib.1.0.0.dylib
./libmy-utils-lib.1.dylib
./libmy-utils-lib.dylib
What am I missing here ?
You are missing to install the library header.
You should add an install command for header just like you did with the library binary.
Moreover you should consider using comparison signs (< and >) instead of double quote for include directive :
#include <my-utils-lib/string_utils/split.h>
One last remark, you are generating and installing a .pc file which is used by pkg-config only and you're not using it.

Right way of creating a library, installing it and linking to another project using CMake

I have a set of files that I want to make into a library and then use that library in another project. This it how it looks like right now
cmake_minimum_required(VERSION 2.8)
aux_source_directory(. SRC_LIST)
SET(CMAKE_CXX_FLAGS "-std=c++11 -fopenmp -fPIC")
add_library (helperlibs lib1.cpp lib2.cpp lib3.cpp lib4.cpp )
INSTALL(TARGETS helperlibs
DESTINATION "${HOME}/lib"
)
INSTALL(FILES lib1.h lib2.h lib3.h lib4.h helperheader.h
DESTINATION "${HOME}/include/helperlibs"
)
In this code Lib4 depends on Lib1-3 and Lib3 depends on Lib1-2 and Lib2 depends on Lib1. Each of these cpp files also depend on a helperheader.h file that contains some definitions and structs.
In my project I have the following CMake file
cmake_minimum_required(VERSION 2.8)
aux_source_directory(. SRC_LIST)
SET( CMAKE_CXX_FLAGS "-std=c++11 -fopenmp -fPIC")
SET(MYINCS ${HOME}/include/helperlibs)
SET(MYLIBDIR ${HOME}/lib)
SET(MYLIBS ${MYLIBDIR}/libhelperlibs.a )
include_directories(${MYINCS})
add_executable(main main.cpp)
target_link_libraries(main ${MYLIBS})
So what I am wondering if you want to create a static library and link to from a another project using cmake is this the way you should write?
Embed the search paths into the library target as properties and create an export.
This way executables in the same build tree will find the library and its include files without you having to specify paths (they become implicit).
I needed to read the cmake documentation carefully a few times before it dawned on me how it should work.
http://www.cmake.org/cmake/help/v3.0/manual/cmake-packages.7.html#creating-packages
http://www.cmake.org/cmake/help/v3.0/manual/cmake-buildsystem.7.html
excerpt from a live example:
add_library(trustportal-util ${CMAKE_CURRENT_LIST_FILE} ${_source_files} ${_disabled_source_files} )
target_link_libraries(trustportal-util ${Boost_LIBRARIES})
if(APPLE)
find_library(SECURITY_FRAMEWORK Security)
target_link_libraries(trustportal-util ${SECURITY_FRAMEWORK})
else()
find_library(LIB_SSL ssl)
find_library(LIB_CRYPTO crypto)
target_link_libraries(trustportal-util ${LIB_SSL} ${LIB_CRYPTO})
endif()
target_compile_definitions(trustportal-util PUBLIC BOOST_MOVE_USE_STANDARD_LIBRARY_MOVE)
target_include_directories(trustportal-util PUBLIC ${Boost_INCLUDE_DIRS})
target_include_directories(trustportal-util PRIVATE
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
$<INSTALL_INTERFACE:include>)
target_include_directories(trustportal-util SYSTEM PUBLIC
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
$<INSTALL_INTERFACE:include>)
install(TARGETS trustportal-util
EXPORT trustportal-utilExport
DESTINATION lib
INCLUDES DESTINATION include
)
INSTALL(EXPORT trustportal-utilExport DESTINATION lib)
One option is to do what you are currently doing where you place the includes and libs in a common location, perhaps /usr/include and /usr/lib on linux, or ${HOME} on both Windows/Linux, up to you.
Another option is available if you use git. You can include the project inside another using submodules. Then use include_directory(${submodule}) and build and link directly for every project. The advantage of this approach is that dependencies are more localised. One problem with this method is if you recursively do this, you may end up with duplicates of some projects and cmake will complain that two libraries have the same name.