Как я могу заставить GNU Make не создавать параллельный рецепт? - PullRequest
12 голосов
/ 03 декабря 2010

Как я могу сказать gnu make не строить какой-то рецепт параллельно.Допустим, у меня есть следующий make-файл:

sources = a.xxx b.xxx c.xxx
target  = program

all : $(target)

$(target) : $(patsubst %.xxx,%.o,$(sources))
    $(CXX) -o $@ $<

%.o : %.cpp
    $(CXX) -c -o $@ $<

%.cpp : %.xxx
    my-pre-processor -o $@ $<

Однако команда my-pre-processor создает временные файлы с фиксированным именем (я не могу изменить это).Это работает нормально, если я просто использую make без параметра -j.Однако, если используется опция -j, сборка иногда завершается сбоем, потому что два одновременных вызова my-pre-processor перезаписывают их временные файлы.не должен создавать попытку распараллелить выполнение %.cpp : %.xxx рецептов.

Ответы [ 5 ]

15 голосов
/ 28 мая 2014

Solution 1

GNU Make содержит специальную встроенную псевдо-цель .NOTPARALLEL

Пример:

.PHONY: all clean

.NOTPARALLEL:

anotherTarget: dependency1

Solution 2

Вы также можете использовать флаг -j <n>, --jobs[=<n>] в командной строке, где n - количество получателей, разрешенных для параллельной работы.

Использование: make -j <n> или make --jobs=<n>

Пример: make -j 1 или make --jobs=1

примечание: пропуск <n> позволит выполнить произвольное количество рецептов, ограниченное только доступными вашей системойresources


Solution 3

Наконец, вы можете назначить флаг командной строки в решении 2 переменной MAKEFLAGS из вашего Makefile

Пример: MAKEFLAGS := -j 1 или MAKEFLAGS := --jobs=1

8 голосов
/ 09 сентября 2015

Соответствующим решением является указание точного порядка, в котором вы хотите, чтобы вещи строились (вместо того, чтобы говорить «не строить параллельно»).

Чтобы указать точный заказ, вы можете использовать предварительные условия только для заказа . Вот справочная страница GNU make:

Иногда, однако, у вас возникает ситуация, когда вы хотите навязать конкретный порядок на правилах, которые будут вызываться без принуждения цель обновляется, если выполняется одно из этих правил. В таком случае, Вы хотите определить предварительные условия только для заказа. Предварительные условия только для заказа можно указать, поместив символ трубы (|) в предварительные условия список: все предпосылки слева от символа канала являются нормальными; любой предпосылки справа только для заказа:

targets : normal-prerequisites | order-only-prerequisites

А вот пример, который они предлагают:

OBJDIR := objdir
OBJS := $(addprefix $(OBJDIR)/,foo.o bar.o baz.o)

$(OBJDIR)/%.o : %.c
        $(COMPILE.c) $(OUTPUT_OPTION) $<

all: $(OBJS)

$(OBJS): | $(OBJDIR)

$(OBJDIR):
        mkdir $(OBJDIR)

Мне пришлось использовать предварительные условия только для заказа по правилу make dist из-за состояния гонки. Рецепт dist зависел от правил distclean и diff, а правило diff выполняло svn diff -r XXX, чтобы показать точные изменения. В некоторых случаях make удаляет только что созданный diff, поскольку правило очистки запускается после правила diff.

6 голосов
/ 03 декабря 2010

Это ужасный клудж, но он сделает свою работу:

b.cpp: a.cpp

c.cpp: b.cpp

Или, если их на самом деле много, вы можете выпить несколько крепких напитков и сделать это:

c-sources = $(sources:.xxx=.cpp)

ALLBUTFIRST = $(filter-out $(firstword $(c-sources)), $(c-sources))
ALLBUTLAST = $(filter-out $(lastword $(c-sources)), $(c-sources))
PAIRS = $(join $(ALLBUTLAST),$(addprefix :,$(ALLBUTFIRST)))

$(foreach pair,$(PAIRS),$(eval $(pair)))

(Это работает в GNUMake, я не знаю о других версиях.)

6 голосов
/ 03 декабря 2010

Если временные файлы создаются в текущем рабочем каталоге, вы можете использовать подкаталоги (не очень, но редко):

sources = a.xxx b.xxx c.xxx
target  = program

all : $(target)

$(target) : $(patsubst %.xxx,%.o,$(sources))
    $(CXX) -o $@ $<

%.o : %.cpp
    $(CXX) -c -o $@ $<

%.cpp : %.xxx
    mkdir $@.d
    s=`realpath $<` && cd $@.d && my-pre-processor -o ../$@ "$${s}" || { $(RM) -r $@.d && false; }
    $(RM) -r $@.d

Кроме того, поскольку вы используете синтаксис, но не функции, которые доступны исключительно для GNU make, обратите внимание, что следующий эквивалентный Makefile должен быть более переносимым

sources = a.xxx b.xxx c.xxx
target  = program

all : $(target)

$(target) : $(sources:.xxx=.o)
    $(CXX) -o $@ $<

.cpp.o:
    $(CXX) -c -o $@ $<

.xxx.cpp:
    mkdir $@.d
    s=`realpath $<` && cd $@.d && my-pre-processor -o ../$@ "$${s}" || { $(RM) -r $@.d && false; }
    $(RM) -r $@.d

.PHONY: all
.SUFFIXES: .xxx .cpp .o

Также обратите внимание, что встроенное правило GNU make .cpp.o: позволяет пользователям указывать флаги в командной строке (аналогично)

.cpp.o:
    $(CXX) $(CPPFLAGS) $(CXXFLAGS) -c -o $@ $<

, что может понравиться вашим пользователям, когда им нужно предоставить, скажем, пользовательские каталоги включения через -L...

3 голосов
/ 04 декабря 2010

Хорошо бы изучить работу инструмента ylwrap, который поставляется с automake: он решает большинство тех же проблем для старых версий lex и yacc:

http://git.savannah.gnu.org/cgit/automake.git/tree/lib/ylwrap

...