cmake to link to dll without lib - c++

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.

Related

How to link .pdbs to a project using CMake?

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.

How to make CMAKE find specific version of lib curl on OSX?

Question is: How do I get CMAKE to set/make the CURL_INCLUDE_DIR be "/usr/local/Cellar/curl/7.75.0/include/"?
No matter what I do, CMAKE only finds the Xcode lib version/location.
My CMAKE file contains:
set(CURL_ROOT_DIR /usr/local/Cellar/curl/7.75.0/include/)
find_package(curl REQUIRED)
message(STATUS ">>> CURL Dir Found: " ${CURL_INCLUDE_DIR})
No matter what I've tried, the above CMAKE code will result in a "CURL_INCLUDE_DIR" of
"/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.13.sdk/usr/include
Note, utilizing "include(findcurl)" instead of "find_package(curl REQUIRED)" gives same result.
I've resorted to adding
set(CMAKE_CXX_FLAGS "-I/usr/local/Cellar/curl/7.57.0/include
-L/usr/local/Cellar/curl/7.57.0/lib")
to get CMAKE to actually utilize the lib version/location that I want. Is there a better way that I'm failing to figure out?
The cmake FindCURL module (at least in the version I have, 3.5.1) does not make reference to CURL_ROOT_DIR anywhere, so I don't believe setting that variable will have any effect.
In fact the FindCURL module is really quite basic and doesn't offer any means for customising where to look, which means it will be looking only in "standard" paths (eg /usr/lib, /usr/local/lib etc)
You can, however, use find_library which takes a PATHS argument.
set(CURL_PATH "/usr/local/Cellar/curl/7.75.0")
find_library(
LIB_CURL
NAMES
curl
PATHS
${CURL_PATH}/lib)
At this point you will have a variable ${LIB_CURL} which points to the location of libcurl
Link against the library and set the include path:
You can then just link against ${LIB_CURL} directly, and manually specify the include path for your target too, such as:
add_executable(
foo
main.cpp)
target_link_libraries(
foo
${LIB_CURL})
target_include_directories(
foo
SYSTEM
PRIVATE "${CURL_PATH}/include")
IMPORTED library target:
Another option, if you want to be fancy, is to create an IMPORTED library target using ${LIB_CURL}, and set the INTERFACE_INCLUDE_DIRECTORIES on that target, so consumers get the include path set automatically.
add_library(
libcurl
IMPORTED)
set_target_properties(
libcurl
PROPERTIES
IMPORTED_LOCATION "${LIB_CURL}")
set_target_properties(
libcurl
PROPERTIES
INTERFACE_INCLUDE_DIRECTORIES "${CURL_PATH}/include")
Now you will just be able to link against target libcurl directly, and your app will automatically have its include paths updated
add_executable(
foo
main.cpp)
target_link_libraries(
foo
libcurl)

CMake Add (Test) Executable

I would like to create two executables: one executable for the application, and one for the testing of the application. To that end, I have the following in my CMakeLists.txt file:
include_directories(include)
file(GLOB SOURCE "src/*.cc")
file(GLOB TEST "test/*.cc")
add_executable(interest_calc ${SOURCE})
add_executable(interest_calc_test "src/interest_calc.cc" ${TEST})
Since both src and test directories contain main functions, I have to manually add source files to the "test" executable. Is there another, non-manual, way to add required source files to the "test" executable?
Further, is there a better way to test functionality than creating a separate test executable? If so, what/how?
One way to improve your process would be to pull the guts of your executable into a library, then have a nominal "main" executable which just calls into your library and a "test" executable which exercises the library however you want to test it.
This way, any changes you need to make go into the library and the executable build process is untouched.
Edit to show CMake with your example:
include_directories(include)
file(GLOB SOURCE "src/*.cc")
# Remove main from library, only needed for exec.
list(REMOVE_ITEM SOURCE "main.cc")
file(GLOB TEST "test/*.cc")
add_library(interest_calc_lib STATIC ${SOURCE})
add_executable(interest_calc "main.cc")
target_link_libraries(interest_calc interest_calc_lib)
add_executable(interest_calc_test ${TEST})
target_link_libraries(interest_calc_test interest_calc_lib)
There are already some good answers from Soeren and mascoj but I would like to give a more concrete recommendation.
When you already have a CMakeLists.txt for your executable and you like to add testing, I recommend adding a static dummy library. This library can have all the sources of the executable except the main method (it may be easiest to single out the main method in a separate file if you do not have that already). Using a static library will give you two benefits:
The final executable will behave exactly as your current one, so there is no need to deal with distribution of a new, shared library
You do not need to deal with exporting symbols or throwing exceptions across shared object boundaries
The changes to your CMakeLists.txt can be quite small. I will give an example here, assuming you use cmake 3.0 or newer. First, an example of CMakeLists.txt before adding the dummy library:
project(MyProject)
set(SOURCES src/First.cc src/Second.cc src/Third.cc)
add_executable(${PROJECT_NAME} ${SOURCES} src/Main.cc)
target_include_directories(${PROJECT_NAME}
${CMAKE_CURRENT_SOURCE_DIR}/include
${CMAKE_CURRENT_SOURCE_DIR}/src
${CMAKE_CURRENT_BINARY_DIR})
target_compile_options(${PROJECT_NAME}
$<$<CXX_COMPILER_ID:GNU>:-Wall;-pedantic)
target_compile_definitions(${PROJECT_NAME}
$<$<CONFIG:Debug>:DEBUG;_DEBUG>)
set_target_properties(${PROJECT_NAME}
PROPERTIES CXX_STANDARD 14)
target_link_libraries(${PROJECT_NAME}
Threads::Threads)
To add the dummy library and testing, you need to introduce a new target with a different name. I choose here to use ${PROJECT_NAME}_lib because this will be very non-intrusive on the CMakeLists.txt. Here is the updated version. Notice the use of ${PROJECT_NAME}_lib in place of ${PROJECT_NAME} in almost all places. Most properties are now passed down to the executable by making them PUBLIC. Only calls to set_target_properties() are not transitive and must be duplicated for library and executable.
project(MyProject)
set(SOURCES src/First.cc src/Second.cc src/Third.cc)
add_library(${PROJECT_NAME}_lib STATIC ${SOURCES})
add_executable(${PROJECT_NAME} src/Main.cc)
target_include_directories(${PROJECT_NAME}_lib PUBLIC
${CMAKE_CURRENT_SOURCE_DIR}/include
${CMAKE_CURRENT_SOURCE_DIR}/src
${CMAKE_CURRENT_BINARY_DIR})
target_compile_options(${PROJECT_NAME}_lib PUBLIC
$<$<CXX_COMPILER_ID:GNU>:-Wall;-pedantic)
target_compile_definitions(${PROJECT_NAME}_lib PUBLIC
$<$<CONFIG:Debug>:DEBUG;_DEBUG>)
set_target_properties(${PROJECT_NAME}_lib
PROPERTIES CXX_STANDARD 14)
set_target_properties(${PROJECT_NAME}
PROPERTIES CXX_STANDARD 14)
target_link_libraries(${PROJECT_NAME}
${PROJECT_NAME}_lib
Threads::Threads)
Now you can link your tests against ${PROJECT_NAME}_lib with a different main method.
You could do like this :
In the current CMakeLists.txt, put these lines :
add_subdirectory(src)
add_subdirectory(test)
and then, in each directories add a CMakeLists.txt that link correctly sources to each files.
About test, I've heard that CMake can do test automation, but I don't really know how it works.
In my opinion the best solution is to create a library (shared or static) and two executables (one for the main program and one for the test main). After that you should link the library against the two applications.
In this answer I write down a explanation with a little example how you could managed the project with cmake.

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.

CMake link single class in another project

its one of my first c++ projects and i got problems with CMake.
I have MyProject with an executeable and i got a Project tests with boost unit tests.
I tried it the following way, but i failed. Obviously i cant have two executeables this way and i dont know how to fix fix it.
This is the CMake of MyProject
project (MyProject)
find_package( Boost 1.48.0 COMPONENTS thread )
set(MYPROJECT_SRCS main.cpp foo.h foo.cpp)
add_executable(MyProject ${MYPROJECT_SRCS})
target_link_libraries(MyProject ${Boost_LIBRARIES})
This is the CMake of tests
project (tests)
find_package( Boost 1.48.0 COMPONENTS thread unit_test_framework)
find_package( Boost 1.48.0 COMPONENTS thread )
include_directories("../MyProject")
set(TEST_SRCS test.cpp )
add_executable(tests ${TEST_SRCS})
target_link_libraries(tests ${Boost_LIBRARIES} MyProject)
add_test( example_test tests )
CMake Error at tests/CMakeLists.txt:13 (target_link_libraries):
Target "MyProject" of type EXECUTABLE may not be linked into another
target. One may link only to STATIC or SHARED libraries, or to
executables with the ENABLE_EXPORTS property set.
I tried to "ENABLE_EXPORTS property set", but i think i did it wrong.
You shouldn't link your executable file with tests, instead you need include source files of your main project in tests source list:
set(TEST_SRCS test.cpp ../MyProject/foo.cpp)
target_link_libraries(tests ${Boost_LIBRARIES} )
P.S. Also it will be useful when you want to analyze test coverage.
There are several modifications to be done in MyProject project (the one you are referencing).
CMakeList.txt file has to include a ENABLE_EXPORTS property:
add_executable(MyProject foo.c)
set_property(TARGET MyProject PROPERTY ENABLE_EXPORTS 1)
This property reacts differently depending on OS. With Windows OS it will create a .lib file aside a regular .exe file.
External signatures have to be exported:
#define EXPORTED_API __declspec(dllexport)
class EXPORTED_API MyProject {...}
or
int EXPORTED_API func() {...}