¿Cómo obligas a un archivo MAKE a reconstruir un objetivo?


184

Tengo un archivo MAKE que se construye y luego llama a otro archivo MAKE. Dado que este archivo MAKE llama a más archivos MAKE que hacen el trabajo, realmente no cambia. Por lo tanto, sigue pensando que el proyecto está construido y actualizado.

dnetdev11 ~ # make
make: `release' is up to date.

¿Cómo fuerzo el archivo MAKE para reconstruir el objetivo?

clean = $(MAKE) -f ~/xxx/xxx_compile.workspace.mak clean


build = svn up ~/xxx                                                       \
        $(clean)                                                                \
        ~/cbp2mak/cbp2mak -C ~/xxx ~/xxx/xxx_compile.workspace        \
        $(MAKE) -f ~/xxx/xxx_compile.workspace.mak $(1)                    \


release:
        $(build )

debug:
        $(build DEBUG=1)

clean:
        $(clean)

install:
        cp ~/xxx/source/xxx_utility/release/xxx_util /usr/local/bin
        cp ~/xxx/source/xxx_utility/release/xxxcore.so /usr/local/lib

Nota: nombres eliminados para proteger a los inocentes

Edición: versión fija final:

clean = $(MAKE) -f xxx_compile.workspace.mak clean;


build = svn up;                                         \
        $(clean)                                        \
        ./cbp2mak/cbp2mak -C . xxx_compile.workspace;   \
        $(MAKE) -f xxx_compile.workspace.mak    $(1);   \


.PHONY: release debug clean install

release:
        $(call build,)

debug:
        $(call build,DEBUG=1)

clean:
        $(clean)

install:
        cp ./source/xxx_utillity/release/xxx_util /usr/bin
        cp ./dlls/Release/xxxcore.so /usr/lib

Lodle, dado que esta es una pregunta visitada con frecuencia, ¿le gustaría editar la pregunta para que sea más moderna? (Parece que .PHONYno fue su único problema, y ​​realmente no se supone que edite la solución en la pregunta, o al menos ya no.)
Keith M

Respuestas:


23

Podría declarar que uno o más de sus objetivos son falsos .

Un objetivo falso es uno que no es realmente el nombre de un archivo; más bien es solo un nombre para que una receta se ejecute cuando realiza una solicitud explícita. Hay dos razones para usar un objetivo falso: para evitar un conflicto con un archivo del mismo nombre y para mejorar el rendimiento.

...

Un objetivo falso no debe ser un requisito previo de un archivo de destino real; si es así, su receta se ejecutará cada vez que make vaya a actualizar ese archivo. Siempre que un objetivo falso nunca sea un requisito previo de un objetivo real, la receta del objetivo falso se ejecutará solo cuando el objetivo falso sea un objetivo específico


68
Esta respuesta, aunque es "aceptada" y altamente "votada", está realmente fuera de lugar. Primero, dice "declarar objetivos falsos" pero luego dice "objetivo falso no es realmente el nombre de un archivo". Bueno, si su objetivo es un archivo, eso es una contradicción en la respuesta. En segundo lugar, dice que "el objetivo falso no debe ser un requisito previo de un verdadero", bueno, ¿y si lo es? La pregunta original no especificaba si es o no es. La respuesta correcta es no declarar que sus objetivos son falsos, sino más bien declarar un objetivo falso adicional y, a continuación, depender de los objetivos que desea reconstruir.
Mark Galeck

2
@MarkGaleck. Cuando la respuesta dice que "Un objetivo falso es uno que no es realmente el nombre de un archivo", se cita directamente del manual de gcc make. Es perfectamente correcto
drlolly

"Destino" es un término Make que se refiere al texto a la izquierda de los dos puntos :, no solo al resultado final que desea crear (por ejemplo, su archivo binario). En la pregunta, release, debug, clean, y installson los blancos no tomamos, xxx_utilo xxxcore.soo cualquier otra cosa.
Keith M

728

El -Bcambio para hacer, cuya forma larga es --always-make, le dice makeque no tenga en cuenta las marcas de tiempo y haga los objetivos especificados. Esto puede anular el propósito de usar make, pero puede ser lo que necesita.


44
@ MarkKCowan Estoy completamente de acuerdo! Esta opción es exactamente lo que estaba buscando, no una solución alternativa como sugiere Dave.
Maarten Bamelis

8
La advertencia con este enfoque es que simplemente construye demasiadas cosas. Específicamente con las herramientas automáticas, lo vi volver a ejecutar la configuración ... ¡¡¡Deseo que se pueda construir una solución basada en LD_PRELOAD !!
vrdhn

sí, ¡e incluso puede reescribir archivos que no quería! como las bibliotecas del sistema global que aparecen en las dependencias y se reconstruyen y se sobrescriben ...
Julio Guerra

18

Un truco que solía estar documentado en un manual de Sun makees utilizar un objetivo (inexistente) '.FORCE'. Puede hacer esto creando un archivo, force.mk, que contenga:

.FORCE:
$(FORCE_DEPS): .FORCE

Luego, suponiendo que se llame a su archivo MAKE existente makefile, puede ejecutar:

make FORCE_DEPS=release -f force.mk -f makefile release

Como .FORCEno existe, todo lo que dependa de él estará desactualizado y reconstruido.

Todo esto funcionará con cualquier versión de make; en Linux, tiene GNU Make y, por lo tanto, puede usar el objetivo .PHONY como se discutió.

También vale la pena considerar por qué makeconsidera que el lanzamiento está actualizado. Esto podría deberse a que tiene un touch releasecomando entre los comandos ejecutados; podría deberse a que existe un archivo o directorio llamado 'versión' que existe y no tiene dependencias, por lo que está actualizado. Luego está la razón real ...


14

Alguien más sugirió .PHONY, que es definitivamente correcto. .PHONY debe usarse para cualquier regla para la cual una comparación de fecha entre la entrada y la salida no sea válida. ¡Ya que no tienes ningún objetivo de la forma output: input, debes usar .PHONY para TODOS!

Dicho todo esto, probablemente debería definir algunas variables en la parte superior de su archivo MAKE para los diversos nombres de archivo, y definir reglas de creación reales que tengan secciones de entrada y salida para que pueda usar los beneficios de la creación, es decir, que solo compilará ¡Cosas que son necesarias para copilar!

Editar: ejemplo agregado. Sin probar, pero así es como lo haces .PHONY

.PHONY: clean    
clean:
    $(clean)

1
Bueno, si pudieras mostrarme un ejemplo, sería bueno. Cajero automático, solo estoy hackeando tratando de hacer que la presa funcione: P
Lodle

1
La ubicación del .PHONYobjetivo no importa. Puede estar en cualquier parte del Makefile.
Adrian W

5

Si recuerdo correctamente, 'make' usa marcas de tiempo (hora de modificación del archivo) para determinar si un objetivo está actualizado o no. Una forma común de forzar una reconstrucción es actualizar esa marca de tiempo, utilizando el comando 'táctil'. Puede intentar invocar 'toque' en su archivo MAKE para actualizar la marca de tiempo de uno de los objetivos (tal vez uno de esos sub-archivos MAKE), lo que podría obligar a Make a ejecutar ese comando.


5

Esta técnica simple permitirá que el archivo MAKE funcione normalmente cuando no se desee forzar. Crea un nuevo objetivo llamado fuerza al final de tu archivo MAKE . El objetivo forzado tocará un archivo del que depende su objetivo predeterminado. En el ejemplo a continuación, he agregado touch myprogram.cpp . También agregué una llamada recursiva para hacer . Esto hará que el objetivo predeterminado se haga cada vez que escriba forzar .

yourProgram: yourProgram.cpp
       g++ -o yourProgram yourProgram.cpp 

force:
       touch yourProgram.cpp
       make

Nunca debe usar makedentro de un Makefile. Usar en su $(MAKE)lugar.
Benjamin Crawford Ctrl-Alt-Tut

3

Intenté esto y funcionó para mí

agregue estas líneas a Makefile

clean:
    rm *.o output

new: clean
    $(MAKE)     #use variable $(MAKE) instead of make to get recursive make calls

guardar y ahora llamar

make new 

y recompilará todo de nuevo

¿Que pasó?

1) 'nuevas' llamadas limpias. 'clean' do 'rm' que elimina todos los archivos de objetos que tienen la extensión de '.o'.

2) 'nuevas' llamadas 'make'. 'make' ve que no hay archivos '.o', por lo que crea todos los '.o' nuevamente. entonces el enlazador enlaza todo el archivo .o en una salida ejecutable

Buena suerte


1
En la receta para un newmejor uso $(MAKE)quemake
Basile Starynkevitch

1

De acuerdo con el Recursivo de Miller que se considera perjudicial, ¡debes evitar llamar $(MAKE)! En el caso que muestres, es inofensivo, porque este no es realmente un archivo MAKE, solo un script de envoltura, que bien podría haber sido escrito en Shell. Pero usted dice que continúa así en niveles de recursión más profundos, por lo que probablemente haya encontrado los problemas que se muestran en ese ensayo revelador.

Por supuesto, con GNU es difícil evitarlo. Y a pesar de que son conscientes de este problema, es su forma documentada de hacer las cosas.

OTOH, makepp fue creado como una solución para este problema. Puede escribir sus archivos MAKE en un nivel por directorio, pero todos se unen en una vista completa de su proyecto.

Pero los makefiles heredados se escriben de forma recursiva. Entonces, hay una solución alternativa en la $(MAKE)que no hace nada más que canalizar las subrequests al proceso principal de makepp. Solo si hace cosas redundantes o, lo que es peor, contradictorias entre sus submakes, debe solicitar --traditional-recursive-make(lo que, por supuesto, rompe esta ventaja de makepp). No conozco tus otros archivos MAKE, pero si están escritos de manera limpia, con makepp, las reconstrucciones necesarias deberían ocurrir automáticamente, sin la necesidad de ningún truco sugerido aquí por otros.


No respondió la pregunta: tangente al punto principal y debería ser un comentario, no una respuesta.
flungo

Quizás no fui lo suficientemente claro. Con makepp no se necesita todo este archivo de envoltorio. Al conocer las dependencias exactas (todas ellas, no solo lo que aparece después de :), siempre se reconstruirá cuando sea necesario.
Daniel

1

Si no necesita conservar ninguna de las salidas que ya compiló con éxito

nmake /A 

reconstruye todo


0

De hecho, depende de cuál sea el objetivo. Si es un objetivo falso (es decir, el objetivo NO está relacionado con un archivo), debe declararlo como .PHONY.

Sin embargo, si el objetivo no es un objetivo falso pero solo desea reconstruirlo por alguna razón (un ejemplo es cuando usa la macro de preprocesamiento __TIME__), debe usar el esquema FORCE descrito en las respuestas aquí.



0

Ya se mencionó, pero pensé que podría agregar a usar touch

Si touchcompila todos los archivos de origen, el touchcomando cambia las marcas de tiempo de un archivo a la hora del sistematouch se ejecutó comando.

La marca de tiempo del archivo fuente es lo que se makeutiliza para "saber" que un archivo ha cambiado y necesita ser compilado nuevamente

Por ejemplo: si el proyecto era un proyecto de C ++, entonces hazlo touch *.cpp, luego makevuelve a ejecutarlo y make debería recompilar todo el proyecto.


0

Como señaló abernier, hay una solución recomendada en el manual de creación de GNU, que utiliza un objetivo 'falso' para forzar la reconstrucción de un objetivo:

clean: FORCE
        rm $(objects)
FORCE: ; 

Esto se ejecutará limpio, independientemente de cualquier otra dependencia.

Agregué el punto y coma a la solución del manual, de lo contrario se requiere una línea vacía.


-1

En mi sistema Linux (Centos 6.2), hay una diferencia significativa entre declarar el objetivo .PHONY y crear una dependencia falsa en FORCE, cuando la regla realmente crea un archivo que coincide con el objetivo. Cuando el archivo se debe volver a generar cada vez, se requiere tanto la FUERZA de dependencia falsa en el archivo como .PHONY para la dependencia falsa.

incorrecto:

date > $@

Derecha:

FORCE
    date > $@
FORCE:
    .PHONY: FORCE

Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.