I want to disallow people from cluttering our source tree with generated CMake files... and, more importantly, disallow them from stepping on existing Makefiles
that are not part of the same build process we're using CMake for. (best not to ask)
The way I have come up with to do this is to have a few lines at the top of my CMakeLists.txt
, as follows:
if("${PROJECT_SOURCE_DIR}" STREQUAL "${PROJECT_BINARY_DIR}")
message(SEND_ERROR "In-source builds are not allowed.")
endif("${PROJECT_SOURCE_DIR}" STREQUAL "${PROJECT_BINARY_DIR}")
However, doing it this way seems too verbose. Additionally, if I try an in-source build it still creates the the CMakeFiles/
directory, and the CMakeCache.txt
file in the source tree before the error is thrown.
Am I missing a better way to do this?
CMake has two undocumented options: CMAKE_DISABLE_SOURCE_CHANGES
and CMAKE_DISABLE_IN_SOURCE_BUILD
cmake_minimum_required (VERSION 2.8) # add this options before PROJECT keyword set(CMAKE_DISABLE_SOURCE_CHANGES ON) set(CMAKE_DISABLE_IN_SOURCE_BUILD ON) project (HELLO) add_executable (hello hello.cxx)
-
andrew@manchester:~/src% cmake . CMake Error at /usr/local/share/cmake-2.8/Modules/CMakeDetermineSystem.cmake:160 (FILE): file attempted to write a file: /home/andrew/src/CMakeFiles/CMakeOutput.log into a source directory.
/home/selivanov/cmake-2.8.8/Source/cmMakefile.cxx
bool cmMakefile::CanIWriteThisFile(const char* fileName) { if ( !this->IsOn("CMAKE_DISABLE_SOURCE_CHANGES") ) { return true; } // If we are doing an in-source build, than the test will always fail if ( cmSystemTools::SameFile(this->GetHomeDirectory(), this->GetHomeOutputDirectory()) ) { if ( this->IsOn("CMAKE_DISABLE_IN_SOURCE_BUILD") ) { return false; } return true; } // Check if this is subdirectory of the source tree but not a // subdirectory of a build tree if ( cmSystemTools::IsSubDirectory(fileName, this->GetHomeDirectory()) && !cmSystemTools::IsSubDirectory(fileName, this->GetHomeOutputDirectory()) ) { return false; } return true; }
Include a function like this one. It is similar to what you do with these differences:
It is encapsulated in a function, which is called when you include the PreventInSourceBuilds.cmake
module. Your main CMakeLists.txt must include it:
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${CMAKE_CURRENT_SOURCE_DIR}/CMake)
include(PreventInSourceBuilds)
It uses get_filename_component() with REALPATH parameter that resolves symlinks before comparing the paths.
In case the github link changes, here's the module source code (which should be placed in a PreventInSouceBuilds.cmake
, in a directory called CMake
, in the above example):
#
# This function will prevent in-source builds
function(AssureOutOfSourceBuilds)
# make sure the user doesn't play dirty with symlinks
get_filename_component(srcdir "${CMAKE_SOURCE_DIR}" REALPATH)
get_filename_component(bindir "${CMAKE_BINARY_DIR}" REALPATH)
# disallow in-source builds
if("${srcdir}" STREQUAL "${bindir}")
message("######################################################")
message("# ITK should not be configured & built in the ITK source directory")
message("# You must run cmake in a build directory.")
message("# For example:")
message("# mkdir ITK-Sandbox ; cd ITK-sandbox")
message("# git clone http://itk.org/ITK.git # or download & unpack the source tarball")
message("# mkdir ITK-build")
message("# this will create the following directory structure")
message("#")
message("# ITK-Sandbox")
message("# +--ITK")
message("# +--ITK-build")
message("#")
message("# Then you can proceed to configure and build")
message("# by using the following commands")
message("#")
message("# cd ITK-build")
message("# cmake ../ITK # or ccmake, or cmake-gui ")
message("# make")
message("#")
message("# NOTE: Given that you already tried to make an in-source build")
message("# CMake have already created several files & directories")
message("# in your source tree. run 'git status' to find them and")
message("# remove them by doing:")
message("#")
message("# cd ITK-Sandbox/ITK")
message("# git clean -n -d")
message("# git clean -f -d")
message("# git checkout --")
message("#")
message("######################################################")
message(FATAL_ERROR "Quitting configuration")
endif()
endfunction()
AssureOutOfSourceBuilds()
I have a cmake()
shell function in my .bashrc
/.zshrc
similar to this one:
function cmake() {
# Don't invoke cmake from the top-of-tree
if [ -e "CMakeLists.txt" ]
then
echo "CMakeLists.txt file present, cowardly refusing to invoke cmake..."
else
/usr/bin/cmake $*
fi
}
I prefer this low ceremony solution. It got rid of my colleagues' biggest complaint when we switched to CMake, but it doesn't prevent people who really want to do an in-source/top-of-tree build from doing so—they can just invoke /usr/bin/cmake
directly (or not use the wrapper function at all). And it's stupid simple.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With