I'm trying to get cmake to build into a directory 'build', as in project/build
, where the CMakeLists.txt is in project/
.
I know I can do:
mkdir build
cd build
cmake ../
but that is cumbersome. I could put it in a script and call it, but then it's unpleasant to provide different arguments to cmake (like -G "MSYS Makefiles"), or I would need to edit this file on each platform.
Preferably I would do something like SET(CMAKE_OUTPUT_DIR build) in the main CMakeLists.txt. Please tell me that this is possible, and if so, how? Or some other out of source build method that makes it easy to specify different arguments?
Based on the previous answers, I wrote the following module that you can include to enforce an out-of-source build.
This works, however I already noticed two downsides:
cmake .
, they will always see aFATAL_ERROR
. I could not find another way to prevent CMake from doing any other operations and exit early.cmake
will not be passed to the "out-of-source build call".Suggestions to improve this module are welcome.
CMake 3.13 or newer supports the command line options
-S
and-B
to specify source and binary directory, respectively.This will look for the
CMakeLists.txt
in the current folder and create abuild
folder (if it does not yet exist) in it.For older versions of CMake, you can use the undocumented CMake options
-H
and-B
to specify the source and binary directory upon invokingcmake
:Note that there must not be a space character between the option and the directory path.
A solution that I found recently is to combine the out-of-source build concept with a Makefile wrapper.
In my top-level CMakeLists.txt file, I include the following to prevent in-source builds:
Then, I create a top-level Makefile, and include the following:
The default target
all
is called by typingmake
, and invokes the target./build/Makefile
.The first thing the target
./build/Makefile
does is to create thebuild
directory using$(MKDIR)
, which is a variable formkdir -p
. The directorybuild
is where we will perform our out-of-source build. We provide the argument-p
to ensure thatmkdir
does not scream at us for trying to create a directory that may already exist.The second thing the target
./build/Makefile
does is to change directories to thebuild
directory and invokecmake
.Back to the
all
target, we invoke$(MAKE) -C build
, where$(MAKE)
is a Makefile variable automatically generated formake
.make -C
changes the directory before doing anything. Therefore, using$(MAKE) -C build
is equivalent to doingcd build; make
.To summarize, calling this Makefile wrapper with
make all
ormake
is equivalent to doing:The target
distclean
invokescmake ..
, thenmake -C build clean
, and finally, removes all contents from thebuild
directory. I believe this is exactly what you requested in your question.The last piece of the Makefile evaluates if the user-provided target is or is not
distclean
. If not, it will change directories tobuild
before invoking it. This is very powerful because the user can type, for example,make clean
, and the Makefile will transform that into an equivalent ofcd build; make clean
.In conclusion, this Makefile wrapper, in combination with a mandatory out-of-source build CMake configuration, make it so that the user never has to interact with the command
cmake
. This solution also provides an elegant method to remove all CMake output files from thebuild
directory.P.S. In the Makefile, we use the prefix
@
to suppress the output from a shell command, and the prefix@-
to ignore errors from a shell command. When usingrm
as part of thedistclean
target, the command will return an error if the files do not exist (they may have been deleted already using the command line withrm -rf build
, or they were never generated in the first place). This return error will force our Makefile to exit. We use the prefix@-
to prevent that. It is acceptable if a file was removed already; we want our Makefile to keep going and remove the rest.Another thing to note: This Makefile may not work if you use a variable number of CMake variables to build your project, for example,
cmake .. -DSOMEBUILDSUSETHIS:STRING="foo" -DSOMEOTHERBUILDSUSETHISTOO:STRING="bar"
. This Makefile assumes you invoke CMake in a consistent way, either by typingcmake ..
or by providingcmake
a consistent number of arguments (that you can include in your Makefile).Finally, credit where credit is due. This Makefile wrapper was adapted from the Makefile provided by the C++ Application Project Template.
This answer was originally posted here. I thought it applied to your situation as well.