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 alljest wywoływany przez wpisanie makei wywołuje cel./build/Makefile .
Pierwszą rzeczą, którą ./build/Makefilerobi cel , jest utworzenie buildkatalogu $(MKDIR), który jest zmienną dla mkdir -p. Katalog buildjest miejscem, w którym wykonamy naszą kompilację poza źródłami. Podajemy argument, -paby upewnić się, że mkdirnie krzyczy na nas próba stworzenia katalogu, który może już istnieć.
Drugą rzeczą, którą ./build/Makefilerobi cel , jest zmiana katalogów do buildkatalogu i wywołanie cmake.
Wracamy do allcelu, $(MAKE) -C buildgdzie wywołujemy $(MAKE)zmienną Makefile , dla której jest automatycznie generowana make. make -Czmienia katalog przed zrobieniem czegokolwiek. Dlatego używanie $(MAKE) -C buildjest równoważne działaniucd build; make .
Podsumowując, wywołanie tego opakowania Makefile za pomocą make alllub makejest równoważne z:
mkdir build
cd build
cmake ..
make
Cel distcleanwywołuje cmake .., a następnie make -C build cleanostatecznie usuwa całą zawartość z buildkatalogu. 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 buildprzed 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 buildkatalogu.
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 rmjako elementu distcleandocelowego polecenie zwróci błąd, jeśli pliki nie istnieją (mogły zostać usunięte już przy użyciu wiersza polecenia z rm -rf buildlub 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 cmakestałą 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 ++ .