NET автоверсионность сборок
Напоминалка самому себе. Ибо постоянно искать подобную информацию ну так себе затея.
Есть задача - автоматически проставлять версию сборки, загружаемой в *CAD. При этом автоматом должен увеличиваться номер ревизии. Хоть до упора ![]()
По ходу дела - для "сопутствующих" сборок надо повторить поведение, как бы это было в NET Framework, когда можно было назначить только мажор и минор, остальное каким-то магическим вариантом вычислялось самостоятельно.
Уточнение: все csproj-файлы выполнены в SDK-стиле. Для NET Standard / NET5+ это и так в норме, а вот для NET Framework придется поколдовать слегка. Ну или пересоздать проект ![]()
Для "загружаемой" в CAD придется слегка поколдовать. Рядом с csproj создаю отдельный файл version.txt, в котором и прописываю, к примеру, версию "3.0.0.0". Как только будет выполнена компиляция в Release, версия загружаемой сборки должна стать 3.0.0.0, а в version.txt надо записать 3.0.0.1. Если понадобится менять мажор, минор или билд - добро пожаловать в редактирование verion.txt (понятно, что файлу надо установить свойство "не копировать в выходной каталог при компиляции"). А в csproj прописать примерно следующее:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 | <Target Name="IncrementVersion" BeforeTargets="BeforeBuild" Condition="'$(Configuration)' == 'Release'"> <PropertyGroup> <LastVersionFile>$(IntermediateOutputPath)last_build_version.txt</LastVersionFile> <PsScript>$(IntermediateOutputPath)increment_version.ps1</PsScript> </PropertyGroup> <WriteLinesToFile File="$(PsScript)" Lines="$verFile = $args[0] $lastFile = $args[1] $desired = Get-Content $verFile -ErrorAction SilentlyContinue if (-not $desired) { $desired = '1.0.0.0' } $desired = $desired.Trim() if (Test-Path $lastFile) { $last = (Get-Content $lastFile -ErrorAction Stop).Trim() } else { $last = $null } if ($desired -eq $last) { $parts = $desired -split '\.' if ($parts.Count -eq 4) { $parts[3] = ([int]$parts[3]) + 1 $new = $parts -join '.' } else { $new = $desired } Set-Content -Path $verFile -Value $new Set-Content -Path $lastFile -Value $new Write-Output $new } else { Set-Content -Path $lastFile -Value $desired Write-Output $desired }" Overwrite="true" Encoding="utf-8" /> <Exec Command="powershell -NoProfile -ExecutionPolicy Bypass -File "$(PsScript)" "$(MSBuildProjectDirectory)\version.txt" "$(LastVersionFile)"" ConsoleToMSBuild="true"> <Output TaskParameter="ConsoleOutput" PropertyName="IncrementedVersion" /> </Exec> </Target> |
Плюс в PropertyGroup добавить:
1 2 3 | <Version>$(IncrementedVersion.Trim())</Version> <AssemblyVersion>$(Version)</AssemblyVersion> <FileVersion>$(Version)</FileVersion> |
Выглядит как черная магия, согласен. Чисто по логике:
- Самая первая попытка сборки Release увидит, что в каталоге \obj\Release (это если никакие дополнительные настройки не были изменены) файл last_build_version.txt отсутвует. То есть это типа изменение. Читается файл version.txt и устанавливается версия для сборки из файла version.txt
- Следующая сборка видит, что файлы version.txt и last_build_version.txt одинаковы (если last_build.version.txt вообще существует
). Оба файла перезаписать, увеличив номер ревизии. И увеличенный номер и назначить как версию.
По идее даже очистка каталогов \obj и \bin подход разрушить не должна. У меня по крайней мере пока все работает как часы ![]()
Для "сопутствующих" сборок ситуация попроще. Достаточно в csproj-файл в разделе PropertyGroup добавить следующее:
1 2 3 4 5 6 | <VersionPrefix>3.0</VersionPrefix> <Build>$([System.DateTime]::Now.DayOfYear)</Build> <Revision>$([System.DateTime]::Now.TimeOfDay.TotalMinutes.ToString("F0"))</Revision> <AssemblyVersion>$(VersionPrefix).$(Build).$(Revision)</AssemblyVersion> <Version>$(VersionPrefix).$(Build).$(Revision)</Version> <FileVersion>$(VersionPrefix).$(Build).$(Revision)</FileVersion> |
Билд будет высчитываться как количество дней от начала года, а ревизия - как количество минут с полуночи текущей даты.
Если обновление dll выполняется лиспом (лично у меня достаточно частая ситуация, если честно), то чем сравнивать даты dll на локальной машине и серверной версии, значительно проще получать данные о версии сборки прямо из текстового файлика, который автоматом будет создаваться рядом с dll (прописывать текст надо тоже в csproj):
1 2 3 | <Target Name="CreateVersionFile" AfterTargets="Build" Condition="'$(Configuration)' == 'Release'"> <WriteLinesToFile File="$(TargetDir)$(TargetName).txt" Lines="$(Version)" Overwrite="true" Encoding="utf-8" /> </Target> |
P.S. Может, когда-нибудь и удастся дорасти до более вменяемых вариантов. Пока что живу так, кайфую. Хотя и начинаю уже подозревать, что есть более вменяемые и простые механизмы решения подобных задач ![]()