Подтвердить что ты не робот

Определение версии сборки во время события после сборки

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

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

call foo.bat $(AssemblyVersion)

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

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

4b9b3361

Ответ 1

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

Если вы используете событие post-build, вы можете использовать инструмент 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%

Получить файлver.exe отсюда: http://support.microsoft.com/kb/913111

Если вы используете событие pre-build, вы можете извлечь его из файла 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%

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

Ответ 2

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

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

@(VersionNumber)

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

<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>

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

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

@(VersionNumber)

Готово!

Ответ 3

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

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

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>");
        }
    }
}

Ответ 4

Этот ответ является незначительной модификацией ответа Брент Ариас. Его 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>

UPDATE 2017-05-24: Я исправил регулярное выражение таким образом: "1.2.0.0" будет переведен на "1.2.0", а не "1.2", как ранее закодировано.


И чтобы ответить на комментарий Ehryk Apr, вы можете адаптировать регулярное выражение, чтобы сохранить только часть номера версии. В качестве примера, чтобы сохранить "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;))" />

Ответ 5

Я думаю, что самое лучшее, что вы можете сделать, это посмотреть 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;
            }
        }
    }
}

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

Ответ 6

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

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

Ответ 7

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

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

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. Шаг: использование команды post build в нашей среде IDE

После успешного выполнения приложения для вас:

В вашей среде разработки 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"

Ответ 8

Если у вас есть проект библиотеки, вы можете попробовать использовать утилиту 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%

Ответ 9

Мне понадобилось именно это для автоматического помещения числа в файл 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 batch: echo без новой строки). Вы также можете сохранить эту строку в другом текстовом файле и просто скопировать ее в "readme-header.txt".
  • Вторая строка: запустите приложение для получения номера версии с только что созданным exe файлом в качестве параметра и добавьте его вывод в файл заголовка.
  • Третья строка: добавьте в файл заголовка любой другой материал (в данном случае, кредиты). Это также добавляет разрыв строки до конца.

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

Ответ 10

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

$ (ApplicationVersion) сделал работу для меня.

Редактировать:

Хорошо, я только что видел, что Проблема $ (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