Problème Makefile: façon intelligente de scanner l'arborescence des répertoires.c dossiers

je suis en train de faire un projet qui se développe assez rapidement et garder les fichiers objet à jour n'est pas une option. Le problème au-delà de la commande wildcard se situe quelque part entre "Je ne veux pas de makefiles récursifs" et "je ne veux pas qu'il liste à la main". Les objets sont censés aller dans un répertoire distinct, qui fonctionne déjà. Note: je ne suis pas utilisé pour makefiles, je connais les bases, mais tout au-delà...

Donc ma question: comment scanner un dossier src de façon récurrente et intelligente?

Je l'ai déjà fait avec plusieurs variables SRC mais c'est laid et embrouille tout le makefile avec un nombre croissant de répertoires.

Ce que j'utilise actuellement est:

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"

grâce à la réponse #1, cela se résume à comment résoudre ceci:

$(BUILDDIR)/%.o: $(SOURCEDIR)/%.c
    $(CC) $(CFLAGS) $(LDFLAGS) $(SOURCETREE) -c $< -o $@

surtout dans le cas de BUILDDIR = build et SOURCEDIR devant être remplacé par le single .c fichiers provenant de SOURCES, y compris leurs chemins :/

44
demandé sur hamaney 2010-09-23 04:16:59

4 réponses

l'option la plus simple pour faire ce que vous voulez est probablement d'utiliser une escape shell et appeler find:

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

ceci vous donne une liste de fichiers sources avec des chemins. Notez que l'utilisation d'une assignation immédiat := au lieu d'une affectation récursive = qui est important ici: vous n' vous voulez être en cours d'exécution du shell échapper à chaque fois SOURCES est inspecté par la marque (ce qui se produit beaucoup plus que vous ne le pensez dans les Makefiles). Règle générale, je trouve utile est de toujours utilisez l'assignation immédiate à moins que j'ai réellement besoin d'une extension récursive (ce qui est rare; il semble que toutes vos assignations dans cet exemple pourraient être immédiates). Cela signifie donc que l'utilisation d'une assignation récursive est également un signal utile que la variable doit être utilisée avec soin.

revenons à votre problème. Ce que vous faites ensuite dépend si vous voulez un miroir de votre arborescence des sources dans votre arborescence de compilation, ou si le répertoire de compilation est juste supposé contenir une liste plate des fichiers objets pour tous vos fichiers source, ou si vous voulez un build dir séparé sous chaque source dir dans l'arborescence.

en supposant que vous voulez l'arbre de construction en miroir, vous pouvez faire quelque chose comme ce qui suit:

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

cela ne tient pas tout à fait compte de toute la complexité du travail, car cela ne garantit pas que les répertoires dans l'arbre de compilation existent réellement (ce qui serait modérément douloureux à faire en syntaxe Makefile).

j'ai supprimé les directives-I à partir de votre règle de construction $(binaire), en avez-vous vraiment besoin lorsque vous liez des objets? La raison pour laquelle je ne les ai pas laissés est que vous n'avez plus qu'un seul répertoire source, et c'est non-trivial d'obtenir la liste des répertoires source à partir de la liste des objets (comme tellement dans la syntaxe Makefile ce serait faisable mais vraiment ennuyeux).

60
répondu Ben 2010-09-23 01:07:31

les caractères génériques récursifs peuvent être faits purement dans Make, sans appeler le shell ou la commande find. Faire la recherche en utilisant seulement Make signifie que cette solution fonctionne sur Windows aussi bien, pas juste *nix.

# Make does not offer a recursive wildcard function, so here's one:
rwildcard=$(wildcard ) $(foreach d,$(wildcard *),$(call rwildcard,$d/,))

# 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)

la barre oblique dans le nom du dossier est requise. Ce rwildcard la fonction ne prend pas en charge plusieurs caractères spéciaux qui Rendent intégré générique fonction le fait, mais en ajoutant que le soutien serait simple avec un couple de plus utilise des foreach.

40
répondu LightStruk 2012-10-18 16:48:07

j'aime faire le suivant.

Créer des Variables pour Chaque Répertoire du Projet

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

CFLAGS = -g -Wall -O3

Obtenir Seulement la Structure Interne de SRCDIR de manière Récursive

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

Get All Files inside the STRUCTURE Variable

CODEFILES := $(addsuffix /*,$(STRUCTURE))
CODEFILES := $(wildcard $(CODEFILES))            

Filtrer Uniquement Les Fichiers Spécifiques

# 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))

Maintenant, il est Temps de créer les Règles

compile: $(OBJFILES)

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

avec cette approche, vous pouvez voir que j'utilise la variable de STRUCTURE seulement pour obtenir les fichiers dans le répertoire SRCDIR, mais il peut aussi être utilisé dans d'autres buts, comme miroir le SRCDIR dans OBJDIR une fois que la STRUCTURE stocke seulement les sous-répertoires internes. Il est tout à fait utile après des opérations propres comme:

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

NOTE: la règle de compilation ne fonctionne bien que si pour chaque *.c il y a le *correspondant.h file (avec le même nom de base, je veux dire).

14
répondu 2015-09-21 17:14:32

une autre bonne solution à ce problème semble être - implémenter un makefile non récursif tel que celui décrit ici. http://www.xs4all.nl/~evbergen/nonrecursive-make.html