web-dev-qa-db-fra.com

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

Je fais un projet qui se développe assez rapidement et la mise à jour des fichiers objets n'est pas une option. Le problème au-delà de la commande générique 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 séparé, qui fonctionne déjà. Remarque: je ne suis pas que habitué aux makefiles, je connais les bases, mais tout au-delà ...

Donc ma question: Comment analyser un dossier src récursivement et le faire de manière intelligente?

Je l'ai déjà fait avec plusieurs variables SRC mais c'est moche et encombre 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"

Merci à la réponse n ° 1, cela se résume à la façon de résoudre ce problème:

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

en particulier dans le cas de BUILDDIR = build et SOURCEDIR devant être remplacé par les fichiers .c uniques de SOURCES y compris leurs chemins: /

54
drahnr

L'option la plus simple pour faire ce que vous voulez est probablement d'utiliser simplement un échappement Shell et d'appeler find:

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

Cela vous donne une liste de fichiers source avec des chemins. Notez que l'utilisation de l'affectation immédiate := plutôt que l'affectation récursive = est important ici: vous voulez pas vouloir exécuter l'échappement Shell à chaque fois que SOURCES est inspecté par make (ce qui se produit beaucoup plus que vous ne le pensez dans Makefiles). Une règle générale que je trouve utile est de toujours utiliser l'affectation immédiate sauf si j'ai réellement besoin d'une expansion récursive (ce qui est rare; cela ressemble à toutes vos affectations dans ce exemple pourrait être immédiat). Cela signifie alors que l'utilisation d'une affectation récursive est également un signal utile que la variable doit être utilisée avec précaution.

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

En supposant que vous vouliez l'arborescence de génération en miroir, vous pouvez faire quelque chose comme ceci:

# 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 prend pas tout à fait en compte la complexité complète du travail, car cela ne garantit pas que les répertoires dans l'arborescence de construction existent réellement (ce qui serait modérément difficile à faire dans la syntaxe Makefile).

J'ai supprimé les directives -I de votre règle de construction $ (BINARY); en avez-vous vraiment besoin pour lier 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 il n'est pas trivial d'obtenir la liste des répertoires source à partir de la liste des objets (comme dans la syntaxe Makefile, ce serait faisable mais vraiment énervant).

77
Ben

Les caractères génériques récursifs peuvent être effectués uniquement dans Make, sans appeler le shell ou la commande find. Faire la recherche en utilisant uniquement Make signifie que cette solution fonctionne également sur Windows, pas seulement * 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)

La barre oblique de fin dans le nom du dossier est requise. Cette fonction rwildcard ne prend pas en charge plusieurs caractères génériques comme le fait la fonction intégrée wildcard de Make, mais l'ajout de cette prise en charge serait simple avec quelques utilisations supplémentaires de foreach.

48
LightStruk

J'aime faire ce qui suit.

Créer des variables dans chaque répertoire du projet

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

CFLAGS = -g -Wall -O3

Obtenir uniquement la structure interne du SRCDIR de manière récursive

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

Récupérer tous les fichiers dans la variable STRUCTURE

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

Filtrer uniquement des 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))

Il est maintenant 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 STRUCTURE uniquement pour obtenir les fichiers dans le répertoire SRCDIR, mais elle peut également être utilisée à d'autres fins, comme mettre en miroir le SRCDIR dans OBJDIR une fois que STRUCTURE stocke uniquement le sous-répertoire interne. répertoires. Il est très utile après des opérations de nettoyage comme:

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

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

14
user4713908

Une autre bonne solution à ce problème semble être - implémentez un makefile non récursif tel que celui décrit ici: http://sites.e-advies.nl/nonrecursive-make.html . Cette approche est agréable car elle semble assez évolutive - les développeurs peuvent ajouter des informations de dépendance dans le répertoire avec les fichiers source sans avoir à se soucier du makefile global.

2
Alex Reece