在构建后事件期间确定程序集版本

时间:2010-02-11 09:55:40

标签: c# .net visual-studio deployment visual-studio-2005

假设我想创建一个随每个版本一起提供的静态文本文件。我希望使用版本的版本号(在AssemblyInfo.cs中指定)更新文件,但我不想手动执行此操作。

我希望我可以使用post-build事件并将版本号提供给这样的批处理文件:

call foo.bat $(AssemblyVersion)

但是我找不到任何合适的变量或宏来使用。

有没有办法实现这一点,我错过了?

10 个答案:

答案 0 :(得分:85)

如果(1)您不想下载或创建检索程序集版本的自定义可执行文件,并且(2)您不介意编辑Visual Studio项目文件,那么有一个简单的解决方案允许您使用看起来像这样的宏:

@(Targets->'%(Version)')

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

此代码段有一个示例&lt; PostBuildEvent&gt;已经在里面了。不用担心,您可以在重新加载项目后将其重置为真正的构建后事件。

现在按照承诺,使用此宏可以使用汇编版本进行构建后事件:

@(VersionNumber)

完成!

答案 1 :(得分:16)

如果您更喜欢编写脚本,这些方法可能对您有用:

如果您正在使用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%

从此处获取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%

这使用了unix命令行工具sed,您可以从许多地方下载,例如:http://unxutils.sourceforge.net/ - iirc可以正常工作。

答案 2 :(得分:12)

作为一种解决方法,我编写了一个托管控制台应用程序,它将目标作为参数,并返回版本号。

我仍然有兴趣听一个更简单的解决方案 - 但我发布这个以防其他人发现它有用。

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

答案 3 :(得分:7)

这个答案是布伦特阿里亚斯答案的一个小修改。他的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”。

要回答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;))" />

答案 4 :(得分:1)

我认为您可以做的最好的事情是查看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;
            }
        }
    }
}

您也可以传入文本文件位置...

答案 5 :(得分:1)

我已经开始添加一个单独的项目,该项目最后构建并将一个post build事件添加到自己运行的项目中。然后我只是在那里以编程方式执行我的后期构建步骤。

这使得做这样的事情变得容易多了。然后,您可以检查所需的任何程序集的程序集属性。到目前为止,它的工作非常棒。

答案 6 :(得分:1)

从我的理解......

您需要一个生成器用于构建后事件。

<强> 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中将其用作后期构建命令

应用程序运行满意之后:

在开发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"

答案 7 :(得分:1)

如果您有一个图书馆项目,则可以尝试使用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%

答案 8 :(得分:0)

我需要将此数字自动放入输出文件夹中的自述文件中。最后,正如温斯顿史密斯所展示的那样,一个小型的外部工具是一个非常好的解决方案,它具有你可以根据需要格式化它的优势。

此应用程序将格式化版本输出到控制台。我在后期构建事件中使用它来构建自述文件,方法是使用>>调用它来将其输出重定向到自述文件。

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 \文件夹中;包含上述代码的应用程序,以及&#34; readme-body.txt&#34;包含实际的自述文件。

  • 第一行:创建&#34; readme-header.txt&#34;在我的项目的\ Readme \文件夹中的文件,并将程序名称放在其中。 (<nul set /p dummyset=是我在这里找到的一个技巧:Windows batch: echo without new line)。您还可以将此字符串存储在另一个文本文件中,然后将其复制到&#34; readme-header.txt&#34;代替。
  • 第二行:使用新生成的exe文件作为参数运行版本号检索应用程序,并将其输出添加到头文件中。
  • 第三行:将任何其他内容(在本例中为Cred)添加到头文件中。这也为最后添加了换行符。

这三个一起给你一个&#34; readme-header.txt&#34;使用&#34;我的应用程序v1.2.3由Nyerguds&#34;提交文件,然后在其中进行换行。然后我添加构建日期和另一个打开的行,并将头文件和自述文件文件一起复制到最终构建文件夹中的一个文件。请注意,我特意使用二进制副本,否则它会产生奇怪的结果。您必须确保正文文件在开头不包含UTF-8字节顺序标记,否则您的最终文件中会出现奇怪的字节。

答案 9 :(得分:0)

我寻找了相同的功能,并且在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。它仍然以一种简单的方式为我完成了工作。所以也许有人也需要它。

另一种解决方案:

您可以在PostBuild上调用PowerShell脚本,在这里您可以直接从Assembly中读取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中获取版本