Решение, которое я недавно нашел, состоит в том, чтобы объединить концепцию сборки вне исходного кода с оболочкой Makefile.
В мой файл CMakeLists.txt верхнего уровня я включаю следующее, чтобы предотвратить сборку в исходном коде:
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()
Затем я создаю Makefile верхнего уровня и включаю следующее:
# -----------------------------------------------------------------------------
# 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
Цель по умолчанию all
вызывается с помощью ввода make
и вызывает цель ./build/Makefile
.
Первое, что ./build/Makefile
делает цель , - это создает build
каталог с $(MKDIR)
помощью переменной mkdir -p
. В каталоге build
мы будем выполнять сборку вне исходного кода. Мы приводим аргумент, -p
чтобы mkdir
не кричать на нас за попытку создать каталог, который уже может существовать.
Второе, что ./build/Makefile
делает цель - это изменить каталоги на build
каталог и вызвать его cmake
.
Возвращаясь к all
цели, мы вызываем $(MAKE) -C build
, где $(MAKE)
автоматически создается переменная Makefile make
. make -C
меняет каталог, прежде чем делать что-либо. Следовательно, использование $(MAKE) -C build
эквивалентно действию cd build; make
.
Подводя итог, вызов этой оболочки Makefile с помощью make all
или make
эквивалентен выполнению:
mkdir build
cd build
cmake ..
make
Цель distclean
вызывает cmake ..
, а затем make -C build clean
, наконец, удаляет все содержимое из build
каталога. Я считаю, что это именно то, что вы просили в своем вопросе.
Последний фрагмент Makefile оценивает, является ли предоставленная пользователем цель или нет distclean
. Если нет, он изменит каталоги, build
прежде чем вызывать его. Это очень мощно, потому что пользователь может напечатать, например make clean
, и Makefile преобразует это в эквивалент cd build; make clean
.
В заключение, эта оболочка Makefile в сочетании с обязательной конфигурацией CMake для сборки из источника делает ее такой, чтобы пользователю никогда не приходилось взаимодействовать с командой cmake
. Это решение также предоставляет элегантный метод для удаления всех выходных файлов CMake из build
каталога.
PS В Makefile мы используем префикс @
для подавления вывода команды оболочки и префикс @-
для игнорирования ошибок команды оболочки. При использовании в rm
качестве части distclean
цели, команда вернет ошибку, если файлы не существуют (возможно, они уже были удалены с помощью командной строки rm -rf build
или никогда не создавались). Эта ошибка возврата заставит наш Makefile выйти. Мы используем префикс, @-
чтобы предотвратить это. Это приемлемо, если файл уже был удален; мы хотим, чтобы наш Makefile продолжал работать и удалял все остальное.
Еще один момент, на который следует обратить внимание: этот Makefile может не работать, если вы используете переменное число переменных CMake для построения вашего проекта, например cmake .. -DSOMEBUILDSUSETHIS:STRING="foo" -DSOMEOTHERBUILDSUSETHISTOO:STRING="bar"
. Этот Makefile предполагает, что вы вызываете CMake последовательным способом, набирая cmake ..
или предоставляя cmake
непротиворечивое количество аргументов (которые вы можете включить в ваш Makefile).
Наконец, кредит, где кредит должен. Эта оболочка Makefile была адаптирована из Makefile, предоставленного шаблоном проекта приложения C ++ .