Stellt Ninja GNU Make in den Schatten?

Build-Systeme sind unerlässliche Tools in der Software-Entwicklung. Sie automatisieren das Kompilieren des Codes, linken Libraries und erzeugen schliesslich ausführbare Dateien. GNU Make ist das wohl verbreitetste Build-System. Dennoch hört man immer wieder von Ninja als bessere Alternative.
Was sind die Unterschiede sowie Vor- und Nachteile der beiden Build-Systeme?

Ninja

Ninja ist ein schnelles und leichtgewichtiges Build-System, das für effiziente inkrementelle Builds entwickelt wurde. Nachfolgend sind einige wichtige Merkmale von Ninja aufgelistet:

  1. Geschwindigkeit: Ninja ist für seine Schnelligkeit bekannt. Insbesondere für inkrementelle Builds ist Ninja schnell und effizient, was es ideal für grosse Projekte mit vielen Abhängigkeiten macht.
  2. Einfach und effizient: Ninja enthält nur die Funktionen, die nötig sind, um inkrementelle Builds korrekt hinzukriegen. Die meisten komplexen Aufgaben werden auf einen Build-Generator ausgelagert.
  3. Klare Syntax: Ninja-Build-Dateien verwenden eine übersichtliche Syntax, die leicht zu lesen und zu schreiben ist. Der Fokus liegt auf Einfachheit und Leistung.
  4. Allein unbrauchbar: Ninja-Build-Dateien sind nicht dafür gedacht, manuell erstellt zu werden. Deshalb ist die Verwendung eines Build-Generators wie CMake oder Meson zwingend.

GNU Make

GNU Make ist ein klassisches Build-Tool, das seit Jahrzehnten existiert. Es verwendet Makefiles (geschrieben in einer spezifischen Make-Syntax), um Build-Regeln zu definieren. Nachfolgend einige Aspekte von GNU Make:

  1. Weit verbreitet: GNU Make wird auf verschiedenen Plattformen verwendet und unterstützt. Es ist das Standard-Build-System vieler Projekte.
  2. Reich an Funktionen: GNU Make verfügt über viel Funktionalität. Einige Beispiele: Conditional Statements, Funktionen für Text Manipulationen, Suffix-Regeln, integrierte Regeln. Dadurch können Makefiles leistungsstark sein, aber auch komplex werden.
  3. Kryptisch: Makefiles verwenden eine spezielle Syntax, die für Einsteiger und Gelegenheitsnutzer oft undurchsichtig ist. Die Kombination von Variablen, Regeln, Abhängigkeiten und Tabulatoren kann verwirrend sein und erfordert eine umfangreiche Einarbeitung.
  4. Eigenständig: GNU Make wurde ursprünglich als eigenständiges Tool entwickelt und wurde mit der Idee entworfen Makefiles manuell zu schreiben. Build-Generatoren kamen erst später dazu und deren Verwendung ist daher optional.
gegenueberstellung

Abbildung 1: Gegenüberstellung Ninja-Build-Datei und Makefile zur Veranschaulichung der Syntax

Erfahrungen mit Ninja

Während der Entwicklung eines Build-Generators, der Ninja-Build-Dateien erzeugt, haben wir sowohl positive als auch negative Erfahrungen gemacht. Hier sind die wichtigsten Erkenntnisse:

Flache Lernkurve: Dank den wenigen Features die Ninja bietet ist das Ninja-Manual übersichtlich und die Syntax rasch verstanden. Ninja bietet ein Python Modul `ninja_syntax.py`, welches für das Generieren von Ninja-Build-Dateien verwendet werden kann.

Compilation Database: Mit `ninja -t compdb` kann eine Compilation Database erstellt werden. Dies ist eine JSON-Datei welche beschreibt, wie einzelne Kompilationsvorgänge unabhängig vom Build-System wiederholt werden können. Viele Tools, z.B. zur statischen Code Analyse, unterstützen dieses Format als Schnittstelle. Die Compilation Database einfach aus der Ninja-Build-Dateien extrahieren zu können, erleichterte die Integration dieser Tools sehr.

Absolute- und Relative-Pfade: Auszug aus dem Ninja-Manual:

«Dateipfade werden so verglichen, wie sie sind. Das bedeutet, dass ein absoluter Pfad und ein relativer Pfad, die auf dieselbe Datei verweisen, von Ninja als unterschiedlich betrachtet werden»

Das ist insbesondere im Zusammenhang mit depfiles, wo man nicht selbst die Kontrolle über die generierten Pfade hat, eine Herausforderung. Unser Fazit dazu: Das Mischen von absoluten und relativen-Pfaden ist zu vermeiden. Was sich als nicht immer einfach herausstellt (siehe Issue auf GitHub – offen seit über 8 Jahren).

Fazit

Wenn ein Build-Generator wie CMake verwendet wird, ist das Kompilieren mit Ninja deutlich effizienter als mit GNU Make. Darum bietet es sich an dort Ninja zu verwenden, die meisten Build-Generatoren unterstützen das Generieren von Ninja-Build-Dateien.

Entwickelt man einen Build-Generator ist es sinnvoll auf das moderne Ninja zu setzen. Das manuelle Schreiben von Ninja-Dateien wird jedoch aufgrund möglicher Fehler nicht empfohlen. GNU Make bleibt eine zuverlässige Option für kleinere Projekte, aber seine Einschränkungen werden in grösseren, komplexeren Codebasen deutlich.

Wählen Sie das Build-System, das am besten zu den Anforderungen Ihres Projekts und Ihrem Entwicklungsworkflow passt. Sowohl Ninja als auch GNU Make haben ihre Stärken.

Wir von CSA Engineering unterstützen sie gerne und kompetent, unabhängig davon für welches Build-System sie sich entscheiden.

Pascal Hari

Pascal Hari
BSc BFH in Mikrotechnik
Embedded Software Engineer

Über den Autor

Pascal Hari arbeitet seit 6 Jahren als Embedded Software Engineer bei der CSA Engineering AG. Sein Fokus liegt auf der Entwicklung von Firmware in C++ auf STM32. Er hat aber auch eine grosse Leidenschaft für Themen wie Buildumgebung, Toolchain und Docker. In seinen jüngsten Tätigkeiten leitet er die Neuentwicklung eines python-basierten build systems welches auf Ninja setzt. 

Kontaktiere uns