How to link a library from git submodule - c++

Assume I want to use QHotKey in my project. The source is checked out as git submdule. I tried:
add_subdirectory(QHotkey)
target_link_libraries(${PROJECT_NAME} PRIVATE qhotkey)
However QHotkey has an install section and on install QHotkey will be installed as well, although I only want to statically link. How do I correctly link QHotKey in my project?

Based on their CMakeLists.txt file (line 44):
if(BUILD_SHARED_LIBS)
target_compile_definitions(qhotkey PRIVATE QHOTKEY_LIBRARY)
target_compile_definitions(qhotkey PUBLIC QHOTKEY_SHARED)
endif()
I would assume you need to:
set(BUILD_SHARED_LIBS FALSE)
To get a static library out of it. This is just based on what I skimmed through their CMakeLists. The install target is invoked only by calling make install but if you don't want it at all then you need to delete it from their CMakeLists.txt
EDIT: The other option that I've noticed a few times would be to use EXCLUDE_FROM_ALL flag when adding the subdirectory, bare in mind it might not be exactly what you want. Here is the link

Related

How to have sub_directory use VCPKG in find_package() in CMake?

I'm trying to build PROJ from source. I don't have many of its dynamic dependencies on Centos 7, nor is it possible for me to obtain these dependencies through the system package manager.
In order to skirt these issues, I sought to use VCPKG. However VCPKG is in a lot of turmoil over PROJ and I cannot physically use any version past 7.2.x in VCPKG (I need newer features) and I can't load geo tiffs either (so most non trivial projections will straight up not work). So I can't use VCPKG and PROJ together at all.
However I can still comfortably get the dependencies required to run PROJ through VCPKG.
I tried to convert PROJ source into a subdirectory, however to my suprise it was still trying to use my system libraries despite -DCMAKE_TOOLCHAIN_FILE=... -DVCPKG_TARGET_TRIPLET=x64-linux being set.
I tried using the directory as a standalone library, but with the VCPKG variables set, and... it worked.
So I tried setting cache variables to see if somehow that wasn't propagated downwards, and it didn't work (error message same as below)
set(CMAKE_TOOLCHAIN_FILE "/home/user/Documents/vcpkg/scripts/buildsystems/vcpkg.cmake" CACHE STRING "TEST")
set(VCPKG_TARGET_TRIPLET "x64-linux" CACHE STRING "TEST")
add_subdirectory(external/PROJ)
My project is setup like this:
- CMakeLists.txt
- main.cpp
- external
- PROJ
...
my cmake is like this:
cmake_minimum_required(VERSION 3.13)
project(test)
set(CMAKE_CXX_STANDARD 17)
add_subdirectory(external/PROJ)
add_executable(test main.cpp)
target_link_libraries(test PRIVATE PROJ::proj)
and I keep getting
-- Found Sqlite3: /usr/lib64/libsqlite3.so
-- Sqlite3 version: 3.7.17
CMake Error at external/PROJ/CMakeLists.txt:180 (message):
sqlite3 >= 3.11 required!
when PROJ is a subdirectory
and I get the correct thing:
-- Found Sqlite3: /home/user/Documents/vcpkg/installed/x64-linux/debug/lib/libsqlite3.a
-- Sqlite3 version: 3.36.0
when I don't use it as a sub-directory.
After the first project() call, it is much too late for CMAKE_TOOLCHAIN_FILE to do anything useful. Indeed, it won't even be read if it changes after the first project() call. Vcpkg, Conan, and the like must be used globally or not at all.
You could get away with writing something like this:
cmake_minimum_required(VERSION 3.21) # upgrade!! 3.13 is ancient
# Use vcpkg by default
if (EXISTS "$ENV{VCPKG_ROOT}/scripts/buildsystems/vcpkg.cmake")
set(CMAKE_TOOLCHAIN_FILE "$ENV{VCPKG_ROOT}/scripts/buildsystems/vcpkg.cmake"
CACHE STRING "TEST")
set(VCPKG_TARGET_TRIPLET "x64-linux"
CACHE STRING "default vcpkg triplet")
endif ()
project(test)
set(CMAKE_CXX_STANDARD 17
CACHE STRING "C++ standard to use")
if (CMAKE_CXX_STANDARD LESS 17)
message(FATAL_ERROR "Must compile `test` with C++17 or newer")
endif ()
add_subdirectory(external/PROJ)
add_executable(test main.cpp)
target_link_libraries(test PRIVATE PROJ::proj)
It is customary to set the environment variable VCPKG_ROOT to point to your vcpkg installation.
After fiddling around with this answer and wondering why it still wasn't working, I tried making an exact copy of the current MCVE in a different directory. To my surprise, it worked, and even worked with out caching (normal CMake command line variables). When using PROJ as a sub_directory, it actually only exposes PROJ as a target. However, I was trying to use PROJ::proj, a target apparently made by VCPKG to link against their install. Well I had uninstalled that, and wasn't using it as a dependency. Despite that, left over files in a separate build folder apparently were messing with my install, my directory originally looked like this:
- CMakeLists.txt
- main.cpp
- external
- PROJ
- cmake-build-debug
- cmake-build-release
Well, VCPKG stores some meta information about package caches and such in these directories. But with out completely deleting the whole directory I was not able to get it to "forget" about this old install. What's more, it took deleting both directories to get it to "fully" forget about proj.
So I deleted these files, like this:
- CMakeLists.txt
- main.cpp
- external
- PROJ
And reset/reloaded the CMake cache and file, and re-generated those build directories.
After doing this, I no longer had to worry about this extra target.

If I find_package in CMakeLists.txt, must I find_dependency in my installed config.cmake?

I'm using CMake to build and to install a certain library, foo.
My library depends on some other library, bar, which has a config CMake script, so I have:
find_package(bar REQUIRED)
target_link_libraries(foo PUBLIC bar::bar)
That's as far as building goes. For installation, I have appropriate install() commands, with exports, and version configuration and all that stuff. This generates the package's -config.cmake file (as well as a version config file), so I don't need to keep one in the repository, nor generate one line-by-line within my CMakeLists.txt
Now, CMake has a module named find_dependency(), whose documentation suggests is to be used in package configuration files. But - I don't explicitly add it there. Should I? And more generally: Under which circumstances should I manually ensure a package configuration file has a find_dependency() for various find_package()'s?
First, CMake does not support "Transitive" behavior for find_package() (check this question).
The documentation recommends that "All REQUIRED dependencies of a package should be found in the Config.cmake file":
# <package>Config.cmake file
include(CMakeFindDependencyMacro)
find_dependency(Stats 2.6.4)
include("${CMAKE_CURRENT_LIST_DIR}/ClimbingStatsTargets.cmake") # They depend on Stats
include("${CMAKE_CURRENT_LIST_DIR}/ClimbingStatsMacros.cmake")
So, answering your questions:
"I don't explicitly add it there. Should I?" You actually have to, at least for REQUIRED packages.
"Under which circumstances should I manually ensure a package configuration file has a find_dependency() for various find_package()'s?" For required packages, you must. For optional package, you might want to add it in the configuration file so that optional features will be available.
I am working on a project which depends on an external package (Catch2). In my top level CMakelists.txt I have:
# Top level CMakelists.txt
set(Catch2_DIR "${PATH_TO_CATCH2}/lib/cmake/Catch2/")
find_package(Catch2 ${CATCH2_VERSION} REQUIRED)
Then I added the following to my package configuration file:
# <package>Config.cmake file
include(CMakeFindDependencyMacro)
set(Catch2_DIR "#PATH_TO_CATCH2#/lib/cmake/Catch2/") #be careful, path hard coded
find_dependency(Catch2 REQUIRED)
Just be careful because the find_dependency is a macro and it will change the value of PACKAGE_PREFIX_DIR variable in your package configuration file.

CMake not building a library when added as a subdirectory

I added the xgboost library as a git submodule of my project and I'm trying to add it to cmake as a subdirectory. Unfortunately it's not working. A simple hello world project with the following CMakeLists.txt replicates the error that I'm getting.
cmake_minimum_required(VERSION 3.2)
project(foo)
add_subdirectory(xgboost)
add_executable(${PROJECT_NAME} foo.cpp)
target_link_libraries(${PROJECT_NAME} xgboost)
After building the library there is nothing in the xgboost/lib directory so I get the following error.
clang: error: no such file or directory:
'/Users/.../myproject/xgboost/lib/libxgboost.dylib'
I think that the problem is generated in their CMakeLists file since they have two different targets. Maybe cmake is choosing the wrong target but I'm not familiar enough with cmake to figure it out. The following code is from xgboost's CMakeLists.
# Executable
add_executable(runxgboost $<TARGET_OBJECTS:objxgboost> src/cli_main.cc)
set_target_properties(runxgboost PROPERTIES
OUTPUT_NAME xgboost
)
set_output_directory(runxgboost ${PROJECT_SOURCE_DIR})
target_link_libraries(runxgboost ${LINK_LIBRARIES})
# Shared library
add_library(xgboost SHARED $<TARGET_OBJECTS:objxgboost>)
target_link_libraries(xgboost ${LINK_LIBRARIES})
set_output_directory(xgboost ${PROJECT_SOURCE_DIR}/lib)
#Ensure these two targets do not build simultaneously, as they produce outputs with conflicting names
add_dependencies(xgboost runxgboost)
My questions in order of importance are:
Is there any way to fix it without modifying xgboost's CMakeLists.txt file?
Is it reasonable to try to add xgboost to my project as a git submodule?
Is there any reason cmake is not instructing to build the library?
Note: There were several edits to this question since I tried to narrow down the problem and to provide more information.
(I would love to ask for few things beforehand in the comment section, but I have too low reputation to do so, so I will just give it a shot ;))
I have few suspects, and one of them is ${CMAKE_SOURCE_DIR} of the submodule's root CMakeLists.txt. Although the paths are set properly when you run that CMakeLists.txt alone, cmake gets confused the moment you add it as your subdirectory. Have you looked into another directories for your output binaries?
First I would suggest testing this hypothesis, and then I would suggest writing similar, but separate CMakeLists.txt file for xgboost library, and then substitute it in the project temporarily. Unfortunately the CMakeLists.txt filename is hardcoded and there is no possibility to have two files of that kind in one directory; so it seems that the answer to 1) is, that you rather have to change the file.
For the 2): as long as it does not require huge additional logic in your CMakeLists.txt, it makes sense. Other viable option is to create an install target, which you can use to install your xgboost library locally (using CMAKE_INSTALL_PREFIX(doc) variable), and then add the installation path to your CMAKE_LIBRARY_PATH(doc).

Building tensorflow serving client with cmake

I searched for the best way to do this, but I was unable to find a clear answer.
Was anyone able to build a tensorflow-serving client using cmake?
I am having difficulties with generating CPP files from proto, since they are needed for prediction service. Those proto files also include proto files from tensorflow.
so far I have come up with this:
project(serving C CXX)
find_package(Protobuf REQUIRED)
file(GLOB_RECURSE proto_files RELATIVE ${serving_SOURCE_DIR}/tensorflow/
"${serving_SOURCE_DIR}/tensorflow/*.proto")
set(PROTOBUF_GENERATE_CPP_APPEND_PATH OFF)
include_directories(${PROTOBUF_INCLUDE_DIRS})
include_directories( ${CMAKE_CURRENT_BINARY_DIR} )
PROTOBUF_GENERATE_CPP(PROTO_SRCS PROTO_HDRS ${proto_files})
add_library(tf_protos ${PROTO_SRCS} ${PROTO_HDRS})
target_link_libraries(tf_protos PUBLIC ${PROTOBUF_LIBRARIES})
Cmake builds successfully but the make command gives me an error:
No rule to make target '../tensorflow/tools/proto_text/test.proto', needed by 'tensorflow/tools/proto_text/test.pb.cc'. Stop.
To overcome the problem of .proto includes not being found I used command
set(PROTOBUF_GENERATE_CPP_APPEND_PATH OFF)
which was explained here: https://groups.google.com/forum/#!topic/protobuf/eow2fNDUHvc
My current folder structure is
serving/
CmakeLists.txt
tensorflow/
tensorflow_serving/
apis/
Folder apis contains .proto files that are needed in the client implementation and they include .proto files from the folder tensorflow.
Is this even the right way to go?
Any help/advice would be much appreciated.
I was able to get it to work in the layout you have where the CMakeLists.txt file is placed in the same level as the serving repository here. You'll need to install Tensorflow too though (using tensorflow_cc).
However, you probably don't want to muck with a fork of the official tensorflow/serving repository so I went a step further and moved the CMakeLists.txt out so you can just submodule the official repository. I made an example here
The gist is that the protobuf CMake submodule expects proto files to be laid out in the same directory from which it's called. I made some modifications to the submodules to let us call it from a level above serving and to ensure it invokes the compiler with the include paths in the right order to support the nested structure of the proto files in serving/tensorflow_serving/apis/* (and placing it accordingly in the specified build directory)
Hopefully someone else with better knowhow can make this better!
These worked for me.
https://github.com/wardsng/inception_cmake
https://github.com/FloopCZ/tensorflow_cc
You can choose a private install directory instead of the default, e.g. /usr/local/...
cmake -DCMAKE_INSTALL_PREFIX= ..

CPack: Exclude INSTALL commands from subdirectory (googletest directory)

I'm using CMake for a project and googletest for my test cases.
Looking around the internet, it seems to be common practise to just copy the googletest source into a subfolder of your repository and include it with "add_subdirectory(googletest)". I did that.
Now I'm using CPack to generate debian packages for my project. Unfortunately, the packages generated by CPack install googletest alongside with my project. This is of course not what I want.
Looking in the googletest directory, I found some INSTALL cmake commands there, so it is clear, why it happens. The question is now - how can I avoid it? I don't like modifying the CMakeLists.txt files from googletest, because I would have to remember re-applying my modifications on an update. Is there another way to disable these installs in CPack?
So there is the macro option #Tsyvarev mentioned that was originally suggested here:
# overwrite install() command with a dummy macro that is a nop
macro (install)
endmacro ()
# configure build system for external libraries
add_subdirectory(external)
# replace install macro by one which simply invokes the CMake
install() function with the given arguments
macro (install)
_install(${ARGV})
endmacro(install)
Note ${ARGV} and ${ARGN} are the same but the docs currently suggest using ${ARGN}. Also the fact that macro-overwriting prepends _ to the original macro name is not documented, but it is still the behaviour. See the code here.
However, I never got the above code to work properly. It does really weird things and often calls install() twice.
An alternative - also undocumented - is to use EXCLUDE_FROM_ALL:
add_subdirectory(external EXCLUDE_FROM_ALL)
According to some comment I found somewhere this disables install() for that subdirectory. I think what it actually does is set EXCLUDE_FROM_ALL by default for all the install() commands which also probably does what you want. I haven't really tested it, worth a shot though.
Updated: As noted in the other answer,
it seems that EXCLUDE_FROM_ALL option is the most direct and correct way for disable install in the subproject in the subdirectory:
add_subdirectory(googletest EXCLUDE_FROM_ALL)
Previous solutions
If you don't need tests in your project's release (which you want to deliver with CPack), then include googletest subdirectory conditionally, and set conditional to false when packaging:
...
if(NOT DISABLE_TESTS)
add_subdirectory(googletest)
endif()
packaging with
cmake -DDISABLE_TESTS=ON <source-dir>
cpack
Alternatively, if you want tests, but don't want to install testing infrastructure, you may disable install command via defining macro or function with same name:
# Replace install() to do-nothing macro.
macro(install)
endmacro()
# Include subproject (or any other CMake code) with "disabled" install().
add_subdirectory(googletest)
# Restore original install() behavior.
macro(install)
_install(${ARGN})
endmacro()
This approach has also been suggested in CMake mailing.
According to the comments, that way with replacing CMake command is very tricky one and may to not work in some cases: either parameters passed to the modified install are parsed incorrectly or restoring install is not work and even following installs are disabled.
A bit late reply, but I just spent too long a time figuring this out.
In the specific case of googletests, specifying this in your top level CMakeLists.txt does the trick.
option(INSTALL_GMOCK "Install Googletest's GMock?" OFF)
option(INSTALL_GTEST "Install Googletest's GTest?" OFF)
add_subdirectory(googletest)
I read on (I think) the CMake mailing list that making installation conditional on a INSTALL_<package name> inside your package is sort of a defacto standard (and one I'm certainly going to follow from now on!). But I can't find that link now.