[Из песочницы] И ещё пару слов о SandCastle, TFS и магии…
По мотивам только-только проскочившей публикации «Sandcastle и SHFB» решил поделиться своими болями и печалями, а также и success-story при работе с этим продуктом.В тексте не будет скриншотов с подписями «нажмите кнопку ДОБАВИТЬ» и описания настроек/плагинов.В тексте будет описание процесса реализации конкретного кейса: сборки документации SHFB в TFS.Итак, имеющееся окружение:
Team Foundation Server 2013 VisualStudio 2014 В чём проблемаМоей первой и главной заботой при организации документации было максимально отдаление разработчика от дальнейшего процесса построения документации. Т.е. чтоб условный джуниор мог писать код, коммитить его в TFS, а документация сама собиралась уже при удачном билде релизной версии.Так мы подходим к первой проблеме. Заключается она как раз-таки в это джуниор-разработчике. Как заставить его ставить комментарии? С этим нам поможет…
Stylecop А точнее StyleCop checkin policy. Мне пришлось немного допилить его, чтоб забирал конфиг-файл прям из TFS (чтоб не разливать всем разработчикам новую версию каждый раз). Но в целом принцип понятен, да? Настраиваем нужные нам правила, касающиеся документации, включаем policy и настраиваем в TFS оповещение на каждый Policy override — мы не можем совсем запретить его (технически можем, но случаи, когда действительно нужно будет сделать override, превратятся в совершенно запредельную боль), но можем вырисовываться изниоткуда над плечом разработчика через минуту после того, как он нажал «Override policy» и доходчиво объяснять, в чём он неправ. Удобно. Наглядно. Внушает.Итак, с чекинами и форматированием кода разобрались. Едем дальше.
Джуниор регулярно ноет, что ему надоело писать одно и то же. Регулярно возникают ситуации, когда в методе исправили описание, а в его пяти перегрузках — забыли. С этим нам поможет…
///
///
///
Нельзя наследовать описания перегруженных методов в рамках одного класса/интерфейса;
Не всегда достаточно повесить метку у унаследованного метода — иногда требуется ещё поставить его на самом классе, чтоб SHFB догадался, что его предков надо бы просканировать;
Необходимо руками добавлять библиотеки с базовыми классами в DocumentationSources (об этом ниже);
Необходимы дополнительные манипуляции для IntelliSense, потому что «из коробки» в результирующем .xml получаются эти самые
TFS Build Итак, что мы хотим? А хотим мы, чтоб для нашей сборки вместе со всеми проектами в ней была документация.Для начала ставим SHFB на сервер, где крутится наш билд-агент. Иначе работать не будет. Он использует переменные окружения, кучу своих локальных файлов… В-общем надо ставить.Далее открываем gui SHFB, настраиваем проект, добавляем в качестве Documentation Sources наш .sln-файл, сохраняем. Читаем инструкцию. Всё выглядит довольно тривиально. Создаём файлик build.proj по инструкции, чтоб обмануть пляски с OutputDir (без него пробовал — там такой ад с путями начинается, что правда — лучше сделать лишний .proj-обёртку):
Вот ведь ленивая скотинка-то. Ладно, сейчас обучим новым трюкам. Лезем в файл, видим там
Запускаем ещё раз… Ух ты — собирается! Так, т.е. у нас уже есть проект, который можно настраивать в SHFB, включая новые .sln, а потом просто запускать билд в TFS и получать на выходе chm + html?! Прекрасно. Смотрим… ой, что такое? В логе ошибки:
SHFB: Warning GID0002: No comments found for cref 'T: System.Web.Http.Dependencies.IDependencyResolver' on member 'T: My.Api.Server.DependencyResolver'
Смотрим код:
///
///
Никакие плагины не помогут. Никакие References не учитываются. Никакая магия MSBuild, позволяющая на лету модифицировать переменные, тоже не поможет. Потому что в конце концов запускается файлик GenerateInheritedDocs.exe, который тупо парсит файл .shfbproj, достаёт через XPath из него содержимое ноды и перебирает указанные там файлы. Всё, приехали. Я попытался, было, распилить это мракобесие, но там на каждом шагу вставлена прямая работа с файлом — каждый компонент сам по себе лезет в него и читает то, что ему надо — ни о каком общем контексте речи не идёт. Так что я эту затею забросил.
Так что если хотите, чтоб в вашу документацию вставились строчки из компонентов, которые вы используете в проекте (в данном случае я хотел, чтоб там было описание методов из System.Web.Http), то придётся включить эти компоненты в DocumentationSources.
Да, можно включать не саму сборку, а только .xml-файл от неё. От этого не сильно легче.На этом месте мы явно получаем геморрой с поддержкой файла .shfbproj — надо обновлять его каждый раз, когда используются новые компоненты. Надо обновлять его каждый раз, когда обновляем nuget-пакет — потому как меняется путь к файлу! Ужас-ужас. И никак не автоматизировать же.
Нет, конечно, можно сделать такой target, чтоб перебирал содержимое /packages/** и вытаскивал оттуда все .xml… А, нет, нельзя — каждый пакет же может содержать несколько версий под разные версии .net runtime. Значит, надо заходить с другого конца — после сборки каждого проекта перебирать всё содержимое $(OutDir), и все xml/dll-файлы оттуда вписывать в… А вот куда?
Здесь можно немного обыграть: поддерживается включение .shfbproj в качестве Documentation Source. Так что можно на лету создать файл минимального содержимого, в котором будет только DocumentationSources, а его держать единственным включением в основной файл… Но чем-то попахивает от этого, мне кажется.
К (не-)счастью я всем этим занимался в качестве факультатива и из личной заинтересованности, вскоре пришлось заняться другим проектом, а это всё так и осталось в таком виде — собирается, публикуется, но вот обновлять/поддерживать — боль.
Что в остатке? По кнопке «Build project» (или само по правилу Continuos Integration) собирается и публикуется документация в .chm и html для проекта. Это хорошо; По пути сделали правило для контроля нерадивых джуниоров, чтоб они быстрее приходили к просветлению. Это тоже хорошо; Поддерживать и развивать это будет кто-то другой. Просто прекрасно.