web-dev-qa-db-fra.com

Option gcc / g ++ pour placer tous les fichiers objets dans un répertoire séparé

Je me demande pourquoi gcc/g ++ n'a pas d'option pour placer les fichiers objets générés dans un répertoire spécifié.

Par exemple:

mkdir builddir
mkdir builddir/objdir
cd srcdir

gcc -c file1.c file2.c file3.c **--outdir=**../builddir/objdir

Je sais qu'il est possible d'y parvenir avec des options -o distinctes données au compilateur, par exemple:

gcc -c file1.c -o ../builddir/objdir/file1.o
gcc -c file2.c -o ../builddir/objdir/file2.o
gcc -c file3.c -o ../builddir/objdir/file3.o

... et je sais que je peux écrire des Makefiles via les directives VPATH et vpath pour simplifier cela.

Mais cela représente beaucoup de travail dans un environnement de construction complexe.

Je pourrais aussi utiliser

gcc -c file1.c file2.c file3.c

Mais quand j'utilise cette approche, mon srcdir est plein de déchets .o après.

Je pense donc qu'une option avec la sémantique de --outdir serait très utile.

Quel est ton opinion?

EDIT : nos Makefiles sont écrits de telle manière que les fichiers .o soient réellement placés dans builddir/obj. Mais je me demande simplement s'il pourrait y avoir une meilleure approche.

EDIT : Il existe plusieurs approches qui imposent la tâche d'obtenir le comportement souhaité pour le système de construction (aka Make, CMake etc.). Mais je les considère tous comme étant des solutions de contournement pour une faiblesse de gcc (et d'autres compilateurs aussi).

59
anon

C'est le makefile coupé pour un de mes projets, qui compile les sources dans 'src' et place les fichiers .o dans le répertoire "obj". Le bit clé est l'utilisation de la fonction patsubst () - voir le manuel GNU make (qui est en fait une assez bonne lecture) pour plus de détails:

OUT = lib/alib.a
CC = g++
ODIR = obj
SDIR = src
INC = -Iinc

_OBJS = a_chsrc.o a_csv.o a_enc.o a_env.o a_except.o \
        a_date.o a_range.o a_opsys.o
OBJS = $(patsubst %,$(ODIR)/%,$(_OBJS))


$(ODIR)/%.o: $(SDIR)/%.cpp 
    $(CC) -c $(INC) -o $@ $< $(CFLAGS) 

$(OUT): $(OBJS) 
    ar rvs $(OUT) $^

.PHONY: clean

clean:
    rm -f $(ODIR)/*.o $(OUT)
68
anon

Que diriez-vous de passer au répertoire et d'exécuter la compilation à partir de là:

cd builddir/objdir
gcc ../../srcdir/file1.c ../../srcdir/file2.c ../../srcdir/file3.c

C'est ça. gcc interprétera les inclusions de la forme #include "path/to/header.h" comme commençant dans le répertoire, le fichier existe donc vous n'avez rien à modifier.

18
R Samuel Klatchko

Une solution de contournement triviale mais efficace consiste à ajouter ce qui suit juste après l'appel gcc dans votre Makefile:

mv *.o ../builddir/objdir

ou même un soft-clean (éventuellement récursif) après la compilation, comme

rm -f *.o

ou

find . -name \*.o -exec rm {} \;
15
Davide

Vous pouvez utiliser un simple wrapper autour de gcc qui générera le nécessaire -o options et appelez gcc:

$ ./gcc-wrap -c file1.c file2.c file3.c --outdir=obj 
gcc -o obj/file1.o -c file1.c
gcc -o obj/file2.o -c file2.c
gcc -o obj/file3.o -c file3.c

Voici un tel gcc_wrap script dans sa forme la plus simple:

#!/usr/bin/Perl -w

use File::Spec;
use File::Basename;
use Getopt::Long;
Getopt::Long::Configure(pass_through);

my $GCC = "gcc";
my $outdir = ".";
GetOptions("outdir=s" => \$outdir)
    or die("Options error");

my @c_files;
while(-f $ARGV[-1]){
    Push @c_files, pop @ARGV;
}
die("No input files") if(scalar @c_files == 0);

foreach my $c_file (reverse @c_files){
    my($filename, $c_path, $suffix) = fileparse($c_file, ".c");
    my $o_file = File::Spec->catfile($outdir, "$filename.o");
    my $cmd = "$GCC -o $o_file @ARGV $c_file";
    print STDERR "$cmd\n";
    system($cmd) == 0 or die("Could not execute $cmd: $!");
}

Bien sûr, la méthode standard consiste à résoudre le problème avec Makefiles, ou plus simplement, avec CMake ou bakefile, mais vous avez spécifiquement demandé une solution qui ajoute la fonctionnalité à gcc, et je pense que la seule façon est d'écrire un tel wrapper. Bien sûr, vous pouvez également patcher les sources gcc pour inclure la nouvelle option, mais cela peut être difficile.

11
Frank

Je crois que vous avez fait reculer le concept ...?!

L'idée derrière Makefiles est qu'ils ne traitent que les fichiers qui ont été mis à jour depuis la dernière génération, pour réduire les temps de (re) compilation. Si vous regroupez plusieurs fichiers en une seule exécution du compilateur, vous échouez à cette fin.

Votre exemple:

gcc -c file1.c file2.c file3.c **--outdir=**../builddir/objdir

Vous n'avez pas donné la règle "make" qui va avec cette ligne de commande; mais si l'un des trois fichiers a été mis à jour, vous devez exécuter cette ligne et recompiler les trois fichiers, qui peuvent ne pas être nécessaires du tout. Il empêche également 'make' de générer un processus de compilation séparé pour chaque fichier source, comme il le ferait pour la compilation séparée (lors de l'utilisation de l'option '-j', comme je le suggère fortement).

J'ai écrit un tutoriel Makefile ailleurs, qui donne des détails supplémentaires (tels que la détection automatique de vos fichiers source au lieu de les coder en dur dans le Makefile, la détermination automatique des dépendances d'inclusion et les tests en ligne) .

Tout ce que vous auriez à faire pour obtenir votre répertoire d'objets séparé serait d'ajouter les informations de répertoire appropriées au OBJFILES := line et le %.o: %.c Makefile règle de ce tutoriel. La réponse de Neil Butterworth a un bel exemple de la façon d'ajouter les informations d'annuaire.

(Si vous souhaitez utiliser DEPFILES ou TESTFILES comme décrit dans le didacticiel, vous devez adapter le DEPFILES := et TSTFILES := lignes plus %.t: %.c Makefile pdclib.a règle aussi.)

5
DevSolar

Pendant ce temps, j'ai trouvé une solution "à mi-chemin" en utilisant l'option -combine.

Exemple:

mkdir builddir
mkdir builddir/objdir
cd srcdir

gcc -combine -c file1.c file2.c file3.c -o ../builddir/objdir/all-in-one.o

cela "combine" tous les fichiers source en un seul fichier objet.

Cependant, c'est encore "à mi-chemin" car il doit tout recompiler quand un seul fichier source change.

2
anon

Je pense que dire à gcc de passer n'a pas d'option distincte pour dire où mettre le fichier objet, car il l'a déjà. C'est "-c" - il indique dans quel répertoire placer l'objet.

Le fait d'avoir un indicateur supplémentaire pour le répertoire uniquement doit changer le sens de "-c". Par exemple:

gcc -c file.c -o /a/b/c/file.o --put-object-in-dir-non-existing-option /a1/a2/a3

Vous ne pouvez pas mettre /a/b/c/file.o sous/a1/a2/a3, car les deux chemins sont absolus. Par conséquent, "-c" doit être modifié pour ne nommer que le fichier objet.

Je vous conseille d'envisager un remplacement de makefile, comme cmake , scons et autres. Cela permettra d'implémenter un système de construction aussi bien pour un projet simple que pour un plus grand aussi.

Voyez par exemple comment il est facile de compiler en utilisant cmake votre exemple. Créez simplement le fichier CMakeList.txt dans srcdir /:

cmake_minimum_required(VERSION 2.6)
project(test) 

add_library(test file1.c file2c file3.c) 

Et maintenant tapez:

mkdir -p builddir/objdir
cd builddir/objdir
cmake ../../srcdir
make

C'est tout, les fichiers objets résideront quelque part sous builddir/objdir.

J'utilise personnellement cmake et le trouve très pratique. Il génère automatiquement des dépendances et a d'autres goodies.

2
dimba

J'essaie de comprendre la même chose. Pour moi, cela a fonctionné

CC = g++
CFLAGS = -g -Wall -Iinclude
CV4LIBS = `pkg-config --libs opencv4`
CV4FLAGS = `pkg-config --cflags opencv4`

default: track

track:  main.o
    $(CC) -o track $(CV4LIBS) ./obj/main.o

ALLFLAGS = $(CFLAGS) $(CV4FLAGS)
main.o: ./src/main.cpp ./include/main.hpp
    $(CC) $(ALLFLAGS) -c ./src/main.cpp $(CV4LIBS) -o ./obj/main.o
``
1
Venkatesh Jatla

C'est parmi les problèmes autoconf résout.

Si vous avez déjà fait ./configure && make vous savez ce qu'est autoconf: c'est l'outil qui génère ces scripts de configuration Nice. Ce que tout le monde ne sait pas, c'est que vous pouvez à la place mkdir mybuild && cd mybuild && ../configure && make et cela fonctionnera comme par magie, car autoconf est génial de cette façon.

Le script configure génère des Makefiles dans le répertoire de construction . Ensuite, tout le processus de construction s'y déroule. Donc, tous les fichiers de construction y apparaissent naturellement, pas dans l'arborescence source.

Si vous avez des fichiers source faisant #include "../banana/peel.h" et vous ne pouvez pas les changer, alors c'est difficile de faire fonctionner correctement (vous devez copier ou lier tous les fichiers d'en-tête dans le répertoire de construction). Si vous pouvez modifier les fichiers source pour dire #include "libfood/comedy/banana/peel.h" à la place, alors vous êtes prêt.

autoconf n'est pas exactement facile , en particulier pour un grand projet existant. Mais il a ses avantages.

1
Jason Orendorff

Personnellement, pour les fichiers uniques, je fais cela,

rm -rf temps; mkdir temps; cd temps/ ; gcc -Wall -v --save-temps  ../thisfile.c ; cd ../ ; geany thisfile.c temps/thisfile.s temps/thisfile.i

le dossier temps conservera tous les fichiers objet, prétraités et d'assemblage.

C'est une façon grossière de faire les choses et je préférerais les réponses ci-dessus en utilisant Makefiles.

0
nikhil chaubey