Makefile (Генерация автозависимостей) - PullRequest
25 голосов
/ 06 ноября 2011

только для быстрой терминологии:

#basic makefile rule
target: dependencies
    recipe

Проблема: я хочу автоматически генерировать зависимости.

Например, я надеюсь включить это:

#one of my targets
file.o: file.cpp 1.h 2.h 3.h 4.h 5.h 6.h 7.h 8.h another.h lots.h evenMore.h
    $(COMPILE)

В это:

#one of my targets
file.o: $(GENERATE)
    $(COMPILE)

и я не слишком уверен, если это возможно ..

Что я знаю:

Я могу использовать этот флаг компилятора:

g++ -MM file.cpp

и он вернет правильную цель и зависимость.
поэтому из примера он вернет:

file.o: file.cpp 1.h 2.h 3.h 4.h 5.h 6.h 7.h 8.h another.h lots.h evenMore.h  

однако, make не позволяет мне явно писать шелл-код в целевом или зависимом разделе правила: (
Я знаю, что есть функция make, называемая shell

но я не могу подключить это как зависимость и выполнять магию синтаксического анализа, потому что он опирается на макрос $ @, который представляет цель ... или, по крайней мере, я думаю, что в этом проблема

Я даже пытался просто заменить зависимость "file.cpp" этой функцией makefile, и это тоже не сработает ..

#it's suppose to turn the $@ (file.o) into file.cpp
THE_CPP := $(addsuffix $(.cpp),$(basename $@))

#one of my targets
file.o: $(THE_CPP) 1.h 2.h 3.h 4.h 5.h 6.h 7.h 8.h another.h lots.h evenMore.h
    $(COMPILE)
#this does not work

Так что во всем гугле, кажется, есть два решения. оба из которых я не совсем понимаю.
Из GNU Make Manual

Некоторые сайты, на которых написано, что GNU Make Manual один устарел

Итак, мой последний вопрос: возможно ли сделать это так, как я хочу,
и если нет, может кто-нибудь сломать код с одного из этих сайтов и подробно объяснить мне, как они работают. Я буду реализовывать это одним из следующих способов, если придется, но мне надоело просто вставить кусок кода в мой make-файл, прежде чем понять это

Ответы [ 7 ]

25 голосов
/ 18 апреля 2012

Более новые версии GCC имеют опцию -MP, которую можно использовать с -MD. Я просто добавил -MP и -MD в переменную CPPFLAGS для моего проекта (я не писал собственного рецепта для компиляции C ++) и добавил строку «-include $ (SRC: .cpp = .d)».

Использование -MD и -MP дает файл зависимостей, который включает в себя как зависимости (без использования какого-то странного sed), так и фиктивные цели (чтобы удаление заголовочных файлов не вызывало ошибок).

19 голосов
/ 06 ноября 2011

Чтобы манипулировать именами файлов, когда вы уже знаете, какими должны быть зависимости, вы можете использовать шаблонное правило:

file.o: %.o : %.cpp 1.h 2.h 3.h 4.h 5.h 6.h 7.h 8.h another.h lots.h evenMore.h
    $(COMPILE)

И вы можете повторно использовать правило для других целей:

# Note these two rules without recipes:
file.o: 1.h 2.h 3.h 4.h 5.h 6.h 7.h 8.h another.h lots.h evenMore.h
anotherFile.o: 4.h 9.h yetAnother.h

file.o anotherFile.o: %.o : %.cpp
    $(COMPILE)

Но если вы хотите, чтобы Make автоматически вычислял список зависимостей, лучший способ (из известных мне) - Advanced Auto-Dependency Generation .Это выглядит так:

%.o : %.cc
        @g++ -MD -c -o $@ $<
        @cp $*.d $*.P; \
             sed -e 's/#.*//' -e 's/^[^:]*: *//' -e 's/ *\\$$//' \
                 -e '/^$$/ d' -e 's/$$/ :/' < $*.d >> $*.P; \
             rm -f $*.d

-include *.P

Обычно, когда он строит file.o, он также строит file.d.Затем он запускает file.d через изумительную команду sed, которая превращает список зависимостей в правило без рецептов.Последняя строка является инструкцией для include любых таких существующих правил.Логика здесь тонкая и гениальная: вам не нужны зависимости в первый раз, когда вы создаете foo.o, потому что Make уже знает, что foo.o должен быть собран, потому что он не существует.При следующем запуске Make он будет использовать список зависимостей, созданный в прошлый раз.Если вы измените один из файлов так, чтобы фактически появилась новая зависимость, которой нет в списке, Make все равно перестроит foo.o, потому что вы изменили файл, который был зависимостью .Попробуйте, это действительно работает!

7 голосов
/ 02 мая 2014

Отличные ответы, но в моей сборке я поместил файлы .obj в подкаталог, основанный на типе сборки (то есть: отладка или выпуск).Например, если я собираю debug, я помещаю все объектные файлы в папку build / debug.Попытка заставить указанную выше команду multiline sed использовать правильную папку назначения была сложной задачей, но после некоторых экспериментов я наткнулся на решение, которое отлично подходит для моей сборки.Надеюсь, это поможет кому-то еще.

Вот фрагмент:

# List my sources
CPP_SOURCES := foo.cpp bar.cpp

# If I'm debugging, change my output location
ifeq (1,$(DEBUG))
  OBJ_DIR:=./obj/debug
  CXXFLAGS+= -g -DDEBUG -O0 -std=c++0x
else
  CXXFLAGS+= -s -O2 
  OBJ_DIR:=./obj/release
endif

# destination path macro we'll use below
df = $(OBJ_DIR)/$(*F)

# create a list of auto dependencies
AUTODEPS:= $(patsubst %.cpp,$(OBJ_DIR)/%.d,$(CPP_SOURCES))

# include by auto dependencies
-include $(AUTODEPS)

.... other rules

# and last but not least my generic compiler rule
$(OBJ_DIR)/%.o: %.cpp 
    @# Build the dependency file
    @$(CXX) -MM -MP -MT $(df).o -MT $(df).d $(CXXFLAGS) $< > $(df).d
    @# Compile the object file
    @echo " C++ : " $< " => " $@
    @$(CXX) -c $< $(CXXFLAGS) -o $@

Теперь для деталей: первое выполнение CXX в моем общем правиле сборки является интересным.Обратите внимание, что я не использую никаких команд "sed".Более новые версии gcc делают все, что мне нужно (я использую gcc 4.7.2).

-MM создает главное правило зависимости, включая заголовки проекта, но не системные заголовки.Если я оставлю это так, мой файл .obj НЕ будет иметь правильный путь.Поэтому я использую опцию -MT, чтобы указать «реальный» путь к моему адресу .obj.(используя созданный мною макрос "df").
Я также использую вторую опцию -MT, чтобы убедиться, что результирующий файл зависимостей (т. е. файл .d) имеет правильный путь и включен в список целей.и поэтому имеет те же зависимости, что и исходный файл.

Последнее, но не менее важное - это включение опции -MP.Это заставляет gcc также создавать правила-заглушки для каждого заголовка, решая проблему, которая возникает, если я удаляю заголовок, в результате чего make вызывает ошибку.

Я подозреваю, что, поскольку я использую gcc для всей генерации зависимостей вместоСкорее всего, моя сборка быстрее (хотя мне еще предстоит доказать это, поскольку моя сборка на данный момент относительно мала).Если вы видите, как я могу улучшить это, я всегда открыт для предложений.Наслаждайтесь

5 голосов
/ 13 января 2014

Кстати, вот как я теперь автоматически генерирую зависимости:

CPPFLAGS = -std=c++1y -MD -MP 

SRC = $(wildcard *.cpp)
all: main

main: $(SRC:%.cpp=%.o)
    g++ $(CPPFLAGS) -o $@ $^

-include $(SRC:%.cpp=%.d)

Флажок компилятора -MD и -MP помогают добиться цели

4 голосов
/ 06 ноября 2011

Во-первых, вы можете иметь THE_CPP=$(patsubst %.o,%.cpp,$@)

Затем вы можете запустить make -p, чтобы понять встроенные правила make

Обычный способ сделать это - сгенерировать зависимости make-файла в *.md файлах:

%.o: %.c
       $(COMPILE.c) $(OUTPUT_OPTION) $< -MMD -MF $(patsubst %.c,%.md,$@)

и позже в вашем Makefile включая их с чем-то вроде

-include $(wildcard *.md)

Но вы также можете рассмотреть возможность использования других сборщиков, таких как omake и многих других

1 голос
/ 29 сентября 2015

Я предпочитаю использовать функцию $ (shell ...) с find. Вот образец одного из моих Makefiles:

SRCDIR = src
OBJDIR = obj
LIBDIR = lib
DOCDIR = doc

# Get Only the Internal Structure of Directories from SRCDIR
STRUCTURE := $(shell find $(SRCDIR) -type d)

#Filter-out hidden directories
STRUCTURE := $(filter-out $(shell find $(SRCDIR)/.* -type d),$(STRUCTURE))

# Get All Files From STRUCTURE
CODEFILES := $(addsuffix /*,$(STRUCTURE))
CODEFILES := $(wildcard $(CODEFILES))


## Filter Only Specific Files
SRCFILES := $(filter %.c,$(CODEFILES))
HDRFILES := $(filter %.h,$(CODEFILES))
OBJFILES := $(subst $(SRCDIR),$(OBJDIR),$(SRCFILES:%.c=%.o))
DOCFILES := $(addprefix $(DOCDIR)/,             \
            $(addsuffix .md,                    \
            $(basename $(SRCFILES))))


# Filter Out Function main for Libraries
LIBDEPS := $(filter-out $(OBJDIR)/main.o,$(OBJFILES))

При таком подходе я сначала получаю всю внутреннюю структуру каталогов с любой глубиной. Затем я получаю все файлы внутри структуры. В настоящее время я могу использовать filter, filter-out, adduffix и т. Д., Чтобы каждый раз получать именно то, что мне нужно.

В этом примере рассматриваются файлы * .c, но вы также можете изменить его на * .cpp.

1 голос
/ 09 ноября 2011

WOOO! Мне удалось получить код в посте Беты для работы над небольшим тестовым проектом.
Я должен отметить, для всех, кто может столкнуться с этим, Если вы используете оболочку bash (которой я и являлся), вам нужно будет добавить escape-символ перед знаком фунта, чтобы избежать превращения оставшейся части выражения в комментарий. (см. 4-ую строку кода)

%.o : %.cpp  
    g++ -c -MD -o $@ $<  
    cp $*.d $*.P; \  
    sed -e 's/\#.*//' -e 's/^[^:]*: *//' -e 's/ *\\$$//' \  
        -e '/^$$/ d' -e 's/$$/ :/' < $*.d >> $*.P; \  
    rm -f $*.d  
-include *.P  

Теперь я хочу поделиться информацией, найденной в Управление проектами с помощью GNU Make, 3-е издание. , потому что она указывает на некоторые важные проблемы по этому вопросу и предоставляет код, который я до сих пор не до конца понимаю еще.
В книге появляется метод, который похож на метод, описанный на странице Make manual .
Это выглядит так:

include $(subst .c,.d,$(SOURCES))

%.d: %.c
    $(CC) -M $(CPPFLAGS) $< > $@.$$$$; \
    sed 's,\($*\).o[ :]*,\1.o $@ : ,g' < $@.$$$$ > $@; \
    rm -f $@.$$$$

Это то, что я считаю, происходит.
Сразу же, 'make' хочет включить файл ".d" для каждого исходного файла.
Поскольку изначально файлов .d не существует, кусок кода запускается снова и снова для создания всех отсутствующих файлов .d.
Это означает, что make будет запускаться снова и снова, пока каждый файл .d не будет создан и включен в make-файл.
Каждый файл ".d" - это то, что говорит Бета: цель с набором зависимостей и НЕТ рецепта.

Если заголовочный файл когда-либо изменяется, для тех правил, которые включены в него, сначала потребуется обновить зависимости. Это то, что меня немного сбивает с толку, как получается, что кусок кода может быть вызван снова? Он используется для обновления файлов .d, поэтому, если файл .h изменяется, как он вызывается? Помимо этого, я понимаю, что для компиляции объекта используется правило по умолчанию. Любые разъяснения / неправильные представления к этому объяснению приветствуются.


Далее в книге указываются проблемы с этим методом, а также проблемы, которые, как я считаю, также существуют в реализации Advanced Auto-Dependency Generation.
Задача 1: Это неэффективно. make должен перезапускаться каждый раз, когда создается файл .d
Проблема 2: make генерирует предупреждающие сообщения для всех отсутствующих файлов .d - это в основном просто неприятность и может быть скрыто добавлением «-» перед оператором включения.
Проблема 3: Если вы удалите файл src, потому что он больше не нужен, 'make' потерпит крах при следующей попытке компиляции, потому что в некотором файле .d отсутствует src в качестве зависимости, а также потому, что существует нет правила воссоздать этот src, make не пойдет дальше.

Говорят, что исправлением этих проблем является метод Тромея, но код выглядит сильно отличающимся от кода на сайте. Возможно, это просто потому, что они использовали некоторые макросы, сделали это вызовом функции и написали его немного по-другому. Я все еще изучаю это, но хотел поделиться некоторыми открытиями, которые я сделал до сих пор. Надеюсь, что это откроет немного больше дискуссий и приблизит меня к сути всего этого.

...