Проблема с Makefile: умный способ сканировать дерево каталогов для файлов .c - PullRequest
54 голосов
/ 23 сентября 2010

Я делаю проект, который растет довольно быстро, и поддерживать объектные файлы в актуальном состоянии - не вариант.Проблема за пределами команды подстановки находится где-то между: «Я не хочу рекурсивные 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 $@

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 $@

, особенно в случае, когда BUILDDIR = build и SOURCEDIR необходимо заменить одним .cфайлы из источников, включая их пути: /

Ответы [ 4 ]

75 голосов
/ 23 сентября 2010

Самый простой способ сделать то, что вы хотите, это, вероятно, просто использовать escape-команду и вызвать find:

SOURCES := $(shell find $(SOURCEDIR) -name '*.c')

Это дает вам список исходных файлов с путями. Обратите внимание, что здесь важно использовать немедленное присвоение :=, а не рекурсивное присвоение =: вы не хотите запускать экранирование оболочки каждый раз, когда SOURCES проверяется командой make (что происходит намного больше, чем вы думаете в Makefiles). Общее правило, которое я считаю полезным, заключается в том, чтобы всегда использовал немедленное назначение, если только мне не нужно рекурсивное расширение (что редко; похоже, что все ваши назначения в этом примере могут быть немедленными). Это означает, что использование рекурсивного присваивания также является полезным сигналом о том, что переменную необходимо использовать осторожно.

Вернемся к вашей проблеме. То, что вы будете делать дальше, зависит от того, хотите ли вы в своем дереве компоновки создать зеркало своего исходного кода, или же каталог сборки должен содержать плоский список объектных файлов для всех ваших исходных файлов, или же вы хотите отдельный каталог сборки. под каждым источником 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 $@

Это не совсем учитывает всю сложность работы, так как не гарантирует, что каталоги в дереве сборки действительно существуют (что было бы умеренно болезненно делать в синтаксисе Makefile).

Я удалил директивы -I из вашего правила сборки $ (BINARY); они вам действительно нужны при связывании объектов? Причина, по которой я их не оставил, заключается в том, что у вас больше нет только одного исходного каталога, и нетривиально получить список исходных каталогов из списка объектов (как в синтаксисе Makefile, это было бы выполнимо, но очень раздражает).

44 голосов
/ 18 октября 2012

Рекурсивные подстановочные знаки могут быть сделаны исключительно в Make без вызова shell или команды find. Выполнение поиска с использованием только Make означает, что это решение работает и в Windows, а не только * nix.

# Make does not offer a recursive wildcard function, so here's 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 не поддерживает множественные символы подстановки, как это делает встроенная функция Make wildcard , но добавление этой поддержки было бы простым, если использовать еще foreach .

14 голосов
/ 21 сентября 2015

Мне нравится делать следующее.

Создать переменные для каждого каталога проекта

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

CFLAGS = -g -Wall -O3

Рекурсивно получить только внутреннюю структуру SRCDIR

STRUCTURE := $(shell find $(SRCDIR) -type d)     

Получить все файлы внутри переменной 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))

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

Теперь пришло время создать правила

compile: $(OBJFILES)

$(OBJDIR)/%.o: $(addprefix $(SRCDIR)/,%.c %.h)
    $(CC) -c $< -o $@ $(CFLAGS) 

При таком подходе вы можете видеть, что я используюПеременная STRUCTURE используется только для получения файлов внутри каталога SRCDIR, но ее можно использовать и для других целей, например, для зеркалирования SRCDIR внутри OBJDIR, когда STRUCTURE сохраняет только внутренние подкаталоги.Это очень полезно после чистых операций, таких как:

clean:
    -rm -r $(OBJDIR)/*

ПРИМЕЧАНИЕ. Правило компиляции работает только в том случае, если для каждого * .c существует соответствующий файл * .h (я имею в виду то же самое базовое имя).

1 голос
/ 27 сентября 2010

Другое хорошее решение этой проблемы - реализовать нерекурсивный make-файл, такой как описанный здесь: http://sites.e -advies.nl / nonrecursive-make.html . Этот подход хорош тем, что кажется достаточно масштабируемым - разработчики могут добавлять информацию о зависимостях в каталог с исходными файлами, не беспокоясь об общем make-файле.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...