tutorial make create makefile gnu-make

create - Makefile paralelo requiere orden de dependencia



makefile linux c (5)

Tengo la siguiente pieza de makefile:

CXXFLAGS = -std=c++0x -Wall SRCS = test1.cpp test2.cpp OBJDIR = object OBJS = $(SRCS:%.cpp=$(OBJDIR)/%.o) all: test1 release: clean test1 test1: $(OBJS) $(CXX) -o $@ $(OBJS) $(OBJDIR)/%.o: %.cpp $(CXX) $(CXXFLAGS) -MD -c -o $@ $< -include $(SRCS:.cpp=.d) clean: rm -rf $(OBJDIR)/* .PHONY: all clean release

Ahora, si trato de invocar "make -j4 release", el objetivo limpio a menudo se ejecuta en medio de la creación de archivos, lo que hace que la compilación falle. Mi pregunta es cómo garantizar que el objetivo limpio se haya completado antes de comenzar la versión de lanzamiento.


En el caso de la versión, debe asegurarse de que la clean complete antes de compilar. Por lo tanto (solo) lo agrega como una dependencia a la regla de compilación (y no al objetivo falso). Varias formas de hacer esto, como variables específicas del objetivo, o:

$(OBJDIR)/%.o: %.cpp $(if $(filter release,${MAKECMDGOALS}),clean) ...


Mi preferencia es para

release: $(MAKE) clean $(MAKE) test1

Esto obliga a los dos objetivos a realizarse consecutivamente sin alterar su paralelismo interno (si lo hubiera).


No estoy exactamente seguro de en qué versiones se admite esta función, pero puede usar la función de order-only :

my_target: dep1 dep2 | must_run_1st must_run_2nd

Todas las dependencias a la izquierda de la | Los caracteres se procesan de forma normal. Dependencias a la derecha de | se ejecutan ''solo para orden''

Esta característica se describe en:

https://www.gnu.org/software/make/manual/html_node/Prerequisite-Types.html

En su caso, la siguiente definición de reglas sería suficiente:

release: | clean test1 test1: | clean


Para una solución sin una invocación recursiva de make, puede intentar esto.

ifneq ($(filter release,$(MAKECMDGOALS)),) test1: clean endif


Puede dividir la ejecución en fases no paralelas (para release ) y en paralelo (para los destinos restantes).

ifneq ($(filter release,$(MAKECMDGOALS)),) .NOTPARALLEL: endif release: clean $(MAKE) test1

.NOTPARALLEL target suprimirá la ejecución paralela si el destino de release se menciona en la línea de comando. El objetivo de release se volverá a ejecutar Make después de limpiar y construir test1 en paralelo.

UPD.

Una solución más inteligente también volvería a invocar Make para cada objetivo individual en caso de que haya más de un destino en la línea de comando, de modo que una presencia de destino de release no obligue al resto a ejecutarse de forma no paralela.

ifneq ($(words $(MAKECMDGOALS)),1) .NOTPARALLEL: $(sort all $(MAKECMDGOALS)): @$(MAKE) -f $(firstword $(MAKEFILE_LIST)) $@ else # ... endif

Actualización por James Johnston

La solución inteligente anterior no funciona en las versiones de GNU que no son compatibles con los servidores de trabajo. Por ejemplo, las compilaciones MinGW / nativas publicadas de GNU make anteriores a la versión 4.0 no son compatibles con los servidores de trabajo. (Las compilaciones Cygwin / MSYS de GNU make do.) El siguiente código usa la variable .FEATURES introducida en la make 3.81 para detectar si los servidores de trabajo son compatibles. El síntoma de no usar esta solución alternativa cuando es necesario es que su compilación "paralela" se serializará.

# Check if job server supported: ifeq ($(filter jobserver, $(.FEATURES)),) # Job server not supported: sub-makes will only start one job unless # you specify a higher number here. Here we use a MS Windows environment # variable specifying number of processors. JOBSARG := -j $(NUMBER_OF_PROCESSORS) else # Job server is supported; let GNU Make work as normal. JOBSARG := endif # .FEATURES only works in GNU Make 3.81+. # If GNU make is older, assume job server support. ifneq ($(firstword $(sort 3.81 $(MAKE_VERSION))),3.81) # If you are using GNU Make < 3.81 that does not support job servers, you # might want to specify -jN parameter here instead. JOBSARG := endif ifneq ($(words $(MAKECMDGOALS)),1) .NOTPARALLEL: # The "all" target is required in the list, # in case user invokes make with no targets. $(sort all $(MAKECMDGOALS)): @$(MAKE) $(JOBSARG) -f $(firstword $(MAKEFILE_LIST)) $@ else # Put remainder of your makefile here. endif