在生成后事件期间确定程序集版本
比方说,我想创build一个每个版本附带的静态文本文件。 我想用该版本的版本号(如AssemblyInfo.cs
指定)更新文件,但我不想手动执行此操作。
我希望我可以使用后生成事件,并将版本号提供给batch file,如下所示:
call foo.bat $(AssemblyVersion)
但是我找不到任何合适的variables或macros使用。
有没有办法做到这一点,我已经错过了?
如果您更喜欢使用脚本,这些方法可能也适用于您:
如果您正在使用生成后事件,则可以使用filever.exe工具将其从已构build的程序集中取出:
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,其中一个正常工作。
如果(1)你不想下载或创build一个自定义可执行文件来检索程序集版本,(2)你不介意编辑Visual Studio项目文件,那么有一个简单的解决scheme,允许你使用macros看起来像这样:
@(目标- > '%(版)')
@(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>的例子。 不用担心,重新加载项目后,可以将其重置为真正的构build后事件。
现在按照承诺,程序集版本可用于您的发布后事件与这个macros:
@(VersionNumber)
完成!
作为一种解决方法,我编写了一个托pipe控制台应用程序,它将目标作为参数,并返回版本号。
我仍然有兴趣听到一个更简单的解决scheme – 但我张贴这个,以防其他人认为有用的情况下。
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(); 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>"); } } }
这个答案是对布伦特·阿里亚斯答案的一个小修改。 他的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生成的确切的包文件名,我现在使用这个自适应macros(在VS2015中testing):
<Target Name="PostBuildMacros"> <GetAssemblyIdentity AssemblyFiles="$(TargetPath)"> <Output TaskParameter="Assemblies" ItemName="Targets" /> </GetAssemblyIdentity> <ItemGroup> <VersionNumber Include="$([System.Text.RegularExpressions.Regex]::Replace("%(Targets.Version)", "^(.+?)(\.0+)$", "$1"))" /> </ItemGroup> </Target> <PropertyGroup> <PostBuildEventDependsOn> $(PostBuildEventDependsOn); PostBuildMacros; </PostBuildEventDependsOn> <PostBuildEvent>echo HELLO, THE ASSEMBLY VERSION IS: @(VersionNumber)</PostBuildEvent> </PropertyGroup>
更新2017-05-24:我以这种方式更正了正则expression式:“1.2.0.0”将被转换为“1.2.0”,而不是“1.2”,如前所述。
要回答Ehryk Apr的评论,你可以修改正则expression式来保留版本号的一部分。 作为保留“Major.Minor”的例子,replace:
<VersionNumber Include="$([System.Text.RegularExpressions.Regex]::Replace("%(Targets.Version)", "^(.+?)(\.0+)$", "$1"))" />
通过
<VersionNumber Include="$([System.Text.RegularExpressions.Regex]::Replace("%(Targets.Version)", "^([^\.]+)\.([^\.]+)(.*)$", "$1.$2"))" />
我认为你可以做的最好的事情是看MSBuild和MsBuild扩展包,你应该能够编辑你的解决scheme文件,以便发生后build立事件并写入到你的testing文件。
如果这太复杂,那么你可以简单地创build一个小程序来检查你输出目录中的所有程序集,并在后期构build中执行它,你可以使用variables名称来传递输出目录…例如在后期构build事件中。 ..
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; } } } }
你也可以通过文本文件的位置…
我已经开始添加一个单独的项目,最后build立并添加一个后期构build事件到自己运行的项目。 然后我只是在那里编程执行我的构build步骤。
这样做比这更容易。 然后你可以检查你想要的组件的组件属性。 到目前为止,它的工作非常棒。
从那我明白…
你需要一个生成器来build立事件。
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, ie: 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中使用它作为后期构build命令
在应用程序正在为您运行之后:
在您的开发IDE中,使用以下命令行进行构build后事件。
C:\Projects\pathto\bin\Release\AppCast.exe "C:\Projects\pathto\bin\Release\MyApp.exe" "c:\pathto\www.example.com\root\pathto\updates\AppCast.xml"
我需要这个自动把数字自动input文件夹中的自述文件。 最后,正如温斯顿·史密斯所说的那样,一个小型的外部工具是一个非常好的解决scheme,而且你可以根据自己的需要进行格式化。
这个应用程序输出格式化的版本到控制台。 我在后生成事件中使用它来构build自述文件,通过调用它来将其输出redirect到自述文件。
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-body.txt”。
- 第一行:在我的项目的\ Readme \文件夹中创build“readme-header.txt”文件,并将其中的程序名称。 (
<nul set /p dummyset=
是我在这里find的一个技巧: Windows批处理:回声没有新行 )。 您也可以将此string存储在另一个文本文件中,并将其复制到“readme-header.txt”中。 - 第二行:使用新生成的exe文件作为参数运行版本号检索应用程序,并将其输出添加到头文件。
- 第三行:添加任何其他的东西(在这种情况下,信用)到头文件。 这也增加了一个换行符到最后。
这三者一起给你一个“readme-header.txt”文件,里面有“My Application v1.2.3 by Nyerguds”,后面跟着一个换行符。 然后添加生成date和另一个打开的行,并将头文件和自述文件正文文件一起复制到最终生成文件夹中的一个文件中。 请注意,我特别使用二进制副本,否则会给出奇怪的结果。 您必须确保主体文件在开始时不包含UTF-8字节顺序标记,否则在最终文件中会出现奇怪的字节。