Проблема с Makefile: умный способ сканирования дерева каталогов для файлов .c
Я делаю проект, который растет довольно быстро, и хранение объектных файлов до даты не является вариантом. Проблема, лежащая в основе подстановочной команды, лежит где-то между "Я не хочу рекурсивных make файлов" и "Я не хочу, чтобы это список вручную". Предполагается, что объекты перейдут в отдельный каталог, который уже работает.
Примечание: Я не привык к make файлам, я знаю основы, но все, что нужно...
Итак, мой вопрос:
Как сканировать папку src рекурсивно и делать это умным образом?
Я уже делал это с несколькими переменными SRC, но это уродливо и загромождает весь make файл с увеличением числа каталогов.
В настоящее время я использую:
OS = Linux
VERSION = 0.0.1
CC = /usr/bin/gcc
CFLAGS = -Wall -g -D_REENTRANT -DVERSION=\"$(VERSION)\"
LDFLAGS = -lm `pkg-config --cflags gtk+-2.0` `pkg-config --libs gtk+-2.0`
BUILDDIR = build
SOURCEDIR = src
HEADERDIR = src
SOURCES = $(wildcard $(SOURCEDIR)/*.c)
OBJECTS = $(patsubst $(SOURCEDIR)/%.c, $(BUILDDIR)/%.o, $(SOURCES))
NAME = cinnamon
BINARY = cinnamon.bin
ECHO = echo
RM = rm -rf
MKDIR = mkdir
INSTALL = install
.PHONY: all clean setup
all: $(BINARY)
$(BINARY): $(BUILDDIR)/$(OBJECTS)
$(CC) $(CFLAGS) $(LDFLAGS) -I$(HEADERDIR) -I$(SOURCEDIR) $(OBJECTS) -o $(BINARY)
$(BUILDDIR)/%.o: $(SOURCEDIR)/%.c
$(CC) $(CFLAGS) $(LDFLAGS) -I$(HEADERDIR) -I$(SOURCEDIR) -c $< -o [email protected]
setup:
$(MKDIR) -p $(BUILDDIR)
install:
$(INSTALL) -m 755 -o 0 -g 0 -d $(DESTDIR)/usr/local/bin/
$(INSTALL) -m 755 -o 0 -g 0 $(BINARY) $(DESTDIR)/usr/local/bin/$(BINARY)
$(INSTALL) -m 755 -o 0 -g 0 -d $(DESTDIR)/usr/local/$(NAME)/ui/
$(INSTALL) -m 644 -o 0 -g 0 ./ui/*.ui $(DESTDIR)/usr/local/$(NAME)/ui/
# $(INSTALL) -m 755 -o 0 -g 0 -d $(DESTDIR)/usr/local/$(NAME)/model/
# $(INSTALL) -m 644 -o 0 -g 0 ./model/*.model $(DESTDIR)/usr/local/$(NAME)/model/
clean:
$(RM) $(BINARY) $(OBJECTS)
distclean: clean
help:
@$(ECHO) "Targets:"
@$(ECHO) "all - buildcompile what is necessary"
@$(ECHO) "clean - cleanup old .o and .bin"
@$(ECHO) "install - not yet fully supported"
Благодаря ответу # 1 это сводится к тому, как это решить:
$(BUILDDIR)/%.o: $(SOURCEDIR)/%.c
$(CC) $(CFLAGS) $(LDFLAGS) $(SOURCETREE) -c $< -o [email protected]
особенно в случае BUILDDIR = build и SOURCEDIR, которые должны быть заменены одиночными .c файлами из SOURCES, включая их пути:/
Ответы
Ответ 1
Самый простой способ сделать то, что вам нужно, - это, вероятно, просто использовать escape-код оболочки и вызвать find
:
SOURCES := $(shell find $(SOURCEDIR) -name '*.c')
Это дает вам список исходных файлов с путями. Обратите внимание, что использование немедленного назначения :=
вместо рекурсивного присваивания =
важно здесь: вы не хотите, чтобы выполнялся запуск оболочки каждый раз, когда SOURCES
проверяется make (что происходит намного больше, чем вы думаете в Make файлах). Общее правило, которое я считаю полезным, - всегда использовать непосредственное назначение, если мне действительно не нужно рекурсивное расширение (что редко, похоже, что все ваши назначения в этом примере могут быть немедленными). Это означает, что использование рекурсивного присваивания также является полезным сигналом, что переменная должна быть тщательно использована.
Вернитесь к своей проблеме. То, что вы делаете дальше, зависит от того, хотите ли вы создать зеркало вашего исходного дерева в дереве сборки, или же, как предполагается, должен содержать только строгий список объектных файлов для всех ваших исходных файлов или вам нужен отдельный сборщик под каждым источником dir в дереве.
Предполагая, что вы хотите зеркальное дерево построения, вы можете сделать что-то вроде следующего:
# Get list of object files, with paths
OBJECTS := $(addprefix $(BUILDDIR)/,$(SOURCES:%.c=%.o))
$(BINARY): $(OBJECTS)
$(CC) $(CFLAGS) $(LDFLAGS) $(OBJECTS) -o $(BINARY)
$(BUILDDIR)/%.o: %.c
$(CC) $(CFLAGS) $(LDFLAGS) -I$(HEADERDIR) -I$(dir $<) -c $< -o [email protected]
Это не совсем учитывает всю сложность задания, так как это не гарантирует, что каталоги в дереве сборки фактически существуют (что было бы очень болезненно для синтаксиса Makefile).
Я удалил -I директивы из вашего правила построения $(BINARY); вам действительно нужны они при связывании объектов? Причина, по которой я не оставил их, заключается в том, что у вас больше нет одного источника-источника, и нетривиально получить список исходных dirs из списка объектов (например, в синтаксисе Makefile это было бы выполнимо, но действительно раздражает).
Ответ 2
Рекурсивные подстановочные знаки могут выполняться исключительно в Make, без вызова оболочки или команды find. Выполнение поиска с использованием только Make означает, что это решение работает и на Windows, а не только на * nix.
# Make does not offer a recursive wildcard function, so here one:
rwildcard=$(wildcard $1$2) $(foreach d,$(wildcard $1*),$(call rwildcard,$d/,$2))
# How to recursively find all files with the same name in a given folder
ALL_INDEX_HTMLS := $(call rwildcard,foo/,index.html)
# How to recursively find all files that match a pattern
ALL_HTMLS := $(call rwildcard,foo/,*.html)
Требуется конечная косая черта в имени папки. Эта функция rwildcard не поддерживает множественные подстановочные знаки так, как делает функцию встроенного подстановочного знака, но добавив, что поддержка будет простой с еще несколькими использованием foreach.
Ответ 3
Мне нравится делать следующее.
Создание переменных для каждой директории проекта
SRCDIR = src
OBJDIR = obj
LIBDIR = lib
DOCDIR = doc
HDRDIR = include
CFLAGS = -g -Wall -O3
Получить только внутреннюю структуру SRCDIR рекурсивно
STRUCTURE := $(shell find $(SRCDIR) -type d)
Получить все файлы внутри переменной СТРУКТУРА
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))
# Filter Out Function main for Libraries
LIBDEPS := $(filter-out $(OBJDIR)/main.o,$(OBJFILES))
Теперь пришло время создать Правила
compile: $(OBJFILES)
$(OBJDIR)/%.o: $(addprefix $(SRCDIR)/,%.c %.h)
$(CC) -c $< -o [email protected] $(CFLAGS)
При таком подходе вы можете видеть, что я использую переменную STRUCTURE только для получения файлов внутри каталога SRCDIR, но ее можно использовать и для других целей, например, зеркально отобразить SRCDIR внутри OBJDIR, когда STRUCTURE хранит только внутренние подкаталоги. Это полезно после чистых операций, таких как:
clean:
-rm -r $(OBJDIR)/*
ПРИМЕЧАНИЕ. Правило компиляции работает только хорошо, если для каждого *.c есть соответствующий *.h файл (с тем же базовым именем, я имею в виду).
Ответ 4
Еще одно хорошее решение этой проблемы - реализовать нерекурсивный make файл, такой как описанный здесь. http://www.xs4all.nl/~evbergen/nonrecursive-make.html. Этот подход хорош, потому что он кажется достаточно масштабируемым - разработчики могут добавлять информацию о зависимостях в каталог с исходными файлами, не беспокоясь об общем make файле.