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

от автора

Если вы регулярно пишете на 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.

Далее, скопируем все заголовочники из 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 и вуаля:

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

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

ссылка на оригинал статьи https://habrahabr.ru/post/276883/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *