I am trying to build a project that depends on Google Protocol Buffers compiled from source. My project should be platform independent and also should support cross-compilation, which is the reason that i prefer to use a locally built protobuf. However I would prefer not to include the whole library as a subproject as it would take too much to build.
My simplified CMakeLists.txt is:
cmake_minimum_required(VERSION 3.5)
project(sample-protobuf)
# find a boost install with the libraries filesystem and system
find_package(Protobuf REQUIRED)
set(SOURCES
main.cpp
)
add_executable(sample
${SOURCES}
)
target_link_libraries(sample
PRIVATE
protobuf::libprotobuf
)
I invoke CMake on Linux as:
cmake -DCMAKE_PREFIX_PATH=/path/to/built/protobuf/ ..
but it does not find the library and I get the following message:
Could not find a package configuration file provided by "Protobuf" with any
of the following names:
ProtobufConfig.cmake
protobuf-config.cmake
Add the installation prefix of "Protobuf" to CMAKE_PREFIX_PATH or set
"Protobuf_DIR" to a directory containing one of the above files. If
"Protobuf" provides a separate development package or SDK, be sure it has
been installed.
On Windows this procedure works.
I built the library on Linux using the suggested approach, which is not with CMake but with autotools.
What should I do differently?
cd protobuf/cmake
mkdir build
cd build
cmake....
make...
sudo make install
Related
I have git cloned, built (with MSVC for both Debug and Release) and then installed wxWidgets:
cmake -B build wxWidgets
cmake --build build --config <CONFIG>
cmake --install build --prefix my_install --config <CONFIG>
with <CONFIG> = Debug and <CONFIG> = Release.
Then I used the following CMake script to link against it, as suggested by the wiki:
cmake_minimum_required(VERSION 3.16)
project(Test)
add_executable(Test WIN32 Main.cpp)
# wxWidgets
SET(wxWidgets_ROOT_DIR ${CMAKE_CURRENT_LIST_DIR}/../thirdparty/my_install)
find_package(wxWidgets COMPONENTS core base REQUIRED)
include(${wxWidgets_USE_FILE})
target_link_libraries(Test PRIVATE ${wxWidgets_LIBRARIES})
# Copy runtime DLLs to the directory of the executable.
add_custom_command(TARGET Test POST_BUILD
COMMAND ${CMAKE_COMMAND} -E echo "Runtime Dlls: $<TARGET_RUNTIME_DLLS:Test>"
)
My goal is to automatically copy the DLLs into the directory of the built executable, so that they can be found at runtime. For that I'm using the TARGET_RUNTIME_DLLS generator expression (follwing the sample code in the docs). In the code above, I only print out the expression at build time for testing purposes. The problem is that it is empty.
The approach worked for me before when installing and linking SDL, but SDL provides package configuration files which create imported targets, defining the DLL location(s) via IMPORTED_LOCATION_RELEASE or IMPORTED_LOCATION_DEBUG. For wxWidgets one is apparently supposed to use the FindwxWidgets.cmake script shipped with CMake, which sadly doesn't define the produced binaries. Maybe that's why TARGET_RUNTIME_DLLS isn't populated.
Does anyone know, either how to get TARGET_RUNTIME_DLLS filled or how to obtain the list of built wxWidgets DLLs for the current configuration (Release/Debug) post build copying?
Thanks a lot in advance!
I am dealing with a similar problem.
First sanity checks:
You have to work on windows platform otherwise this feature does not
work.
Your Cmake is 3.21 or above
Next comes fuzzy part. I think the library that you are trying to include have to be a Shared Imported library and you have to set a set_target_properties for IMPORTED_IMPLIB which is a path to a .lib file of sort (dll import library, I think it is called) So you have to make sure that it is all set in the package library that you trying to link with your executable.
If you have those dll avaiable and you just want to use them and not actually build them then you can write your own cmake script that will do just what I said above. Then you can include that cmake file in your project and then link against your app.
Note: I also work on similar issue right now and what I just said have not been working very reliably. I got some dlls to be copied and some do not.
Edit:
Cmake docs give a more detailed explanation on how this library setting should look like if you use find_package feature.
Found here: https://cmake.org/cmake/help/latest/command/add_library.html#imported-libraries
An UNKNOWN library type is typically only used in the implementation
of Find Modules. It allows the path to an imported library (often
found using the find_library() command) to be used without having to
know what type of library it is. This is especially useful on Windows
where a static library and a DLL's import library both have the same
file extension.
I'm trying to use the CMake functions protobuf_generate in one of my projects. However, I'd like to the build as lightweight as possible. If at all possible I'd like to do this without having to compile protobuf's source code, or install some package on my (windows) machine.
Is there a way to just download binaries and headers and tell CMake: "Here is everything you need", so I can swap new proto versions whenever I feel like?
# CMakeList.txt : Top-level CMake project file, do global configuration
# and include sub-projects here.
#
cmake_minimum_required (VERSION 3.5)
project ("CMakeProject1")
set(TARGET_NAME CMakeProject1)
SET(PROTOBUF_INCLUDE_DIR "/path/to/include")
SET(PROTOBUF_LIBRARY "/path/to/libprotobuf.lib")
SET(PROTOBUF_PROTOC_EXECUTABLE "/path/to/protoc.exe")
find_package(Protobuf)
protobuf_generate_cpp(PROTO_SRCS PROTO_HDRS path/to/some_object.proto)
add_library(${TARGET_NAME} STATIC ${PROTO_SRCS} ${PROTO_HDRS})
I am trying to use the assimp library in a cross platform C++ project. I include the repo as a git submodule, so, effectively, if someone downloads my project they will also download the ASSIMP project.
After I go through the assimp build / CMAKE instructions and (on Linux) type make install and from then on in my project I can use:
target_link_libraries(${PROJECT_NAME} assimp)
However, there is no make install on Windows.
The only other way I have been able to include the library on Linux is to put (in my CmakeLists.txt file):
target_link_libraries(${PROJECT_NAME} ${CMAKE_SOURCE_DIR}/build/assimp/code/libassimp.so)
This is not cross platform as it hardcodes the name and location of the .so file which will not work on Windows.
How can I expose the library so that I can do something like target_link_libraries(${PROJECT_NAME} assimp) on all platforms?
My directory tree looks like:
- src
- include
- assimp
- bin
Where the assimp directory in the include directory is the git submodule
I think you're going about this the wrong way. You don't need to build assimp in a separate step from your project, and you don't need to make install to make it available.
There are a number of ways of handling third party dependencies in Cmake, since you've already chosen to submodule the assimp repository, we'll start there. Assuming assimp is located in the root of your repository in a directory called assimp/ this would be a barebones project including it:
cmake_minimum_required(VERSION 3.0)
project(Project myassimpproj)
# include your directories
include_directories(
${CMAKE_CURRENT_SOURCE_DIR}
)
# set any variables you might need to set for your app and assimp
set(BUILD_ASSIMP_TOOLS ON)
set(ASSIMP_BUILD_STATIC_LIB ON)
# add assimp source dir as a subdirectory, effectively making
# assimp's CMakeLists.txt part of your build
add_subdirectory(/path/to/assimp ${CMAKE_BINARY_DIR}/assimp)
add_executable(assimp_target main.cpp)
# be sure to link in assimp, use platform-agnostic syntax for the linker
target_link_libraries(assimp_target assimp)
There may be a better way of phrasing this using generator expressions syntax, but I haven't looked at assimp's CMakeLists.txt to know if it's supported (and this is a more generic way anyway.)
Not every project uses Cmake, so you may not be able to just add_subdirectory(). In those cases, you can effectively "fake" a user call to build them using their build commands on respective platforms. execute_process() runs a command at configure time add_custom_command() and add_custom_target() run commands at build time. You then create a fake target to make integration and cross your fingers they support Cmake someday.
You can also use the ExternalProject commands added to Cmake to create a custom target to drive download, update/patch, configure, build, install and test steps of an external project, but note that this solution and the next download the dependency rather than using the submodule'd source code.
Finally, I prefer to work with prebuilt dependencies, cuts down on build time, and they can be unit tested on their own outside of the project. Conan is an open source, decentralized and multi-platform package manager with very good support for C++ and almost transparent support for Cmake when used the right way. They have grown very stable in the last year. More information on how to use Conan with Cmake can be found here.
You have a CMake-enabled library project. You need to use it in another library or executable. How to use CMake to find and link to the library? You may have the following preferences:
write the least possible amount of boiler-plate code
decouple the internal details of the linked library from the consuming target
Ideally, the usage of the library should look like this:
add_executable(myexe ...)
target_link_libraries(myexe mylib::mylib)
Let me demonstrate a possible solution on a concrete example:
The myapp project
We have an executable target myapp. We're linking it with mylib, which is built in its own build tree. In the CMakeLists.txt of myapp we find and specify mylib as a dependency of myexe:
find_package(mylib REQUIRED)
...
add_executable(myexe ...)
target_link_libraries(myexe mylib::mylib)
Let's see how to set up mylib and the build of myexe to make this work.
The mylib project
The directory layout of mylib:
mylib
- CMakeLists.txt
- mylib.c
+ include
- mylib.h # single public header
In the CMakeLists.txt of mylib we need to create the target and specify its source files:
add_library(mylib mylib.c include/mylib.h)
The public header mylib.h will be included as #include "mylib.h" both by mylib and the clients of mylib:
mylib itself and other targets built in mylib's CMake project (for example tests) need to find include/mylib.h from the mylib source tree
clients of mylib built in their own projects (like myexe) need to find include/mylib.h at its installed location
CMake allows us to specify both include paths for mylib:
target_include_directories(mylib PUBLIC
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
$<INSTALL_INTERFACE:include>)
We're using the PUBLIC option here since this header is needed on the public interface of mylib. Use PRIVATE for include paths internal to mylib.
The INSTALL_INTERFACE specifies a path relative to the install root, that is, CMAKE_INSTALL_PREFIX. To actually install the public header:
set_target_properties(mylib PROPERTIES PUBLIC_HEADER include/mylib.h")
We also need to install the library itself and the so-called config-module and related files. The config-module is the file which will be used by consuming projects, like myapp to find mylib and get all the parameters needed to link with it. It is similar to the pkg-config's .pc files.
We need two, related install commands. The first one:
install(TARGETS mylib
EXPORT mylib-targets
PUBLIC_HEADER DESTINATION include
ARCHIVE DESTINATION lib
LIBRARY DESTINATION lib
RUNTIME DESTINATION bin)
The list of destinations needed to cover all the standard install locations of static libraries, dll's and so's. If you're sure your library will be built exclusively as a static lib, a single DESTINATION lib would make it.
The interesting part is the EXPORT option. It assigns the list of targets (currently, it's only mylib) to the identifier mylib-targets. This identifier will be used in the next command to generate and install some special files which make find_package(mylib) work in the consuming projects:
install(EXPORT mylib-targets
NAMESPACE mylib::
FILE mylib-config.cmake
DESTINATION lib/cmake/mylib)
This command generates multiple files:
one file for each build configuration (Debug, Release, etc..) which describes the library file and configuration-dependent parameters
a file which describes the configuration-agnostic parameters and also includes all the config-dependent files. Since this file can also be used as a config-module on its own we simply rename it as mylib-config.cmake
The files will be installed into ${CMAKE_INSTALL_PREFIX}/lib/cmake/mylib which is one of the many standard locations the find_package(mylib) command will search for mylib-config.cmake.
Building mylib
We need to specify an install location in the variable CMAKE_INSTALL_PREFIX:
mkdir build
cd build
cmake -DCMAKE_INSTALL_PREFIX=$PWD/../out ../mylib
and build and install the library:
cmake --build . --target install
Building myexe
myexe needs to know where to look for mylib. The variable CMAKE_PREFIX_PATH can be a list of paths. We need to specify the previous install location:
mkdir build
cd build
cmake -DCMAKE_PREFIX_PATH=$PWD/../out ../myexe
cmake --build .
A note on building multiple configurations
Usually we need to build multiple configurations (Debug, Release). A critical issue is to specify configuration-dependent filenames or install locations. For example, you can set the default value of the DEBUG_POSTFIX property for the library project:
set(CMAKE_DEBUG_POSTFIX d)
The debug version of the mylib library file will be named libmylibd.lib (or mylibd.lib on Windows). The generated EXPORT files will contain the modified filenames.
If you're using makefile-style CMake generators you can control the build configuration by setting the CMAKE_BUILD_TYPE variable:
cmake -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=$PWD/../out ../mylib
cmake --build . --target install
You may need seperate build directories for each configuration or you can re-use the same build dir. In that case, to play it safe it's best to explicitly clean before build:
cmake --build . --target install --clean-first
If you're using a multiconfig IDE generator, like Xcode or Visual Studio, you need to specify the configuration in build time:
cmake -DCMAKE_INSTALL_PREFIX=$PWD/../out ../mylib
cmake --build . --target install --config Release
References
You can clone and build this repository which contains the mylib and myexe projects (tested on Windows and Linux).
Check out the CMake documentation. The most important related commands are:
add_library
target_link_libraries
find_package
install
target_include_directories
target_compile_definitions
and two detailed articles:
Build System
Packages
I have just downloaded the OpenSceneGraph source, unzip it into
"~/OpenSceneGraph-3.0.1" directory and use CMake to create an out-of-source
eclipse make project in "~/OpenSceneGraph-3.0.1-build-eclipse-cdt"
directory. When I execute "make" in
"~/OpenSceneGraph-3.0.1-build-eclipse-cdt" directory, OpenSceneGraph builds
successfully. I have not run "sudo make install" as I do not want to
install OpenSceneGraph tightly into my Ubuntu system.
Now I want to use CMake to create a project using the compiled
OpenSceneGraph libraries. I use the following codes in CMakeLists.txt :
CMAKE_MINIMUM_REQUIRED(VERSION 2.6)
PROJECT( test_proj )
FIND_PACKAGE(OpenSceneGraph)
ADD_EXECUTABLE(test test.cpp )
INCLUDE_DIRECTORIES(${OPENSCENEGRAPH_INCLUDE_DIRS})
TARGET_LINK_LIBRARIES(test ${OPENSCENEGRAPH_LIBRARIES} )
But it seems that OpenSceneGraph could not be found by CMake.
Does anyone know how CMake could find the compiled OpenSceneGraph
libraries in the "~/OpenSceneGraph-3.0.1-build-eclipse-cdt" directory and
use it to create projects as if I have tightly installed OpenSceneGraph
using "sudo make install". Thanks for any suggestion.
You don't need to install OpenSceneGraph system-wide. Just choose a CMAKE_INSTALL_PREFIX that suits you (eg. ~/osg).
Using the install command makes sure that everything is correctly in place (i.e. in the correct directory structure) for FindOpenSceneGraph.cmake (the script CMake invokes when you call FIND_PACKAGE( OpenSceneGraph ) ) to find it.
Then, you should point any of OSG_DIR, OSGDIR, or OSG_ROOT as environment variable and point it to your install location, so CMake knows where to look for it.
Edit:
#Hugues: I'll try to make it a bit clearer:
Setup up OpenSceneGraph:
Get OSG source.
When running CMake for it, choose a CMAKE_INSTALL_PREFIX that suits you, eg. ~/osg if you don't want a system-wide installation in (default) /usr/local. Do it either by stating -DCMAKE_INSTALL_PREFIX=/home/hugues/osg on the command-line or by setting it using a gui tool like ccmake or cmake-gui.
Run make install to build and install OSG.
Set the environment variable OSG_DIR to whatever you pointed CMAKE_INSTALL_PREFIX. (export OSG_DIR=<whereever_you_installed_osg>)
Setup your project:
In your CMakeLists.txt, use FIND_PACKAGE( OpenSceneGraph ) (add desired optional arguments as desired).
Use the resulting variables (like ${OpenSceneGraph_LIBRARIES} in the appropriate places in your cmake file.
Run CMake for your project.
Add this below command in your CMakeLists.txt:
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake")
Then put the file FindOpenSceneGraph.cmake in src/cmake dir.
FindOpenSceneGraph.cmake can be found here.