Wie lautet die CMake-Syntax zum Festlegen und Verwenden von Variablen?

167

Ich bitte dies, um mich daran zu erinnern, wenn ich CMake das nächste Mal benutze. Es bleibt nie hängen und die Google-Ergebnisse sind nicht großartig.

Wie lautet die Syntax zum Festlegen und Verwenden von Variablen in CMake?

CivFan
quelle

Antworten:

280

Beim Schreiben von CMake-Skripten müssen Sie viel über die Syntax und die Verwendung von Variablen in CMake wissen.

Die Syntax

Zeichenfolgen mit set():

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

Oder mit string():

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

Listen mit set():

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

Oder besser mit list():

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

Listen der Dateinamen:

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

Die Dokumentation

Der Bereich oder "Welchen Wert hat meine Variable?"

Zuerst gibt es die "normalen Variablen" und Dinge, die Sie über ihren Umfang wissen müssen:

  • Normale Variablen sind auf die CMakeLists.txtsie gesetzt in und alles rief von dort aus ( add_subdirectory(), include(), macro()und function()).
  • Die Befehle add_subdirectory()und function()sind etwas Besonderes, da sie ihren eigenen Bereich eröffnen.
    • Bedeutet, dass Variablen set(...)dort nur dort sichtbar sind und eine Kopie aller normalen Variablen der Bereichsebene erstellen, von der aus sie aufgerufen werden (als übergeordneter Bereich bezeichnet).
    • Wenn Sie sich also in einem Unterverzeichnis oder einer Funktion befinden, können Sie eine bereits vorhandene Variable im übergeordneten Bereich mit ändern set(... PARENT_SCOPE)
    • Sie können dies beispielsweise in Funktionen verwenden, indem Sie den Variablennamen als Funktionsparameter übergeben. Ein Beispiel wäre das function(xyz _resultVar)Einstellenset(${_resultVar} 1 PARENT_SCOPE)
  • Auf der anderen Seite ändern alle von Ihnen festgelegten Skripte include()oder macro()Skripte Variablen direkt in dem Bereich, von dem aus sie aufgerufen werden.

Zweitens gibt es den "Global Variables Cache". Dinge, die Sie über den Cache wissen müssen:

  • Wenn im aktuellen Bereich keine normale Variable mit dem angegebenen Namen definiert ist, sucht CMake nach einem passenden Cache-Eintrag.
  • Cache-Werte werden in der CMakeCache.txtDatei in Ihrem Binärausgabeverzeichnis gespeichert .
  • Die Werte im Cache können in der GUI- Anwendung von CMake geändert werden, bevor sie generiert werden. Daher haben sie - im Vergleich zu normalen Variablen - a typeund a docstring. Normalerweise verwende ich die GUI nicht, daher set(... CACHE INTERNAL "")setze ich meine globalen und dauerhaften Werte.

    Bitte beachten Sie, dass der INTERNALTyp der Cache-Variablen dies impliziertFORCE

  • In einem CMake-Skript können Sie vorhandene Cache-Einträge nur ändern, wenn Sie die set(... CACHE ... FORCE)Syntax verwenden. Dieses Verhalten wird zB von CMake selbst verwendet, da es normalerweise keine Cache-Einträge selbst erzwingt und Sie es daher mit einem anderen Wert vordefinieren können.

  • Sie können die Befehlszeile verwenden, um Einträge im Cache mit oder ohne Syntax cmake -D var:type=valuefestzulegen .cmake -D var=valuecmake -C CMakeInitialCache.cmake
  • Sie können unset Einträge im Cache mit unset(... CACHE).

Der Cache ist global und Sie können sie praktisch überall in Ihren CMake-Skripten festlegen. Ich würde Ihnen jedoch empfehlen, zweimal darüber nachzudenken, wo Cache-Variablen verwendet werden sollen (sie sind global und dauerhaft). Normalerweise bevorzuge ich die Syntax set_property(GLOBAL PROPERTY ...)und set_property(GLOBAL APPEND PROPERTY ...), um meine eigenen nicht persistenten globalen Variablen zu definieren.

Variable Fallstricke und "Wie werden Variablenänderungen debuggt?"

Um Fallstricke zu vermeiden, sollten Sie Folgendes über Variablen wissen:

  • Lokale Variablen verbergen zwischengespeicherte Variablen, wenn beide denselben Namen haben
  • Die find_...Befehle schreiben - falls erfolgreich - ihre Ergebnisse als zwischengespeicherte Variablen, "damit kein Aufruf erneut sucht".
  • Listen in CMake sind nur Zeichenfolgen mit Semikolon-Trennzeichen, daher sind die Anführungszeichen wichtig
    • set(MyVar a b c)ist "a;b;c"und set(MyVar "a b c")ist"a b c"
    • Die Empfehlung lautet, dass Sie immer Anführungszeichen verwenden, mit der einzigen Ausnahme, wenn Sie eine Liste als Liste angeben möchten
    • Bevorzugen Sie im Allgemeinen den list()Befehl zum Behandeln von Listen
  • Das gesamte oben beschriebene Umfangsproblem. Insbesondere wird die Verwendung functions()anstelle von empfohlen, macros()da Ihre lokalen Variablen nicht im übergeordneten Bereich angezeigt werden sollen.
  • Viele von CMake verwendete Variablen werden mit den Aufrufen project()und festgelegt enable_language(). Daher kann es wichtig werden, einige Variablen festzulegen, bevor diese Befehle verwendet werden.
  • Umgebungsvariablen können sich davon unterscheiden, wo CMake die make-Umgebung generiert hat und wann die make-Dateien verwendet werden.
    • Eine Änderung einer Umgebungsvariablen löst den Generierungsprozess nicht erneut aus.
    • Insbesondere eine generierte IDE-Umgebung kann von Ihrer Befehlszeile abweichen. Es wird daher empfohlen, Ihre Umgebungsvariablen in etwas zu übertragen, das zwischengespeichert ist.

Manchmal hilft nur das Debuggen von Variablen. Folgendes kann Ihnen helfen:

  • Verwenden Sie einfach den alten printfDebugging-Stil mit dem message()Befehl. Es gibt auch einige gebrauchsfertige Module, die mit CMake selbst geliefert werden : CMakePrintHelpers.cmake , CMakePrintSystemInformation.cmake
  • Suchen Sie in der CMakeCache.txtDatei in Ihrem Binärausgabeverzeichnis. Diese Datei wird sogar generiert, wenn die eigentliche Generierung Ihrer make-Umgebung fehlschlägt.
  • Verwenden Sie variable_watch (), um zu sehen, wo Ihre Variablen gelesen / geschrieben / entfernt werden.
  • Schauen Sie sich die Verzeichniseigenschaften CACHE_VARIABLES und VARIABLES an
  • Rufen Sie cmake --trace ...an, um den vollständigen Analyseprozess des CMake zu sehen. Das ist eine Art letzte Reserve, weil sie viel Output generiert.

Spezielle Syntax

  • Umgebungsvariablen
    • Sie können Umgebungsvariablen lesen $ENV{...}und schreibenset(ENV{...} ...)
  • Generatorausdrücke
    • Generatorausdrücke $<...>werden nur ausgewertet, wenn der Generator von CMake die make-Umgebung schreibt (Vergleich mit normalen Variablen, die vom Parser "an Ort und Stelle" ersetzt werden).
    • Sehr praktisch, z. B. in Compiler / Linker-Befehlszeilen und in Umgebungen mit mehreren Konfigurationen
  • Verweise
    • Mit können ${${...}}Sie einer Variablen Variablennamen geben und auf deren Inhalt verweisen.
    • Wird häufig verwendet, wenn ein Variablenname als Funktions- / Makroparameter angegeben wird.
  • Konstante Werte (siehe if()Befehl)
    • Mit if(MyVariable)können Sie eine Variable direkt auf wahr / falsch prüfen (hier ist keine Beilage erforderlich ${...})
    • Wahr , wenn die Konstante ist 1, ON, YES, TRUE, Y, oder eine Zahl ungleich Null.
    • Falsch , wenn die Konstante 0, OFF, NO, FALSE, N, IGNORE, NOTFOUND, die leere Zeichenkette, oder endet in dem Suffix -NOTFOUND.
    • Diese Syntax wird häufig für so etwas verwendet if(MSVC), kann jedoch für jemanden verwirrend sein, der diese Syntaxverknüpfung nicht kennt.
  • Rekursive Substitutionen
    • Sie können Variablennamen mithilfe von Variablen erstellen. Nachdem CMake die Variablen ersetzt hat, wird erneut geprüft, ob das Ergebnis selbst eine Variable ist. Dies ist eine sehr leistungsstarke Funktion, die in CMake selbst verwendet wird, z. B. als eine Art Vorlageset(CMAKE_${lang}_COMPILER ...)
    • Beachten Sie jedoch, dass dies bei if()Befehlen zu Kopfschmerzen führen kann . Hier ist ein Beispiel, wo CMAKE_CXX_COMPILER_IDist "MSVC"und MSVCist "1":
      • if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC") ist wahr, weil es auswertet if("1" STREQUAL "1")
      • if(CMAKE_CXX_COMPILER_ID STREQUAL "MSVC") ist falsch, weil es zu auswertet if("MSVC" STREQUAL "1")
      • Die beste Lösung hier wäre also - siehe oben - direkt zu prüfen if(MSVC)
    • Die gute Nachricht ist, dass dies in CMake 3.1 mit der Einführung der Richtlinie CMP0054 behoben wurde . Ich würde empfehlen, immer cmake_policy(SET CMP0054 NEW)"nur if()Argumente als Variablen oder Schlüsselwörter zu interpretieren, wenn sie nicht in Anführungszeichen stehen" zu setzen.
  • Der option()Befehl
    • Hauptsächlich nur zwischengespeicherte Zeichenfolgen, die nur ONoder sein können OFFund eine spezielle Behandlung ermöglichen, z. B. Abhängigkeiten
    • Aber seien Sie sich bewusst , verwechseln Sie das nicht optionmit dem setBefehl. Der angegebene Wert optionist eigentlich nur der "Anfangswert" (der im ersten Konfigurationsschritt einmal in den Cache übertragen wurde) und soll anschließend vom Benutzer über die Benutzeroberfläche von CMake geändert werden .

Verweise

Florian
quelle
Wenn ich benutze, if ("${MyString}" ...)sehe ich Warnungen : Policy CMP0054 is not set: Only interpret if() arguments as variables or keywords when unquoted. Siehe zum Beispiel Build 367 . Irgendwelche Ideen?
JWW
Und das Aufheben ${MyString}der Anführungszeichen führt zu einer Reihe von Fehlern für "wenn gegebene Argumente ..." wie CMake error in der Nähe von if: "wenn gegebene Argumente", gefolgt von Klammern, "NOT", "EQUALS" und ähnlichem .
JWW
@jww Die Warnung bedeutet, dass MyStringsie einen Variablennamen enthält, auf den dann erneut verwiesen wird. Ich glaube, dass niemand dieses OLDVerhalten wirklich will . Aus meiner Sicht ist es also absolut sicher und abwärtskompatibel, nur die Richtlinien CMP0054festzulegen NEW(siehe die Diskussion hier ).
Florian
@jww Und der sicherste Weg, um diese Probleme / Warnungen zu vermeiden, ist einfach zu tun if (MyString ...)(wenn es Ihr Code ist, der die Warnung gibt).
Florian
Vielen Dank. Wir haben alle Vorkommen von entfernt ${MyString}und durch ersetzt MyString(oder ich glaube, wir haben sie alle entfernt). Immer noch keine Freude: Build 372 . Der Mist kommt nicht einmal von unserem Code. Es scheint von CMake zu kommen. Zeile 283 ist if (CMAKE_CXX_COMPILER_ID STREQUAL "MSVC").
JWW
18

Hier sind einige grundlegende Beispiele, um schnell und schmutzig loszulegen.

Eine Elementvariable

Variable setzen:

SET(INSTALL_ETC_DIR "etc")

Variable verwenden:

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

Variable mit mehreren Elementen (dh Liste)

Variable setzen:

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

Variable verwenden:

add_executable(program "${PROGRAM_SRCS}")

CMake-Dokumente zu Variablen

CivFan
quelle
1

$ENV{FOO}zur Verwendung, wo FOOvon der Umgebungsvariablen abgeholt wird. Andernfalls verwenden Sie as ${FOO}, wo FOOsich eine andere Variable befindet. Zum Einstellen SET(FOO "foo")würde in CMake verwendet.

Parasrish
quelle