Konfiguration

Wenn es mehrere Konfigurationsoptionen gibt, ist die Übergabe über Compiler-Flags sehr mühsam. Außerdem sind die Konfigurationseinstellungen schwer zu überprüfen. Um die Dinge zu vereinfachen, unterstützt Meson die Generierung von Konfigurationsdateien. Dieses Feature ist ähnlich zu einem, das in anderen Build-Systemen wie CMake zu finden ist.

Nehmen wir an, wir haben den folgenden Meson-Schnipsel

conf_data = configuration_data()
conf_data.set('version', '1.2.3')
configure_file(input : 'config.h.in',
               output : 'config.h',
               configuration : conf_data)

und der Inhalt von config.h.in ist

#define VERSION_STR "@version@"

Meson erstellt dann eine Datei namens config.h im entsprechenden Build-Verzeichnis, deren Inhalt wie folgt aussieht.

#define VERSION_STR "1.2.3"

Genauer gesagt, Meson findet alle Zeichenketten vom Typ @varname@ und ersetzt sie durch die entsprechenden Werte, die in conf_data festgelegt sind. Sie können dasselbe configuration_data-Objekt beliebig oft verwenden, aber es wird unveränderlich, nachdem es an die Funktion configure_file übergeben wurde. Das heißt, nachdem es einmal zur Erzeugung von Ausgaben verwendet wurde, wird die Funktion set unbrauchbar und der Versuch, sie aufzurufen, führt zu einem Fehler. Eine Kopie eines unveränderlichen configuration_data ist immer noch unveränderlich.

Für komplexere Konfigurationsdatei-Generierungen bietet Meson eine zweite Form. Um sie zu verwenden, fügen Sie eine Zeile wie diese in Ihre Konfigurationsdatei ein.

#mesondefine TOKEN

Die stattfindende Ersetzung hängt vom Wert und Typ von TOKEN ab

#define TOKEN     // If TOKEN is set to boolean true.
#undef TOKEN      // If TOKEN is set to boolean false.
#define TOKEN 4   // If TOKEN is set to an integer or string value.
/* undef TOKEN */ // If TOKEN has not been set to any value.

Beachten Sie, dass Sie, wenn Sie eine C-Zeichenkette definieren möchten, die Anführungszeichen selbst wie folgt setzen müssen

conf_data.set('TOKEN', '"value"')

Da dies eine sehr häufige Operation ist, bietet Meson eine Komfortmethode

plain_var = 'value'
conf_data.set_quoted('TOKEN', plain_var) # becomes #define TOKEN "value"

Oft haben Sie einen booleschen Wert in Meson, müssen aber das C/C++-Token als 0 oder 1 definieren. Meson bietet für diesen Anwendungsfall eine Komfortfunktion.

conf_data.set10(token, boolean_value)
# The line above is equivalent to this:
if boolean_value
  conf_data.set(token, 1)
else
  conf_data.set(token, 0)
endif

Konfiguration ohne Eingabedatei

Wenn die Eingabedatei nicht definiert ist, generiert Meson eine Header-Datei mit allen Einträgen im Konfigurationsdatenobjekt. Die Ersetzungen sind die gleichen wie bei der Generierung von #mesondefine-Einträgen

conf_data.set('FOO', '"string"') => #define FOO "string"
conf_data.set('FOO', 'a_token')  => #define FOO a_token
conf_data.set('FOO', true)       => #define FOO
conf_data.set('FOO', false)      => #undef FOO
conf_data.set('FOO', 1)          => #define FOO 1
conf_data.set('FOO', 0)          => #define FOO 0

In diesem Modus können Sie auch einen Kommentar angeben, der vor dem Wert platziert wird, damit Ihre generierten Dateien selbstdokumentierend sind.

conf_data.set('BAR', true, description : 'Set BAR if it is available')

Erzeugt

/* Set BAR if it is available */
#define BAR

Umgang mit Dateikodierungen

Die Standard-Meson-Dateikodierung für Konfigurationsdateien ist utf-8. Wenn Sie eine Datei konfigurieren müssen, die nicht in utf-8 kodiert ist, können Sie mit dem Schlüsselwort encoding die zu verwendende Dateikodierung angeben. Es wird jedoch dringend empfohlen, Ihre Nicht-UTF-8-Datei nach Möglichkeit in UTF-8 zu konvertieren. Unterstützte Dateikodierungen sind die von python3, siehe standard-encodings.

Verwendung von Dictionaries

Seit 0.49.0 nimmt configuration_data() ein optionales Dictionary als erstes Argument. Wenn es bereitgestellt wird, wird jedes Schlüssel/Wert-Paar in die configuration_data aufgenommen, als ob die Methode set() für jedes davon aufgerufen worden wäre. Das configuration-Schlüsselwortargument von configure_file() akzeptiert ebenfalls ein Dictionary anstelle eines configuration_data-Objekts.

Beispiel

cdata = configuration_data({
  'STRING' : '"foo"',
  'INT' : 42,
  'DEFINED' : true,
  'UNDEFINED' : false,
})

configure_file(output : 'config1.h',
  configuration : cdata,
)

configure_file(output : 'config2.h',
  configuration : {
    'STRING' : '"foo"',
    'INT' : 42,
    'DEFINED' : true,
    'UNDEFINED' : false,
  }
)

Ein vollständiges Beispiel

Das Generieren und Verwenden einer Konfigurationsdatei erfordert die folgenden Schritte

  • die Datei generieren
  • ein Include-Verzeichnisobjekt für das Verzeichnis erstellen, das die Datei enthält
  • es in einem Target verwenden

Wir werden den traditionellen Ansatz verfolgen, eine Header-Datei im Top-Verzeichnis zu generieren. Der übliche Name ist config.h, aber wir werden einen eindeutigen Namen verwenden. Dies vermeidet das Problem, versehentlich die falsche Header-Datei einzubinden, wenn ein Projekt mit vielen Unterprojekten kompiliert wird.

Auf der obersten Ebene generieren wir die Datei

conf_data = configuration_data()
# Set data
configure_file(input : 'projconfig.h.in',
  output : 'projconfig.h',
  configuration : conf_data)

Unmittelbar danach generieren wir das Include-Objekt.

configuration_inc = include_directories('.')

Schließlich legen wir dies in einem Target fest, das sich in jedem Unterverzeichnis befinden kann.

executable(..., include_directories : configuration_inc)

Nun kann jede Quelldatei in diesem Target die Konfigurations-Header wie folgt einbinden

#include<projconfig.h>

Die Ergebnisse der Suche sind