Решение, которое я недавно нашел, состоит в том, чтобы объединить концепцию сборки вне исходного кода с оболочкой 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 ++ .