Rozwiązaniem, które ostatnio znalazłem, jest połączenie koncepcji kompilacji poza źródłami z pakietem Makefile.
Do mojego najwyższego poziomu pliku CMakeLists.txt dołączam następujące elementy, aby zapobiec kompilacjom źródłowym:
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()
Następnie tworzę plik Makefile najwyższego poziomu i dołączam następujące elementy:
# -----------------------------------------------------------------------------
# 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
Domyślny cel all
jest wywoływany przez wpisanie make
i wywołuje cel./build/Makefile
.
Pierwszą rzeczą, którą ./build/Makefile
robi cel , jest utworzenie build
katalogu $(MKDIR)
, który jest zmienną dla mkdir -p
. Katalog build
jest miejscem, w którym wykonamy naszą kompilację poza źródłami. Podajemy argument, -p
aby upewnić się, że mkdir
nie krzyczy na nas próba stworzenia katalogu, który może już istnieć.
Drugą rzeczą, którą ./build/Makefile
robi cel , jest zmiana katalogów do build
katalogu i wywołanie cmake
.
Wracamy do all
celu, $(MAKE) -C build
gdzie wywołujemy $(MAKE)
zmienną Makefile , dla której jest automatycznie generowana make
. make -C
zmienia katalog przed zrobieniem czegokolwiek. Dlatego używanie $(MAKE) -C build
jest równoważne działaniucd build; make
.
Podsumowując, wywołanie tego opakowania Makefile za pomocą make all
lub make
jest równoważne z:
mkdir build
cd build
cmake ..
make
Cel distclean
wywołuje cmake ..
, a następnie make -C build clean
ostatecznie usuwa całą zawartość z build
katalogu. Uważam, że dokładnie o to prosiłeś w swoim pytaniu.
Ostatni fragment pliku Makefile ocenia, czy podany przez użytkownika cel jest, czy nie distclean
. Jeśli nie, zmieni katalogi na build
przed wywołaniem. Jest to bardzo potężne, ponieważ użytkownik może na przykład pisać make clean
, a Makefile przekształci go w odpowiednik cd build; make clean
.
Podsumowując, to opakowanie Makefile w połączeniu z obowiązkową konfiguracją CMake kompilacji poza źródłami sprawia, że użytkownik nigdy nie musi wchodzić w interakcje z poleceniem cmake
. To rozwiązanie zapewnia również elegancką metodę usuwania wszystkich plików wyjściowych CMake z build
katalogu.
PS W pliku Makefile używamy prefiksu, @
aby ukryć dane wyjściowe polecenia powłoki, a prefiksu, @-
aby ignorować błędy polecenia powłoki. Podczas używania rm
jako elementu distclean
docelowego polecenie zwróci błąd, jeśli pliki nie istnieją (mogły zostać usunięte już przy użyciu wiersza polecenia z rm -rf build
lub nigdy nie zostały wygenerowane). Ten błąd powrotu zmusi nasz Makefile do wyjścia. Używamy przedrostka, @-
aby temu zapobiec. Dopuszczalne jest, jeśli plik został już usunięty; chcemy, aby nasz Makefile kontynuował pracę i usunął resztę.
Kolejna rzecz do zapamiętania: ten plik Makefile może nie działać, jeśli na przykład używasz zmiennej liczby zmiennych CMake do zbudowania projektu cmake .. -DSOMEBUILDSUSETHIS:STRING="foo" -DSOMEOTHERBUILDSUSETHISTOO:STRING="bar"
. Ten Makefile zakłada, że wywołujesz CMake w spójny sposób, wpisując cmake ..
lub podając cmake
stałą liczbę argumentów (które możesz dołączyć do swojego Makefile).
Wreszcie kredyt, w którym kredyt jest należny. To opakowanie Makefile zostało zaadaptowane z pliku Makefile dostarczonego przez szablon projektu aplikacji C ++ .