Nuget++ для бедных

в 12:31, , рубрики: c++

Если вы регулярно пишете на C++ с использованием сторонних библиотек (окромя boost) и вам надоело постоянно прописывать пути до папок с заголовочниками и lib-ами, то под катом вы найдете один из способов несколько это дело автоматизировать.

Всякий раз, начиная небольшой проект ConsoleApplication в VisualStudio, дабы опробовать пришедшую в голову идею, я испытываю боль. Боль от постоянных настроек в разделах Properties->C/C++->General->Additional Include Directories и Properties->Linker->General->Additional Library Directories.

Если в Solution-е 1-3 проекта и пара сторонних библиотек, то все еще терпимо, но для >10 проектов и > 5 библиотек все выглядит по-другому. И когда однажды мое терпение лопнуло, я решил как-то это дело автоматизировать. Взяв за основу систему для boost-а, стал выпиливать свой велосипед. Что хотелось получить от решения:

  1. Подключение сторонних библиотек путем прописывания 2-х уровневого include-a, включающего имя библиотеки, например
    #include <gtest/gtest.h>
    #include <vsqlite/connection.hpp>
    

  2. Автоматическая линковка соответствующих *.lib файлов при подключении *.hpp файла
  3. Поддержка x86/x64, Debug/Release, Static/Dynamic Runtime
  4. Возможность быстрого переноса на другую машину


Для начала стоит определиться с папкой, в которой наш репозиторий будет храниться.У меня это C:/nuget++. Внутри созданы папки include,lib/x86,lib/x64,projects. С первыми 3-мя все понятно, а в третьей папке я храню проекты для сборки этих самых библиотек.

Для примера возьмем библиотеку gtest, разархивируем ее в папку projects и откроем gtest-md.sln из папки msvc.

Nuget++ для бедных - 1Далее, скопируем все заголовочники из projects/gtest/include/gtest в include/gtest/. Для выполнения первого пункта достаточно добавить ссылку на nuget++/include в глобальные настройки проектов c++. В Visual Studio 2015 (и, насколько я помню, в 2013 тоже) это делает путем открытия окна Property Manager (View->Property Manager), и редактирования файлов Microsoft.Cpp.Win32.user и Microsoft.Cpp.x64.user в любой из конфигураций любого C++ проекта. А именно, нас интересует раздел Common Properties->C/C++->General->Additional Include Directories-просто дописываем тупа путь до include-папки нашего nuget, например C:nuget++include;%(AdditionalIncludeDirectories).

Для выполнения 2-го и 3-го пункта придется немного повозиться. Для начала создадим для нашей библиотеки 4 конфигурации для x86/x64 и Static/Dynamic Runtime. В свойствах проекта поменяем имя выходного файла и вариант линковки runtime-библиотеки для каждой из конфигураций соответственно в разделах Properties->Configuration Properties->General->Target Name и Properties->Configuration Properties->C/C++->Code Generation.

Конфигурация Имя выходного файла Runtime библиотека
Release Static Runtime $(ProjectName)-vc$(PlatformToolsetVersion)-mt-s Multi-threaded (/MT)
Release Dynamic Runtime $(ProjectName)-vc$(PlatformToolsetVersion)-mt Multi-threaded DLL (/MD)
Debug Static Runtime $(ProjectName)-vc$(PlatformToolsetVersion)-mt-sgd Multi-threaded Debug (/MTd)
Debug Dynamic Runtime $(ProjectName)-vc$(PlatformToolsetVersion)-mt-gd Multi-threaded Debug DLL (/MDd)

Сделав batch-build, мы получим 2 группы(x86 и x64) по 4 lib-файла, которые необходимо перенести в папки lib/x86 и lib/x64.
Для автоматической линковки указанных файлов придется немного пошаманить над самой библиотекой.Прежде всего добавим в проект файл auto_link.hpp и за-include-им его из всех остальных заголовочников. Покопавшись в MSDN на тему ключей /MT,/MD,/MTd,/MDd можно составить таблицу с уникальным для каждой конфигурации набором макросов. Немного магии с макросами и auto_link.hpp готов:

auto_link.hpp
#pragma once

#if _MSC_VER == 1500
#define  GTEST_LIB_TOOLSET  "vc90"
#elif _MSC_VER == 1600
#define  GTEST_LIB_TOOLSET  "vc100"
#elif _MSC_VER == 1700
#define  GTEST_LIB_TOOLSET  "vc110"
#elif _MSC_VER == 1800
#define  GTEST_LIB_TOOLSET  "vc120"
#elif _MSC_VER == 1900
#define  GTEST_LIB_TOOLSET  "vc140"
#endif


#if defined(_MT) || defined(__MT__)
#  define GTEST_LIB_THREAD_OPT "mt"
#else
#  define GTEST_LIB_THREAD_OPT
#endif


#if defined(_DEBUG)
#if defined(_DLL)
#  define GTEST_LIB_RT_OPT "-gd"
#else
#  define GTEST_LIB_RT_OPT "-sgd"
#endif
#else
#if defined(_DLL)
#  define GTEST_LIB_RT_OPT ""
#else
#  define GTEST_LIB_RT_OPT "-s"
#endif
#endif

#ifndef GTEST_LIB
	#define GTEST_LIB_FULL_NAME "gtest-" GTEST_LIB_TOOLSET "-" GTEST_LIB_THREAD_OPT GTEST_LIB_RT_OPT ".lib"
	#pragma comment(lib, GTEST_LIB_FULL_NAME)
	#pragma message("linking to gtest:" GTEST_LIB_FULL_NAME)
#endif

Создаем тестовый проект, несколько строчек кода:

#include <gtest/gtest.h>
int main(int argc, char** argv)
{
	::testing::InitGoogleTest(&argc, argv);
	return RUN_ALL_TESTS();
}

TEST(autolink_tests, can_autolink_gtests){}

Build и вуаля:

Nuget++ для бедных - 2

Для переноса полученного репозитория(4 пункт) достаточно скопировать его на другую машину и прописать пути до папок include, lib/x86 и lib/x64.

В качестве бонуса получаем совместимость с boost, достаточно просто скопировать hpp,lib и dll файлы в соответствующий директории. В итоге, потратив 1 рабочий день на организацию совего nuget++ для бедных, я избавился от головной боли при использовании ходовых сторонних библиотек. Сейчас в моем арсенале boost, cryptopp, gmock, gtest, jsoncpp, pugixml, vsqlite и этот список постоянно расширяется. В планах создать единый Solution и настроить Build event-ы для автоматического копирования необходимых файлов в целевые директории.

Автор: ukhegg

Источник

* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js