Getting CMake to build from source without packaging scripts

I am trying to get CMake to embed in the 'build' directory, as in project/build , where CMakeLists.txt is in project/ .

I know what I can do:

 mkdir build cd build cmake ../ 

but this is cumbersome. I could put it in a script and call it, but then it would be unpleasant to provide different CMake arguments (for example, -G "MSYS Makefiles"), or I would have to edit this file on each platform.

It is advisable that I do something like SET(CMAKE_OUTPUT_DIR build) basically CMakeLists.txt. Please tell me that this is possible, and if so, how? Or some other build method outside of the source code that makes it easy to specify different arguments?

+25
cmake configuration
Jun 21 2018-12-12T00:
source share
3 answers

CMake 3.13 or later supports the -S and -B command line options for specifying the source and binary directory, respectively.

 cmake -S . -B build -G "MSYS Makefiles" 

This will allow you to find CMakeLists.txt in the current folder and create a folder for build (if it does not already exist).

In older versions of CMake, you can use the undocumented CMake options -H and -B to specify the source and binary directory when invoking cmake :

 cmake -H. -Bbuild -G "MSYS Makefiles" 

Please note that there should not be a space between the parameter and the directory path.

+44
Jun 21 '12 at 18:05
source share
— -

The solution I found recently is to combine the concept of building outside the source code with the Makefile shell.

In my top-level CMakeLists.txt file, I include the following to prevent inline builds:

 if ( ${CMAKE_SOURCE_DIR} STREQUAL ${CMAKE_BINARY_DIR} ) message( FATAL_ERROR "In-source builds not allowed. Please make a new directory (called a build directory) and run CMake from there. You may need to remove CMakeCache.txt." ) endif() 

Then I create a top-level Makefile and include the following:

 # ----------------------------------------------------------------------------- # CMake project wrapper Makefile ---------------------------------------------- # ----------------------------------------------------------------------------- SHELL := /bin/bash RM := rm -rf MKDIR := mkdir -p all: ./build/Makefile @ $(MAKE) -C build ./build/Makefile: @ ($(MKDIR) build > /dev/null) @ (cd build > /dev/null 2>&1 && cmake ..) distclean: @ ($(MKDIR) build > /dev/null) @ (cd build > /dev/null 2>&1 && cmake .. > /dev/null 2>&1) @- $(MAKE) --silent -C build clean || true @- $(RM) ./build/Makefile @- $(RM) ./build/src @- $(RM) ./build/test @- $(RM) ./build/CMake* @- $(RM) ./build/cmake.* @- $(RM) ./build/*.cmake @- $(RM) ./build/*.txt ifeq ($(findstring distclean,$(MAKECMDGOALS)),) $(MAKECMDGOALS): ./build/Makefile @ $(MAKE) -C build $(MAKECMDGOALS) endif 

The default target, all is invoked by typing make and invoking the target ./build/Makefile .

The first thing the ./build/Makefile goal ./build/Makefile is create the build directory using $(MKDIR) , which is the variable for mkdir -p . The build directory is where we build outside the source. We provide the -p argument so that mkdir does not yell at us, trying to create a directory that may already exist.

Secondly, the goal of ./build/Makefile is to change directories to the build directory and invoke cmake .

Returning to the all target, we call $(MAKE) -C build , where $(MAKE) is the Makefile variable automatically generated for make . make -C changes the directory before doing anything. Therefore, using $(MAKE) -C build equivalent to doing cd build; make cd build; make .

To summarize, calling this Makefile shell with make all or make equivalent to doing:

 mkdir build cd build cmake .. make 

The distclean invokes cmake .. , then make -C build clean and finally removes all the contents from the build directory. I believe that this is exactly what you asked in your question.

The last part of the Makefile evaluates whether the user-defined target is or is not a distclean . If not, this will change the directories to build before build it. This is very powerful because the user can print, for example, make clean , and the Makefile converts it to the equivalent of cd build; make clean cd build; make clean .

In conclusion, this Makefile, combined with the mandatory configuration of CMake to build outside the source code, makes it so that the user should never interact with the cmake command. This solution also provides an elegant method to remove all CMake output files from the build directory.

PS In the Makefile, we use the @ prefix to suppress the output from the shell command, and the @- prefix to ignore errors from the shell command. When using rm as part of the distclean target distclean command returns an error if the files do not exist (they may have already been deleted using the command line using rm -rf build , or they were never generated in the first place). This reverse error will force our Makefile to exit. We use the @- prefix to prevent this. This is acceptable if the file has already been deleted; we want our Makefile to keep going and delete the rest.

One more note: this Makefile may not work if you use a variable number of CMake variables to create your project, for example cmake .. -DSOMEBUILDSUSETHIS:STRING="foo" -DSOMEOTHERBUILDSUSETHISTOO:STRING="bar" . This Makefile assumes that you invoke CMake sequentially, either by typing cmake .. or by providing cmake consistent number of arguments (which you can include in your Makefile).

Finally, a loan, which should be a loan. This Makefile shell was adapted from the Makefile provided by the C ++ Application Project Template .

This answer was originally posted here . I thought this applied to your situation.

+6
Apr 16 '15 at 15:10
source share

Based on the previous answers, I wrote the following module, which you can enable for forced assembly of an unincorporated assembly.

 set(DEFAULT_OUT_OF_SOURCE_FOLDER "cmake_output") if (${CMAKE_SOURCE_DIR} STREQUAL ${CMAKE_BINARY_DIR}) message(WARNING "In-source builds not allowed. CMake will now be run with arguments: cmake -H. -B${DEFAULT_OUT_OF_SOURCE_FOLDER} ") # Run CMake with out of source flag execute_process( COMMAND ${CMAKE_COMMAND} -H. -B${DEFAULT_OUT_OF_SOURCE_FOLDER} WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}) # Cause fatal error to stop the script from further execution message(FATAL_ERROR "CMake has been ran to create an out of source build. This error prevents CMake from running an in-source build.") endif () 

This works, however, I already noticed two drawbacks:

  • When the user is lazy and just starts cmake . , he will always see FATAL_ERROR . I could not find another way to prevent CMake from using other operations and exit earlier.
  • Any command line arguments passed to the original cmake will not be passed to the "invoke assembly outside the server."

Suggestions for improving this module are welcome.

+2
May 31 '16 at 8:35
source share



All Articles