Is it possible to build Boost with CMake? [closed] - c++

Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 8 years ago.
Improve this question
Rather than include static libraries in my source tree in a cross-compiled project I'd like to add boost directly into cmake and build it. Is this available?

We've struggled with this a fair bit too at my workplace. While I certainly can't claim to know the "best" way, I can offer the following thoughts on my experiences.
We initially just required devs to install boost separately and had CMake do its normal checks in the form of a find_package(Boost...) call. This was easy, but not automated, and caused problems for devs with older versions of boost already installed.
We then changed tack and added a copy of the boost sources which we cloned from one of the projects you mentioned above. I can't recall the specifics, but I think it was a precursor to the one currently being worked on in the Ryppl project. The main point was that it already had support for CMake; the boost libraries were actual CMake targets added via add_library calls, which made them easier to work with in the CMake code.
While this solved the previous problems by automating the use of boost in our project, it ultimately became a maintenance nightmare. The boost project we had cloned from changed radically and is now very dependent on Ryppl-specific CMake functions. We didn't want to add Ryppl as a dependency, so we changed tack again!
We looked at the projects you mentioned in your question, and likewise found none of them to be usable.
Our current setup makes use of CMake's ExternalProject module. This allows us to download and build boost to our build tree.
Advantages:
Low maintenance
Automated, so all devs use the same version built with the same flags
Keeps our own source tree free from third-party code
Multiple copies of boost can happily co-exist (so no chance of accidentally linking to a copy built with a different compiler/stdlib combination)
Disadvantages
Deleting your build tree means having to download and build boost from scratch. This could be ameliorated by e.g. downloading to a fixed location (say, system temp dir), so the download/unzip step could be skipped if an existing copy of the boost sources is found.
The boost libraries are not proper CMake targets (i.e. they haven't been added via add_library calls)
Here's a link to our CMake code. There are a few ways in which this needs improved, but it currently works reasonably well for us.
I hope that soon this answer becomes out of date and a decent, modularised, CMake-compatible solution becomes available.

I found Fraser's answer above to be a good starting point, but ran into some issues using Boost 1.55.0 on our system.
First we wanted to have a self-contained source code package for our applications, so preferred not to use the CMake ExternalProject. We were only using the thread and date_time libraries from Boost, so we used bcp to create a subset of Boost with the build tools, thread and other dependent libraries:
$ bcp tools/build thread system date_time ../boost_1_55_0_threads_only
and checked this into our svn repository.
Next, I was able to adapt Fraser's CMake file to build on Linux, but ran into problems running the bootstrap.bat file on Windows with CMake's execute_process command. To run bootstrap.bat we first needed to run the relevant Visual Studio vcvarsall.bat script to set environment variables (we could probably figure out which individual variables need to be set, but it was easier to run the whole script). To run two .bat files in the same shell using execult_process, we used cmd /c and listed the files separated by a & as the argument.
Also bootstrap.bat did not set the exit code to non-zero in case of failure, so using the execute_process RESULT_VARIABLE to check for success didn't work. Instead we checked that the b2.exe executable had been created after the command was run.
One last issue: bootstrap.sh supports the --prefix= option, which bootstrap.bat does not. I also found that specifying the --prefix option for b2.exe on windows worked, but
using the --prefix option for b2 on Linux, without specifying it for bootstrap.sh gave errors. (I haven't understood why yet).
So the relevant part of our CMake file looks like:
#
# run bootstrap
#
if(WIN32)
if(MSVC10)
set(VCVARS_CMD "C:\\Program^ Files^ ^(x86^)\\Microsoft^ Visual^ Studio^ 10.0\\VC\\vcvarsall.bat")
elseif(MSVC11)
set(VCVARS_CMD "C:\\Program^ Files^ ^(x86^)\\Microsoft^ Visual^ Studio^ 11.0\\VC\\vcvarsall.bat")
elseif(MSVC12)
set(VCVARS_CMD "C:\\Program^ Files^ ^(x86^)\\Microsoft^ Visual^ Studio^ 12.0\\VC\\vcvarsall.bat")
# elseif(...)
# add more options here
endif(MSVC10)
set(BOOTSTRAP_CMD "${VCVARS_CMD} & bootstrap.bat")
message("Executing command: ${BOOTSTRAP_CMD}")
execute_process(COMMAND cmd /c "${BOOTSTRAP_CMD}" WORKING_DIRECTORY ${APT_BOOST_SRC}
RESULT_VARIABLE BS_RESULT OUTPUT_VARIABLE BS_OUTPUT ERROR_VARIABLE BS_ERROR)
if(NOT EXISTS ${APT_BOOST_SRC}/b2.exe)
message(FATAL_ERROR "Failed running cmd /c ${BOOTSTRAP_CMD} in ${APT_BOOST_SRC}:\n${BS_OUTPUT}\n${BS_ERROR}\n")
else(NOT EXISTS ${APT_BOOST_SRC}/b2.exe)
message("bootstrap output:\n${BS_OUTPUT}")
endif(NOT EXISTS ${APT_BOOST_SRC}/b2.exe)
else(WIN32)
set(BOOTSTRAP_CMD "./bootstrap.sh")
set(BOOTSTRAP_ARGS "--prefix=${APT_BOOST_BIN}")
message("Executing command: ${BOOTSTRAP_CMD} ${BOOTSTRAP_ARGS}")
execute_process(COMMAND "${BOOTSTRAP_CMD}" ${BOOTSTRAP_ARGS} WORKING_DIRECTORY ${APT_BOOST_SRC}
RESULT_VARIABLE BS_RESULT OUTPUT_VARIABLE BS_OUTPUT ERROR_VARIABLE BS_ERROR)
if(NOT BS_RESULT EQUAL 0)
message(FATAL_ERROR "Failed running ${BOOTSTRAP_CMD} ${BOOTSTRAP_ARGS} in ${APT_BOOST_SRC}:\n${BS_OUTPUT}\n${BS_ERROR}\n")
endif()
endif(WIN32)
#
# run b2
#
set(B2_ARGS "link=static" "threading=multi" "runtime-link=static" "variant=release")
foreach(COMP IN LISTS APT_BOOST_COMPONENTS)
set(B2_ARGS "--with-${COMP}" ${B2_ARGS})
endforeach(COMP IN LISTS APT_BOOST_COMPONENTS)
if(WIN32)
if(MSVC11)
set(B2_ARGS "--toolset=msvc-11.0" ${B2_ARGS})
elseif(MSVC12)
set(B2_ARGS "--toolset=msvc-12.0" ${B2_ARGS})
endif(MSVC11)
file(TO_NATIVE_PATH ${APT_BOOST_BIN} APT_BOOST_BIN_WIN)
set(B2_ARGS "--prefix=${APT_BOOST_BIN_WIN}" ${B2_ARGS} "architecture=x86" "address-model=64")
endif(WIN32)
set(B2_ARGS ${B2_ARGS} install)
set(B2_CMD "./b2")
message("Executing command: ${B2_CMD} ${B2_ARGS}")
execute_process(COMMAND ${B2_CMD} ${B2_ARGS} WORKING_DIRECTORY ${APT_BOOST_SRC}
RESULT_VARIABLE B2_RESULT OUTPUT_VARIABLE B2_OUTPUT ERROR_VARIABLE B2_ERROR)
if(NOT B2_RESULT EQUAL 0)
message(FATAL_ERROR "Failed running ${B2_CMD} in ${APT_BOOST_SRC}:\n${B2_OUTPUT}\n${B2_ERROR}\n")
endif()
In the above APT_BOOST_SRC is the location of the Boost subdirectory in our source directory, APT_BOOST_BIN is the location we use to store the libraries in our CMake build directory, and APT_BOOST_COMPONENTS is a list of the Boost libraries we are using.

Related

Use CMake to build dependency (oneTBB as git submodule) as dynamic library?

I have a project that depends on Intel's oneTBB. My project is structured as follows:
external/
| - CMakeLists.txt
| - oneTBB/ (this is a git submodule)
| - ...
include/
lib/
include/
CMakeLists.txt
I currently get things to compile by manually building oneTBB and installing it inside a prefix directory located at external/oneTBB/prefix by running the following (bash) commands:
cd oneTBB
mkdir -p prefix
mkdir -p build
cd build
cmake -DCMAKE_INSTALL_PREFIX=../prefix -DTBB_TEST=OFF ..
cmake --build .
cmake --install .
I then simply include and link using this prefix. (I got this from following the oneTBB READMEs)
While this works without issue, I'm currently trying to clean up my CMake such that its easier to build on Windows as well. Ideally, I'm looking to get to a point where I can simply run:
mkdir build
cd build
cmake ..
cmake --build .
and my project will build itself and all dependencies.
I got this working with other dependencies such as glfw and eigen by simply adding (to the CMakeLists.txt in external/:
add_subdirectory(glfw)
add_subdirectory(eigen)
But adding add_subdirectory(oneTBB) throws a LOT of warnings, starting with:
CMake Warning at external/oneTBB/CMakeLists.txt:116 (message):
You are building oneTBB as a static library. This is highly discouraged
and such configuration is not supported. Consider building a dynamic
library to avoid unforeseen issues.
-- TBBBind build targets are disabled due to unsupported environment
-- Configuring done
CMake Warning (dev) at external/oneTBB/src/tbb/CMakeLists.txt:15 (add_library):
Policy CMP0069 is not set: INTERPROCEDURAL_OPTIMIZATION is enforced when
enabled. Run "cmake --help-policy CMP0069" for policy details. Use the
cmake_policy command to set the policy and suppress this warning.
INTERPROCEDURAL_OPTIMIZATION property will be ignored for target 'tbb'.
This warning is for project developers. Use -Wno-dev to suppress it.
I have no need to build oneTBB as a static library.
Am I doing something wrong in my attempt? Really all I need is for oneTBB to be built as a dynamic library and placed somewhere I can link it to (without installing it on the system overall)
Similar question:
I am also including the METIS library which depends on GKlib. Currently I'm doing this in a similar way to what I did for oneTBB where I manually build each using the following script:
# Setup the GKlib library:
cd GKlib
mkdir -p prefix
mkdir -p build
cd build
cmake -DCMAKE_INSTALL_PREFIX=../prefix ..
cmake --build . -j
cmake --install .
cd ../../
# Setup the METIS library:
cd METIS
mkdir -p prefix
make config prefix=../prefix gklib_path=../GKlib/prefix #(GKLib path is done from root, install path done relative to build)
make install -j
cd ../
When I try to add them using:
add_subdirectory(GKlib)
add_subdirectory(METIS)
it throws errors that METIS cannot find GKlib:
CMake Error at external/METIS/CMakeLists.txt:50 (add_subdirectory):
add_subdirectory given source "build/xinclude" which is not an existing
directory.
While I recognize this is a separate issue, I figured to include it here as it is related to my issues with add_subdirectory()
Many projects expect that you invoke CMake on them separately instead of adding them into an existing project with add_subdirectory. While there might be a way to make add_subdirectory work with oneTBB, there is an easier way.
CMake has a function that allows you to download, build, and install external projects at build time: ExternalProject_Add.
Here's an example for spdlog, taken straight from one of my own projects:
# project_root/thirdparty/spdlog/CMakeLists.txt
string(TOUPPER ${CMAKE_BUILD_TYPE} CMAKE_BUILD_TYPE_UPPER)
ExternalProject_Add(spdlog-project
GIT_REPOSITORY https://github.com/gabime/spdlog
GIT_TAG edc51df1bdad8667b628999394a1e7c4dc6f3658
GIT_SUBMODULES_RECURSE ON
GIT_REMOTE_UPDATE_STRATEGY CHECKOUT
INSTALL_DIR "${CMAKE_CURRENT_BINARY_DIR}/install"
LIST_SEPARATOR |
CMAKE_CACHE_ARGS
"-DCMAKE_CXX_FLAGS_${CMAKE_BUILD_TYPE_UPPER}:STRING=${CMAKE_CXX_FLAGS_${CMAKE_BUILD_TYPE_UPPER}}"
"-DCMAKE_C_FLAGS_${CMAKE_BUILD_TYPE_UPPER}:STRING=${CMAKE_C_FLAGS_${CMAKE_BUILD_TYPE_UPPER}}"
"-DCMAKE_EXE_LINKER_FLAGS_${CMAKE_BUILD_TYPE_UPPER}:STRING=${CMAKE_EXE_LINKER_FLAGS_${CMAKE_BUILD_TYPE_UPPER}}"
"-DCMAKE_SHARED_LINKER_FLAGS_${CMAKE_BUILD_TYPE_UPPER}:STRING=${CMAKE_SHARED_LINKER_FLAGS_${CMAKE_BUILD_TYPE_UPPER}}"
"-DCMAKE_BUILD_TYPE:STRING=${CMAKE_BUILD_TYPE}"
"-DCMAKE_INSTALL_PREFIX:STRING=<INSTALL_DIR>"
"-DSPDLOG_BUILD_EXAMPLE:BOOL=OFF"
)
add_library(ext-spdlog INTERFACE)
add_dependencies(ext-spdlog spdlog-project)
ExternalProject_Get_property(spdlog-project INSTALL_DIR)
target_include_directories(ext-spdlog SYSTEM INTERFACE "${INSTALL_DIR}/include")
target_link_directories(ext-spdlog INTERFACE "${INSTALL_DIR}/lib")
target_link_libraries(ext-spdlog INTERFACE spdlog$<$<CONFIG:Debug>:d>)
After that, my project simply links against the created library target:
target_link_libraries(my_project ext-spdlog)
To adapt this for oneTBB, you have to switch out the repository URL and commit hash, and add your own CMake definitions (i.e. "-DTBB_TEST=OFF"). Depending on how oneTBB has its include and library directories set up, you may also have to change the target_include_directories and/or target_link_directories lines. I haven't looked this up yet, but I'm sure you can figure it out.
This works regardless of whether the external project is built as a static or shared library. You shouldn't use git submodules, though - instead, let CMake do the downloading. (It'll only download and build once; subsequent builds will not re-build the external project if it's already built and up-to-date.)
I have no need to build oneTBB as a static library. Am I doing something wrong in my attempt? Really all I need is for oneTBB to be built as a dynamic library and placed somewhere I can link it to (without installing it on the system overall)
All your diagnostic messages indicate that it's actually being configured to be built as a static library, and additional clues point to the probability that you've set BUILD_SHARED_LIBS to false in the scope where you add_subdirectory(oneTBB).
CMake Warning at external/oneTBB/CMakeLists.txt:116 (message):
You are building oneTBB as a static library. This is highly discouraged
and such configuration is not supported. Consider building a dynamic
library to avoid unforeseen issues.
If you look in oneTBB's CMakeLists.txt file, you'll the following:
if (NOT DEFINED BUILD_SHARED_LIBS)
set(BUILD_SHARED_LIBS ON)
endif()
if (NOT BUILD_SHARED_LIBS)
set(CMAKE_POSITION_INDEPENDENT_CODE ON)
message(WARNING "You are building oneTBB as a static library. This is highly discouraged and such configuration is not supported. Consider building a dynamic library to avoid unforeseen issues.")
endif()
And then right after that, you get
-- TBBBind build targets are disabled due to unsupported environment
The corresponding section of oneTBB's CMakeLists.txt file is:
if (TBB_FIND_PACKAGE OR TBB_DIR)
...
else()
if (APPLE OR NOT BUILD_SHARED_LIBS)
message(STATUS "TBBBind build targets are disabled due to unsupported environment")
else()
add_subdirectory(src/tbbbind)
endif()
...
Both of these clues indicate that in the variable scope at which you add_subdirectory(oneTBB), BUILD_SHARED_LIBS is set to a falsy value.
Set BUILD_SHARED_LIBS it to a truthy value (Ex. 1, TRUE, YES, ON, etc.) before doing add_subdirectory(oneTBB) and then restore the previous value afterward.
Ex.
set(BUILD_SHARED_LIBS_TEMP "${BUILD_SHARED_LIBS}")
set(BUILD_SHARED_LIBS YES)
add_subdirectory(oneTBB)
set(BUILD_SHARED_LIBS "${BUILD_SHARED_LIBS_TEMP}")
unset(BUILD_SHARED_LIBS_TEMP)

Graph-tool: compile and connect to local CGAL library, in Linux? (no sudo)

[Ubuntu]
I have compiled CGAL locally:
/path/to/cgal/
/lib/
libCGAL_Core.so libCGAL_Core.so.13.0.2
libCGAL_ImageIO.so.13 libCGAL.so libCGAL.so.13.0.2
libCGAL_Core.so.13 libCGAL_ImageIO.so
libCGAL_ImageIO.so.13.0.2 libCGAL.so.13
/include/
/CGAL/
version.h compiler_config.h
And I have managed to satisfy all of the graph-tool requirements except cgal (at least all of the requirements checked up to cgal):
./configure --with-boost=/path/to/boost --with-cgal=/path/to/cgal
And I get all successes up and until I get the following error message:
checking for __gmpz_init in -lgmp... yes
checking for __gmpz_init in -lgmp... (cached) yes
checking whether CGAL is available in /path/to/cgal... no
configure: error: CGAL library not found.
// the harshest part is that it seems to be searching in the correct
// directory.
I have tried specifying different points in the cgal build directory. The cgal compilation command I used was (from build directory):
cmake path/to/cgal_src_dir -DCMAKE_BUILD_TYPE=Release;
Next, I tried adding includes:
./configure --with-boost=$boost --with-cgal=path/to/cgal CPPFLAGS="-I path/to/cgal/include -I $HOME/.local/include" LDFLAGS="-L path/to/cgal/lib -L $HOME/.local/lib -Wl,-rpath=$HOME/.local/lib"
I will admit that I don't understand the -Wl,-rpath= part, I copied that from the graph-tool installation guide. The .local/lib folder contains the files for the other components, such as gmp, expat, sparsehash, etc.
This is not exact answer but as asked by OP will help in finishing installation, so please don't vote blindly.
To create debian package of libcgal open your CMakeList.txt and at the end of file add:
#--------------------------------------------------------------------
# Create debian files
#--------------------------------------------------------------------
if (UNIX AND NOT APPLE)
SET(CPACK_GENERATOR "DEB")
SET(CPACK_PACKAGE_NAME "libcgal-all")
SET(CPACK_PACKAGE_VERSION "${CGAL_VERSION}")
SET(CPACK_PACKAGE_DESCRIPTION_SUMMARY "C++ library for computational geometry (development files)\n CGAL (Computational Geometry Algorithms Library) makes the most important of the solutions and methods developed in computational geometry available to users in industry and academia in a C++ library. The goal is to provide easy access to useful, reliable geometric algorithms.\n .\n This package contains the header files and static libraries for libCGAL.so, libCGAL_Core.so, and libCGAL_ImageIO.so. The header files and static libraries for libCGAL_Qt4.so can be found in the package libcgal-qt4-dev.")
SET(CPACK_PACKAGE_CONTACT "bordeo")
SET(CPACK_DEBIAN_PACKAGE_DEPENDS "libboost-dev, libboost-thread-dev, libboost-system-dev, libboost-program-options-dev, libgmp10-dev, libmpfr-dev, zlib1g-dev")
SET(CPACK_DEBIAN_PACKAGE_REPLACES "libcgal10, libcgal-dev")
INCLUDE(CPack)
endif()
In case you don't have any dependency remove whole line of SET(CPACK_DEBIAN_PACKAGE_DEPENDS "libcln6, libcln-dev, libreadline6, libreadline6-dev, flex, bison"), and change others as it seems fit.
Now go to the terminal and issue following commands in cgal directory
mkdir build
cd build
cmake-gui ..
# set CMAKE_INSTALL_PREFIX to `~/.local
cmake ..
make -j4
cpack ..
you will find your debian built. Extract or install the debian to ~/.local.
Once this is done go to graph tool directory and start the build like
./configure --prefix="/wherever" --with-boost=/path/to/boost --with-cgal=~/.local
make -j4
make install
Hope this will solve your problem.

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).

Isolating gitsubmodule projects in CMake

I'm trying to manage my C++ project dependencies using CMake and gitsubmodules. I'm following the layout described here: http://foonathan.net/blog/2016/07/07/cmake-dependency-handling.html and it's worked really well for me on smaller projects. But I've started to use it on much larger projects and I'm hitting some issue with CMake.
My current setup
All my external build dependencies are in a contrib/ subfolder inside my main project. Each is a submodule and has its own separate directory.
/contrib
- /eigen
- /curl
- /leapserial
- /zlib
- /opencv
etc.
The contrib/CMakeListst.txt simply initializes the submodule and adds the subdirectory for each external dependency
# EIGEN
execute_process(COMMAND git submodule update --recursive --init -- eigen
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR})
# options..
add_subdirectory(eigen EXCLUDE_FROM_ALL)
# CURL
execute_process(COMMAND git submodule update --recursive --init -- curl
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR})
# Initialize cache with CMake build options..
add_subdirectory(curl EXCLUDE_FROM_ALL)
# LEAP SERIAL
execute_process(COMMAND git submodule update --recursive --init -- leapserial
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR})
# Initialize cache with CMake build options..
add_subdirectory(leapserial EXCLUDE_FROM_ALL)
# ZLIB
execute_process(COMMAND git submodule update --recursive --init -- zlib
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR})
# Initialize cache with CMake build options..
add_subdirectory(zlib EXCLUDE_FROM_ALL)
# OPENCV
execute_process(COMMAND git submodule update --recursive --init -- opencv
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR})
# Initialize cache with CMake build options..
add_subdirectory(opencv EXCLUDE_FROM_ALL)
This setup has worked fantastically for me:
It's system/packagemanager independent. You don't need to install any libraries to get started developing
I can maintain the exact versions of my dependencies by setting the submodule to a particular commit. There are no surprises with some external library breaking your build
Adding the libraries to my build in the root CMakeListst.txt is trivial. Since I have the target available I just have something like
add_executable(someProjectImWorkingOn
${CMAKE_CURRENT_SOURCE_DIR}/src/main.cpp )
target_link_libraries(someProjectImWorkingOn
opencv_world
eigen
zlib
etc.)
when you hook up an existing library target to your own target executable/library CMake will automatically (through the target interface) add include directories to your target and add any other necessary options the library target requires for it to be used
I can pick a toolchain/compiler-option/build-type in the root CMakeLists.txt and it'll propogate to all the subprojects (I need to build for multiple systems. So this is a big big deal)
Since it's all in one "mega-project" it makes it very easy to hook up to rtags/kdevelop/clion to navigate not on your own code, but also the library code
Some issues that I can't resolved:
1
Subdirectories will define targets with the same name. In the example I gave, both Eigen OpenCV as well as another library define an 'uninstall' target
I tried to update the
add_subdirectory(blah)
to
add_subdirectory(blah EXCLUDE_FROM_ALL)
but this doesn't fix the issue for some reason
Enabling the variable ALLOW_DUPLICATE_CUSTOM_TARGETS kinda works.. but this is a hack, only work with Make files, and the libraries are essentially still "mixing" so it's still an issue
2
The second issue came up in LeapSerial but illustrates a bigger issue. The project no longer knows it's own name. LeapSerial tried to determine the version of LeapSerial, but when it asks for the project version it's getting the root project version. Ie. when cmake code in a subproject asks for "what project am I in" it's getting the root project, and not the immediate project it's in.
So again, the parent "namespace"s are leaking everywhere. This is bound to create more and more issues down the line. I need to the submodules to be self-contained
Is there are a cleaner solution?
ExternalProjectAdd might solve some of these problems, but has a lot more issues of its own. It's a real non-starter b/c it doesn't do most of what I've listed. The central issue is that it doesn't expose the sub-project's targets - and just vomits back variables that you then have to juggle
As the asker said in the comments, they resolved their issue by using the Hunter package manager. The rest of this answer is about actually answering the question as posed.
Concerning your first issue with target name clashes when using add_subdirectory-based approaches to using dependencies, a very similar (or essentially the same?) question has also since been asked here: How to avoid namespace collision when using CMake FetchContent?. When the clashes are between targets from different project dependencies, there's nothing you can do right now except politely ask the project maintainers to consider modifying their non-import/export target names to be namespaced like.
For example, for a library target, that might look like:
add_library(projectname_targetnamepart)
add_library("importexportnamespacename::targetnamepart" ALIAS projectname_targetnamepart)
set_target_properties(projectname_targetnamepart PROPERTIES EXPORT_NAME targetnamepart)
set_target_properties(projectname_targetnamepart PROPERTIES OUTPUT_NAME targetnamepart)
install(TARGETS projectname_targetnamepart EXPORT projectname_targets ...)
install(EXPORT projectname_targets NAMESPACE "importexportnamespacename::" ...)
There's a Kitware issue ticket by Craig Scott proposing a CMake feature for Project-level namespaces. Here's an excerpt:
A common problem when pulling in subprojects using add_subdirectory() is that target names must be unique, but different subprojects might try to use the same target name, which results in a name clash. This issue proposes to introduce the concept of a project namespace to address this and related name uniqueness problems (this is the primary goal of this issue).
Sometimes the upstream maintainer will just decline to support the add_subdirectory / FetchContent use-case. That's the case with OpenCV, as shown in this issue ticket (#16896). As for eigen, there's an open ticket that hasn't had any activity in a while (#1892).
Concerning your second issue, there's not enough detail in your question post to confidently troubleshoot. What is LeapSerial? Are you referring to the leapmotion/leapserial GitHub repo? What version and what commit are you referring to? At the latest commit before the time of your question post, 41515db, it's not immediately obvious what's wrong.
Variables in CMake are scoped by directory, so even if a project is added by add_subdirectory and doesn't used the <PROJECT-NAME>_VERSION variable and instead uses the more general PROJECT_VERSION variable, it should be okay. It just shouldn't attempt to use the CMAKE_PROJECT_VERSION variable to get its own version, since that one is fixed to refer to the top-level project's version.

how to get CMake to add MagickWand library linking automatically everywhere

I want to use CMake in my software that uses MagickWand.
CMake works on my machine and generates a useful Makefile.
On another machine, I have to manually add
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -lMagickWand-6.Q16 -lMagickCore-6.Q16")
otherwise the linker can't find MagickWandGenesis() and other functions.
I found that -l flags via pkg-config --cflags --libs MagickWand.
Shouldn't CMake already generate linker flags for me with TARGET_LINK_LIBRARIES?
Did I miss something obvious, or why is this not working everywhere?
I have this code in CMakeLists.txt:
FIND_PACKAGE(ImageMagick
REQUIRED
COMPONENTS MagickWand
)
[...]
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DMAGICKCORE_HDRI_ENABLE=0 -DMAGICKCORE_QUANTUM_DEPTH=16")
[...]
INCLUDE_DIRECTORIES(
${Boost_INCLUDE_DIR}
${ImageMagick_INCLUDE_DIRS}
${ImageMagick_MagickWand_INCLUDE_DIRS}
)
[...]
TARGET_LINK_LIBRARIES(application_name
[...]
${Boost_LIBRARIES}
${CURL_LIBRARIES}
${ImageMagick_LIBRARIES}
${ImageMagick_MagickWand_LIBRARY}
)
That last ${ImageMagick_MagickWand_LIBRARY} shouldn't even be necessary.
Using Magick 6.8.9.9, CMake 3.0.2 on both machines (Debian Jessie).
Short answer: the package ImageMagick is buggy.
Looking in CMake's sources, the REQUIRED mechanism is handled exclusively through the variable package-_FOUND, independently of the required components.
Looking in the package ImageMagick here, ImageMagick_FOUND is set as follows:
set(ImageMagick_FOUND ${IMAGEMAGICK_FOUND})
But IMAGEMAGICK_FOUND is not set anywhere in the package, so the call will always unset ImageMagick_FOUND, and it will always be evaluated to true (not actively set to false), wether or not the components are effectively found.
You can either debug the package (and propose a pull request) or check the component variable:
if(NOT ImageMagick_MagickWand_FOUND)
message(FATAL_ERROR "MagickWand not found")
endif()
I guess the test will fail on your second machine.
By the way, you should only use ImageMagick_INCLUDE_DIRS and ImageMagick_LIBRARIES to link to the library (the ImageMagick_MagickWand* variables are here redundant). If you choose to debug the package, you may also declare imported targets.
Figured it out, despite the output of
MESSAGE(${ImageMagick_FOUND})
MESSAGE(${ImageMagick_INCLUDE_DIRS})
MESSAGE(${ImageMagick_LIBRARIES})
MESSAGE(${ImageMagick_MagickWand_FOUND})
MESSAGE(${ImageMagick_MagickWand_INCLUDE_DIRS})
MESSAGE(${ImageMagick_MagickWand_LIBRARY})
being identical, the installed packages differed. I installed the magick-dev packages via virtual packages in aptitude, which for some reason used the graphicsmagick suite for some packages (a imagemagick fork) instead of the original imagemagick suite.
For reference, the used aptitude search one-liner was aptitude search 'magick ?installed' | sort which listed three graphicsmagick packages on the second machine where imagemagick packages were on the first machine.