es.davy.ai

Preguntas y respuestas de programación confiables

¿Tienes una pregunta?

Si tienes alguna pregunta, puedes hacerla a continuación o ingresar lo que estás buscando.

Manejo de todas las combinaciones de símbolos en el archivo makefile.

Supongamos que he diseñado una biblioteca en C++ y quiero probar exhaustivamente todas las características.
Algunas de estas características están definidas en tiempo de compilación, a través de símbolos que están definidos o no.

// library.h
A foo( const B& b )
{
#ifdef OPTION_X
   ... hazlo de esa manera
#else
   ... hazlo de otra manera
#endif
}

Construyo un programa de prueba que quiero compilar y ejecutar para todas las configuraciones posibles, para asegurarme de que todas las pruebas pasen:

// mytest.cpp
#include "library.h"
int main()
{
    ... some test code
#ifdef OPTION_X
    ... hazlo de esa manera
#else
   ... hazlo de otra manera
#endif
    ... más cosas con más opciones
}

Si tengo 1 opción (llamémosla “A”), quiero ejecutar las pruebas si está “activada” (_AY para la opción “A”: Sí) o “desactivada” (_AN para la opción “A”: No)

Mi archivo makefile contiene esto:

.PHONY: test

test: BUILD/mytest_AY BUILD/mytest_AN
    BUILD/mytest_AY
    BUILD/mytest_AN

BUILD/mytest_AY: CXXFLAGS+=-DOPTION_A

BUILD/mytest_AY: mytest.cpp
    $(CXX) $(CXXFLAGS) -o $@ $< $(LDFLAGS)

BUILD/mytest_AN: mytest.cpp
    $(CXX) $(CXXFLAGS) -o $@ $< $(LDFLAGS)

Esto está bien.

Pero ahora, si tengo 2 opciones para probar (digamos “A” y “B”), se entiende el punto:
Tendré 4 objetivos para compilar y ejecutar:

test: BUILD/mytest_AYBY BUILD/mytest_ANBY BUILD/mytest_AYBN BUILD/mytest_ANBN
    BUILD/mytest_AYBY
    BUILD/mytest_ANBY
    BUILD/mytest_AYBN
    BUILD/mytest_ANBN

BUILD/mytest_AYBN: CXXFLAGS+=-DOPTION_A
BUILD/mytest_AYBY: CXXFLAGS+="-DOPTION_A -DOPTION_B"
BUILD/mytest_ANBY: CXXFLAGS+=-DOPTION_B

BUILD/mytest_AYBY: mytest.cpp
    $(CXX) $(CFLAGS) -o $@ $< $(LDFLAGS)

BUILD/mytest_ANBY: mytest.cpp
    $(CXX) $(CFLAGS) -o $@ $< $(LDFLAGS)

BUILD/mytest_AYBN: mytest.cpp
    $(CXX) $(CFLAGS) -o $@ $< $(LDFLAGS)

BUILD/mytest_ANBN: mytest.cpp
    $(CXX) $(CFLAGS) -o $@ $< $(LDFLAGS)

Tengo dos preguntas:

  • ¿Existe alguna manera de tener una única regla/receta en lugar de las cuatro? Son todas iguales, excepto por el nombre del objetivo.

  • Este enfoque, si bien es válido para 1 o 2 opciones en tiempo de compilación, no se escala bien.
    Con 3 opciones ya sería bastante engorroso, y con más se convierte en una pesadilla.
    ¿Cómo manejarías esa situación?

Tags:  , ,

Answer

  1. Avatar for davy.ai
    1. Solución para una única regla/receta
      Para tener una única regla/receta para todos los objetivos, el archivo Make puede utilizar una variable para mantener la lista de todas las configuraciones posibles y utilizar una regla de patrón para construir cada objetivo. Aquí tienes un ejemplo:
    .PHONY: test
    
    OPTIONS := A B
    
    test: $(patsubst %, BUILD/mytest_%_Y, $(OPTIONS)) $(patsubst %, BUILD/mytest_%_N, $(OPTIONS))
        $(foreach opt, $(OPTIONS), BUILD/mytest_$(opt)_Y &&) true
        $(foreach opt, $(OPTIONS), BUILD/mytest_$(opt)_N &&) true
    
    BUILD/mytest_%_Y: CXXFLAGS+=-DOPTION_$*
    BUILD/mytest_%_N: CXXFLAGS+=-DOPTION_$*_N
    
    BUILD/mytest_%_Y BUILD/mytest_%_N: mytest.cpp
        $(CXX) $(CXXFLAGS) -o $@ $< $(LDFLAGS)
    

    En esta solución, el archivo Make utiliza un bucle foreach para construir y ejecutar todos los programas de prueba configurados. La variable OPTIONS contiene una lista de configuraciones posibles, y la función patsubst se expande a la lista de todos los objetivos posibles. La regla de patrón BUILD/mytest_%_Y y BUILD/mytest_%_N define la receta para cada objetivo. El símbolo % en la regla es un comodín que coincide con cualquier cadena.

    1. Solución para un enfoque escalable
      Para un enfoque escalable, el archivo Make puede utilizar un archivo de configuración que contenga todas las posibles combinaciones de opciones de tiempo de compilación. Aquí tienes un ejemplo:
    .PHONY: test
    
    OPTIONS := A B C
    
    define CONFIGS
    AY
    AN
    BY
    BN
    CY
    CN
    ABY
    ABN
    ACN
    BCN
    ABYCN
    endef
    
    test: $(patsubst %, BUILD/mytest_%, $(strip $(CONFIGS)))
        $(foreach cfg, $(CONFIGS), BUILD/mytest_$(cfg) &&) true
    
    BUILD/mytest_%: mytest.cpp
        $(eval optstr = $(subst Y, -DOPTION_, $(subst N, -DOPTION_$(patsubst %N,%,$(notdir $@))_N, $(notdir $@))))
        $(CXX) $(CFLAGS) $(optstr) -o $@ $< $(LDFLAGS)
    

    En esta solución, el archivo Make utiliza una declaración define para crear una variable CONFIGS que contiene todas las posibles combinaciones de opciones de tiempo de compilación. La variable se limpia con la función strip para eliminar espacios adicionales. La función patsubst se expande a una lista de todos los objetivos posibles. La regla de patrón BUILD/mytest_% define la receta para cada objetivo. La función eval evalúa la cadena y asigna el resultado a la variable optstr, que contiene la lista de opciones para pasar al compilador. La función subst reemplaza la marca Y/N con la opción correspondiente, y la función notdir elimina la parte del directorio del nombre del objetivo.

Comments are closed.