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