opzione gcc / g ++ per posizionare tutti i file oggetto in directory separate

StackOverflow https://stackoverflow.com/questions/1814270

  •  06-07-2019
  •  | 
  •  

Domanda

Mi chiedo perché gcc / g ++ non abbia un'opzione per posizionare i file oggetto generati in una directory specificata.

Ad esempio:

mkdir builddir
mkdir builddir/objdir
cd srcdir

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

So che è possibile ottenerlo con opzioni -o separate fornite al compilatore, ad es .:

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

... e so che posso scrivere Makefile tramite VPATH e direttive vpath per semplificare questo.

Ma c'è molto lavoro in un ambiente di compilazione complesso.

Potrei anche usare

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

Ma quando uso questo approccio il mio srcdir è pieno di .o immondizia in seguito.

Quindi penso che un'opzione con la semantica di --outdir sarebbe molto utile.

Qual è la tua opinione?

MODIFICA : i nostri Makefile sono scritti in modo tale che i file .o siano effettivamente inseriti in builddir / obj. Ma mi chiedo semplicemente se potrebbe esserci un approccio migliore.

MODIFICA : esistono diversi approcci che pongono l'onere di ottenere il comportamento desiderato per il sistema di compilazione (ovvero Make, CMake ecc.). Ma li considero tutti soluzioni alternative per una debolezza di gcc (e anche di altri compilatori).

È stato utile?

Soluzione

Questo è il makefile ridotto per uno dei miei progetti, che compila i sorgenti in 'src' e posiziona i file .o nella directory " obj " ;. Il bit chiave è l'uso della funzione patsubst () - vedi il manuale di GNU make (che in realtà è una lettura abbastanza buona) per i dettagli:

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)

Altri suggerimenti

Che ne dici di passare alla directory ed eseguire la compilazione da lì:

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

Questo è tutto. gcc interpreterà le inclusioni del modulo #include "path/to/header.h" come a partire dalla directory in cui esiste il file, quindi non è necessario modificare nulla.

Una soluzione banale ma efficace è aggiungere quanto segue subito dopo la chiamata gcc nel tuo Makefile:

mv *.o ../builddir/objdir

o anche un soft-clean (possibilmente ricorsivo) dopo aver completato la compilazione, come

rm -f *.o

o

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

Puoi usare un semplice wrapper intorno a gcc che genererà le -o opzioni necessarie e chiamerà gcc_wrap:

$ ./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

Ecco un tale Makefiles script nella sua forma più semplice:

#!/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: $!");
}

Naturalmente, il modo standard è di risolvere il problema con CMake, o più semplice, con bakefile o <=>, ma hai chiesto specificamente una soluzione che aggiunge la funzionalità a <=>, e penso l'unico modo è scrivere un tale wrapper. Ovviamente, potresti anche patchare le <=> fonti per includere la nuova opzione, ma potrebbe essere difficile.

Credo che tu abbia il concetto al contrario ...?!

L'idea alla base di Makefiles è che elaborano solo i file che sono stati aggiornati dall'ultima build, per ridurre i tempi di (ri) compilazione. Se si raggruppano più file in un'unica esecuzione del compilatore, praticamente si annulla tale scopo.

Il tuo esempio:

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

Non hai dato la regola 'make' che va con questa riga di comando; ma se qualsiasi dei tre file è stato aggiornato, è necessario eseguire questa riga e ricompilare tutti e tre i file , il che potrebbe non essere affatto necessario. Inoltre impedisce a "make" di generare un processo di compilazione separato per ciascun file sorgente, come farebbe per la compilazione separata (quando si utilizza l'opzione '-j', come suggerirei fortemente).

Ho scritto un Tutorial Makefile altrove, che fornisce ulteriori dettagli (come il rilevamento automatico i tuoi file sorgente invece di averli codificati nel Makefile, determinando automaticamente dipendenze e test in linea).

Tutto quello che dovresti fare per ottenere la tua directory di oggetti separata sarebbe aggiungere le informazioni di directory appropriate alla linea OBJFILES := e alla regola %.o: %.c Makefile da quel tutorial. La risposta di Neil Butterworth ha un bell'esempio di come aggiungere le informazioni sulla directory.

(Se vuoi usare DEPFILES o TESTFILES come descritto nel tutorial, dovresti adattare anche le linee DEPFILES := e TSTFILES := più la regola %.t: %.c Makefile pdclib.a .)

Penso che raccontare pass gcc non abbia un'opzione separata per dire dove mettere il file oggetto, dal momento che ce l'ha già. È & Quot; -c & Quot; - dice in quale directory mettere l'oggetto.

Avere flag aggiuntivi solo per la directory deve cambiare il meening di " -c " ;. Ad esempio:

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

Non puoi mettere /a/b/c/file.o in / a1 / a2 / a3, poiché entrambi i percorsi sono assoluti. Quindi & Quot; -c & Quot; dovrebbe essere modificato solo nel nome del file oggetto.

Ti consiglio di prendere in considerazione una sostituzione del makefile, come cmake , scons e altri. Ciò consentirà di implementare il sistema di compilazione sia per progetti semplici sia per progetti più grandi.

Vedi ad esempio come è facile compilare usando cmake il tuo esempio. Basta creare il file CMakeList.txt in srcdir /:

cmake_minimum_required(VERSION 2.6)
project(test) 

add_library(test file1.c file2c file3.c) 

E ora digita:

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

Questo è tutto, i file oggetto risiederanno da qualche parte sotto builddir / objdir.

Personalmente uso cmake e lo trovo molto comodo. Genera automaticamente dipendenze e ha altre chicche.

Nel frattempo ho trovato un " a metà strada " soluzione utilizzando l'opzione -combine.

Esempio:

mkdir builddir
mkdir builddir/objdir
cd srcdir

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

questo " combina " tutti i file di origine in un unico file oggetto.

Tuttavia, questo è ancora " a metà strada " perché deve ricompilare tutto quando cambia un solo file sorgente.

Questo è uno dei problemi che autoconf risolve.

Se hai mai fatto ./configure && make sai cos'è autoconf: è lo strumento che genera quegli script di configurazione gradevoli. Ciò che non tutti sanno è che puoi invece fare mkdir mybuild && cd mybuild && ../configure && make e funzionerà magicamente, perché autoconf è fantastico in questo modo.

Lo script configure genera Makefile nella directory di compilazione . Quindi l'intero processo di compilazione avviene lì. Quindi tutti i file di build appaiono naturalmente lì, non nell'albero dei sorgenti.

Se hai file sorgente in esecuzione #include "../banana/peel.h" e non puoi cambiarli, è un problema farlo funzionare correttamente (devi copiare o collegare in modo simbolico tutti i file di intestazione nella directory di compilazione). Se invece puoi modificare i file di origine per dire #include "libfood/comedy/banana/peel.h", allora sei pronto.

autoconf non è esattamente facile , specialmente per un grande progetto esistente. Ma ha i suoi vantaggi.

Sto cercando di capire la stessa cosa. Per me ha funzionato

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
``
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top