Durante los últimos años me enseñé a escribir partituras usando LilyPond y además a facilitar el manejo de archivos usando scripts de Bash . Recientemente comencé un proyecto un poco más grande que solo una o dos páginas y una vez más me encontré con el artículo sobre Makefiles en el documental de LilyPond .
Aunque al leer este artículo me resultó un poco difícil comprender la técnica real detrás de la plantilla, Makefile
también porque el artículo no proporciona el repositorio de archivos con el que está trabajando.
Así que pensé en tomarme el tiempo para inventar un proyecto de ejemplo para preguntar cómo Makefile
podría ser una en este escenario. Cómo lo construiría paso a paso y cómo ejecuto el Makefile. ( Editar: refiriéndose a una página de manual de creación, entendí que se puede decir que uno usa make
como intérprete como bash
en un shell-script.sh
. El comando simplemente parece make -f Makefile
ejecutarse en el directorio raíz del proyecto).
El proyecto tiene una estructura de archivos como esta:
├── 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
Los archivos de entrada de ambas voces tienen un formato como este:
\relative c {
\clef bass
\time 3/4
\key c major
c4( d e f | %01
g1) \bar "|." | %02
}
Los archivos de partitura tienen el propósito de compilarse para la salida de PDF y MIDI. y simplemente tenga este aspecto (a pesar del hecho de que los Scores
dos sistemas contienen otro 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 {}
}
La parte del libro es la parte con la que todavía estoy bastante descontento. Preferiría esto bastante simple con solo usar los Score*.ly
archivos como \includes
, pero tengo problemas con los \includes
que ya están en los Score.ly
archivos, ya que no solo contienen el \score
bloque para ser compilables por ellos mismos.
Bueno, me vendría bien \book
establecer un nombre de salida de libro como \bookOutputSuffix "OutputName"
, pero luego Book.ly
se convertiría en un archivo enorme, que tardaría mucho tiempo en compilarse, incluso para un pequeño cambio en una sola pieza.
Así que en este momento mi Book.ly
archivo tiene el siguiente formato y el único propósito de compilar todo el libro con dos voces en dos pentagramas, pero con todas las piezas, aquí 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 {}
}
}
Mi flujo de trabajo es el siguiente:
input-file.ily
bash-script.sh
que crea los archivos compilables Score.ly
desde elinput-files/*.ily
bash-script.sh
que crea el Book.ly
archivo compilable desde elinput-files/*.ily
Score.ly
archivos uno por uno o ejecuto un for file in *.ly; do lilypond "$file"; done
ciclo simple, pero en cada uno de los tres directorios de Score. Uso un script para mover los archivos PDF y MIDI a sus carpetas correspondientes.lilypond
para compilar el Book.ly
archivo.HECHO
El proyecto real para el que se hace esta pregunta se puede encontrar aquí en GitHub
Actualización 1:
mi sistema:
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
Agregué my shell-scripts
a un Git-Repository separado
Actualización 2:
Este es un gráfico muy simplificado de las dependencias. Suponiendo que solo hubiera uno 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
buscará en el directorio actual un archivo llamado Makefile
o makefile
, por lo que a menudo es más simple nombrarlo con una de estas dos opciones y luego invocarlo con el comando simple:
$ hacer
Si usa la 'M' mayúscula, el archivo generalmente aparecerá en la parte superior de acuerdo con el orden alfabético o de intercalación.
make
opera mediante el uso de reglas sobre cómo crear una salida a partir de una entrada. El formato de una regla es el destino seguido de dos puntos, luego una lista de dependencias delimitada por espacios, seguida de comandos sangrados con TAB.
objetivo: dependencias comandos
El destino o las dependencias pueden ser nombres de archivo o símbolos que correspondan a otras reglas.
Si no especifica un objetivo en la línea de comando, invocará la primera regla que encuentre. Entonces, una técnica común es hacer que la primera regla sea una regla "ficticia" que no produce un archivo sino que simplemente recopila todos los pasos o resultados juntos. P.ej.
todo: salida1
La invocación make
con este archivo MAKE intentará crear output1
si no existe. Si hay una regla para crear output1
más adelante en el archivo MAKE, la usará.
Para su caso, sugiero hacer una regla de nivel superior para crear Score.ly
yBook.ly
todo: Score.ly Book.ly
Para reemplazar su ciclo de shell, puede usar una regla de patrón.
%.pdf: %.ly estanque de lirios $^
Esta regla dice: Para crear un archivo .pdf, ejecútelo lilypond
en el archivo .ly correspondiente .
Tenga en cuenta que el comando a ejecutar debe comenzar con un carácter TAB literal. La%^
variable se refiere al archivo de entrada mencionado anteriormente. Otras variables útiles son$@
para el destino y$<
para la primera entrada si hay más de una.
Esto maneja solo una parte de su ciclo de shell, definiendo la transformación de un archivo de entrada a un archivo de salida. Para la otra tarea de generar una lista de archivos, hay algunas variables especiales disponibles en GNU make para esto.
entradas= $(notdir $(comodín ./*.ly)) bases= $(nombre base $(entradas)) salidas= $(patsubst %,%.pdf,$(bases))
Después de estas definiciones, puede usar $(outputs)
como una dependencia en una regla, como:
pdf: $(salidas) mkdir -p PDF mv *.pdf PDF mkdir -p MIDI mv *.midi MIDI
Este ejemplo de los documentos de LilyPond muestra que puede colocar varios objetivos a la izquierda de los dos puntos, por lo que puede tener en cuenta ambos tipos de archivos producidos por LilyPond, lo que no hace mi ejemplo aquí. Nuevamente, cada uno de estos comandos debe tener una sangría con un carácter TAB ASCII de buena fe.
Puede usar un archivo MAKE solo para recopilar uno o más scripts de shell juntos, ignorando gran parte de la complejidad de las reglas y dependencias si su situación es muy simple.
Para un proyecto en el que hice un montón de archivos .abc en un solo directorio, todo se maneja con una sola regla:
all:
for f in `ls *.abc` ; \
do ../abcm2ps -O $${f%.abc}.ps $$f ; \
ps2pdf $${f%.abc}.ps ; \
done ;
zip -r evildead.zip *.pdf
make
ha funcionado siempre. Pero trataré de marcarlo más fuerte y con más frecuencia a medida que complete la respuesta.bash
también parece que usan un if statement
para averiguar si los archivos existen o no y luego ejecutan el mv
comando en él.lilypond $^
, usaría lilypond $<
. Esto le permite usar \include
y especificar las dependencias en el archivo MAKE.La respuesta de luser droog da una buena visión general de make
sí misma. Aquí hay un ejemplo de cómo aplicar eso a un proyecto de Lilypond del mundo real con las siguientes características:
Por ejemplo, si su Book.ly
archivo incluye su Nr_01-voiceI.ily
archivo, que a su vez incluye su macros.ily
archivo de definiciones compartidas, entonces, si macros.ily
se cambia, su Makefile necesita saber que necesita recompilarse Book.ly
para actualizarse Book.pdf
.
Por otro lado, si cambias solo una parte, es muy conveniente poder escribir make parts
y hacer que Make solo recompile la parte que se cambió, en lugar de perder el tiempo recompilando todas las demás. Y, si solo desea escuchar un segmento, debería poder make midi
omitir la composición tipográfica lenta y solo volver a hacer la secuencia necesaria.
He escrito un script de shell para generar un Makefile que tiene todas estas propiedades. En particular, recorre automáticamente el \include
gráfico de su proyecto para determinar qué archivos de Lilypond dependen de cuáles otros, e incurrirá en el conjunto mínimo de recompilaciones para cualquier cambio en particular. Simplemente dígale qué "archivos principales" tiene y cuáles quiere en formato PDF y/o MIDI, y él hará el resto.
Aquí está el guión: https://github.com/MutopiaProject/MutopiaProject/blob/918971593735f2dbf4864f289767b8d59a7d950e/ftp/MozartWA/KV488/Mozart-KV488/Mozart-KV488-lys/create_makefile.sh
Aquí hay un ejemplo de la salida Makefile
: https://github.com/MutopiaProject/MutopiaProject/blob/918971593735f2dbf4864f289767b8d59a7d950e/ftp/MozartWA/KV488/Mozart-KV488/Mozart-KV488-lys/Makefile
El mecanismo central es crear un destino secundario para cada archivo de Lilypond, al que he llamado su archivo "lydep" ("dependencias de Lilypond"), de modo que el lydep de un archivo está sucio si y solo si alguna de las fuentes transitivas del archivo está sucia. . En términos prácticos, esto significa que cada destino de Lydep debe depender de su archivo fuente más todos los destinos de Lydep de los que tiene \include
dependencias directas. Luego, la resolución automática de Make se encarga del resto.
La secuencia de comandos se adapta a un proyecto en particular, pero debería poder mantener la infraestructura central en su lugar y personalizar la makefile
función en la parte inferior para sustituirla en la estructura de su proyecto.
Escribí este script para GNU/Linux, y es posible que necesite algunos ajustes menores para macOS/BSD (por ejemplo, cambiar readlink -f
), pero en general es bastante sencillo. Requiere bash
y GNU make
(para objetivos secundarios). Veo que está en Debian, por lo que debería funcionar bien como está.
Todo esto se publica bajo la licencia MIT. Por favor, siéntase libre de tomar lo que encuentre útil.
luser droog
ruso
cargando
andres t