web-dev-qa-db-fra.com

Comment placer des fichiers objet dans un sous-répertoire séparé

Je ne parviens pas à utiliser make pour placer les fichiers objet dans un sous-répertoire séparé, probablement une technique très basique. J'ai essayé d'utiliser les informations de cette page: http://www.gnu.org/software/hello/manual/make/Prerequisite-Types.html#Prerequisite-Types

J'obtiens la sortie suivante de make:

make: *** No rule to make target `ku.h', needed by `obj/kumain.o'.  Stop.

Cependant, ku.h est une dépendance et non une cible (bien que ce soit évidemment # inclus dans les fichiers source c). Lorsque je n'essaie pas d'utiliser un sous-répertoire pour les fichiers objet (c'est-à-dire que je manque les parties OBJDIR), cela fonctionne correctement. Pourquoi faire penser que ku.h est une cible?

mon makefile est le suivant: (le style correspond à la lecture de diverses sources d’informations)

.SUFFIXES:
.SUFFIXES: .c .o

CC=gcc 
CPPFLAGS=-Wall
LDLIBS=-lhpdf
VPATH=%.c src
VPATH=%.h src
VPATH=%.o obj
OBJDIR=obj

objects= $(addprefix $(OBJDIR)/, kumain.o kudlx.o kusolvesk.o kugetpuz.o kuutils.o \
  kurand.o kuASCboard.o kuPDFs.o kupuzstrings.o kugensud.o \
  kushapes.o )

ku : $(objects)
  $(CC) $(CPPFLAGS) -o ku $(objects) $(LDLIBS)

$(objects) : ku.h kudefines.h kuglobals.h kufns.h | $(OBJDIR)

$(OBJDIR):
  mkdir $(OBJDIR)

.PHONY: clean
clean :
  rm $(objects)

Edit: j'ai appliqué le changement pour utiliser la directive vpath. Ma version était un mauvais mélange de VPATH = xxx et de vpath% .c xxx. Cependant, j'ai maintenant un autre problème (qui était le problème d'origine avant d'ajouter le mauvais vpath). C'est maintenant la sortie:

    gcc  -o ku -lhpdf obj/kumain.o obj/kudlx.o obj/kusolvesk.o ..etc
    gcc: obj/kumain.o: No such file or directory
    gcc: obj/kudlx.o: No such file or directory
    gcc: obj/kusolvesk.o: No such file or directory
    gcc: obj/kugetpuz.o: No such file or directory
    gcc: obj/kuutils.o: No such file or directory
    gcc: obj/kurand.o: No such file or directory
    gcc: obj/kuASCboard.o: No such file or directory
    gcc: obj/kuPDFs.o: No such file or directory
    gcc: obj/kupuzstrings.o: No such file or directory
    gcc: obj/kugensud.o: No such file or directory
    gcc: obj/kushapes.o: No such file or directory
    make: *** [ku] Error 1

Il semble que make n'applique pas la règle implicite pour un fichier objet, bien que le manuel indique "Les règles implicites expliquent à make comment utiliser les techniques habituelles de sorte que vous n'ayez pas à les spécifier en détail lorsque vous souhaitez les utiliser. Par exemple, il est une règle implicite pour la compilation C. Les noms de fichiers déterminent les règles implicites à exécuter. Par exemple, la compilation C prend généralement un fichier .c et crée un fichier .o. Donc, make applique la règle implicite pour la compilation C lorsqu'il voit cette combinaison de fin de nom de fichier. " et aussi "La recherche dans les répertoires spécifiés dans VPATH ou avec vpath a également lieu lors de la prise en compte de règles implicites (voir Utilisation de règles implicites)."

De nouveau ici "Par exemple, lorsqu'un fichier foo.o n'a pas de règle explicite, make considère les règles implicites, telles que la règle intégrée pour compiler foo.c si ce fichier existe. Si un tel fichier manque dans le répertoire en cours, les répertoires appropriés sont recherchés. Si foo.c existe (ou est mentionné dans le makefile) dans l’un des répertoires, la règle implicite pour la compilation en C est appliquée. "

Toute aide visant à obtenir des règles implicites pour mon fichier Make serait grandement appréciée.

Edit no 2: Grâce à Jack Kelly, j'ai créé une règle explicite pour compiler les fichiers .c, car je ne pouvais me déplacer nulle part en essayant d'utiliser des règles implicites. Merci également à al_miro pour l'info vpath.

Voici le fichier de travail:

.SUFFIXES:
.SUFFIXES: .c .o

CC=gcc 
CPPFLAGS=-Wall
LDLIBS=-lhpdf
OBJDIR=obj
vpath %.c src
vpath %.h src

objects = $(addprefix $(OBJDIR)/, kumain.o kudlx.o kusolvesk.o kugetpuz.o kuutils.o \
  kurand.o kuASCboard.o kuPDFs.o kupuzstrings.o kugensud.o \
  kushapes.o )

ku : $(objects)
  $(CC) $(CPPFLAGS) -o ku $(objects) $(LDLIBS)

$(OBJDIR) obj/%.o : %.c ku.h kudefines.h kuglobals.h kufns.h 
  $(CC) -c $(CPPFLAGS) $< -o $@

.PHONY : clean
clean :
  rm $(objects)
54
RussellG

Puisque vous utilisez GNUmake, utilisez une règle de modèle pour compiler les fichiers objet:

$(OBJDIR)/%.o: %.c
    $(CC) $(CFLAGS) $(CPPFLAGS) -c -o $@ $<
62
Jack Kelly

C'est le makefile que j'utilise pour la plupart de mes projets,

Il permet de placer les fichiers source, les en-têtes et les fichiers en ligne dans des sous-dossiers, et des sous-dossiers de sous-dossiers, etc., et génère automatiquement un fichier de dépendance pour chaque objet Cela signifie que la modification des en-têtes et des fichiers en ligne déclenchera la recompilation des fichiers dépendants.

Les fichiers source sont détectés via la commande de recherche du shell, il n’est donc pas nécessaire de spécifier explicitement, il suffit de continuer à coder pour le contenu de votre cœur.

Il copiera également tous les fichiers d'un dossier 'ressources' dans le dossier bin lors de la compilation du projet, ce que je trouve pratique la plupart du temps.

Pour fournir le crédit le cas échéant, la fonctionnalité de dépendance automatique était basée en grande partie sur la page de Scott McPeak qui peut être trouvée ICI , avec quelques modifications/ajustements supplémentaires pour mes besoins.

Exemple de Makefile

#Compiler and Linker
CC          := g++-mp-4.7

#The Target Binary Program
TARGET      := program

#The Directories, Source, Includes, Objects, Binary and Resources
SRCDIR      := src
INCDIR      := inc
BUILDDIR    := obj
TARGETDIR   := bin
RESDIR      := res
SRCEXT      := cpp
DEPEXT      := d
OBJEXT      := o

#Flags, Libraries and Includes
CFLAGS      := -fopenmp -Wall -O3 -g
LIB         := -fopenmp -lm -larmadillo
INC         := -I$(INCDIR) -I/usr/local/include
INCDEP      := -I$(INCDIR)

#---------------------------------------------------------------------------------
#DO NOT EDIT BELOW THIS LINE
#---------------------------------------------------------------------------------
SOURCES     := $(Shell find $(SRCDIR) -type f -name *.$(SRCEXT))
OBJECTS     := $(patsubst $(SRCDIR)/%,$(BUILDDIR)/%,$(SOURCES:.$(SRCEXT)=.$(OBJEXT)))

#Defauilt Make
all: resources $(TARGET)

#Remake
remake: cleaner all

#Copy Resources from Resources Directory to Target Directory
resources: directories
    @cp $(RESDIR)/* $(TARGETDIR)/

#Make the Directories
directories:
    @mkdir -p $(TARGETDIR)
    @mkdir -p $(BUILDDIR)

#Clean only Objecst
clean:
    @$(RM) -rf $(BUILDDIR)

#Full Clean, Objects and Binaries
cleaner: clean
    @$(RM) -rf $(TARGETDIR)

#Pull in dependency info for *existing* .o files
-include $(OBJECTS:.$(OBJEXT)=.$(DEPEXT))

#Link
$(TARGET): $(OBJECTS)
    $(CC) -o $(TARGETDIR)/$(TARGET) $^ $(LIB)

#Compile
$(BUILDDIR)/%.$(OBJEXT): $(SRCDIR)/%.$(SRCEXT)
    @mkdir -p $(dir $@)
    $(CC) $(CFLAGS) $(INC) -c -o $@ $<
    @$(CC) $(CFLAGS) $(INCDEP) -MM $(SRCDIR)/$*.$(SRCEXT) > $(BUILDDIR)/$*.$(DEPEXT)
    @cp -f $(BUILDDIR)/$*.$(DEPEXT) $(BUILDDIR)/$*.$(DEPEXT).tmp
    @sed -e 's|.*:|$(BUILDDIR)/$*.$(OBJEXT):|' < $(BUILDDIR)/$*.$(DEPEXT).tmp > $(BUILDDIR)/$*.$(DEPEXT)
    @sed -e 's/.*://' -e 's/\\$$//' < $(BUILDDIR)/$*.$(DEPEXT).tmp | fmt -1 | sed -e 's/^ *//' -e 's/$$/:/' >> $(BUILDDIR)/$*.$(DEPEXT)
    @rm -f $(BUILDDIR)/$*.$(DEPEXT).tmp

#Non-File Targets
.PHONY: all remake clean cleaner resources
47
Nicholas Hamilton

Les lignes VPATH sont fausses, elles devraient être

vpath %.c  src
vpath %.h  src

c'est-à-dire pas capital et sans le =. Dans l'état actuel des choses, il ne trouve pas le fichier .h et pense que c'est un objectif à atteindre.

23
al_miro

En général, vous devez soit spécifier $(OBJDIR) à gauche de toutes les règles qui placent des fichiers dans $(OBJDIR), ou vous pouvez exécuter make à partir de $(OBJDIR). VPATH est pour les sources, pas pour les objets.

Jetez un coup d'œil à ces deux liens pour plus d'explications et une solution de contournement "intelligente".

5
Theo Belaire

La solution suivante n'est pas agréable à mon avis, car j'aime beaucoup les règles intégrées. Cependant, GNU) make ne supporte pas quelque chose comme vpath pour les répertoires de sortie. Et les règles intégrées ne peuvent pas correspondre, comme le % Dans %.o Correspondrait à obj/foo Sur obj/foo.o, Laissant make avec une recherche dans vpath %.c src/ Pour des choses comme src/obj/foo.c, Mais pas src/foo.c.

Mais c’est aussi proche des règles intégrées que vous pouvez l’obtenir et, à ma connaissance, de la meilleure solution disponible.

$(OBJDIR)/%.o: %.c
        $(COMPILE.c) $(OUTPUT_OPTION) $<

Explication: $(COMPILE.c) $(OUTPUT_OPTION) $< est en fait comment .c.o Est implémenté, voir http://git.savannah.gnu.org/cgit/make.git/tree/default.c (et c'est même mentionné dans le manuel)

De plus, si $(OBJDIR) ne contiendrait que des fichiers auto-générés, vous pouvez le créer à la volée avec une condition préalable de commande uniquement, ce qui simplifiera légèrement la règle de nettoyage:

$(OBJDIR):
        mkdir -p $(OBJDIR)

$(OBJDIR)/%.o: %.c | $(OBJDIR)
        $(COMPILE.c) $(OUTPUT_OPTION) $<

.PHONY: clean
clean:
        $(RM) -r $(OBJDIR)

Cela nécessite que la fonctionnalité uniquement sur commande soit disponible, ce que vous pouvez vérifier à l'aide de $(filter order-only, $(.FETAURES)). J'ai vérifié sur Kubuntu 14.04 GNU make 3.81 et OpenSUSE 13.1 GNU make 3.82. Les deux ont été construits avec order- seulement activé et je ne comprends plus pourquoi Kubuntu 14.04 est livré avec une version plus ancienne de GNU make que OpenSUSE 13.1. Quoi qu'il en soit, allez télécharger make 4.1 maintenant :)

2
Christian Hujer

Pour tous ceux qui travaillent avec des règles implicites (et GNU MAKE). Voici un fichier Make simple qui supporte différents répertoires:

#Start of the makefile

VPATH = ./src:./header:./objects

OUTPUT_OPTION = -o objects/$@

CXXFLAGS += -Wall -g -I./header

Target = $(notdir $(CURDIR)).exe

Objects := $(notdir $(patsubst %.cpp,%.o,$(wildcard src/*.cpp)))



all: $(Target)

$(Target): $(Objects)
     $(CXX) $(CXXFLAGS) -o $(Target) $(addprefix objects/,$(Objects))


#Beware of -f. It skips any confirmation/errors (e.g. file does not exist)

.PHONY: clean
clean:
     rm -f $(addprefix objects/,$(Objects)) $(Target)

Regardons de plus près (je ferai référence au répertoire actuel avec curdir):

Cette ligne est utilisée pour obtenir une liste des fichiers .o utilisés qui se trouvent dans curdir/src.

Objects := $(notdir $(patsubst %.cpp,%.o,$(wildcard src/*.cpp)))
#expands to "foo.o myfoo.o otherfoo.o"

Via variable, la sortie est définie dans un répertoire différent (curdir/objects).

OUTPUT_OPTION = -o objects/$@
#OUTPUT_OPTION will insert the -o flag into the implicit rules

Pour vous assurer que le compilateur trouve les objets dans le nouveau dossier d'objets, le chemin est ajouté au nom du fichier.

$(Target): $(Objects)
     $(CXX) $(CXXFLAGS) -o $(Target) $(addprefix objects/,$(Objects))
#                                    ^^^^^^^^^^^^^^^^^^^^    

Ceci est conçu à titre d'exemple et il y a certainement place à amélioration.

Pour plus d’informations, veuillez consulter: Make documentation. Voir chapitre 10.2

Ou: Oracle: Guide des utilitaires de programmation

1
A. Frank