Jaka jest składnia CMake do ustawiania i używania zmiennych?

168

Pytam o to jako przypomnienie, gdy następnym razem użyję CMake. Nigdy się nie trzyma, a wyniki Google nie są świetne.

Jaka jest składnia ustawiania i używania zmiennych w CMake?

CivFan
źródło

Odpowiedzi:

281

Pisząc skrypty CMake, musisz dużo wiedzieć o składni i jak używać zmiennych w CMake.

Składnia

Ciągi używające set():

  • set(MyString "Some Text")
  • set(MyStringWithVar "Some other Text: ${MyString}")
  • set(MyStringWithQuot "Some quote: \"${MyStringWithVar}\"")

Lub z string():

  • string(APPEND MyStringWithContent " ${MyString}")

Listy używające set():

  • set(MyList "a" "b" "c")
  • set(MyList ${MyList} "d")

Lub lepiej z list():

  • list(APPEND MyList "a" "b" "c")
  • list(APPEND MyList "d")

Listy nazw plików:

  • set(MySourcesList "File.name" "File with Space.name")
  • list(APPEND MySourcesList "File.name" "File with Space.name")
  • add_excutable(MyExeTarget ${MySourcesList})

Dokumentacja

Zakres lub „Jaką wartość ma moja zmienna?”

Najpierw są „Zmienne normalne” i rzeczy, które musisz wiedzieć o ich zakresie:

  • Zmienne normalne są widoczne CMakeLists.txtsą ustawione i wszystkiego zwana stamtąd ( add_subdirectory(), include(), macro()ifunction() ).
  • add_subdirectory()ifunction() polecenia są wyjątkowe, ponieważ otwierają się własnym zakresie.
    • Zmienne znaczeniowe set(...) są tam tylko widoczne i tworzą kopię wszystkich normalnych zmiennych z poziomu zakresu, z którego są wywoływane (tzw. Zasięg nadrzędny).
    • Więc jeśli jesteś w podkatalogu lub funkcji, możesz zmodyfikować już istniejącą zmienną w zakresie nadrzędnym za pomocą set(... PARENT_SCOPE)
    • Można to wykorzystać np. W funkcjach przekazując nazwę zmiennej jako parametr funkcji. Przykładem może być function(xyz _resultVar)ustawienieset(${_resultVar} 1 PARENT_SCOPE)
  • Z drugiej strony wszystko, co ustawisz include()lub macro()skrypty, zmodyfikuje zmienne bezpośrednio w zakresie, z którego są wywoływane.

Drugi to „Global Variables Cache”. Rzeczy, które musisz wiedzieć o pamięci podręcznej:

  • Jeśli żadna normalna zmienna o podanej nazwie nie jest zdefiniowana w bieżącym zakresie, CMake będzie szukał pasującego wpisu pamięci podręcznej.
  • Wartości pamięci podręcznej są przechowywane w CMakeCache.txtpliku w katalogu danych binarnych.
  • Wartości w pamięci podręcznej można modyfikować w aplikacji GUI CMake przed ich wygenerowaniem. Dlatego - w porównaniu do normalnych zmiennych - mają a typei a docstring. Zwykle nie używam GUI, więc używamset(... CACHE INTERNAL "") do ustawiania moich globalnych i trwałych wartości.

    Należy pamiętać, że INTERNALtyp zmiennej pamięci podręcznej ma znaczenieFORCE

  • W skrypcie CMake możesz zmienić istniejące wpisy pamięci podręcznej tylko wtedy, gdy używasz set(... CACHE ... FORCE)składni. Zachowanie to jest wykorzystywane np. Przez sam CMake, ponieważ normalnie nie wymusza ono samodzielnie wpisów do pamięci podręcznej i dlatego można je wstępnie zdefiniować inną wartością.

  • Możesz użyć wiersza poleceń, aby ustawić wpisy w pamięci podręcznej za pomocą składni cmake -D var:type=value, tylko cmake -D var=valuelub z cmake -C CMakeInitialCache.cmake.
  • Możesz usunąć wpisy w pamięci podręcznej za pomocą unset(... CACHE).

Pamięć podręczna jest globalna i możesz ją ustawić praktycznie w dowolnym miejscu w skryptach CMake. Ale radziłbym dwa razy zastanowić się, gdzie używać zmiennych pamięci podręcznej (są globalne i trwałe). Zwykle wolę składnię set_property(GLOBAL PROPERTY ...)i set_property(GLOBAL APPEND PROPERTY ...)do definiowania własnych nietrwałych zmiennych globalnych.

Pułapki dotyczące zmiennych i „Jak debugować zmiany zmiennych?”

Aby uniknąć pułapek, powinieneś wiedzieć, co następuje o zmiennych:

  • Zmienne lokalne ukrywają zmienne w pamięci podręcznej, jeśli obie mają taką samą nazwę
  • Te find_...komendy - w razie powodzenia - robić napisać swoje wyniki jako pamięci podręcznej zmiennych „tak, że żadne połączenie nie będzie szukał ponownie”
  • Listy w CMake to po prostu ciągi znaków ze średnikami ograniczającymi, dlatego ważne są cudzysłowy
    • set(MyVar a b c)jest "a;b;c"i set(MyVar "a b c")jest"a b c"
    • Zaleca się, aby zawsze używać cudzysłowów z jednym wyjątkiem, gdy chcesz podać listę jako listę
    • Generalnie preferujesz list()polecenie do obsługi list
  • Cała kwestia zakresu opisana powyżej. Szczególnie zalecane jest używanie functions()zamiast, macros()ponieważ nie chcesz, aby zmienne lokalne pojawiały się w zakresie nadrzędnym.
  • Wiele zmiennych używanych przez CMake jest ustawianych za pomocą wywołań project()i enable_language(). Dlatego może być ważne, aby ustawić pewne zmienne przed użyciem tych poleceń.
  • Zmienne środowiskowe mogą się różnić od tego, gdzie CMake wygenerował środowisko make i kiedy używane są pliki make.
    • Zmiana zmiennej środowiskowej nie powoduje ponownego uruchomienia procesu generowania.
    • Zwłaszcza wygenerowane środowisko IDE może różnić się od linii poleceń, dlatego zaleca się przeniesienie zmiennych środowiskowych do czegoś, co jest buforowane.

Czasami pomaga tylko debugowanie zmiennych. Poniższe mogą Ci pomóc:

  • Po prostu użyj starego printfstylu debugowania, używając message()polecenia. Istnieje również kilka gotowych do użycia modułów dostarczanych z samym CMake: CMakePrintHelpers.cmake , CMakePrintSystemInformation.cmake
  • Zajrzyj do CMakeCache.txtpliku w katalogu wyjściowym binarnym. Ten plik jest nawet generowany, jeśli rzeczywiste generowanie środowiska make zawiedzie.
  • Użyj variable_watch (), aby zobaczyć, gdzie Twoje zmienne są odczytywane / zapisywane / usuwane.
  • Zajrzyj do właściwości katalogu CACHE_VARIABLES i VARIABLES
  • Zadzwoń, cmake --trace ...aby zobaczyć cały proces analizowania CMake. To w pewnym sensie ostatnia rezerwa, ponieważ generuje dużo mocy.

Składnia specjalna

  • Zmienne środowiska
    • Możesz czytać $ENV{...}i zapisywać set(ENV{...} ...)zmienne środowiskowe
  • Wyrażenia generatora
    • Wyrażenia generatora $<...>są oceniane tylko wtedy, gdy generator CMake zapisuje środowisko make (porównanie do zwykłych zmiennych, które są zastępowane „w miejscu” przez parser)
    • Bardzo przydatne np. W wierszach poleceń kompilatora / konsolidatora oraz w środowiskach z wieloma konfiguracjami
  • Bibliografia
    • Dzięki ${${...}}możesz nadać zmiennym nazwy w zmiennej i odwołać się do jej zawartości.
    • Często używane podczas nadawania nazwy zmiennej jako parametru funkcji / makra.
  • Stałe wartości (patrz if()polecenie)
    • Dzięki if(MyVariable)możesz bezpośrednio sprawdzić zmienną pod kątem prawdy / fałszu (nie ma potrzeby dołączania ${...})
    • Prawda, jeśli jest stała 1, ON, YES, TRUE, Y, lub numer niezerowe.
    • False, jeśli jest stała 0, OFF, NO, FALSE, N, IGNORE, NOTFOUND, pusty ciąg znaków lub kończy się przyrostkiem -NOTFOUND.
    • Ta składnia jest często używana do czegoś podobnego if(MSVC), ale może być myląca dla kogoś, kto nie zna tego skrótu składniowego.
  • Rekurencyjne podstawienia
    • Nazwy zmiennych można konstruować za pomocą zmiennych. Gdy CMake podstawi zmienne, sprawdzi ponownie, czy wynik sam w sobie jest zmienną. Jest to bardzo potężna funkcja używana w samym CMake, np. Jako rodzaj szablonuset(CMAKE_${lang}_COMPILER ...)
    • Ale należy pamiętać, to może dać ci ból głowy w if()poleceniach. Oto przykład, gdzie CMAKE_CXX_COMPILER_IDjest "MSVC"i MSVCjest "1":
      • if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC") jest prawdą, ponieważ wartościuje do if("1" STREQUAL "1")
      • if(CMAKE_CXX_COMPILER_ID STREQUAL "MSVC") jest fałszem, ponieważ wartościuje do if("MSVC" STREQUAL "1")
      • Dlatego najlepszym rozwiązaniem byłoby - patrz wyżej - bezpośrednie sprawdzenie if(MSVC)
    • Dobra wiadomość jest taka, że ​​problem ten został naprawiony w CMake 3.1 wraz z wprowadzeniem polityki CMP0054 . Zalecałbym zawsze ustawienie cmake_policy(SET CMP0054 NEW)„interpretuj if()argumenty jako zmienne lub słowa kluczowe tylko wtedy, gdy nie są cytowane”.
  • option()komenda
    • Głównie po prostu zbuforowane łańcuchy, które mogą być tylko ONlub OFFi pozwalają na pewną specjalną obsługę, jak np. Zależności
    • Ale pamiętaj , nie pomyl tego optionz setpoleceniem. Wartość podana optionjest w rzeczywistości tylko "wartością początkową" (przesyłana raz do pamięci podręcznej podczas pierwszego kroku konfiguracji) i jest później przeznaczona do zmiany przez użytkownika za pomocą GUI CMake .

Bibliografia

Florian
źródło
Gdy używam if ("${MyString}" ...)Jestem ostrzeżenia zobaczyć: Policy CMP0054 is not set: Only interpret if() arguments as variables or keywords when unquoted. Zobacz na przykład Build 367 . Jakieś pomysły?
jww
A brak cytowania ${MyString}prowadzi do wielu błędów dla „jeśli podane argumenty ...”, takich jak CMake błąd blisko if: „jeśli podane argumenty”, po których następują parantze, „NIE”, „RÓWNE” i podobne .
jww
@jww Ostrzeżenie oznacza, że MyStringnie zawiera nazwy zmiennej, która następnie zostanie ponownie usunięta . Uważam, że nikt tak naprawdę nie chce takiego OLDzachowania. Z mojego punktu widzenia jest to całkowicie bezpieczne i wstecznie kompatybilne, aby po prostu ustawić politykę CMP0054na NEW(zobacz dyskusję tutaj ).
Florian
@jww A najbezpieczniejszym sposobem uniknięcia tych problemów / ostrzeżeń jest po prostu zrobienie tego if (MyString ...)(jeśli to twój kod daje ostrzeżenie).
Florian
Dzięki. Usunęliśmy wszystkie wystąpienia ${MyString}i zastąpiliśmy je MyString(lub wydaje mi się, że usunęliśmy je wszystkie). Wciąż brak radości: zbuduj 372 . Bzdury nawet nie pochodzą z naszego kodu. Wygląda na to, że pochodzi z CMake. Linia 283 jest if (CMAKE_CXX_COMPILER_ID STREQUAL "MSVC").
jww
18

Oto kilka podstawowych przykładów, które pomogą Ci szybko i łatwo zacząć.

Jedna zmienna pozycji

Ustaw zmienną:

SET(INSTALL_ETC_DIR "etc")

Użyj zmiennej:

SET(INSTALL_ETC_CROND_DIR "${INSTALL_ETC_DIR}/cron.d")

Zmienna wielopozycyjna (np. Lista)

Ustaw zmienną:

SET(PROGRAM_SRCS
        program.c
        program_utils.c
        a_lib.c
        b_lib.c
        config.c
        )

Użyj zmiennej:

add_executable(program "${PROGRAM_SRCS}")

CMake dokumentuje zmienne

CivFan
źródło
1

$ENV{FOO}do użytku, gdzie FOOjest pobierany ze zmiennej środowiskowej. w przeciwnym razie użyj jako ${FOO}, gdzie FOOjest jakaś inna zmienna. Do ustawienia SET(FOO "foo")będzie używany w CMake.

parasrish
źródło