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

Проблема с 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, включая их пути:/

4b9b3361

Ответ 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 файле.