Подтвердить что ты не робот

Makefile (Генерация автозависимости)

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

#basic makefile rule
target: dependencies
    recepie

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

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

#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

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

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

#it suppose to turn the [email protected] (file.o) into file.cpp
THE_CPP := $(addsuffix $(.cpp),$(basename [email protected]))

#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

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

Некоторые сайты, которые говорят, что GNU Make Manual устарели

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

4b9b3361

Ответ 1

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

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

Ответ 2

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

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 автоматически определял список зависимостей, лучший способ (который я знаю) Расширенное создание автозависимости. Это выглядит так:

%.o : %.cc
        @g++ -MD -c -o [email protected] $<
        @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, потому что вы изменили файл, который был зависимым. Попробуйте, это действительно работает!

Ответ 3

Отличные ответы, но в моей сборке я поместил файлы .obj в подкаталог на основе типа сборки (то есть: debug vs. release). Например, если я создаю debug, я помещаю все объектные файлы в папку build/debug. Это была задача ошеломления, чтобы попытаться заставить многострочную команду 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++ : " $< " => " [email protected]
    @$(CXX) -c $< $(CXXFLAGS) -o [email protected]

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

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

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

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

Ответ 4

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

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

Обычно для создания зависимостей makefile можно создавать файлы *.md:

%.o: %.c
       $(COMPILE.c) $(OUTPUT_OPTION) $< -MMD -MF $(patsubst %.c,%.md,[email protected])

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

-include $(wildcard *.md)

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

Ответ 5

Для записи, это то, как я автоматически создаю зависимости автоматически:

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

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

main: $(SRC:%.cpp=%.o)
    g++ $(CPPFLAGS) -o [email protected] $^

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

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

Ответ 6

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

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

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

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

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

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

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


Позже в книге он указывает на проблемы с этим методом и проблемы, которые, как я полагаю, также существуют в реализации расширенной генерации автозависимости.
Проблема 1: Это неэффективно. "make" должен перезапускаться каждый раз, когда он создает файл .d
Проблема 2: создает сообщения о предупреждении для всех отсутствующих файлов .d. Это в основном просто неприятно и может быть скрыто путем добавления "-" перед объявлением include. Проблема 3: Если вы удалите файл src, потому что он больше не нужен, "make" выйдет из строя при следующем попытке скомпилировать, потому что в некотором .d файле отсутствует src как зависимость, и потому что не является правилом для воссоздания этого src, make откажется идти дальше.

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

Ответ 7

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

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-out, addsuffix и т.д., Чтобы получить именно то, что мне нужно в каждый момент.

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