syncProj – утилита для генерации Visual Studio C++ проектов.
Привет. Хочу поделится опытом написания генератора проектов для Visual Studio.
В общем до данного момента я сам активно применял premake5 — да и в принципе по прежнему его променяю, но все-таки периодически натыкался на не-состыковки и прямые баги в premake5. Чинить premake5 особо энтузиазма не вызывало, т.к. premake5 базируется на скриптовом языке Lua, а это очередной язык программирования со своими замашками и приколами. В нашей фирме преобладают C++ и C#, я подумал почему бы не попробовать что-то новое и сделать его лаконично и опрятно.
В какой-то момент я экспериментировал с C# и знал, что C# можно скомпилировать и запустить на манер скрипта без особых заморочек – ну и подумал что буду генерировать C++ проект через C# скрипт.
Сначала разкопал Solution (.sln) file format, затем и C++ project file format (.vcxproj) – в принципе ничего особенного в них не было, просто данные. Microsoft наверное официально нигде не публиковал данные file formatы, пришлось частично искать информацию, частично самому reverse engineeriть.
Надо было набросать какую то классовую модель .vcxproj проектов – и сама модель на данный момент очень близка к .vcxproj, не самая оптимальная модель, зато проще подгрузить и Solution и Projectы – все грузится полу-мануально, и частично через C# reflection.
Впрочем начнём с самого простенького скрипта:
test.cs:
//css_ref syncproj.exe
using System;
partial class Builder : SolutionProjectBuilder
{
static void Main(String[] args)
{
solution("out_test");
platforms("Win32", "x64", "ARM", "ARM64");
project("out_test_windows");
platforms("Win32", "x64");
files("test.c");
filter("Debug");
defines("_DEBUG");
filter("Release");
defines("NDEBUG");
} //Main
}; //class Builder
Я подумал почему бы не придерживаться синтакса premake5, и сделать аналогичные функции – так что если в premake5 мы имеем defines { ”_DEBUG” }, то в syncProj это вызов функции – defines (”_DEBUG”);
Я постарался придерживаться абсолютного минимума, так что syncProj не делает ничего лишнего.
В принципе я не знаю станет ли syncProj популярным, будет ли community support, и прочее, так что что не хотелось загадывать в будущее и сделать утилиту простой и минимальной. Хотя по ходу дела я добавлял функции, которые не считаю самыми нужными, но например C# (.csproj) поддержку так и не добавил (хотя это и не сложно).
Практически сразу разработка закрутилась вокруг C# scriptов, то и появилась возможность дебаггировать сами скрипты с Visual Studio.
Tools > Extensions and update, в Онлайн галлерее можно найти ”CS-Script Tools”, которые позволяют дебаггировать C# скрипты, без необходимости создавать проекты.
Такие таги как //css_ref, //css_include взяты напрямую с C# scriptа, и я сам сделал их поддержку, через Regex.
Естественно с command line генерация проектов происходит вот так:
>syncproj.exe test.cs
test.cs :
3 files updated
Если сравнивать утилиты между собой, то cmake генерирует так же custom build action, где на CMakeLists.txt запускается сам cmake. В premake5 такого не происходит автоматом, но можно написать то же самое через определение custom build step. В syncProj я сделал специальную команду для этого – например projectScript(”test.cs); конфигурирует данный custom build step.
Самое интересное что должно происходить если один .cs скрипт, например, делится на два файла – test.cs и helpers.cs, где test.cs указывает на helpers.cs через css_include – по идее при изменении хотя бы одного надо сгенерировать проекты заново. На данный момент — это сделано через additional dependencies, но ничего не мешает пользователю написать два раза projectScript для обеих скриптов. Очень удобно, например, генерировать проекты по Ctrl+F7, сразу же при изменении проекта.
Тестирование.
Как я вообще понял программисты нынче не особо любят делать софт без тестирования, т.к. если сложный код, надо как-то отслеживать что бы ничего не поломалось. С одной стороны, я их понимаю, с другой стороны — не особо охота корпеть над каждым тест кайсом. В общем подумал я подумал и сделал простенькую поддержку unit testинга.
Сначала проект был просто тестируем, но мне хотелось ещё измерять и code coverage, в итоге я слегка обновил тестирование так что бы тестирование можно было запускать через [TestMethod] (один метод) для того что бы измерить code coverage. На данный момент у меня план с каждым бафиксом и улучшением увеличивать code coverage. В последнем committe он был 81.13%, и это можно отслеживать по commit history, я помечаю в commentах (cc: 81.13%).
Все-таки вам наверное интересно как можно написать один метод для тестирования и тестировать кучу функций?
Основная идея в том что если вы обычно пишете какую то тестовую функцию в тестовой аппликации, то я вытащил тестовую функцию за саму syncProj утилиту, спас отдельным файлом (например NoProjectSelected.cs), а также добавил механизм записи outputа консольной аппликации.
Скажем запускаем мы NoProjectSelected.cs, как полагается с command line:
> syncProj.exe NoProjectSelected.cs
А syncProj печатает нам такое:
NoProjectSelected.cs :
NoProjectSelected.cs(10,13): error: Project not specified (Use project(«name» to specify new project)
Все что напечатано, и является результатом тестирования. Результат тестирования спасается в отдельный файл. На момент разработки программист знает, что программа должна напечатать – и он либо одобряет это или нет. И ещё один вариант – это если это новый тест.
В итоге если бы NoProjectSelected.cs был бы новым тестом, syncProj тестирование выдало бы:
Если результат тестирования уже имеется, но отличается – то сообщение примерно то же:
Если выбирается Yes, то создается файл NoProjectSelected.cs.accepted_log.txt который содержит то что утилита отпечатала на экране.
Если выбирается No – то запускается программу для сравнения обычных текстовых файлов. (WinMerge например)
Ну и естественно если происходит heavy refactoring, то программисту достаточно просто «одобрить» новые результаты тестирования.
При минимальных затратах на тестирование, достигается максимальный результат.
Ну и в принципе все – думаю, что C# script позволяет достаточно гибкий подход к созданию проектов – т.е. когда проекты становятся сложными, то соответственно и сам C# скрипт становится сложнее – появляются функции, появляются helperы, и так далее.
Как пример могу показать вот это. Где плагин проекты уже создаются с функциями.
Да, знаю, cmake и premake тоже позволяет писать функции, но нет возможности его дебаггировать.
syncProj правда пока что работает только под Windows, и признает только Visual Studio проекты, но у меня пока что не было необходимости портовать куда-либо ещё. Я попытался довести код до идеала – он полностью откомментирован, так что, если найдутся энтузиасты продолжить мое дело, с удовольствием введу в курс дела.
Да, syncProj дружит с Visual Studio, так что при ошибках можно нажать два раза на строчку где ошибка произошла, и попасть туда где надо исправлять код. Ну да, и если этого не достаточно, то C# script tools, и debug.
Если потребуется что профиксить, сообщайте ошибки, профиксю – ну и да, можно присоединится и к разработке самого syncProj.
Автор проекта: Tarmo Pikaro, tapika-(at)-yahoo-dot-com
Автор: Tarmik