среда, 20 мая 2015 г.

Анализ зависимостей в проекте своими руками

Ранее я уже писал про скрипт, который позволяет построить граф зависимостей между проектами Visual Studio.
В последней версии я добавил поддержку C# проектов. А теперь я расскажу как создавался этот скрипт.

Первое что мне понадобилось сделать — это провести анализ файлов sln и файлов проектов, которые там прописываются. С sln файлами сразу повезло, т.к. в разных версиях студии (начиная с Visual Studio 2008) список проектов выглядит одинаково, как набор строк следующего вида:
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "BaseClasses Apr05", "..\..\3rd_party\BaseClasses_Apr05\baseclasses90.vcproj", "{2A939EC4-235F-4879-9D38-C865FFD54D82}"
EndProject
Вытащить список всех таких строк можно одной строкой на PowerShell:
Select-String -LiteralPath $slnFile -Pattern "Project\(.*vcproj"
Понятно, что $slnFile — это путь к файлу, а "Project\(.*vcproj" — регулярное выражение для выборки строк. В файле sln все пути относительные. Следующей задачей будет вытащить относительные пути из полученных строк и на их основе построить полные пути к файлам. Сделать это можно еще одной строкой:
Join-Path -Path $slnPath -ChildPath ($s -split ',')[1].Trim().Replace('"','')
Здесь предполагается, что $s содержит одну из ранее полученных строк. Понятно, что происходит разделение на подстроки, которые разделены запятой, командой ($s -split ','). Вторая подстрока (получаем с помощью [1]) как раз содержит относительный путь. Удаляем лишние пробелы с помощью Trim и удаляем кавычки с помощью Replace. Потом к относительному пути прикрепляем путь к sln файлу командой Join-Path. Обернув это в цикл, получили набор путей к проектам. Теперь время заняться файлами проектов, чтобы найти зависимости между ними.

Microsoft в момент перехода на Vistual Studio 2010 поменяла формат проектных файлов. Но хорошо то, что файлы солюшенов и в VS2008 и в VS2010 имеют XML формат. Это позволяет использовать язык запросов XPath, чтобы легко получить нужное поле. В PowerShell это делается командой Select-Xml. С ее помощью из файла проекта несложно вытащить зависимости. Также, хочется, чтобы разные типы проектов в конечном графе отображались разными цветами. Для этого надо выяснить какие типы бывают. Это можно сделать, натравив следующую команду на каталог с кучей проектов:
PS D:\test> Get-ChildItem -Path . -Filter *.vcxproj -Recurse | Select-Xml -XPath "//*[local-name()=`"ConfigurationType`"]/text()" | % { Write-Output $_.Node.Value }
Можно заметить, что в PowerShell поток с результатами выполнения команд удобно перенаправлять в следующую команду с помощью символа «|». Команда Get-ChildItem помогает найти все файлы типа vcxproj, а Select-Xml выбирает из них тип конфигурации. Для каждого файла выводим тип проекта на экран. При большом количестве файлов вывод получается не очень информативен, но в PowerShell есть команда Group-Object, которая спасает положение группируя результаты. Итого получаем что-то вроде этого:
PS D:\test> Get-ChildItem -Path . -Filter *.vcxproj -Recurse | Select-Xml -XPath "//*[local-name()=`"ConfigurationType`"]/text()" | % { Write-Output $_.Node.Value } | Group-Object

Count Name                      Group
----- ----                      -----
   92 StaticLibrary             {StaticLibrary, StaticLibrary, StaticLibrary, StaticLibrary...}
  287 Application               {Application, Application, Application, Application...}
  424 DynamicLibrary            {DynamicLibrary, DynamicLibrary, DynamicLibrary, DynamicLibrary...}
Аналогично можно сделать для файлов типов vcproj, csproj и других. В vcproj файлов типы конфигураций кодируются числами, но, что за ними стоит, выяснить не сложно открыв проект в Visual Studio.

На данном этапе у нас есть проекты, их типы и связи между ними. Можно формировать GraphViz файл. Делается это очень просто — сразу записываем заголовок следующего вида:
digraph ProjectName {
size="60,60"; rankdir=LR; overlap=false; splines=true; dpi=300;
node[color=mediumorchid3, style=filled, shape=box, fontsize=10, fontcolor=white];
edge[arrowhead=vee, arrowtail=inv, arrowsize=.7, fontsize=10, fontcolor=navy];
labelloc=t; label="Solution: ProjectName.sln"; fontsize=14;
А далее все проекты и связи между ними:
"project1.vcproj" [color=indigo];
"project1.vcproj" -> "project2.vcproj" [color="#C3E500"];
"project2.vcproj" [color=indigo];
Цвет связи привязан к проекту, чтобы было легче читать граф. Т.е. у каждого проекта все исходящие связи одного цвета. Сделать это можно использовав HSL (от англ. Hue, Saturation, Lightness) представление цвета. Фиксируем S и L компоненты, а H меняем при обработке очередного файла проекта. Потом HSL преобразуем в RGB и записываем в выходной файл.

Исходный код функции выбора цвета, а также всего остального скрипта можно найти на GitHub.

Книги по теме:

Кстати, эта статья попала на Хабр.

Комментировать в ВКонтакте