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.
Related
I have been working on a project which uses rplidar_sdk and in the beginning, I was facing this problem:
How can I link locally installed SDK's static library in my C++ project?
Basically, the SDK generates the library in its local directory, and in its Makefile, it does not have install rules. I mean I can run make but after that, if I run sudo make install then it gives make: *** No rule to make target 'install'. Stop. error.
So, with the help of this & this answer, I was able to build my local project. So far so good.
However, the main problem is that I have to hard-code the RPLidar SDK path in CMakeLists.txt of my repo. Now, whenever someone else in my team starts working on that repo (which is quite obvious) then he/she has to update the CMakeLists.txt first. This is not a good idea/practice!
To fix this, I updated the Makefile of RPLidar SDK as follow:
.
.
.
RPLIDAR_RELEASE_LIB := $(HOME_TREE)/output/Linux/Release/librplidar_sdk.a
install: $(RPLIDAR_RELEASE_LIB)
install -d $(DESTDIR)/usr/local/lib/rplidar/Release/
install -m 644 $(RPLIDAR_RELEASE_LIB) $(DESTDIR)/usr/local/lib/rplidar/Release/
RPLIDAR_DEBUG_LIB := $(HOME_TREE)/output/Linux/Debug/librplidar_sdk.a
install: $(RPLIDAR_DEBUG_LIB)
install -d $(DESTDIR)/usr/local/lib/rplidar/Debug/
install -m 644 $(RPLIDAR_DEBUG_LIB) $(DESTDIR)/usr/local/lib/rplidar/Debug/
RPLIDAR_HEADERS := $(HOME_TREE)/sdk/include
install: $(RPLIDAR_HEADERS)
install -d $(DESTDIR)/usr/local/include/rplidar/
cp -r $(RPLIDAR_HEADERS)/* $(DESTDIR)/usr/local/include/rplidar/
RPLIDAR_HEADERS_HAL := $(HOME_TREE)/sdk/src/hal
install: $(RPLIDAR_HEADERS_HAL)
install -d $(DESTDIR)/usr/local/include/rplidar/
cp -r $(RPLIDAR_HEADERS_HAL) $(DESTDIR)/usr/local/include/rplidar/
Due to this update, now, I can run sudo make install which basically copies the header files of RPLidar SDK from the local directory to /usr/local/rplidar/ directory. It also copies the lib file to /usr/local/lib/rplidar/<Debug> or <Release>/ directory.
Now, in my local project, I updated the CMakeLists.txt to as follow:
cmake_minimum_required(VERSION 3.1.0 FATAL_ERROR)
project(<project_name>)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED TRUE)
SET(CMAKE_CXX_FLAGS -pthread)
include_directories(include)
add_executable(${PROJECT_NAME} src/main.cpp src/another_src_file.cpp)
find_package(rplidar REQUIRED)
include_directories(${rplidar_INCLUDE_DIRS})
link_directories(${rplidar_LIBRARY_DIRS})
target_link_libraries(${PROJECT_NAME} ${rplidar_LIBRARY})
However, upon running cmake .. command, I'm getting this error:
.
.
.
CMake Error at CMakeLists.txt:12 (find_package):
By not providing "Findrplidar.cmake" in CMAKE_MODULE_PATH this project has
asked CMake to find a package configuration file provided by "rplidar", but
CMake did not find one.
Could not find a package configuration file provided by "rplidar" with any
of the following names:
rplidarConfig.cmake
rplidar-config.cmake
Add the installation prefix of "rplidar" to CMAKE_PREFIX_PATH or set
"rplidar_DIR" to a directory containing one of the above files. If
"rplidar" provides a separate development package or SDK, be sure it has
been installed.
-- Configuring incomplete, errors occurred!
As far as I know, RPLidar SDK does not have rplidarConfig.cmake or rplidar-config.cmake file.
How can I fix this error?
Rants from my soul:
It sucks when you have to use any library foo when the author fails to provide a foo-config.cmake for you to use easily by invoking find_package(foo). It's absolutely outrageous when a reasonably modern project still uses hand written Makefiles as its build system. I myself is stuck with a much worse constructed SDK than yours right now.
Short answer:
Since the author of the SDK fails to provide a config file to support your cmake usage, if you still insists on invoking find_package on the library (and you should!), you are required to write your own Module file to clean up their mess. (Yeah, you are doing the work for the library authors).
To truly achieve cross platform usage, you should write a Findrplidar.cmake module file to find the libraries for you.
To write a reasonable module file, you would most likely use API find_path for header files and find_library for libs. You should check out its docs and try using them, and maybe Google a few tutorials.
Here is my version of Findglog.cmake for your reference. (glog authors have updated their code and supports Config mode. Unfortunately, Ubuntu build doesn't use it, so I still have to write my own file)
find_path(glog_INCLUDE_DIR glog/logging.h)
message(STATUS "glog header found at: ${glog_INCLUDE_DIR}")
find_library(glog_LIB glog)
message(STATUS "libglog found at: ${glog_LIB}")
mark_as_advanced(glog_INCLUDE_DIR glog_LIB)
include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(glog REQUIRED_VARS
glog_INCLUDE_DIR
glog_LIB
)
if(glog_FOUND AND NOT TARGET glog::glog)
add_library(glog::glog SHARED IMPORTED)
set_target_properties(glog::glog PROPERTIES
IMPORTED_LINK_INTERFACE_LANGUAGES "CXX"
IMPORTED_LOCATION "${glog_LIB}"
INTERFACE_INCLUDE_DIRECTORIES
"${glog_INCLUDE_DIR}"
)
endif()
And you can use it like this:
find_package(glog)
target_link_libraries(main PRIVATE glog::glog)
Long answer:
The history of developers using cmake is an absolute nightmare. The internet is filled with bad practice/examples of how not to use cmake in your project, including the old official cmake tutorial (which still might be there). Mostly because no one really gives a **** (If I can build my project, who cares if it's cross platform). Another valid reason is that cmake documentations are really daunting to beginners.
This is why I am writing my own answer here, lest you get misguided by Googling elsewhere.
The nightmare is no more. The wait has ended. "The Messiah" of cmake (source) is come. He bringeth hope to asm/C/C++/CUDA projects written in 2020 and on. Here is The Word.
The link above points to the only way how cmake projects should be written and truly achieve cross platform once and for all. Note the material is not easy at all to follow for beginners. I myself spent an entire week to fully grasp what was covered in The Word, when I had become somewhat familiar with cmake concepts at the time (but lost in my old sinful ways).
The so-called "long answer" is actually shorter. It's just a pointer to the real answer. Good luck reading the Word. Embrace the Word, for anything against that is pure heresy.
Response of comment 1-5:
Good questions. A lot of those can be obtained from the Word. But the word is better digested when you become more familiar with CMake. Let me answer them in decreasing of relevance to your problem at hand.
For the ease of discussion, I'll just use libfoo as an example.
Let's say you always wants to use libfoo like this:
find_package(foo)
target_link_libraries(your_exe ... foo::foo)
Pretend foo is installed at the following location:
- /home/dev/libfoo-dev/
- include
- foo
- foo.h
- bar.h
- ...
- lib
- libfoo.so
- share
- foo/foo-config.cmake # This may or may not exist. See discussion.
Q: Only one .h file. Why?
A: Because in the case of libfoo (also true for glog), only one search of header location is necessary. Just like the example from libfoo,
where foo/foo.h and foo/bar.h are at the same location. So their output of find_path would be the same: /home/dev/libfoo-dev/include.
Q: Why I'm getting NOTFOUND for my headers and libs?
A: The function find_path and find_library only search locations specify in the documentations. By default they search system locations,
like /usr/include and /usr/lib respectively. Refer to the official docs for details on system locations. In the case of libfoo, however,
they reside in /home/dev/libfoo-dev. So you must specify these locations in cmake variable CMAKE_PREFIX_PATH. It's a ; seperated string.
One would do cmake -D CMAKE_PREFIX_PATH="/home/dev/libfoo-dev;/more/path/for/other/libs/;...;/even/more/path" .... on the command line.
One very important note: unlike Unix command find, find_path will only search specific paths inside /home/dev/libfoo-dev, not all the way down:
include (usually also include/{arch} where {arch} is sth like x86_64-linux-gnu for x86 Linux) for find_path; lib variant for find_library,
respectively. Unusual locations would require passing in more arguments, which is uncommon and most likely unnecessary.
For this very reason, for libfoo, calling find_path(... foo.h ...) is undesired. One would want find_path(... foo/foo.h ...). Refer to the docs
for more details. You can also try out yourself.
Also for this reason, it is desirable to organize libraries in the usual bin include lib share quad on Unix-like systems. I'm not familiar with Windows.
Q: Debug & Release
A: There are several options. The easiest one might be:
Prepare rplidar debug and release build in two different folders, /path/to/debug & /path/to/release for instance
Passing to Debug & Release build respectively (cmake -D CMAKE_PREFIX_PATH="/path/to/debugORrelease" ....)
There are definitely others ways, but perhaps requires special care in your Findrplidar.cmake script (maybe some if statements).
Q: Why glog::glog rather than glog?
A: It's just modern cmake practice, with small benefits. Not important right now. Refer to the Word if you are interested.
Q: You mentioned that you are writing rplidarConfig.cmake. Instead you should rename the file to Findrplidar.cmake.
A: CMake philosophy is as such:
Library authors should write foo-config.cmake or fooConfig.cmake
When they fail to provide one, it sucks. And according to the Messiah, it should be reported as a bug.
In this case, you as library user, should write Findfoo.cmake by guessing how to describe the dependencies for libfoo. For simple libraries, this is not so bad. For complex ones, like Boost, this sucks!
A few side note on this topic:
Note how Findfoo.cmake is written by library users, from guessing.
This is insane! Users shouldn't do this. This is the authors' fault, to put their users in this uncomfortable situation.
A foo-config.cmake file is extremely easy to write for authors of libfoo, IF they follow the Word exactly.
Extremely easy for the authors in the sense that: cmake can take care of everything. It will generate scripts automatically for the authors to use in their foo-config.cmake file.
Guaranteed to be cross-platform and easy to use by the users, thanks to cmake.
However, the reality sucks. Now you have to write Findfoo.cmake
Q: Why only find_package & target_link_libraries?
A: This is what the Word says. It's therefore good practice. Why the Word says so is something you have to find out yourself.
It's not possible for me to explain the gist of the Word in this answer, nor would it be convincing to you. I would just say the following:
It's very easy to write spaghetti CMakeLists that are next to impossible to maintain. The spirit of the Word helps you avoid that by
forcing you to carefully think about:
library structure: public vs private headers, for example. This makes you think about what to include in your headers and public APIs.
build specification: what is necessary to build a library you write (what to include; what to link)
usage requirement: what is necessary for others to use a library you write (what to include; what to link)
dependencies: what is the relationship of the library you write & its dependencies
Maybe more
If you think about it, these aspects are crucial to writing a cross-platform and maintainable library.
include_directories, link_directories and add_definitions are all very bad practice
(according to lots of sources, including the official documentations of these APIs). Bad practice tends to obscure the aspects above,
and causes problems later on when everything gets integrate together as a whole. E.g. include_directories will add -I to compiler for every
target written in the directory of that CMakeLists.txt. Read this sentence a few times and Spock will tell you it's illogical.
Don't worry. It's okay for now to use them when you are not familiar with the Word (Why else would this be in the last section). Once you know the Word, refactor your CMakeLists when you have time. Bad practice might cause problem later on, when your project becomes more complex. (In my professional experience, 5 very small groups of people is enough to cause a nightmare. By nightmare I mean hard code everything in CMakeLists; Create a git branch for every single different platform/device/environment; Fixing a bug meaning to cherry-pick one commit to each branch. I've been there before knowing the Word.)
The practice of the Word very well utilize the philosophy of modern CMake, to encapsulate build specifications and usage requirements inside
CMake targets. So when target_link_libraries is called, these properties gets propagated properly.
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.
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.
I have a c++ cmake project. In this project I build (among other) one example, where I need to use another project, call it Foo. This Foo project does not offer a cmake build system. Instead, it has a pre-made Makefile.custom.in. In order to build an executable that uses Foo's features, one needs to copy this makefile in his project, and modify it (typically setting the SOURCES variable and a few compiler flags). Basically, this Makefile ends up having the sources for your executable and also all the source files for the Foo project. You will not end up using Foo as a library.
Now, this is a design I don't like, but for the sake of the question, let's say we stick with it.
To create my example inside my cmake build I added a custom target:
CONFIGURE_FILE( ${CMAKE_CURRENT_SOURCE_DIR}/Makefile.custom.in Makefile.custom)
ADD_CUSTOM_TARGET(my_target COMMAND $(MAKE) -f Makefile.custom
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR})
This works. I can specify some variables to cmake, which get resolved in the call to CONFIGURE_FILE, and I end up with a working Makefile.custom. Then, invoking make my_target from the build directory, I can build the executable. I can even add it to the all target (to save me the effort of typing make my_target) with
SET_TARGET_PROPERTIES(my_target PROPERTIES EXCLUDE_FROM_ALL FALSE)
Sweet. However, cmake appears to assign a single job to the custom target, slowing down my compilation time (the Foo source folder contains a couple dozens cpp files). On top of that, the make clean target does not forward to the custom makefile. I end up having to add another target:
ADD_CUSTOM_TARGET(really-clean COMMAND "$(MAKE)" clean
COMMAND "$(MAKE)" -f Makefile.custom clean
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR})
which, unlike my_target with all, I can't include in the clean target (can I?).
Now, I know that a cleaner solution would be to have the Foo project be built as an external project, and then link to it. However, I've been 'recommended' to use their Makefile.custom.in makefile, modifying the few lines I need (adding my sources, specifying compiler flags, and few other minor modifications). So, regardless of how neat and clean this design pattern is, my questions are:
is there a way to tell cmake that make should use more than 1 job when making the target my_target?
is there a cleaner way to include a pre-existing makefile in a cmake project? Note that I don't want (can't?) use Foo as a library (and link against it). I want (need?) to compile it together with my executable using a makefile not generated by cmake (well, cmake can help a bit, through CONFIGURE_FILE, by resolving some variables, but that's it).
Note: I am aware of ExternalProject (as suggested also in this answer), but I think it's not exactly what I need here (since it would build Foo and then use it as a library). Also, both my project and Foo are written exclusively in C++ (not sure this matter at all).
I hope the question makes sense (regardless of how ugly/annoying/unsatisfactory the resulting design would be).
Edit: I am using cmake version 3.5.2
First, since you define your own target, you can assign more cores to the build process for the target my_target, directly inside your CMakeLists.txt.
You can include the Cmake module ProcessCount to determine the number of cores in your machine and then use this for a parallel build.
include(ProcessorCount)
ProcessorCount(N)
if(NOT N EQUAL 0)
# given that cores != 0 you could modify
# math(EXPR N "${N}+1") # modify (increment/decrement) N at your will, in this case, just incrementing N by one
set(JOBS_IN_PARALLEL -j${N})
endif(NOT N EQUAL 0)
and when you define your custom target have something like the following:
ADD_CUSTOM_TARGET(my_target
COMMAND ${CMAKE_MAKE_PROGRAM} ${JOBS_IN_PARALLEL} -f Makefile.custom
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR})
by the way, I don't think there's the need for you to include also CMAKE_BUILD_TOOL among the COMMANDs in your target.
I believe that instead of modifying the lines as above, you could call
make -j8 my_target
and it might start 8 jobs (just an example) without modifying the CMakeLists.txt, but I cannot guarantee this works having defined the COMMAND the way you have, just try if that's enough.
For the second point, I cannot think right now of a "cleaner" way.
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.