Определить версию сборки во время события после сборки - PullRequest
52 голосов
/ 11 февраля 2010

Допустим, я хотел создать статический текстовый файл, который поставляется с каждым выпуском. Я хочу, чтобы файл обновлялся с помощью номера версии выпуска (как указано в AssemblyInfo.cs), но я не хочу делать это вручную.

Я надеялся, что смогу использовать событие после сборки и передать номер версии в командный файл, например так:

call foo.bat $(AssemblyVersion)

Однако я не могу найти подходящую переменную или макрос для использования.

Есть ли способ достичь этого, что я пропустил?

Ответы [ 10 ]

85 голосов
/ 15 октября 2013

Если (1) вы не хотите загружать или создавать собственный исполняемый файл, который извлекает версию сборки, и (2) вы не возражаете против редактирования файла проекта Visual Studio, то существует простое решение, которое позволяет вам используйте макрос, который выглядит следующим образом:

@ (Цели -> '% (Версия)')

@(VersionNumber)

Для этого разгрузите ваш проект. Если проект где-то определяет свойство , вырежьте его из проекта и временно сохраните в другом месте (блокнот?). Затем в самом конце проекта, перед конечным тегом, поместите это:

<Target Name="PostBuildMacros">
  <GetAssemblyIdentity AssemblyFiles="$(TargetPath)">
    <Output TaskParameter="Assemblies" ItemName="Targets" />
  </GetAssemblyIdentity>
  <ItemGroup>
    <VersionNumber Include="@(Targets->'%(Version)')"/>
  </ItemGroup>
</Target>
<PropertyGroup>
  <PostBuildEventDependsOn>
    $(PostBuildEventDependsOn);
    PostBuildMacros;
  </PostBuildEventDependsOn>    
  <PostBuildEvent>echo HELLO, THE ASSEMBLY VERSION IS: @(VersionNumber)</PostBuildEvent>
</PropertyGroup>

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

Теперь, как и было обещано, версия сборки доступна для вашего события после сборки с этим макросом:

@(VersionNumber)

Готово!

16 голосов
/ 04 августа 2010

Если вы предпочитаете создавать сценарии, эти методы могут также работать для вас:

Если вы используете событие после сборки, вы можете использовать инструмент filever.exe, чтобы извлечь его из уже созданной сборки:

for /F "tokens=4" %%F in ('filever.exe /B /A /D bin\debug\myapp.exe') do (
  set VERSION=%%F
)
echo The version is %VERSION%

Получите filever.exe отсюда: http://support.microsoft.com/kb/913111

Если вы используете событие перед сборкой, вы можете извлечь его из файла AssemblyInfo.cs следующим образом:

set ASMINFO=Properties\AssemblyInfo.cs
FINDSTR /C:"[assembly: AssemblyVersion(" %ASMINFO% | sed.exe "s/\[assembly: AssemblyVersion(\"/SET CURRENT_VERSION=/g;s/\")\]//g;s/\.\*//g" >SetCurrVer.cmd
CALL SetCurrVer.cmd
DEL SetCurrVer.cmd
echo Current version is %CURRENT_VERSION%

Используется инструмент sed командной строки unix, который можно загрузить из многих мест, например, здесь: http://unxutils.sourceforge.net/ - iirc, с которым все работает нормально.

12 голосов
/ 11 февраля 2010

В качестве обходного пути я написал управляемое консольное приложение, которое принимает цель в качестве параметра и возвращает номер версии.

Мне все еще интересно услышать более простое решение - но я публикую это на тот случай, если кто-нибудь еще найдет его полезным.

using System;
using System.IO;
using System.Diagnostics;
using System.Reflection;

namespace Version
{
    class GetVersion
    {
        static void Main(string[] args)
        {
            if (args.Length == 0 || args.Length > 1) { ShowUsage(); return; }

            string target = args[0];

            string path = Path.IsPathRooted(target) 
                                ? target 
                                : Path.GetDirectoryName(Process.GetCurrentProcess().MainModule.FileName) + Path.DirectorySeparatorChar + target;

            Console.Write( Assembly.LoadFile(path).GetName().Version.ToString(2) );
        }

        static void ShowUsage()
        {
            Console.WriteLine("Usage: version.exe <target>");
        }
    }
}
7 голосов
/ 04 мая 2017

Этот ответ является незначительной модификацией ответа Брента Ариаса. Его PostBuildMacro работал довольно хорошо для меня до обновления версии Nuget.exe.

В последних выпусках Nuget обрезает несущественные части номера версии пакета, чтобы получить семантическую версию, например "1.2.3". Например, версия сборки "1.2.3.0" отформатирована Nuget.exe "1.2.3". И «1.2.3.1» имеет формат «1.2.3.1», как и ожидалось.

Поскольку мне нужно определить точное имя файла пакета, сгенерированное Nuget.exe, я теперь использую этот адаптированный макрос (протестирован в VS2015):

<Target Name="PostBuildMacros">
  <GetAssemblyIdentity AssemblyFiles="$(TargetPath)">
    <Output TaskParameter="Assemblies" ItemName="Targets" />
  </GetAssemblyIdentity>
  <ItemGroup>
    <VersionNumber Include="$([System.Text.RegularExpressions.Regex]::Replace(&quot;%(Targets.Version)&quot;, &quot;^(.+?)(\.0+)$&quot;, &quot;$1&quot;))" />
  </ItemGroup>
</Target>
<PropertyGroup>
  <PostBuildEventDependsOn>
    $(PostBuildEventDependsOn);
    PostBuildMacros;
  </PostBuildEventDependsOn>    
  <PostBuildEvent>echo HELLO, THE ASSEMBLY VERSION IS: @(VersionNumber)</PostBuildEvent>
</PropertyGroup>

ОБНОВЛЕНИЕ 2017-05-24: Я исправил регулярное выражение следующим образом: «1.2.0.0» будет переводиться в «1.2.0», а не в «1.2», как ранее закодировано.


И чтобы ответить на комментарий Эрика Апра, вы можете адаптировать регулярное выражение, чтобы сохранить только некоторую часть номера версии. В качестве примера, чтобы сохранить «Major.Minor», заменить:

<VersionNumber Include="$([System.Text.RegularExpressions.Regex]::Replace(&quot;%(Targets.Version)&quot;, &quot;^(.+?)(\.0+)$&quot;, &quot;$1&quot;))" />

К

<VersionNumber Include="$([System.Text.RegularExpressions.Regex]::Replace(&quot;%(Targets.Version)&quot;, &quot;^([^\.]+)\.([^\.]+)(.*)$&quot;, &quot;$1.$2&quot;))" />
1 голос
/ 07 августа 2018

Если у вас есть проект библиотеки, вы можете попробовать использовать утилиту WMIC (доступно в Windows). Вот пример. Хорошо, что вам не нужно использовать какие-либо внешние инструменты.

SET pathFile=$(TargetPath.Replace("\", "\\"))

FOR /F "delims== tokens=2" %%x IN ('WMIC DATAFILE WHERE "name='%pathFile%'" get  Version /format:Textvaluelist')  DO (SET dllVersion=%%x)
echo Found $(ProjectName) version %dllVersion%
1 голос
/ 24 января 2013

Из того, что я понимаю ...

Вам нужен генератор для событий после сборки.

1. Шаг: написание генератора

/*
* Author: Amen RA
* # Timestamp: 2013.01.24_02:08:03-UTC-ANKH
* Licence: General Public License
*/
using System;
using System.IO;

namespace AppCast
{
    class Program
    {
        public static void Main(string[] args)
        {
            // We are using two parameters.

            // The first one is the path of a build exe, i.e.: C:\pathto\nin\release\myapp.exe
            string exePath = args[0];

            // The second one is for a file we are going to generate with that information
            string castPath = args[1];

            // Now we use the methods below
            WriteAppCastFile(castPath, VersionInfo(exePath));
        }


        public static string VersionInfo(string filePath)
        {
            System.Diagnostics.FileVersionInfo myFileVersionInfo = System.Diagnostics.FileVersionInfo.GetVersionInfo(filePath);
            return myFileVersionInfo.FileVersion;
        }


        public static void WriteAppCastFile(string castPath, string exeVersion)
        {
            TextWriter tw = new StreamWriter(castPath);
            tw.WriteLine(@"<?xml version=""1.0"" encoding=""utf-8""?>");
            tw.WriteLine(@"<item>");
            tw.WriteLine(@"<title>MyApp - New version! Release " + exeVersion + " is available.</title>");
            tw.WriteLine(@"<version>" + exeVersion + "</version>");
            tw.WriteLine(@"<url>http://www.example.com/pathto/updates/MyApp.exe</url>");
            tw.WriteLine(@"<changelog>http://www.example.com/pathto/updates/MyApp_release_notes.html</changelog>");
            tw.WriteLine(@"</item>");
            tw.Close();
        }
    }
}

2. Шаг: использование его в качестве команды после сборки в нашей IDE

После удовлетворительного запуска приложения:

В вашей среде разработки используйте следующую командную строку для событий после сборки.

C:\Projects\pathto\bin\Release\AppCast.exe "C:\Projects\pathto\bin\Release\MyApp.exe" "c:\pathto\www.example.com\root\pathto\updates\AppCast.xml"
1 голос
/ 25 августа 2010

Я начал добавлять отдельный проект, который собирался последним, и добавлять событие пост-сборки в тот проект, который запускается сам. Затем я просто выполняю свои шаги пост-сборки программно там.

Намного легче делать такие вещи. Затем вы можете просто проверить атрибуты сборки любой сборки, которую вы хотите. Пока это работает довольно круто.

1 голос
/ 11 февраля 2010

Я думаю, что лучшее, что вы можете сделать, это взглянуть на MSBuild и MsBuild Extension Pack , у вас должна быть возможность редактировать файл решения таким образом, чтобы событие после сборки происходило и записывалось в ваш тестовый файл. *

Если это слишком сложно, то вы можете просто создать небольшую программу, которая проверяет все сборки в выходном каталоге и выполнять ее при посткомпиляции, вы можете передать в выходной каталог, используя имя переменной ... например, в посте построить событие ...

AssemblyInspector.exe "$ (TargetPath)"

class Program
{
    static void Main(string[] args)
    {
        var assemblyFilename = args.FirstOrDefault();
        if(assemblyFilename != null && File.Exists(assemblyFilename))
        {
            try
            {
                var assembly = Assembly.ReflectionOnlyLoadFrom(assemblyFilename);
                var name = assembly.GetName();

                using(var file = File.AppendText("C:\\AssemblyInfo.txt"))
                {
                    file.WriteLine("{0} - {1}", name.FullName, name.Version);
                }
            }
            catch (Exception ex)
            {
                throw;
            }
        }
    }
}

Вы также можете передать местоположение текстового файла ...

0 голосов
/ 27 апреля 2019

Я искал ту же функцию, и я нашел решение на MSDN. https://social.msdn.microsoft.com/Forums/vstudio/de-DE/e9485c92-98e7-4874-9310-720957fea677/assembly-version-in-post-build-event?forum=msbuild

$ (ApplicationVersion) выполнил работу за меня.

Edit:

Хорошо, я только что увидел, что Проблема $ (ApplicationVersion) не из AssemblyInfo.cs, это PublishVersion, определенный в Свойствах проекта. Это все еще делает работу для меня простым способом. Так что, может, кому-то это тоже нужно.

Другое решение:

Вы можете вызвать скрипт PowerShell в PostBuild, здесь вы можете прочитать AssemblyVersion непосредственно из вашей сборки. Я вызываю скрипт с TargetDir в качестве параметра

Команда PostBuild:

PowerShell -ExecutionPolicy Unrestricted $(ProjectDir)\somescript.ps1 -TargetDir $(TargetDir)

Сценарий PowerShell:

param(
    [string]$TargetDir
)

$Version = (Get-Command ${TargetDir}Example.exe).FileVersionInfo.FileVersion

Таким образом, вы получите версию из AssemblyInfo.cs

0 голосов
/ 06 февраля 2016

Мне нужно было именно это для автоматического помещения числа в файле readme в выходной папке. В конце концов, как показал Уинстон Смит, небольшой внешний инструмент - очень хорошее решение для этого, и у него есть то преимущество, что вы можете отформатировать его так, как хотите.

Это приложение выводит отформатированную версию на консоль. Я использовал его в своих событиях после сборки, чтобы создать файл readme, вызвав его с >>, чтобы перенаправить вывод в файл readme.

public class GetVerNum
{
    static void Main(String[] args)
    {
        if (args.Length == 0)
            return;
        try
        {
            FileVersionInfo ver = FileVersionInfo.GetVersionInfo(args[0]);
            String version = "v" + ver.FileMajorPart.ToString() + "." + ver.FileMinorPart;
            if (ver.FileBuildPart > 0 || ver.FilePrivatePart > 0)
                version += "." + ver.FileBuildPart;
            if (ver.FilePrivatePart > 0)
                version += "." + ver.FilePrivatePart;
            Console.Write(version);
        }
        catch { }
    }
}

Мои события после сборки:

<nul set /p dummyset=My Application > "$(ProjectDir)\Readme\readme-header.txt"
"$(ProjectDir)\Readme\GetVersionNumber.exe" "$(TargetPath)" >>"$(ProjectDir)\Readme\readme-header.txt"
echo  by Nyerguds>>"$(ProjectDir)\Readme\readme-header.txt"
echo Build date: %date% %time% >> "$(ProjectDir)\Readme\readme-header.txt"
echo.>>"$(ProjectDir)\Readme\readme-header.txt"
copy /b "$(ProjectDir)\Readme\readme-header.txt" + "$(ProjectDir)\Readme\readme-body.txt" "$(TargetDir)\$(ProjectName).txt"

Я поместил все связанные с созданием readme материалы в папку \ Readme \ моего проекта; приложение, содержащее приведенный выше код, и «readme-body.txt», содержащий фактический материал readme.

  • Первая строка: создайте файл «readme-header.txt» в папке \ Readme \ моего проекта и поместите в него имя программы. (<nul set /p dummyset= - это трюк, который я нашел здесь: Пакет Windows: эхо без новой строки ). Вы также можете сохранить эту строку в другом текстовом файле и просто скопировать ее в «readme-header.txt».
  • Вторая строка: запустите приложение для извлечения номера версии, используя только что сгенерированный exe-файл в качестве параметра, и добавьте его вывод в файл заголовка.
  • Третья строка: добавить любой другой материал (в данном случае, кредиты) в файл заголовка. Это также добавляет разрыв строки до конца.

Эти три вместе дают вам файл «readme-header.txt» с «My Application v1.2.3 by Nyerguds», за которым следует разрыв строки. Затем я добавляю дату сборки и еще одну открытую строку и копирую заголовочный файл и файл тела readme вместе в один файл в папке окончательной сборки. Обратите внимание, что я специально использую двоичную копию, иначе она дает странные результаты. Вы должны убедиться, что основной файл не содержит метки порядка байтов UTF-8 в начале, или вы получите странные байты в вашем конечном файле.

...