Настройка Visual Studio C ++ для нескольких проектов - PullRequest
0 голосов
/ 05 марта 2020

0. Отказ от ответственности

Этот вопрос касается только конфигурации проекта / решения Visual Studio C ++ и может включать субъективность.

Однако идея этого поста состоит в том, чтобы поделиться нашими подходами к настройке большое решение Visual Studio.

Я не рассматриваю какой-либо инструмент, подобный CMake / Premake здесь.

1. Проблема

  • Как вы справляетесь с крупномасштабной архитектурой и конфигурацией приложений C ++ с помощью Visual Studio?
  • Что для вас является наилучшим способом установки нового решения Visual Studio, состоящего из несколько проектов?
  • Какую функцию конфигурации проекта / решения Visual Studio вы пытаетесь избежать? ( Пример: фильтры вместо папок )

2. Личный подход

2.1. Контекст

Я разработчик программного обеспечения для компании, выпускающей видеоигры, поэтому я возьму очень упрощенную архитектуру игрового движка, чтобы проиллюстрировать мои слова:

enter image description here

2.2. Структура файла

Мое решение Visual Studio, вероятно, будет выглядеть примерно так:

enter image description here

Где Application - исполняемый файл и все остальные проекты - это библиотеки (динамически связанные).

Мой подход состоит в том, чтобы разделить каждый проект на две папки: include, src

enter image description here

И внутренняя структура будет разделена на папки в соответствии с моими пространствами имен:

enter image description here

2,3. Конфигурация проекта

В следующих строках предполагается, что доступен только один $(Configuration) и $(Platform) (например: Release-x64), и что ссылки на файлы .lib в Linker/Input/Additional Dependencies выполняются для каждого проекта .

Здесь я бы определил папки Bin (Вывод), * ​​1079 * (Промежуточный вывод) и Build (Организованный вывод), скажем, они находятся в $(SolutionDir):

  • $(SolutionDir)Bin\
  • $(SolutionDir)Bin-Int\
  • $(SolutionDir)Build\

Папки Bin и Bin-Int являются игровыми площадками для компилятор, в то время как папка Build заполняется каждым событием проекта после сборки:

  • $(SolutionDir)Build\$(ProjectName)\include\ (проект включает в себя)
  • $(SolutionDir)Build\$(ProjectName)\lib\ (.lib файлы)
  • $(SolutionDir)Build\$(ProjectName)\bin\ (файлы .dll)

Таким образом, каждый $(SolutionDir)Build\$(ProjectName)\ может использоваться как независимая библиотека.

Примечание: следующие пояснения может пропустить $(SolutionDir) из пути к папке Build для упрощения чтения.

Если B зависит от A, Build\B\include\ будет содержать B и A включительно. Точно так же, Build\B\bin\ будет содержать B и A двоичные файлы, а Build\B\lib\ будет содержать B и A .lib файлов (Если и только если B в порядке, чтобы выставить A его пользователь, в противном случае к Build\B\lib\) будут добавлены только файлы B .lib.

Проекты ссылаются на папки Build\. Таким образом, если B зависит от A, то путь включения B будет ссылаться на $(SolutionDir)Build\A\include\ (а не $(SolutionDir)A\include\), поэтому любое включение, используемое A, будет доступно для B без указания его явно , (Но приведите в разделах 2.6.2., 2.6.3. и 2.6.4. технические ограничения).

После этого я проверяю, что мое решение имеет правильную конфигурацию Project Dependencies, поэтому Build Order при сборке В целом решение будет учитывать и уважать зависимости моего проекта.

2.4. Конфигурация проекта пользователя

Наш EngineSDK пользователь (работающий на Application) должен будет только настроить Application, например:

  • Включить каталоги : $(SolutionDir)Build\Engine\include\
  • Библиотечный каталог : $(SolutionDir)Build\Engine\lib\
  • Посткомпоновка : копирование $(SolutionDir)Build\Engine\bin\* в $(OutDir)
  • Дополнительные зависимости : Любой вышеперечисленный .lib-файл в иерархии зависимостей приведен здесь

Это типичный поток конфигурации Visual Studio для большого количества библиотек C ++.

Архитектура папок общей библиотеки, которую я пытаюсь сохранить:

lib\
include\
bin\

Вот несколько примеров библиотек, использующих эту модель архитектуры папок (обратите внимание, что bin предназначен исключительно для динамически связанных библиотек, поскольку статически связанные библиотеки не работают с DLL):

2,5. Преимущества

  • Очистить архитектуру папок
  • Возможность экспортировать библиотеку напрямую путем вставки копии или архивирования подпапки $(SolutionDir)Build\

2.6. Технические ограничения

Подход, который я здесь написал, имеет некоторые недостатки. Эти ограничения являются причиной этого поста, поскольку я хочу улучшить себя:

2.6.1. Утомительная конфигурация

Работа с 10 или менее проектами - это хорошо, однако с более крупными решениями (более 15 проектов) он может быстро превратиться в беспорядок. Конфигурации проекта очень жесткие, и небольшое изменение в архитектуре проекта может привести к часам конфигурации и отладки проекта.

2.6.2. Ограничение после сборки

Рассмотрим простой случай зависимости:

  • C зависит от B, а B зависит от A.
  • C - это исполняемый файл, а B и A - библиотеки
  • B и A после сборки обновляют каталог Build\$(ProjectName)\

При изменении исходного кода A и его компиляции Build\A\ будет обновлено. Однако, поскольку B был предварительно скомпилирован (до изменения A), его папка Build\B\ содержит копию предыдущих двоичных файлов A и включает в себя. Таким образом, при выполнении C (который знает только о B как о зависимости), будут использоваться старые A binaries / includes. Обходной путь, который я нашел для этой проблемы, состоит в том, чтобы вручную вызвать событие B после сборки перед выполнением C. Однако если вы забудете запустить промежуточный проект после сборки, это может привести к головным болям во время отладки (не загруженные символы, неправильное поведение ...).

2.6.3. Многократная ссылка на один заголовок

Другим ограничением для этого подхода является «Многократная ссылка на один заголовок».

Эту проблему можно объяснить, рассмотрев изображение зависимости проекта в разделе 2.1. . Учитывая, что Graphics и Physics включают в себя Maths заголовки, а Engine включает Build\Graphics\include\ и Build\Physics\include\, при вводе имени заголовка будет отображаться несколько идентичных результатов:

enter image description here

2.6.4. Десинхронизированная ссылка на символ

Если B зависит от A и любые изменения заголовка в A (например, мы добавляем новую функцию), потребуется Rescan File / Rescan Solution чтобы получить доступ к новому символу из B.

Кроме того, переход к файлам или символу может заставить нас перейти к неправильному заголовку (скопированный заголовок вместо исходного).

3. Опросы и перспективы обучения

3.1. Справочник по проекту

Во время моего пути разработчика программного обеспечения для Visual Studio я прошел через концепцию проекта Reference, но я не могу найти, как он может решить технические ограничения моего текущего подхода, или как он может помочь мне переосмыслить это.

3.2. Листы свойств

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

3.3. Изучение GitHub

В настоящее время я пытаюсь найти несколько хороших ссылок на архитектуру проекта. Я был бы рад найти какое-нибудь настроенное решение Visual Studio на GitHub или любой платформе для совместного использования кода. (Я знаю, что CMake и Premake предпочтительнее в большинстве случаев при совместном использовании кода, однако моя цель - узнать больше о конфигурации проекта Visual Studio).

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

...