I am a CMake noob trying to put together a package installer so i can move between my windows development machine and my cluster.
I have the following directory tree for my files (an example)
-Primary
--Library Source
--CMakeLists.txt
--src1.cpp
--src1.h
--Application Source
--CMakeLists.txt
--src1.cpp
--src1.h
--CMakeLists.txt
Each CMakeLists.txt is
Primary/CMakeLists.txt
CMAKE_MINIMUM_REQUIRED(VERSION 2.8)
PROJECT(BloodVesselRadiationDamageSimulations CXX)
SET(FIND_LIBRARY_USE_LIB64_PATHS true)
SET(CMAKE_INSTALL_PREFIX ${CMAKE_SOURCE_DIR}) #only for testing
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_ROOT}/ ${CMAKE_SOURCE_DIR}/cmake/Modules/")
FIND_PACKAGE(OpenMP)
FIND_PACKAGE(MPI)
FIND_PACKAGE(HDF5)
FIND_PACKAGE(GTest)
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} /MT")
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} /MTd")
SET(CMAKE_DEBUG_POSTFIX _d)
ADD_SUBDIRECTORY(Source)
ADD_SUBDIRECTORY(SourceUnitTest)
Library Source/CMakeLists.txt
ADD_LIBRARY(VesselProjectBaseLibrary STATIC Src1.cpp
Src1.h)
INSTALL(TARGETS VesselProjectBaseLibrary DESTINATION x64/Debug CONFIGURATIONS Debug)
INSTALL(TARGETS VesselProjectBaseLibrary DESTINATION x64/Release CONFIGURATIONS Release|RelWithDebInfo)
Application Source/CMakeLists.txt
INCLUDE_DIRECTORIES("${GTEST_INCLUDE_DIRS}")
ADD_EXECUTABLE (SourceUnitTests Src1.cpp
Src1.h)
TARGET_LINK_LIBRARIES(SourceUnitTests ${GTEST_LIBRARY})
TARGET_LINK_LIBRARIES(SourceUnitTests debug VesselProjectBaseLibrary_d optimized VesselProjectBaseLibrary)
I am able to generate the projects correctly; I see all the correct files in the projects. However, when i go to compile the debug build I get the following error:
1>ipo: : error #11018: Cannot open VesselProjectBaseLibrary_d.lib
1>LINK : fatal error LNK1104: cannot open file 'VesselProjectBaseLibrary_d.lib'
If i compile my release build everything works perfectly and compilation is successful. The library compiles successfully under both builds.
When you link against a library created within the project, you need to specify library target name, not a library file. CMake will care about proper filename, path and other things:
TARGET_LINK_LIBRARIES(SourceUnitTests VesselProjectBaseLibrary)
Variable CMAKE_DEBUG_POSTFIX affects on library's filename. While file VesselProjectBaseLibrary_d.lib is actually created in debug build, it cannot be found automatically by the linker. Again, use target name and let CMake do all other work.
Related
I want to use DirectXTK via vcpkg and link it with my cmake project.
vcpkg.exe list shows the following:
directxtk:x86-windows apr2021 A collection of helper classes for writing Direc...
directxtk:x86-windows-static-md apr2021 A collection of helper classes for writing Direc...
libzip:x86-windows 1.7.3#2 A library for reading, creating, and modifying z...
libzip:x86-windows-static-md 1.7.3#2 A library for reading, creating, and modifying z...
zlib:x86-windows 1.2.11#10 A compression library
zlib:x86-windows-static-md 1.2.11#10 A compression library
[dependnencies from these are not included here]
You can see that DirectXTK,libzip and zlib is installed.
Now my CMakeLists.txt looks like this:
find_package(ZLIB REQUIRED)
target_link_libraries(Game PRIVATE ZLIB::ZLIB)
if(WIN32)
find_package(directxtk CONFIG REQUIRED)
target_link_libraries(Game PRIVATE DirectXTK)
endif()
find_package(libzip REQUIRED)
target_link_libraries(Game PRIVATE libzip)
When I try to compile my project, it says
fatal error LNK1104: cannot open file 'DirectXTK.lib'
fatal error LNK1104: cannot open file 'libzip.lib'
It can't find the lib files for DirectXTK and libzip when linking.
It compiles fine though, which means it can find zlib.h and DirectXTK/WICTextureLoader for example.
How am I supposed to fix that?
The cmake Toolchain is correctly set to vcpkg.cmake and I used vcpkg.exe integrate install prior.
I'm building a target that depends on some provided libraries. My src directory hierarchy looks like this:
I use the following CMakeLists.txt to build a target and install it within the build/install directory:
cmake_minimum_required(VERSION 2.8.3)
project(example)
include_directories(include)
link_directories(lib)
add_executable(${PROJECT_NAME}
src/example.cpp)
target_link_libraries(${PROJECT_NAME}
curlpp)
set(CMAKE_INSTALL_PREFIX ${CMAKE_CURRENT_BINARY_DIR}/install)
install(TARGETS ${PROJECT_NAME}
LIBRARY DESTINATION lib
ARCHIVE DESTINATION lib
RUNTIME DESTINATION bin
)
install(FILES lib/libcurlpp.a lib/libcurlpp.so lib/libcurlpp.so.1 lib/libcurlpp.so.1.0.0 DESTINATION deps)
When I do a simple build, everything is fine and the target is properly linked to the provided libraries. But when I do make install, the target is generated but fails to link to the libraries:
I understand the linkage failure of the installed target: the install/deps directory is not in the LD_LIBRARY_PATH of my environment. But what has cmake done to make the directly built target link correctly? Can I do something similar to make the installed target work properly?
Here is a minimal replication of the problem
I have the simplest possible c-library which builds and is packed using the following CMakeLists.txt:
cmake_minimum_required(VERSION 3.5)
project (libfoo C)
add_library(foo SHARED impl.c)
target_link_libraries(foo)
install(TARGETS foo LIBRARY DESTINATION lib/)
install(FILES public_header.h DESTINATION include/libfoo)
set(CPACK_GENERATOR "TGZ")
include(CPack)
Working example is located here: https://github.com/bjarkef/cmake-simple/tree/master/libfoo
I execute mkdir -p build; (cd build/; cmake ../; make all package;) to build a .tar.gz package with the compiled shared library along with its public header file. This is all working fine.
Now I wish to modify the CMakeLists.txt to create the FooConfig.cmake and FooConfigVersion.cmake files needed for CMake find_package in a different project to find the foo library. How do I do this?
I have discovered I should used the CMakePackageConfigHelpers: configure_package_config_file and write_basic_package_version_file, and I should create a FooLibraryConfig.cmake.in file. However I cannot figure out how to put it all together.
Note that it is important the the resulting .cmake files only contains relative paths.
I have cmake module included in the top level CmakeList.txt:
# Generate and install package config files
include(PackageConfigInstall)
Within the generic PackageConfigInstall.cmake file, the config files are created from the cmake.in files, and installed. This module can be reused for other packages.
include(CMakePackageConfigHelpers)
# Generate package config cmake files
set(${PACKAGE_NAME}_LIBRARY_NAME ${CMAKE_SHARED_LIBRARY_PREFIX}${PACKAGE_NAME}${CMAKE_STATIC_LIBRARY_SUFFIX})
configure_package_config_file(${PACKAGE_NAME}-config.cmake.in
${CMAKE_CURRENT_BINARY_DIR}/${PACKAGE_NAME}-config.cmake
INSTALL_DESTINATION ${CMAKE_INSTALL_DIR}/${PACKAGE_NAME}
PATH_VARS LIB_INSTALL_DIR INCLUDE_INSTALL_DIR APP_INCLUDE_INSTALL_DIR )
configure_file(${PACKAGE_NAME}-config-version.cmake.in ${CMAKE_CURRENT_BINARY_DIR}/${PACKAGE_NAME}-config-version.cmake #ONLY)
# Install package config cmake files
install(
FILES
${CMAKE_CURRENT_BINARY_DIR}/${PACKAGE_NAME}-config.cmake
${CMAKE_CURRENT_BINARY_DIR}/${PACKAGE_NAME}-config-version.cmake
DESTINATION
${CMAKE_INSTALL_DIR}/${PACKAGE_NAME}
COMPONENT
devel
)
You'll need a package file for your library, such as your_lib-config.cmake.in, which will become your_lib-config.cmake. This will contain the include and library variables that can be used.
get_filename_component(YOUR_LIB_CMAKE_DIR "${CMAKE_CURRENT_LIST_FILE}" PATH)
# flag required by CMakePackageConfigHelpers
#PACKAGE_INIT#
set_and_check(YOUR_LIB_INCLUDE_DIR #PACKAGE_YOUR_LIB_INCLUDE_INSTALL_DIR#/hal)
set_and_check(YOUR_LIB_LIBRARY #PACKAGE_LIB_INSTALL_DIR#/#CMAKE_STATIC_LIBRARY_PREFIX##PROJECT_NAME_LIB##CMAKE_STATIC_LIBRARY_SUFFIX#)
set_and_check(YOUR_LIB_LIBRARIES #PACKAGE_LIB_INSTALL_DIR#/#CMAKE_STATIC_LIBRARY_PREFIX##PROJECT_NAME_LIB##CMAKE_STATIC_LIBRARY_SUFFIX#)
You'll also want a config-version.cmake.in file like this:
set(PACKAGE_VERSION #PACKAGE_VERSION#)
# Check whether the requested PACKAGE_FIND_VERSION is compatible
if("${PACKAGE_VERSION}" VERSION_LESS "${PACKAGE_FIND_VERSION}")
set(PACKAGE_VERSION_COMPATIBLE FALSE)
else()
set(PACKAGE_VERSION_COMPATIBLE TRUE)
if ("${PACKAGE_VERSION}" VERSION_EQUAL "${PACKAGE_FIND_VERSION}")
set(PACKAGE_VERSION_EXACT TRUE)
endif()
endif()
There's quite a bit to the packaging scripts to get it all to work just right. I went through a lot of trial and error to finally get something that works on different targets (both linux server and embedded target). I might have left something out, so please just comment and I'll update answer.
I have one project that produces a library:
project (myCoolLibrary)
ADD_LIBRARY(my_cool_library SHARED ${mysources_SRC})
And another project that should be using this library:
find_package (myCoolLibrary REQUIRED)
INCLUDE_DIRECTORIES("${myCoolLibrary_INCLUDE_DIRS}" )
add_executable(myCoolExe ${my_sources_SRC} )
TARGET_LINK_LIBRARIES(myCoolExe ${myCoolLibrary_LIBRARIES} )
Is there a way that I can change the first file so that the second file works automatically? That by running CMake on the first file and then running make on the output, then running CMake on the second file, CMake is able to find the package?
An answer where I just give the address of where the first project is built to the second package is also acceptable.
Taking the code found in a blog post by #daniperez - Use CMake-enabled libraries in your CMake project (III) - I've come up with the following minimal solution:
myCoolLibrary/CMakeLists.txt
cmake_minimum_required(VERSION 3.3)
project(myCoolLibrary)
function(my_export_target _target _include_dir)
file(
WRITE "${CMAKE_CURRENT_BINARY_DIR}/${_target}Config.cmake"
"
include(\"\$\{CMAKE_CURRENT_LIST_DIR\}/${_target}Targets.cmake\")
set_property(
TARGET ${_target}
APPEND PROPERTY
INTERFACE_INCLUDE_DIRECTORIES \"${_include_dir}\"
)
"
)
export(TARGETS ${_target} FILE "${CMAKE_CURRENT_BINARY_DIR}/${_target}Targets.cmake")
# NOTE: The following call can pollute your PC's CMake package registry
# See comments/alternatives below
export(PACKAGE ${_target})
endfunction(my_export_target)
...
add_library(${PROJECT_NAME} SHARED ${mysources_SRC})
my_export_target(${PROJECT_NAME} "${CMAKE_CURRENT_SOURCE_DIR}")
myCoolExe/CMakeLists.txt
cmake_minimum_required(VERSION 3.3)
project(myCoolExe)
find_package(myCoolLibrary REQUIRED)
...
add_executable(${PROJECT_NAME} ${my_sources_SRC})
target_link_libraries(${PROJECT_NAME} myCoolLibrary)
To make it reusable I have packed everything into my_export_target(). And I'm friend of self-propagating properties like INTERFACE_INCLUDE_DIRECTORIES.
As commented by #ruslo, using export(PACKAGE ...) can pollute your package registry. So alternatively you can:
Write the target configuration files directly to some dedicated place specific for a certain toolchain
See e.g. How to install your custom CMake-Find module and 0003659: FIND_PACKAGE command improvements.
Set CMAKE_MODULE_PATH via the second project's CMake command line (injecting the search path(s) from the outside). If you are building the two projects anyway with a build script, then this is the most direct way to propagate the module search path(s).
Additional References
export()
CMake/Tutorials/Package Registry
Unable to find Eigen3 with CMake
How to instruct CMake to use the build architecture compiler
I have the following cmake setup:
colorizer_root
|
|-------colorizer_lib
|-------colorizer_template_project
The colorizer_root contains the top level CMakeLists.txt which is invoked when running cmake:
colorizer_root CMakeLists.txt
project(colorizer_root)
cmake_minimum_required(VERSION 2.8)
set(CMAKE_BUILD_TYPE Debug)
add_subdirectory(colorizer_lib)
add_subdirectory(colorizer_template_project)
As you can see it contains 2 subdirectories each a project on its own. Basically what the colorizer_lib does is create a shared library named libcolorize.so (no executables here!), which then is to be used by the other project colorizer_template_project (the executable is created in this project). Here are the two CMakeLists.txt files for their respective projects:
colorizer_lib CMakeLists.txt
project(colorizer_lib)
cmake_minimum_required(VERSION 2.8)
set(CMAKE_CXX_FLAGS "--std=gnu++11 ${CMAKE_CXX_FLAGS}")
include_directories(. INCLUDES)
add_library(colorizer SHARED colorizer.cpp)
colorizer_template_project CMakeLists.txt
project(colorizer_template_project)
cmake_minimum_required(VERSION 2.8)
find_library(COLORIZER_LIB colorizer
PATHS ${CMAKE_BINARY_DIR}/colorizer_lib
)
aux_source_directory(. SRC_LIST)
add_executable(${PROJECT_NAME} ${SRC_LIST})
target_link_libraries(${PROJECT_NAME} ${COLORIZER_LIB})
I'm having trouble figuring out how the whole lookup thing works. The problem here is that when I run the top level CMakeLists.txt it goes through both (obviously) but during processing the colorizer_template_project it breaks with a complaint:
CMake Error: The following variables are used in this project, but they are set to NOTFOUND.
Please set them or make sure they are set and tested correctly in the CMake files:
COLORIZER_LIB
linked by target "colorizer_template_project" in directory /home/USER/Programming/C_Cpp/colorizer/colorizer_template_project
This is an expected behaviour since libcolorizer.so cannot be present at the time of running cmake because it is created after make has been invoked.
How do I tell cmake to first process the first project (including the build step!) and then go to the next one? I know that this works if I add an executable to the project that creates the library and then directly link it to the binary but in this case I want separate projects for the library and the executable that is using it.
PS: I haven't given any details about the sources because they are not important here. It is - I believe - a general question, which is not specific to whether I'm using C, C++ or something similar.
project command doesn't make subprojects independent, so colorizer target is actually accessible for colorizer_template_project, and you can directly link with it:
add_executable(${PROJECT_NAME} ${SRC_LIST})
target_link_libraries(${PROJECT_NAME} colorizer)