Estoy intentando hacer lo siguiente. Hay un programa, llámelo foo-bin
, que toma un solo archivo de entrada y genera dos archivos de salida. Una regla tonta de Makefile para esto sería:
file-a.out file-b.out: input.in
foo-bin input.in file-a.out file-b.out
Sin embargo, esto no indica make
de ninguna manera que ambos objetivos se generarán simultáneamente. Eso está bien cuando se ejecuta make
en serie, pero es probable que cause problemas si uno lo intenta make -j16
o algo igualmente loco.
La pregunta es si existe una forma de escribir una regla Makefile adecuada para tal caso. Claramente, generaría un DAG, pero de alguna manera el manual de GNU make no especifica cómo se podría manejar este caso.
Ejecutar el mismo código dos veces y generar solo un resultado está fuera de discusión, porque el cálculo lleva tiempo (piense: horas). La salida de un solo archivo también sería bastante difícil, porque con frecuencia se usa como entrada para GNUPLOT que no sabe cómo manejar solo una fracción de un archivo de datos.
Respuestas:
Lo resolvería de la siguiente manera:
En este caso, la marca paralela 'serializará' creando ayb, pero dado que la creación de b no hace nada, no lleva tiempo.
fuente
file-b.out
se creó como se indica y luego se eliminó misteriosamente,make
no podrá volver a crearlo porquefile-a.out
seguirá presente. ¿Alguna idea?file-a.out
, la solución anterior funciona bien. Esto sugiere un truco: cuando solo existe uno de aob, entonces las dependencias deben ordenarse de manera que el archivo faltante aparezca como resultado deinput.in
. Unos pocos$(widcard...)
s, s,$(filter...)
s,$(filter-out...)
etc., deberían funcionar. Ugh.foo-bin
obviamente creará uno de los archivos primero (usando una resolución de microsegundos), por lo que debe asegurarse de tener el orden de dependencia correcto cuando ambos archivos existan.touch file-a.out
como un segundo comando después de lafoo-bin
invocación.touch file-b.out
como segundo comando en la primera regla y duplique los comandos en la segunda regla. Si faltainput.in
algún archivo o es más antiguo , el comando se ejecutará una sola vez y regenerará ambos archivos confile-b.out
más reciente quefile-a.out
.El truco consiste en utilizar una regla de patrón con varios objetivos. En ese caso, make asumirá que ambos destinos se crean mediante una única invocación del comando.
Esta diferencia en la interpretación entre las reglas de patrón y las reglas normales no tiene exactamente sentido, pero es útil para casos como este y está documentada en el manual.
Este truco se puede utilizar para cualquier número de archivos de salida siempre que sus nombres tengan alguna subcadena común para
%
que coincida. (En este caso, la subcadena común es ".")fuente
make
ejecutará el mismo comando dos veces simultáneamente.foo%in bar%src: input.in
:-)$(subst .,%,$(varA) $(varB)): input.in
(probado con GNU make 4.1).Make no tiene una forma intuitiva de hacer esto, pero hay dos soluciones decentes.
Primero, si los objetivos involucrados tienen una raíz común, puede usar una regla de prefijo (con GNU make). Es decir, si quisiera corregir la siguiente regla:
Podrías escribirlo de esta manera:
(Usando la variable de regla de patrón $ *, que representa la parte coincidente del patrón)
Si desea ser portable a implementaciones Make que no sean GNU o si sus archivos no pueden ser nombrados para que coincidan con una regla de patrón, hay otra forma:
Esto le dice a make que input.in.intermediate no existirá antes de que se ejecute make, por lo que su ausencia (o su marca de tiempo) no hará que foo-bin se ejecute de manera falsa. Y ya sea que file-a.out o file-b.out o ambos estén desactualizados (en relación con input.in), foo-bin solo se ejecutará una vez. Puede usar .SECONDARY en lugar de .INTERMEDIATE, que indicará a make NOT que elimine un nombre de archivo hipotético input.in.intermediate. Este método también es seguro para compilaciones paralelas.
El punto y coma de la primera línea es importante. Crea una receta vacía para esa regla, para que Make sepa que realmente actualizaremos file-a.out y file-b.out (gracias @siulkiulki y otros que señalaron esto)
fuente
-j1
compilaciones). Además, si el archivo MAKE se interrumpe y deja elinput.in.intermediate
archivo alrededor, se vuelve realmente confuso.file-a.out file-b.out: input.in.intermediate
afile-a.out file-b.out: input.in.intermediate ;
Ver stackoverflow.com/questions/37873522/… para obtener más detalles.Esto se basa en la segunda respuesta de @ deemer, que no se basa en reglas de patrón, y soluciona un problema que estaba experimentando con los usos anidados de la solución alternativa.
Habría agregado esto como un comentario a la respuesta de @ deemer, pero no puedo porque acabo de crear esta cuenta y no tengo ninguna reputación.
Explicación: La receta vacía es necesaria para permitir que Make haga la contabilidad adecuada para marcar
file-a.out
yfile-b.out
como reconstruido. Si tiene otro objetivo intermedio del que dependefile-a.out
, Make elegirá no construir el intermedio externo, afirmando:fuente
Después de GNU Make 4.3 (19 de enero de 2020), puede utilizar "objetivos explícitos agrupados". Reemplazar
:
con&:
.El archivo NEWS de GNU Make dice:
fuente
Así es como lo hago. Primero, siempre separo los requisitos previos de las recetas. Entonces, en este caso, un nuevo objetivo para hacer la receta.
La primera vez:
1. Intentamos construir file-a.out, pero dummy-a-and-b.out necesita hacerlo primero, así que ejecute la receta dummy-a-and-b.out.
2. Intentamos construir file-b.out, dummy-a-and-b.out está actualizado.
La segunda vez y las siguientes:
1. Intentamos construir file-a.out: revisamos los prerrequisitos, los prerrequisitos normales están actualizados, los prerrequisitos secundarios faltan, así que se ignoran.
2. Intentamos construir file-b.out: revise los prerrequisitos, los prerrequisitos normales están actualizados, los prerrequisitos secundarios faltan, así que se ignoran.
fuente
file-b.out
make no tiene forma de volver a crearlo.Como una extensión de la respuesta de @ deemer, la he generalizado en una función.
Descompostura:
Quite cualquier espacio en blanco inicial / final del primer argumento
Cree un destino variable con un valor único para utilizarlo como destino intermedio. Para este caso, el valor será .inter.file-a.out_file-b.out.
Cree una receta vacía para las salidas con el objetivo único como dependencia.
Declare el objetivo único como intermedio.
Termine con una referencia al objetivo único para que esta función se pueda usar directamente en una receta.
También tenga en cuenta que el uso de eval aquí se debe a que eval se expande a nada, por lo que la expansión completa de la función es solo el objetivo único.
Debe dar crédito a las Reglas Atómicas en GNU Make, de las cuales se inspira esta función.
fuente
Para evitar la ejecución múltiple en paralelo de una regla con múltiples salidas con
make -jN
, utilice.NOTPARALLEL: outputs
. En tu caso :fuente