Нещодавно я знайшов рішення - поєднати концепцію збірки без джерела з обгорткою 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)
автоматично створюється змінна Makefilemake
. 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 ++ Application Project .