Makefile с созданными исходными файлами C ++ - PullRequest
0 голосов
/ 04 декабря 2018

Я работаю с инструментом, который генерирует исходные файлы C ++, которые мне нужно включить в компиляцию моего проекта C ++.

Чтобы необычайно упростить вещи, мой единственный не сгенерированный исходный файл, main.cpp, этоследующим образом:

extern void bar();

int main(int, char**)
{
    bar();
    return 0;
}

А мой Makefile ниже.Общая структура - это то, что уже существует в нашем проекте, и я просто добавляю в отмеченные части, чтобы сгенерировать необходимый исходный файл bar.cpp и makefile bar.mk, который добавляет bar.cpp к переменной SRCS, поэтомуЯ хотел бы), включается в компиляцию:

SRCS := $(CURDIR)/main.cpp

#################### Adding everything between here... #####
BAR_MK := bar.mk

ifeq (,$(findstring clean,$(MAKECMDGOALS)))
include $(BAR_MK)
endif
#################### ...and here ###########################

OBJ_DIR := $(CURDIR)/obj
OBJS := $(patsubst %.cpp,%.o,$(subst $(CURDIR),$(OBJ_DIR),$(SRCS)))

a.out: $(OBJS)
        @echo OBJS == $(OBJS)
        @echo SRCS == $(SRCS)
        $(CXX) $(OBJS)

#################### Adding everything between here... #####
GEN_DIR := $(CURDIR)/gen

# "Generate" my source file(s), add them to SRCS via an external makefile
$(BAR_MK): $(GEN_DIR)/bar.cpp
$(GEN_DIR)/bar.cpp:
        mkdir -p $(dir $@)
        echo "void bar () {}" > $@
        echo SRCS += $@ > $(BAR_MK)
#################### ...and here ###########################

$(OBJ_DIR)/%.o: %.cpp
        mkdir -p $(dir $@)
        $(CXX) -c -o $@ $<

clean:
        rm -rf $(OBJ_DIR)
        rm -f a.out
        rm -rf $(GEN_DIR)
        rm -f $(BAR_MK)

Проблема в том, что компиляция завершается неудачно при первом запуске make из-за "неопределенной ссылки на bar()", но при немедленном запуске makeпосле успеха.Это связано с тем, что при первом запуске make, когда он оценивает правило a.out:, он видит SRCS только как содержащий main.cpp, и поэтому bar.cpp не компилируется.Затем на втором (и последующих) запусках SRCS содержит и main.cpp, и bar.cpp, поэтому bar.cpp компилируется в это время.

Это кажется мне странным, потому что согласно GNU Make manual о том, «Как перезаписываются make-файлы»:

После того, как все make-файлы были проверены, если они действительно были изменены, make запускается с чистого листа и снова читает все make-файлы..

Так что после того, как GNU Make обрабатывает include bar.mk при моем первом make вызове, видит, что он устарел, и строит его, я ожидаю, что он затем начнется с поцарапайте с разбором Makefile с самого начала, включая bar.mk и, в свою очередь, наличие SRCS, содержащее как main.cpp, так и bar.cpp.

Неужели мои ожидания неверны?Или что-то не так с моим (дополнением к) Makefile?Я использую GNU Make 4.1 в Linux.

1 Ответ

0 голосов
/ 04 декабря 2018

Правило, у которого bar.mk в качестве цели, не имеет рецепта.Так что make считает, что нет способа сгенерировать недостающий bar.mk.Но он не терпит неудачу немедленно, он продолжает анализировать основной Makefile, генерирует bar.cpp ... и bar.mk, но у него не было возможности узнать это заранее.В результате создается bar.mk, но не включается ... и make не завершается ошибкой.Не уверен, следует ли это рассматривать как ошибку или нет.Я заполнил отчет Возможно, ошибка * 1007. ** РЕДАКТИРОВАТЬ : добавить еще несколько комментариев / альтернативных решений

Обратите внимание, что правило $(BAR_MK) все еще не идеально: оно объявляет условие, которое не является реальным.Но перед его удалением мы должны исправить еще кое-что: ваше шаблонное правило не будет соответствовать сгенерированному исходному файлу из-за того, что вы используете $(CURDIR), который дополняет полный путь.Давайте избавимся от этого:

SRCS := main.cpp
BAR_MK := bar.mk

ifeq (,$(findstring clean,$(MAKECMDGOALS)))
include $(BAR_MK)
endif

OBJ_DIR := obj
OBJS := $(patsubst %.cpp,$(OBJ_DIR)/%.o,$(SRCS))

a.out: $(OBJS)
    @echo OBJS == $(OBJS)
    @echo SRCS == $(SRCS)
    $(CXX) $(OBJS)

GEN_DIR := gen

$(BAR_MK):
    echo 'SRCS += $(GEN_DIR)/bar.cpp' > $@

$(GEN_DIR)/bar.cpp:
    mkdir -p $(dir $@)
    echo "void bar () {}" > $@

$(OBJ_DIR)/%.o: %.cpp
    mkdir -p $(dir $@)
    $(CXX) -c -o $@ $<

clean:
    rm -rf $(OBJ_DIR) a.out $(GEN_DIR) $(BAR_MK)

Наконец, если вы действительно хотите сгенерировать bar.mk и gen/bar.cpp с одним и тем же правилом, есть одна возможность, все еще говоря make: patternправило с несколькими целями :

Шаблонные правила могут иметь более одной цели.В отличие от обычных правил, это не так много разных правил с одинаковыми предпосылками и рецептом.Если правило шаблона имеет несколько целей, make знает, что рецепт правила отвечает за создание всех целей.Рецепт выполняется только один раз, чтобы сделать все цели.При поиске правила шаблона, соответствующего цели, шаблоны цели, отличные от той, которая соответствует цели, нуждающейся в правиле, являются случайными: беспокоитесь только о том, чтобы дать рецепт и предварительные условия для рассматриваемого файла.Однако при запуске рецепта этого файла другие цели помечаются как обновленные сами.

В дальнейшем это a из bar, который будет соответствовать шаблону правила шаблона:

PATTERN_TARGET := $(subst bar,b%r,$(BAR_MK) $(GEN_DIR)/bar.cpp)
$(PATTERN_TARGET):
    mkdir -p $(GEN_DIR)
    echo "void bar () {}" > $(GEN_DIR)/bar.cpp
    echo 'SRCS += $(GEN_DIR)/bar.cpp' > bar.mk

Но это, вероятно, слишком странно и может полностью испортить ремонтопригодность.Предпочитайте один из двух других вариантов, они легче для понимания.

...