2016-05-13 8 views
3

Hintergrund


Ich habe ein großes Makefile Projekt, das ich auf dem arbeite ich möchte ein wenig aufzuräumen. Es baut mehrere Dutzend Teilprojekte auf, von denen jedes ungefähr 100 .cpp und .h Dateien enthält. Ich habe es so eingerichtet, dass es debug und release Builds für mehrere Betriebssysteme (Linux, OSX/Mac, QNX usw.) und für mehrere Architekturen (x86/i386, x64/amd64, armhf, arm64/aarch64) parallel erstellen kann. Dies liegt daran, dass es sich um ein riesiges Projekt handelt, und der einzige Weg, um es schnell zu erstellen, ist parallel zu mehreren Tool-Ketten.GCC - Mehrere vorkompilierte Header und bestimmte Pfade

Ich habe eine Master-Regel, die alle Projekte gehorchen, die die Zwischenobjekte (dh: .o Dateien) in temporären Verzeichnissen beim Erstellen speichert. Also, Gebäude test.c für Linux, Arm64, Release-Modus; bauen würde die Objektdatei in dem folgenden Unterverzeichnis in dem vorliegenden Arbeitsverzeichnis:

.tmp/Linux/arm64/release 

Probleme


Diese Funktion ohne Probleme funktioniert in meinem baut, aber mit diesem Setup, ich scheint nicht korrekt vorkompilierte Header (dh: .GCH Dateien) mit GCC zu verwenden. Mit meinem Setup habe ich ein stdafx.h/ Paar. Mit GCC kann ich einfach eine stdafx.h.gch Datei erstellen. Das Projekt scheint es jedoch nur zu verwenden (was den Build beschleunigt), wenn sich die Datei im selben Pfad wie die Quelldateien befindet. Wenn der vorkompilierte Header im Zwischenobjektpfad enthalten ist (z. B. .tmp/Linux/arm64/release), wird er nicht erkannt oder verwendet. Selbst wenn ich den Include-Pfad explizit zum Pfad für Zwischenobjekte hinzufüge, der die gch-Datei enthalten würde, schlägt er fehl. Wenn Sie den vollständigen Pfad zum Dateinamen selbst eingeben, wird er als ungültiges Linker-Skript behandelt und ignoriert.

So, das war meine erste Problemumgehung eine Regel zu machen, alle OS zu zwingen/Bogen auf anfängliche vorkompilierte Header Generation warten baut, anstatt eine gch auf einer Basis pro-OS/Bogen zu bauen. Allerdings, wenn ich die gch mit Release-Modus-Einstellungen und versuchen, make ein Debug-Build bauen, ich die folgende Warnung erhalten:

warning: stdafx.h.gch: created with -gnone, but used with -gdwarf-2 

Erstens, ich weiß nicht, ob das schwerwiegende Folgen für meine Build hat, und die zweite , unterschiedliche Betriebssysteme können unterschiedliche Kompilierzeitdefinitionsflags für die gch Generation durchlaufen, also ist dies kein Anwendungsfall "für alle Fälle", soweit ich das sehen kann.


Frage


Wie kann ich dieses Problem umgehen, so dass der vorkompilierte Header ist in einer anderen Position als die $PWD und kann durch GCC erkannt werden? Ich verwende derzeit gcc v5.3.1.

Vielen Dank.Hier

+0

Können Sie eine der Kompilierung Zeilen schreiben Sie versucht haben, GCC zu bekommen das zu sehen 'gch' wenn es in einem anderen Ordner ist? – user657267

+0

@ user657267 Sicher: 'INC + = .tmp/$ {OS_TYPE}/$ {CPU_TYPE}/$ {REL_TYPE}'. 'gcc $ {INC} -c $ {EINGABE} -o $ {AUSGABE}'. Nicht das hilfreichste, aber mein 'Makefile' ist sehr ausführlich und spiegelt die' include' Pfade wieder, die während des Builds benutzt wurden, und ich kann bestätigen, dass Header normalerweise von diesem Pfad gelöst werden, mit Ausnahme von '.gch' Dateien. – DevNull

+0

Entschuldigung, ich meinte, dass es erweitert wurde, wenn es ausgeführt wird. – user657267

Antwort

2

ist ein MVCE für Ihr Problem Szenario:

main.c

#include <hw.h> 
#include <stdio.h> 

int main(void) 
{ 
    puts(HW); 
    return 0; 
} 

hw.h

#ifndef HW_H 
#define HW_H 
#define HW "Hello World" 
#endif 

Makefile

Dieses Projekt gibt seine Zwischendateien in tmp aus. Die .o Dateien gehen dort und so auch die PCH hw.h.gch.

Erstellen und führen Sie es:

$ make && ./hw 
mkdir -p tmp 
gcc -c -I. -o tmp/hw.h.gch hw.h 
gcc -c -I. -o tmp/main.o main.c 
gcc -o hw tmp/main.o 
Hello World 

So weit so gut. Aber hat es tatsächlich den PCH benutzt? Mal sehen:

$ make clean 
sed -i '/^#error/d' hw.h 
rm -fr hw tmp 
$ make ENFORCE_PCH=true 
mkdir -p tmp 
gcc -c -I. -o tmp/hw.h.gch hw.h 
echo "#error Debug." >> hw.h 
gcc -c -I. -o tmp/main.o main.c 
In file included from main.c:1:0: 
./hw.h:5:2: error: #error Debug. 
#error Debug. 
^
Makefile:15: recipe for target 'tmp/main.o' failed 
make: *** [tmp/main.o] Error 1 

Nein, tat es nicht. Wir wissen, dass da, mit ENFORCE_PCH definiert sind, haben wir eine #error Richtlinie bis zum Ende des hw.hnach die gut tmp/hw.h.gch Erzeugung gehefteten. Also, wenn der ehemalige ist #include -ed überall anstelle der letzteren, bricht der Build. Was es gerade getan hat.

Und das ist so wie es sein sollte. GCC Handbuch 3.21 Using Precompiled Headers, Para. 3:

Eine vorkompilierte Headerdatei wird gesucht, wenn #include in der Kompilierung angezeigt wird. Da es für die Datei enthält (siehe Suchpfad) sucht sucht der Compiler für einen vorkompilierte Header in jedem Verzeichnis, kurz bevor er die Datei in dieses Verzeichnis enthalten aussieht. Der gesuchte Name ist der Name, der in dem # include mit '.gch' angefügt angegeben wird. Wenn die vorkompilierte Header-Datei nicht verwendet werden kann, wird sie ignoriert.

So, da sind Suchpfad ., die Richtlinie #include <hw.h> verursacht gcc für den PCH zu überprüfen ./hw.h.gch vor ./hw.h verwenden, und da es keine ./hw.h.gch ist, wird es ./hw.h verwenden.

Es könnte aus der Dokumentation erscheinen nur zitiert, dass tmp zum Suchpfad gehören das Hinzufügen - CPPFLAGS += -Itmp -I. - tmp/hw.h.gch verwendet werden bevorzugt gegenüber ./hw.h verursachen sollte. Aber in der Tat macht es keinen Unterschied. Die Dokumentation lässt eine entscheidende Qualifikation aus.Der zweite Satz soll lauten:

Da es für die enthaltene Datei sucht der Compiler sucht nach einem vorkompilierte Header in jedem Verzeichnis, kurz bevor er sucht nach der Include-Datei in dieses Verzeichnis (Suchpfad sehen) und verwendet den vorkompilierten Header für die Voreinstellung, wenn die Include-Datei gefunden wird.

gefunden und verwendet werden, hat ein PCH des Anpassungs Header ein Geschwister sein. Und bei Betrachtung ist das, was Sie wollen. Sonst könnte ein a/foo.h.gch ohne eine passende Geschwisterkopfzeile gefunden und dank -Ia verwendet werden, wenn es eine b/foo.h.gch gibt, mit einer übereinstimmenden b/foo.h, die dank einer späteren -Ib gefunden und verwendet werden konnte. Letzteres ist eindeutig die bessere Wahl.

Mit dieser Erkenntnis ist es nicht schwer, eine Lösung zu finden: Wenn Sie wirklich kompilieren und verwenden möchten einen PCH, die keine Geschwister von ihrer Quelle Header ist, stellen Sie sicher, dass es eine falschen Matching-Header geben dass ist ein Geschwister. Sie können dies so einrichten, wie Sie es für richtig halten, z.

Makefile (fix)

srcs := main.c 
objs := $(addprefix tmp/,$(srcs:.c=.o)) 
pch := tmp/hw.h.gch 
# Seek headers in tmp first... 
CPPFLAGS += -Itmp -I. 

.PHONY: all clean 

all: hw 

tmp: 
    mkdir -p tmp 

tmp/%.o: %.c | $(pch) 
    gcc -c $(CPPFLAGS) -o [email protected] $< 

$(pch): hw.h | tmp 
    # Make phony header in tmp... 
    echo "#error You should not be here" > $(basename [email protected]) 
    gcc -c $(CPPFLAGS) -o [email protected] $< 
ifdef ENFORCE_PCH 
    echo "#error Debug." >> $^ 
endif 

hw: $(objs) 
    gcc -o [email protected] $^ 

clean: 
    sed -i '/^#error/d' hw.h 
    rm -fr hw tmp 

Sehen Sie, dass die PCH von tmp verwendet wird:

$ make clean 
sed -i '/^#error/d' hw.h 
rm -fr hw tmp 
$ make ENFORCE_PCH=true && ./hw 
mkdir -p tmp 
# Make phony header in tmp... 
echo "#error You should not be here" > tmp/hw.h 
gcc -c -Itmp -I. -o tmp/hw.h.gch hw.h 
echo "#error Debug." >> hw.h 
gcc -c -Itmp -I. -o tmp/main.o main.c 
gcc -o hw tmp/main.o 
Hello World 
+0

Schön gemacht. Vielen Dank. – DevNull

Verwandte Themen