LilyPond - wie man ein einfaches Makefile schreibt

In den letzten Jahren habe ich mir selbst beigebracht, Partituren mit LilyPond zu schreiben und darüber hinaus das Dateihandling durch die Verwendung von Bash- Skripten zu erleichtern . Kürzlich habe ich ein Projekt gestartet, das etwas größer als nur eine oder zwei Seiten ist, und wieder einmal bin ich auf den Artikel über Makefiles in der LilyPond-Dokumentation gestoßen .

Obwohl ich diesen Artikel gelesen habe, war es für mich etwas schwierig, die eigentliche Technik hinter der Vorlage zu verstehen, Makefileauch weil der Artikel nicht das Datei-Repository enthält, mit dem es arbeitet.

Also habe ich mir die Zeit genommen, ein Beispielprojekt zu erfinden, um zu fragen, wie ein Makefilesolches Szenario aussehen könnte. Wie ich es Schritt für Schritt aufbauen würde und wie ich das Makefile tatsächlich ausführe. ( Edit: Bezug nehmend auf eine Manpage von make habe ich mein Verständnis soweit verstanden, dass man makeals Interpreter wie bashauf einer shell-script.sh. Der Befehl sieht dann einfach so aus, als ob er make -f Makefileim Root-Verzeichnis des Projekts ausgeführt wird.)


Das Projekt hat eine Dateistruktur wie diese:

├── Book.ly
├── Book.pdf
├── global-files
│   ├── copyright.ily
│   ├── Frontpage.ily
│   ├── header.ily
│   └── paper.ily
├── input-files-voiceI
│   ├── Nr_01-voiceI.ily
│   ├── Nr_02-voiceI.ily
│   └── Nr_03-voiceI.ily
├── input-files-voiceII
│   ├── Nr_01-voiceII.ily
│   ├── Nr_02-voiceII.ily
│   └── Nr_03-voiceII.ily
├── README.md
├── single-pages-voiceI
│   ├── MIDI
│   │   ├── Score-Nr_01-voiceI.midi
│   │   ├── Score-Nr_02-voiceI.midi
│   │   └── Score-Nr_03-voiceI.midi
│   ├── PDF
│   │   ├── Score-Nr_01-voiceI.pdf
│   │   ├── Score-Nr_02-voiceI.pdf
│   │   └── Score-Nr_03-voiceI.pdf
│   ├── Score-Nr_01-voiceI.ly
│   ├── Score-Nr_02-voiceI.ly
│   └── Score-Nr_03-voiceI.ly
├── single-pages-voiceI_a_II
│   ├── MIDI
│   │   ├── Score-I_u_II_Nr_01.midi
│   │   ├── Score-I_u_II_Nr_02.midi
│   │   └── Score-I_u_II_Nr_03.midi
│   ├── PDF
│   │   ├── Score-I_u_II_Nr_01.pdf
│   │   ├── Score-I_u_II_Nr_02.pdf
│   │   └── Score-I_u_II_Nr_03.pdf
│   ├── Score-I_u_II_Nr_01.ly
│   ├── Score-I_u_II_Nr_02.ly
│   └── Score-I_u_II_Nr_03.ly
└── single-pages-voiceII
    ├── MIDI
    │   ├── Score-Nr_01-voiceII.midi
    │   ├── Score-Nr_02-voiceII.midi
    │   └── Score-Nr_03-voiceII.midi
    ├── PDF
    │   ├── Score-Nr_01-voiceII.pdf
    │   ├── Score-Nr_02-voiceII.pdf
    │   └── Score-Nr_03-voiceII.pdf
    ├── Score-Nr_01-voiceII.ly
    ├── Score-Nr_02-voiceII.ly
    └── Score-Nr_03-voiceII.ly

Die Eingabedateien beider Stimmen haben folgendes Format:

\relative c {
  \clef bass
  \time 3/4
  \key c major

  c4( d e f      | %01
  g1) \bar "|."  | %02
}

Die Score- Dateien haben den Zweck, für die Ausgabe von PDF und MIDI kompiliert zu werden . und einfach so aussehen (trotz der Tatsache, dass die Scoresfür zwei Systeme ein anderes enthalten Staff):

\version "2.18.2"

#(set-default-paper-size "a4")
#(set-global-staff-size 22)

\include "../global-files/header.ily"

\score {
  \new StaffGroup = "" \with {
    instrumentName = \markup { \bold \huge { \larger "1." }}
  }
  <<
    \new Staff = "celloI" \with { midiInstrument = #"cello" }

    \include "../input-files-voiceI/Nr_01-voiceI.ily"
  >>
  \layout {}
  \midi {}
}

Der Buchteil ist der Teil, mit dem ich immer noch ziemlich unzufrieden bin. Ich würde dies ziemlich einfach bevorzugen , indem ich die Dateien Score*.lyeinfach als .\includes\includesScore.ly\score

Nun, ich könnte a verwenden, \bookum einen Buchausgabenamen wie festzulegen \bookOutputSuffix "OutputName", aber dann Book.lywürde my zu einer riesigen Datei werden, deren Kompilierung ziemlich lange dauern würde, selbst für eine kleine Änderung an einem einzelnen Stück.

Also im Moment Book.lyhat meine Datei folgendes Format und den einzigen Zweck, das ganze Buch mit zwei Stimmen in zwei Systemen zusammenzustellen, aber mit allen Stücken, hier 01-03:

\version "2.18.2"

#(set-default-paper-size "a4")
#(set-global-staff-size 22)

\include "./global-files/paper.ily"

\book {

  \include "./global-files/Frontpage.ily"

  %%%% Score Number: 1 ==================================%%%%

  \score {
    \new StaffGroup = "" \with {
      instrumentName = \markup { \bold \huge { \larger "1." }}}
    <<
      \new Staff = "voiceI" \with { midiInstrument = #"voice" }
      \include "./input-files-voiceI//Nr_01-voiceI.ily"
      \new Staff = "voiceII" \with { midiInstrument = #"voice" }
      \include "./input-files-voiceII//Nr_01-voiceII.ily"
    >>
    \layout {
      \printTupletBow
    }
  }

  %%%% Score Number: 2 ==================================%%%%

  \score {
    \new StaffGroup = "" \with {
      instrumentName = \markup { \bold \huge { \larger "2." }}}
    <<
      \new Staff = "voiceI" \with { midiInstrument = #"voice" }
      \include "./input-files-voiceI//Nr_02-voiceI.ily"
      \new Staff = "voiceII" \with { midiInstrument = #"voice" }
      \include "./input-files-voiceII//Nr_02-voiceII.ily"
    >>
    \layout {}
  }

  %%%% Score Number: 3 ==================================%%%%

  \score {
    \new StaffGroup = "" \with {
      instrumentName = \markup { \bold \huge { \larger "3." }}}
    <<
      \new Staff = "voiceI" \with { midiInstrument = #"voice" }
      \include "./input-files-voiceI//Nr_03-voiceI.ily"
      \new Staff = "voiceII" \with { midiInstrument = #"voice" }
      \include "./input-files-voiceII//Nr_03-voiceII.ily"
    >>
    \layout {}
  }
}

Mein Arbeitsablauf ist folgender:

  1. Ich schreibe die Eingabedateien:input-file.ily
  2. Ich führe eine aus bash-script.sh, die die kompilierbaren Score.lyDateien aus der erstelltinput-files/*.ily
  3. Ich führe eine aus bash-script.sh, die die kompilierbare Book.lyDatei aus der erstelltinput-files/*.ily
  4. Ich kompiliere die Score.lyDateien einzeln oder führe eine einfache for file in *.ly; do lilypond "$file"; doneSchleife aus, aber in jedem der drei Score-Verzeichnisse. Ich verwende ein Skript, um die PDF- und MIDI-Dateien in die entsprechenden Ordner zu verschieben.
  5. Ich führe einfach aus lilypond, um die Datei zu kompilieren Book.ly.

ERLEDIGT


Das eigentliche Projekt, für das diese Frage gestellt wird, finden Sie hier auf GitHub


Aktualisierung 1:

Mein System:

    Operating System: Debian GNU/Linux bullseye/sid
              Kernel: Linux 5.3.0-2-686-pae
        Architecture: x86
        GNU LilyPond: 2.18.2
My Editor - GNU Nano: 4.5
      Guake Terminal: 3.6.3
            GNU Make: 4.2.1

Ich habe meine shell-scriptszu einem separaten Git-Repository hinzugefügt


Aktualisierung 2:

Dies ist ein sehr vereinfachtes Diagramm der Abhängigkeiten. Angenommen es gäbe nur einen voice:

./infiles/                        
  infile{01..03}.ily -------------> ./Book.ly ===> Book.pdf
     |                                 ^ ^ ^
     *---------> Scores{01..03}.ly === | | |=====> Score{01..03}.pdf
                      ^  ^         === | | |=====> Score{01..03}.midi
                      |  |             | | |
./global-files/       |  |             | | |
  header.ily    ------*  |             | | |
  copyright.ily ---------+-------------* | |
  Frontpage.ily -------------------------* |
  paper.ily     ---------------------------*
Haben Sie die bash-script.sh-Dateien irgendwo verfügbar? Ich habe einige Schwierigkeiten, die ganze Prozedur zu grokken.
Informieren Sie auch abt OS und den Redakteur Ihrer Wahl
Zu Ihrer Information: Ich habe meine Antwort aktualisiert, um mehr Kontext und eine kurze technische Beschreibung der Mechanik bereitzustellen.
Diese Frage wurde in der Meta diskutiert .

Antworten (2)

Aufruf

makedurchsucht das aktuelle Verzeichnis nach einer Datei mit dem Namen Makefileoder makefile, daher ist es oft am einfachsten, ihr einen der beiden folgenden Namen zu geben und sie dann mit dem einfachen Befehl aufzurufen:

$ machen

Wenn Sie den Großbuchstaben „M“ verwenden, wird die Datei normalerweise alphabetisch oder sortiert oben aufgeführt.

Regeln

makefunktioniert, indem Regeln verwendet werden, wie eine Ausgabe aus einer Eingabe erstellt wird. Das Format für eine Regel ist das Ziel, gefolgt von einem Doppelpunkt, dann einer durch Leerzeichen getrennten Liste von Abhängigkeiten, gefolgt von Befehlen, die mit TAB eingerückt sind.

Ziel: Abhängigkeiten
    Befehle

Das Ziel oder die Abhängigkeiten können Dateinamen oder Symbole sein, die anderen Regeln entsprechen.

Wenn Sie in der Befehlszeile kein Ziel angeben, wird die erste Regel aufgerufen, auf die es stößt. Eine gängige Technik besteht also darin, die erste Regel zu einer "Dummy"-Regel zu machen, die keine Datei erzeugt, sondern einfach alle Schritte oder Ausgaben zusammenfasst. Z.B.

alle: Ausgang1

Beim Aufrufen makemit diesem Makefile wird versucht, es zu erstellen, output1wenn es nicht existiert. Wenn es eine Regel zum output1späteren Erstellen im Makefile gibt, wird diese verwendet.

Für Ihren Fall schlage ich vor, eine Regel auf oberster Ebene zu erstellen Score.lyund zu erstellenBook.ly

alle: Score.ly Book.ly

Musterregeln

Um Ihre Shell-Schleife zu ersetzen, können Sie eine Musterregel verwenden.

%.pdf: %.ly
    Lilienteich $^

Diese Regel besagt: Um eine .pdf-Datei zu erstellen, führen Sie lilyponddie entsprechende .ly-Datei aus .

Beachten Sie, dass der auszuführende Befehl mit einem wörtlichen TAB-Zeichen beginnen muss. Die%^Variable bezieht sich auf die oben erwähnte Eingabedatei. Andere nützliche Variablen sind$@für das Ziel und$<für die erste Eingabe, falls es mehr als eine gibt.

Dies behandelt nur einen Teil Ihrer Shell-Schleife und definiert die Transformation von einer Eingabedatei in eine Ausgabedatei. Für die andere Aufgabe, eine Liste von Dateien zu generieren, stehen einige spezielle Variablen in GNU make zur Verfügung.

input= $(notdir $(wildcard ./*.ly))
bases= $(Basisname $(Eingaben))
Ausgaben= $(patsubst %,%.pdf,$(Basen))

Nach diesen Definitionen können Sie $(outputs)als Abhängigkeit in einer Regel verwenden, z. B.:

pdfs: $(Ausgaben)
    mkdir -pPDF
    mv *.pdf PDF
    mkdir -p MIDI
    mv *.midi-MIDI

Dieses Beispiel aus der LilyPond-Dokumentation zeigt, dass Sie mehrere Ziele links vom Doppelpunkt platzieren können, sodass Sie beide von LilyPond erzeugten Dateitypen berücksichtigen können, was in meinem Beispiel hier nicht der Fall ist. Auch hier muss jeder dieser Befehle mit einem echten ASCII-TAB-Zeichen eingerückt werden.

Für ganz einfache Fälle

Sie können ein Makefile verwenden, um einfach ein oder mehrere Shell-Skripte zusammenzufassen, wobei Sie einen Großteil der Komplexität von Regeln und Abhängigkeiten ignorieren, wenn Ihre Situation sehr einfach ist.

Für ein Projekt, bei dem ich eine Reihe von .abc-Dateien in einem einzigen Verzeichnis erstellt habe, wird das Ganze von einer einzigen Regel gehandhabt:

all:
    for f in `ls *.abc` ;               \
    do ../abcm2ps -O $${f%.abc}.ps $$f ; \
       ps2pdf $${f%.abc}.ps ;           \
    done ;
    zip -r evildead.zip *.pdf
Das hat nicht funktioniert. Versuche es mit <tty><pre>.
Funktioniert jetzt. Allerdings ist ein semantisch geladener Anfangstab so unnatürlich, nicht nur für Menschen, sondern sogar für wohlmeinende Redakteure, dass eine stärkere Einschränkung angebracht sein könnte??
WAHR. So makehat es immer funktioniert. Aber ich werde versuchen, es lauter und öfter zu kennzeichnen, während ich die Antwort ausfülle.
Ich habe festgestellt, dass dies in den LilyPond-Dokumenten so aussieht, als würde man beide Formate handhaben PDF und MIDI , soweit ich das nach meinem bashWissen beurteilen kann, sieht es auch so aus, als würden sie ein verwenden, if statementum herauszufinden, ob die Dateien existieren oder nicht, und dann den mvBefehl ausführen Es.
Anstelle von lilypond $^würde ich verwenden lilypond $<. Dadurch können Sie \includedie Abhängigkeiten im Makefile verwenden und angeben.
@Rusi Die Überlieferung (tm) besagt, dass das Problem mit den Registerkarten nur wenige Wochen nach der ersten Veröffentlichung des Dienstprogramms (in den 1970er Jahren) bemerkt und nicht geändert wurde, da es bereits mehr als ein Dutzend Benutzer gab, die dadurch belästigt worden wären der Wechsel.
@luserdroog Ich habe ein Diagramm hinzugefügt :-)

Die Antwort von luser droog gibt einen guten Überblick über makesich selbst. Hier ist ein Beispiel dafür, wie man das auf ein reales Lilypond-Projekt mit den folgenden Merkmalen anwendet:

  • Mehrstimmig: Klavier, Fagott, etc.
  • Mehrere Bewegungen.
  • Mehrere gewünschte Ergebnisse: eine einzige „Master“-Partitur (Dirigent), eine Master-Partitur für jeden Satz und eine Einzelpartitur für jedes Instrument.
  • Sowohl PDF- als auch MIDI-Ausgänge.
  • Gemeinsame Definitionen (Makros) für Musik und Präsentation.

Wenn Ihre Datei beispielsweise Book.lyIhre Nr_01-voiceI.ilyDatei enthält, die wiederum Ihre macros.ilygemeinsam genutzte Definitionsdatei enthält, macros.ilymuss Ihr Makefile bei einer Änderung wissen, dass es neu kompiliert werden muss, Book.lyum zu aktualisieren Book.pdf.

Wenn Sie andererseits nur einen Teil ändern, ist es sehr praktisch, nur den geänderten Teil zu schreiben make partsund Make neu kompilieren zu lassen, anstatt Zeit mit der Neukompilierung aller anderen zu verschwenden. Und wenn Sie nur einen Abschnitt anhören möchten, sollten Sie in der Lage sein, den langsamen Satz zu überspringen und nur die notwendige Sequenzierung erneut vorzunehmen.make midi

Ich habe ein Shell-Skript geschrieben, um ein Makefile zu generieren, das alle diese Eigenschaften hat. Insbesondere durchläuft es automatisch den \includeGraphen Ihres Projekts, um herauszufinden, welche Lilypond-Dateien von welchen anderen abhängen, und erfordert die minimale Menge an Neukompilierungen für jede bestimmte Änderung. Sie sagen ihm einfach, welche „Hauptdateien“ Sie haben und welche Sie in PDF- und/oder MIDI-Form haben möchten, und es erledigt den Rest.

Hier ist das Skript: https://github.com/MutopiaProject/MutopiaProject/blob/918971593735f2dbf4864f289767b8d59a7d950e/ftp/MozartWA/KV488/Mozart-KV488/Mozart-KV488-lys/create_makefile.sh

Hier ist ein Beispiel für die Ausgabe Makefile: https://github.com/MutopiaProject/MutopiaProject/blob/918971593735f2dbf4864f289767b8d59a7d950e/ftp/MozartWA/KV488/Mozart-KV488/Mozart-KV488-lys/Makefile

Der Kernmechanismus besteht darin, für jede Lilypond-Datei ein sekundäres Ziel zu erstellen, das ich seine „lydep“-Datei („Lilypond-Abhängigkeiten“) genannt habe, sodass das lydep einer Datei nur dann fehlerhaft ist, wenn eine der transitiven Quellen der Datei fehlerhaft ist . In der Praxis bedeutet dies, dass jedes Lydep-Ziel von seiner Quelldatei und allen Lydep-Zielen abhängig sein muss, von denen es direkte \includeAbhängigkeiten hat. Dann erledigt die automatische Auflösung von Make den Rest.

Das Skript ist auf ein bestimmtes Projekt zugeschnitten, aber Sie sollten in der Lage sein, die Kerninfrastruktur beizubehalten und die makefileFunktion unten anzupassen, um sie in Ihrer Projektstruktur zu ersetzen.

Ich habe dieses Skript für GNU/Linux geschrieben, und es könnte einige kleinere Anpassungen für macOS/BSD erfordern (z. B. Austausch von readlink -f), aber insgesamt ist es ziemlich einfach. Es erfordert bashund GNU make(für sekundäre Ziele). Wie ich sehe, verwenden Sie Debian, also sollte es so laufen, wie es ist.

Dies alles wird unter der MIT-Lizenz veröffentlicht. Bitte zögern Sie nicht, alles zu nehmen, was Sie hilfreich finden.

yuhu mann, das sind ziemlich viele zeilen zum durchlesen. Danke für die Antwort und für das Teilen deiner Arbeit! <3
Sichere Sache. Ich werde sehen, ob ich das Wesentliche erklären/zusammenfassen kann, wenn ich später heute Gelegenheit dazu habe.