У меня есть проект, который в настоящее время не использует предварительно скомпилированные заголовки, но я хотел бы сделать это, поскольку я продемонстрировал, что это приводит к реальному повышению скорости компиляции в проекте.
Я также хотел бы использовать /Zi
, чтобы извлечь выгоду из преимуществ параллельной сборки, связанных с /Zf
, что подразумевается /Zi
.
Я использую компилятор C++ VS2017, но использую систему сборки, отличную от Visual Studio, поэтому ответы, касающиеся настройки VS, бесполезны.
Что я обнаружил, так это то, что я могу настроить сборку для использования предварительно скомпилированных заголовков, или я могу настроить ее для использования /Zi
, но я не могу сформировать правильную серию вызовов для выполнения обоих. Когда я пытаюсь сделать то, что считаю правильным, я получаю ошибку C2958
, останавливающую сборку, и я не вижу, что я делаю неправильно.
Я построил игрушечный проект, чтобы продемонстрировать то, что я вижу. Заголовок pch.hpp
выглядит так:
#pragma once
#include <vector>
И делаем короткую основную строку в main.cpp
:
int main() {
std::vector<int> xs = { 1, 2, 3 };
return xs.size();
}
Обратите внимание, что это полное содержимое файла для main.cpp
: я ничего не пропустил. Я намеренно не включаю здесь pch.hpp
, потому что позже мы собираемся принудительно внедрить его с /Fi
. В реальном проекте нет строк включения для предварительно скомпилированного заголовка во всех нужных местах, и для этого нужно обновить тысячи файлов. Обратите внимание, что подход с использованием /Fi
действительно работает в приведенных ниже командных строках и имеет то преимущество, что механизм, основанный на forceincludes, может работать и с предварительно скомпилированными заголовками в стиле GCC.
Если мы строим вещи без /Zi
, все идет нормально:
cl /Fobuild\pch.obj /TP /nologo /EHsc /errorReport:none /MD /O2 /Oy- /Zc:rvalueCast /Zc:strictStrings /volatile:iso /Zc:__cplusplus /permissive- /std:c++17 /Zc:inline /c pch.hpp /Yc /Fpbuild\pch.pch
pch.hpp
cl /Fobuild\main.obj /c main.cpp /TP /nologo /EHsc /errorReport:none /MD /O2 /Oy- /Zc:rvalueCast /Zc:strictStrings /volatile:iso /Zc:__cplusplus /permissive- /std:c++17 /Zc:inline /FIpch.hpp /Yupch.hpp /Fpbuild/pch.pch
main.cpp
Мы находим файлы, которые мы ожидаем найти в каталоге сборки:
main.obj pch.obj pch.pch
Однако, если мы попытаемся использовать /Fi
и /Fd
для создания .pdb
для каждого файла и управления его именем, это вообще не сработает:
Мы можем скомпилировать предварительно скомпилированный заголовок следующим образом:
cl /Fobuild\pch.obj /TP /nologo /EHsc /errorReport:none /MD /O2 /Oy- /Zc:rvalueCast /Zc:strictStrings /volatile:iso /Zc:__cplusplus /permissive- /std:c++17 /Zc:inline /c pch.hpp /Yc /Fpbuild\pch.pch /Zi /Fdbuild\pch.pch.pdb
И пока в каталоге build
все выглядит нормально:
pch.obj pch.pch pch.pch.pdb
Но когда мы пытаемся построить объектный файл для main, все разваливается:
cl /Fobuild\main.obj /c main.cpp /TP /nologo /EHsc /errorReport:none /MD /O2 /Oy- /Zc:rvalueCast /Zc:strictStrings /volatile:iso /Zc:__cplusplus /permissive- /std:c++17 /Zc:inline /FIpch.hpp /Yupch.hpp /Fpbuild/pch.pch /Zi /Fdbuild\main.obj.pdb
main.cpp
main.cpp: error C2859: Z:\data\acm\src\example\build\main.obj.pdb is not the pdb file that was used when this precompiled header was created, recreate the precompiled header.
Это очень загадочная ошибка, потому что сообщение об ошибке предполагает, что main.obj.pdb
каким-то образом обрабатывается как вход, но на самом деле это имя файла .pdb
, сгенерированного как выход< /em>, согласно значению флага /Fd
для сборки main.obj
.
Поиск в Google не привел к большому количеству полезных указаний, с множеством подсказок о перенастройке параметров VS, что не очень полезно в случае сборки, управляемой чем-то другим.
Я также убедился, что мой обман с /Fi
из pch.hpp
не является проблемой. Если я обновлю main.cpp
до #include pch.hpp
и удалю /Fipch.hpp
из строки компиляции для main.obj
, все равно будет возникать та же ошибка C2859
.
Я понимаю, что при использовании предварительно скомпилированных заголовков нужно правильно настроить множество флагов, среди них флаги /Yc
, /Yu
и /Fp
, но я думаю, что обработал их правильно.
Кто-нибудь видит, где я ошибаюсь?
Правка 1. Демонстрация того, что /Fd может называть разные файлы PDB
В ответ на комментарий ниже, на самом деле не похоже, что все цели должны совместно использовать аргумент /Fd
. Вот пример без использования предварительно скомпилированных заголовков, который демонстрирует, что:
Здесь мне нужно было добавить common.cpp
и создать main1.cpp
и main2.cpp
, которые оба связывают его:
Заголовок common.hpp
выглядит так:
#pragma once
int common(int arg);
С тривиальной реализацией в common.cpp
:
#include "common.hpp"
int common(int arg) {
return arg + 42;
}
Затем введите две разные основные линии:
main1.cpp
:
#include "common.hpp"
int main() {
std::vector<int> xs = { 1, 2, 3 };
return common(xs.size());
}
И main2.cpp
:
#include "common.hpp"
int main() {
std::vector<int> xs = { 1, 2, 3, 4};
return common(xs.size());
}
Затем скомпилируйте все три объектных файла. Обратите внимание, что у нас по-прежнему есть /Fipch.hpp
, чтобы включения работали правильно, но мы не используем какой-либо реальный механизм PCH:
cl /Fobuild\common.obj /c common.cpp /TP /nologo /EHsc /errorReport:none /MD /O2 /Oy- /Zc:rvalueCast /Zc:strictStrings /volatile:iso /Zc:__cplusplus /permissive- /std:c++17 /Zc:inline /FIpch.hpp /Zi /Fdbuild\common.obj.pdb
cl /Fobuild\main1.obj /c main1.cpp /TP /nologo /EHsc /errorReport:none /MD /O2 /Oy- /Zc:rvalueCast /Zc:strictStrings /volatile:iso /Zc:__cplusplus /permissive- /std:c++17 /Zc:inline /FIpch.hpp /Zi /Fdbuild\main1.obj.pdb
cl /Fobuild\main2.obj /c main2.cpp /TP /nologo /EHsc /errorReport:none /MD /O2 /Oy- /Zc:rvalueCast /Zc:strictStrings /volatile:iso /Zc:__cplusplus /permissive- /std:c++17 /Zc:inline /FIpch.hpp /Zi /Fdbuild\main2.obj.pdb
И мы находим то, что и ожидали в каталоге build
:
common.obj common.obj.pdb main1.obj main1.obj.pdb main2.obj main2.obj.pdb
Тогда мы можем пойти дальше и связать:
link /nologo /DEBUG /INCREMENTAL:NO /LARGEADDRESSAWARE /OPT:REF /OUT:build\main1.exe /PDB:build\main1.pdb build\common.obj build\main1.obj
link /nologo /DEBUG /INCREMENTAL:NO /LARGEADDRESSAWARE /OPT:REF /OUT:build\main2.exe /PDB:build\main2.pdb build\common.obj build\main2.obj
И мы обнаруживаем, что получаем как исполняемые файлы, так и файлы PDB для этих исполняемых файлов в каталоге build
:
common.obj main1.exe main1.obj.pdb main2.exe main2.obj.pdb
common.obj.pdb main1.obj main1.pdb main2.obj main2.pdb
.pdb
для каждой TU с помощью разных аргументов для/Fd
для каждого вызоваcl
, а затем связать объектные файлы вместе. - person acm   schedule 01.04.2019common.obj
при использовании/Fi
, учитывая, чтоcommon.obj
связан как сmain1.exe
, так и сmain2.exe
? - person acm   schedule 01.04.2019lib.exe
для создания архивного файла, не связывая аналогcommon.obj
напрямую с несколькими исполняемыми файлами. Однако я не вижу возможности создать PDB для архивной библиотеки сlib.exe
: docs.microsoft.com/en-us/cpp/build/reference/. Поэтому я не уверен, что вижу, как ваше предложение помогает: у нас все еще нет PDB, которую мы могли бы назвать с помощью/Fd
при связывании какmain1.exe
, так иmain2.exe
. - person acm   schedule 02.04.2019