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, Makefile
auch 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 Makefile
solches 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 make
als Interpreter wie bash
auf einer shell-script.sh
. Der Befehl sieht dann einfach so aus, als ob er make -f Makefile
im 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 Scores
fü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*.ly
einfach als .\includes
\includes
Score.ly
\score
Nun, ich könnte a verwenden, \book
um einen Buchausgabenamen wie festzulegen \bookOutputSuffix "OutputName"
, aber dann Book.ly
wü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.ly
hat 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:
input-file.ily
bash-script.sh
, die die kompilierbaren Score.ly
Dateien aus der erstelltinput-files/*.ily
bash-script.sh
, die die kompilierbare Book.ly
Datei aus der erstelltinput-files/*.ily
Score.ly
Dateien einzeln oder führe eine einfache for file in *.ly; do lilypond "$file"; done
Schleife aus, aber in jedem der drei Score-Verzeichnisse. Ich verwende ein Skript, um die PDF- und MIDI-Dateien in die entsprechenden Ordner zu verschieben.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-scripts
zu 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 ---------------------------*
make
durchsucht das aktuelle Verzeichnis nach einer Datei mit dem Namen Makefile
oder 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.
make
funktioniert, 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 make
mit diesem Makefile wird versucht, es zu erstellen, output1
wenn es nicht existiert. Wenn es eine Regel zum output1
späteren Erstellen im Makefile gibt, wird diese verwendet.
Für Ihren Fall schlage ich vor, eine Regel auf oberster Ebene zu erstellen Score.ly
und zu erstellenBook.ly
alle: Score.ly Book.ly
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 lilypond
die 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.
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
make
hat es immer funktioniert. Aber ich werde versuchen, es lauter und öfter zu kennzeichnen, während ich die Antwort ausfülle.bash
Wissen beurteilen kann, sieht es auch so aus, als würden sie ein verwenden, if statement
um herauszufinden, ob die Dateien existieren oder nicht, und dann den mv
Befehl ausführen Es.lilypond $^
würde ich verwenden lilypond $<
. Dadurch können Sie \include
die Abhängigkeiten im Makefile verwenden und angeben.Die Antwort von luser droog gibt einen guten Überblick über make
sich selbst. Hier ist ein Beispiel dafür, wie man das auf ein reales Lilypond-Projekt mit den folgenden Merkmalen anwendet:
Wenn Ihre Datei beispielsweise Book.ly
Ihre Nr_01-voiceI.ily
Datei enthält, die wiederum Ihre macros.ily
gemeinsam genutzte Definitionsdatei enthält, macros.ily
muss Ihr Makefile bei einer Änderung wissen, dass es neu kompiliert werden muss, Book.ly
um zu aktualisieren Book.pdf
.
Wenn Sie andererseits nur einen Teil ändern, ist es sehr praktisch, nur den geänderten Teil zu schreiben make parts
und 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 \include
Graphen 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 \include
Abhä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 makefile
Funktion 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 bash
und 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.
Luser Drog
Russi
wchargin
Andreas T.