2012-06-21 15 views
17

Próbuję przekonwertować cmake do katalogu 'build', tak jak w project/build, gdzie CMakeLists.txt jest w project/.Uzyskiwanie cmake do budowania ze źródła bez pakowania skryptów?

wiem, że mogę zrobić:

mkdir build 
cd build 
cmake ../ 

ale jest to uciążliwe. Mogłabym umieścić go w skrypcie i nazwać go, ale wtedy nieprzyjemne jest dostarczanie różnych argumentów do cmake (jak -G "MSYS Makefiles"), albo musiałbym edytować ten plik na każdej platformie.

Najlepiej byłoby zrobić coś jak SET (CMAKE_OUTPUT_DIR kompilacji) w głównym CMakeLists.txt. Proszę mi powiedzieć, że to jest możliwe, a jeśli tak, to w jaki sposób? Lub inne metody kompilacji źródłowej, które ułatwiają określanie różnych argumentów?

+0

Mam taką samą architekturę projektu jak ty. Moja "kompilacja" jest zawsze tutaj. Osobiście uważam, że pisanie _cmake .._ nie jest tak wielką sprawą. – Offirmo

Odpowiedz

26

Można użyć nieudokumentowane opcje CUpewnij -H i -B aby określić źródło i katalog binarny na wywoływanie cmake:

cmake -H. -Bbuild -G "MSYS Makefiles" 

będzie to wyglądać na CMakeLists.txt w bieżącym folderze i utworzyć folder build (jeśli jeszcze nie istnieje) w nim.

+3

Ładne opcje. Ale Max powiedział: "nieprzyjemne jest dostarczanie różnych argumentów do cmake". Chce go do CMakeList. – Offirmo

4

Rozwiązaniem, które ostatnio znalazłem, jest połączenie koncepcji kompilacji out-of-source z opakowaniem Makefile.

W moim pliku CMakeLists.txt najwyższego poziomu, ja zaliczyć do zapobiegać już w źródle buduje:

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ę najwyższego poziomu Makefile i zawierać 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 obiekt docelowy ./build/Makefile.

Pierwszą rzeczą cel ./build/Makefile robi jest stworzenie katalogu build korzystając $(MKDIR), która jest zmienna dla mkdir -p. Katalog build jest miejscem, w którym wykonamy naszą kompilację poza źródłem. Podajemy argument -p, aby upewnić się, że mkdir nie krzyczy na nas za próbę utworzenia katalogu, który może już istnieć.

Drugą rzeczą, do której zmierza cel ./build/Makefile, jest zmiana katalogu na katalog build i wywołanie cmake.

Wracając do celu all, wywołujemy $(MAKE) -C build, gdzie $(MAKE) jest automatycznie przekształcaną zmienną Makefile dla make. make -C zmienia katalog przed zrobieniem czegokolwiek. Dlatego używanie $(MAKE) -C build jest równoważne wykonaniu cd build; make.

Podsumowując, nazywając ten Makefile owijkę z make all lub make jest równoważna robi:

mkdir build 
cd build 
cmake .. 
make 

Cel distclean wywołuje cmake .., następnie make -C build clean i wreszcie usuwa całą zawartość z katalogu build. Uważam, że jest to dokładnie to, o co prosiłeś w swoim pytaniu.

Ostatni fragment Makefile ocenia, czy podany przez użytkownika cel jest lub nie jest distclean. Jeśli nie, zmieni katalogi na build przed wywołaniem. Jest to bardzo potężne, ponieważ użytkownik może wpisać na przykład make clean, a plik Makefile przekształci go w odpowiednik cd build; make clean.

Podsumowując, to opakowanie Makefile, w połączeniu z obowiązkową konfiguracją CMake, nie powoduje interakcji użytkownika z poleceniem cmake. To rozwiązanie zapewnia również elegancką metodę usuwania wszystkich plików wyjściowych CMake z katalogu build.

P.S. W pliku Makefile używamy prefiksu @, aby wyłączyć wyjście z polecenia powłoki, a prefiks @- zignorować błędy z polecenia powłoki. Podczas korzystania z rm jako części docelowej distclean, polecenie zwróci błąd, jeśli pliki nie istnieją (mogły zostać usunięte przy użyciu wiersza polecenia z rm -rf build lub nigdy nie zostały wygenerowane w pierwszej kolejności). Ten błąd powrotu zmusi nasz plik Makefile do wyjścia. Używamy prefiksu @-, aby temu zapobiec. Dopuszczalne jest, jeśli plik został już usunięty; chcemy, aby nasz plik Makefile działał dalej i usuwał resztę.

Inna sprawa: ten plik Makefile może nie działać, jeśli do zbudowania projektu używasz zmiennej liczby zmiennych CMake, na przykład cmake .. -DSOMEBUILDSUSETHIS:STRING="foo" -DSOMEOTHERBUILDSUSETHISTOO:STRING="bar". Ten plik Makefile zakłada, że ​​wywołujesz CMake w spójny sposób, wpisując cmake .. lub podając cmake spójną liczbę argumentów (które możesz uwzględnić w pliku Makefile).

Wreszcie, gdy kredyt jest należny. To opakowanie Makefile zostało zaadaptowane z pliku Makefile dostarczonego przez C++ Application Project Template. Ta wiadomość została oryginalnie opublikowana here. Myślałem, że to odnosi się również do twojej sytuacji.

1

Na podstawie poprzednich odpowiedzi napisałem następujący moduł, który można dołączyć, aby wymusić kompilację poza źródłem.

set(DEFAULT_OUT_OF_SOURCE_FOLDER "cmake_output") 

if (${CMAKE_SOURCE_DIR} STREQUAL ${CMAKE_BINARY_DIR}) 
    message(WARNING "In-source builds not allowed. CMake will now be run with arguments: 
     cmake -H. -B${DEFAULT_OUT_OF_SOURCE_FOLDER} 
") 

    # Run CMake with out of source flag 
    execute_process(
      COMMAND ${CMAKE_COMMAND} -H. -B${DEFAULT_OUT_OF_SOURCE_FOLDER} 
      WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}) 

    # Cause fatal error to stop the script from further execution 
    message(FATAL_ERROR "CMake has been ran to create an out of source build. 
This error prevents CMake from running an in-source build.") 
endif() 

To działa, jednak ja już zauważyłem dwie wady:

  • Gdy użytkownik jest leniwy i po prostu działa cmake ., będą one zawsze zobaczyć FATAL_ERROR. Nie mogłem znaleźć innego sposobu, aby uniemożliwić CMake zrobienie jakichkolwiek innych operacji i wcześniejsze wyjście.
  • Wszelkie argumenty wiersza polecenia przekazane do pierwotnego połączenia z numerem cmake nie zostaną przekazane do wywołania kompilacji "poza źródłem".

Sugestie do ulepszenia tego modułu są mile widziane.

Powiązane problemy