Una directiva condicional permite que una parte del makefile se ejecute o se ignore según los valores de las variables. Un condicional puede comparar el valor de una variable con el de otra, o el valor de una variable con una cadena fija. Los condicionales controlan lo que make «ve» realmente en el makefile. Por eso, los condicionales no pueden usarse para controlar el comportamiento en el momento en que se ejecuta la receta (recipe).
El siguiente ejemplo de condicional indica a make que use un conjunto de bibliotecas si la variable CC es «gcc», y un conjunto de bibliotecas distinto en caso contrario. Lo consigue controlando cuál de las dos líneas de receta se usará en la regla. El resultado es que pasar «CC=gcc» como argumento a make cambia no solo el compilador que se emplea, sino también las bibliotecas que se enlazan.
libs_for_gcc = -lgnu
normal_libs =
foo: $(objects)
ifeq ($(CC),gcc)
$(CC) -o foo $(objects) $(libs_for_gcc)
else
$(CC) -o foo $(objects) $(normal_libs)
endif
Este condicional emplea tres directivas: ifeq, else y endif.
La directiva ifeq inicia el condicional y especifica la condición. Toma dos argumentos, separados por una coma y rodeados de paréntesis. Se realiza la sustitución de variables en ambos argumentos y luego se comparan. Las líneas del makefile que siguen a ifeq se ejecutan si los dos argumentos coinciden; en caso contrario, se ignoran.
La directiva else hace que las líneas siguientes se ejecuten si la condición anterior no se cumplió. En el ejemplo anterior, esto significa que la segunda orden de enlazado alternativa se usa siempre que no se use la primera. Incluir un else en un condicional es opcional.
La directiva endif termina el condicional. Todo condicional debe terminar con un endif. Después puede seguir el texto incondicional del makefile.
Como ilustra este ejemplo, los condicionales funcionan a nivel textual: las líneas del condicional se tratan como parte del makefile, o se ignoran, según la condición. Por eso, las unidades sintácticas más grandes del makefile, como las reglas, pueden cruzar el comienzo o el final del condicional.
Cuando la variable CC tiene el valor «gcc», el ejemplo anterior produce este efecto:
foo: $(objects)
$(CC) -o foo $(objects) $(libs_for_gcc)
Cuando la variable CC tiene cualquier otro valor, el efecto es este:
foo: $(objects)
$(CC) -o foo $(objects) $(normal_libs)
Se pueden obtener resultados equivalentes de otra manera: haciendo condicional la asignación de una variable y usando luego esa variable de forma incondicional:
libs_for_gcc = -lgnu
normal_libs =
ifeq ($(CC),gcc)
libs=$(libs_for_gcc)
else
libs=$(normal_libs)
endif
foo: $(objects)
$(CC) -o foo $(objects) $(libs)
La sintaxis de un condicional simple, sin else, es la siguiente:
conditional-directive text-if-true endif
text-if-true puede ser cualquier conjunto de líneas de texto, que se considerarán parte del makefile si la condición es verdadera. Si la condición es falsa, no se usa ningún texto en su lugar.
La sintaxis de un condicional complejo es la siguiente:
conditional-directive text-if-true else text-if-false endif
o bien:
conditional-directive-one text-if-one-is-true else conditional-directive-two text-if-two-is-true else text-if-one-and-two-are-false endif
Puede haber tantas cláusulas «else conditional-directive» como sean necesarias. Una vez que una condición dada es verdadera, se usa su text-if-true y no se usa ninguna otra cláusula; si ninguna condición es verdadera, se usa text-if-false. text-if-true y text-if-false pueden constar de cualquier número de líneas de texto.
La sintaxis de conditional-directive es la misma tanto si el condicional es simple como complejo, y tanto si va después de un else como si no. Existen cuatro directivas distintas que comprueban condiciones distintas. He aquí una tabla con ellas:
ifeq (arg1, arg2)ifeq 'arg1' 'arg2'ifeq "arg1" "arg2"ifeq "arg1" 'arg2'ifeq 'arg1' "arg2"Expande todas las referencias a variables en arg1 y arg2 y las compara. Si son idénticas, text-if-true queda en vigor; en caso contrario, queda en vigor text-if-false, si lo hay.
A menudo se desea comprobar si una variable tiene un valor no vacío. Cuando el valor procede de expansiones complejas de variables y funciones, expansiones que usted consideraría vacías pueden contener en realidad caracteres de espacio en blanco y, por tanto, no se ven como vacías. Sin embargo, se puede usar la función strip (consulte Funciones para la sustitución y el análisis de cadenas) para evitar interpretar el espacio en blanco como un valor no vacío. Por ejemplo:
ifeq ($(strip $(foo)),) text-if-empty endif
evaluará text-if-empty aunque la expansión de $(foo) contenga caracteres de espacio en blanco.
ifneq (arg1, arg2)ifneq 'arg1' 'arg2'ifneq "arg1" "arg2"ifneq "arg1" 'arg2'ifneq 'arg1' "arg2"Expande todas las referencias a variables en arg1 y arg2 y las compara. Si son distintas, text-if-true queda en vigor; en caso contrario, queda en vigor text-if-false, si lo hay.
ifdef variable-name
La forma ifdef toma como argumento el nombre de una variable, no una referencia a una variable. Si el valor de esa variable es un valor no vacío, text-if-true queda en vigor; en caso contrario, queda en vigor text-if-false, si lo hay. Las variables que nunca se han definido tienen un valor vacío. El texto variable-name se expande, de modo que podría ser una variable o función que se expanda al nombre de una variable. Por ejemplo:
bar = true foo = bar ifdef $(foo) frobozz = yes endif
La referencia a variable $(foo) se expande y produce bar, que se considera el nombre de una variable. La variable bar no se expande, pero su valor se examina para determinar si es no vacío.
Tenga en cuenta que ifdef solo comprueba si una variable tiene un valor. No expande la variable para ver si ese valor es no vacío. En consecuencia, las comprobaciones que usan ifdef devuelven verdadero para todas las definiciones salvo las del tipo foo =. Para comprobar si el valor es vacío, use ifeq ($(foo),). Por ejemplo, si escribe:
bar = foo = $(bar) ifdef foo frobozz = yes else frobozz = no endif
a «frobozz» se le asigna «yes». En cambio, si escribe:
foo = ifdef foo frobozz = yes else frobozz = no endif
a «frobozz» se le asigna «no».
ifndef variable-name
Si la variable variable-name tiene un valor vacío, text-if-true queda en vigor; en caso contrario, queda en vigor text-if-false, si lo hay. Las reglas de expansión y comprobación de variable-name son idénticas a las de la directiva ifdef.
Al comienzo de la línea de la directiva condicional se permiten espacios adicionales, que se ignoran, pero no se permite una tabulación. (Si la línea empieza con una tabulación, se considerará parte de la receta de una regla.) Salvo por esto, pueden insertarse espacios o tabulaciones adicionales sin efecto alguno en cualquier sitio, excepto dentro del nombre de la directiva o dentro de un argumento. Al final de la línea puede aparecer un comentario que comience con «#».
Las otras dos directivas que intervienen en un condicional son else y endif. Cada una de ellas se escribe como una sola palabra, sin argumentos. Al comienzo de la línea se permiten espacios adicionales, que se ignoran, y se permiten espacios o tabulaciones al final. Al final de la línea puede aparecer un comentario que comience con «#».
Los condicionales afectan a qué líneas del makefile usa make. Si la condición es verdadera, make lee las líneas de text-if-true como parte del makefile; si la condición es falsa, make ignora por completo esas líneas. De ello se sigue que las unidades sintácticas del makefile, como las reglas, pueden dividirse con seguridad a lo largo del comienzo o el final del condicional.
make evalúa los condicionales cuando lee un makefile. En consecuencia, no se pueden usar variables automáticas en las comprobaciones de los condicionales, porque no están definidas hasta que se ejecutan las recetas (consulte Variables automáticas).
Para evitar una confusión intolerable, no está permitido iniciar un condicional en un makefile y terminarlo en otro. No obstante, puede escribir una directiva include dentro de un condicional, siempre que no intente terminar ese condicional dentro del archivo incluido.
Puede escribir un condicional que compruebe indicadores de orden de make, como «-t», usando la variable MAKEFLAGS junto con la función findstring (consulte Funciones para la sustitución y el análisis de cadenas). Esto es útil cuando touch no basta para hacer que un archivo parezca actualizado.
Recuerde que MAKEFLAGS agrupa todas las opciones de una sola letra (como «-t») en la primera palabra, y que esa palabra estará vacía si no se dio ninguna opción de una sola letra. Para manejar esto, conviene añadir un valor al principio para asegurarse de que haya una palabra: por ejemplo, «-$(MAKEFLAGS)».
La función findstring determina si una cadena aparece como subcadena de otra. Si quiere comprobar el indicador «-t», use «t» como primera cadena y la primera palabra de MAKEFLAGS como la otra.
Por ejemplo, así se puede disponer el uso de «ranlib -t» para terminar de marcar un archivo de archivo (archive) como actualizado:
archive.a: …
ifneq (,$(findstring t,$(firstword -$(MAKEFLAGS))))
+touch archive.a
+ranlib -t archive.a
else
ranlib archive.a
endif
El prefijo «+» marca esas líneas de receta como «recursivas», de modo que se ejecuten a pesar del uso del indicador «-t». Consulte Uso recursivo de make.