2016-05-13 4 views
3

фон


У меня есть большой Makefile проект я работаю, на котором я хотел бы немного прибраться. Он создает несколько десятков подпроектов, каждый из которых содержит около 100 .cpp и .h файлов. Я настроил его так, чтобы он смог построить debug и release для нескольких операционных систем (Linux, OSX/Mac, QNX и т. Д.) И для нескольких архитектур (x86/i386, x64/amd64, armhf, arm64/aarch64). Это потому, что это массивный проект, и единственный способ быстро его построить - параллельно с несколькими целями инструментов.GCC - Несколько скомпилированные заголовки и конкретные пути

У меня есть правило, согласно которому все проекты подчиняются тому, что хранит промежуточные объекты (то есть: .o файлов) во временных файлах при создании. Итак, здание test.c для Linux, arm64, режим выпуска; построим объектный файл в следующем подкаталоге в рабочем каталоге:

.tmp/Linux/arm64/release 

Вопросы


Эта функция работает без проблем в моих сборках, но с этой установкой, я не могут правильно использовать предварительно скомпилированные заголовки (то есть: .GCH файлов) с GCC. С моей установкой у меня есть пара stdafx.h/stdafx.cpp. Используя GCC, я могу создать файл stdafx.h.gch достаточно легко. Тем не менее, проект, похоже, использует его (что ускоряет сборку), если файл находится на том же пути, что и исходные файлы. Если предварительно скомпилированный заголовок является промежуточным объектом пути промежуточного объекта (то есть: .tmp/Linux/arm64/release), он не обнаруживается и не используется. Даже если я явно добавлю путь include к пути промежуточных объектов, который будет содержать файл gch, он терпит неудачу. Включение полного пути к имени файла приводит к тому, что он рассматривается как недопустимый сценарий компоновщика и игнорируется.

Итак, первым моим решением было сделать правило, чтобы заставить все сборки OS/arch ждать первоначального предварительно скомпилированного генерации заголовков, а не строить gch на основе каждой ОС/арки. Тем не менее, если я строй gch с настройками режима релиза и попыткой make отладкой сборки, я получаю следующее предупреждение:

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

Первого, я не знаю, если это имеет серьезные последствия для моего телосложения, и второго , разные операционные системы могут передавать разные фреймы определения времени компиляции для генерации gch, поэтому, насколько я могу судить, это не вариант использования «один размер подходит всем».


Вопрос


Как я могу обойти эту проблему, так что предкомпилированный заголовок находится в другом месте, кроме $PWD и он может быть обнаружен с помощью GCC? В настоящее время я использую gcc v5.3.1.

спасибо.

+0

Вы можете разместить один из линий компиляции вы пытались получить GCC, чтобы увидеть 'gch', когда он находится в другой папке? – user657267

+0

@ user657267 Конечно: 'INC + = .tmp/$ {OS_TYPE}/$ {CPU_TYPE}/$ {REL_TYPE}'. 'gcc $ {INC} -c $ {INPUT} -o $ {OUTPUT}'. Не самый полезный, но мой 'Makefile' очень многословный и перекликается с путями' include', которые используются во время сборки, и я могу подтвердить, что заголовки обычно разрешаются с этого пути, за исключением файлов '.gch'. – DevNull

+0

Извините, я имел в виду, что на самом деле он был расширен до того, как он был выполнен. – user657267

ответ

2

Вот MVCE для проблемной сценария:

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

srcs := main.c 
objs := $(addprefix tmp/,$(srcs:.c=.o)) 
pch := tmp/hw.h.gch 
CPPFLAGS += -I. 

.PHONY: all clean 

all: hw 

tmp: 
    mkdir -p tmp 

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

$(pch): hw.h | tmp 
    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 

Этот проект выводит промежуточные файлы в tmp. Файлы .o идут туда , а также PCH hw.h.gch.

Построить и запустить его:

$ 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 

До сих пор так хорошо. Но действительно ли он использовал PCH? Давайте посмотрим:

$ 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 

Нет, это не так. Мы знаем, что, потому что, с ENFORCE_PCH определены, мы пристегивается к #error директиву к концу hw.hпосле генерации хороший tmp/hw.h.gch. Так что если в первом случае #include-где-нибудь вместо последнего, разрывы сборки. Который он только что сделал.

И это точно так, как должно быть. Руководство GCC 3.21 Using Precompiled Headers, п. 3:

Искажен предварительно скомпилированный файл заголовка, когда #include отображается в компиляции. Поскольку он ищет включенный файл (см. Путь поиска), компилятор ищет готовый заголовокв каждом каталоге непосредственно перед тем, как он ищет файл include в этой директории. Имя, которое вы искали, - это имя, указанное в #include с добавлением «.gch» . Если прекомпилированный файл заголовка не может быть использован, он игнорируется.

Таким образом, учитывая пути поиска ., директива #include <hw.h> заставит gcc для проверки РСН ./hw.h.gch перед использованием ./hw.h, и поскольку нет нет ./hw.h.gch, он будет использовать ./hw.h.

Казалось бы, из документации, только что процитированной, что добавление tmp в пути поиска - CPPFLAGS += -Itmp -I. - должно вызывать tmp/hw.h.gch быть использованы в предпочтении к ./hw.h. Но на самом деле это не имеет значения. В документации отсутствует важная квалификация.Второе предложение следует читать:

Как он ищет включаемый файл (см Пути поиска) компилятор ищет предкомпилированного заголовок в каждом каталоге непосредственно перед ищет инклюдник в этого каталог и будет использовать предварительно скомпилированный заголовок для предпочтения, если будет найден файл include .

Для того, чтобы быть найдены и используется, РСН должен быть родственный заголовка соответствия. И на рассмотрение это то, что вы хотите. В противном случае, a/foo.h.gch без заголовка Sibling соответствия может быть найти и использовать благодаря -Ia, когда есть b/foo.h.gch, с соответствующим b/foo.h, что можно было бы найти и использовать благодаря более позднему -Ib. Ясно, что последний является лучшим выбором.

С этим пониманием, что это не трудно увидеть решение: если вы действительно хотите, чтобы скомпилировать и использовать в РСН, что это не родной брат его заголовка источника, убедитесь, чтобы дать ему фальшивый соответствующий заголовок , что является родной брат. Вы можете организовать это, как вы сочтете нужным, например.

Makefile (фиксированный)

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 

Смотрите, что PCH используется из tmp:

$ 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

Красиво сделано. Спасибо. – DevNull