假设我想创建一个随着每个版本发布一起运送的静态文本文件。我希望该文件能使用版本号来更新(在AssemblyInfo.cs
中指定),但是我不想手动进行操作。
我希望可以使用一个构建后事件并将版本号传递给像这样的批处理文件:
call foo.bat $(AssemblyVersion)
然而我找不到任何合适的变量或宏来使用。
我是否错过了实现这一点的方法?
假设我想创建一个随着每个版本发布一起运送的静态文本文件。我希望该文件能使用版本号来更新(在AssemblyInfo.cs
中指定),但是我不想手动进行操作。
我希望可以使用一个构建后事件并将版本号传递给像这样的批处理文件:
call foo.bat $(AssemblyVersion)
然而我找不到任何合适的变量或宏来使用。
我是否错过了实现这一点的方法?
如果 (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>
这段代码中已经包含了一个<PostBuildEvent>的示例。不用担心,您可以在重新加载项目后将其重置为实际的后期构建事件。
现在,如承诺所述,使用此宏可使程序集版本在您的后期构建事件中可用:
@(VersionNumber)
完成!
Major.Minor
,或者分别获取Major
和Minor
。但是似乎无论我尝试什么都不起作用。我该如何分别获取这些属性? - Ehryk如果您喜欢使用脚本,则这些方法也可能适用于您:
如果您正在使用后生成事件,您可以使用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/ - 如果我没记错的话,那个工具还不错。
作为一种解决方法,我编写了一个托管控制台应用程序,它以目标作为参数并返回版本号。
如果有更简单的解决方案,我仍然很感兴趣 - 但我发布这篇文章是为了让其他人发现它的用处。
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>");
}
}
}
这个答案是Brent Arias的答案的一个小修改。他的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("%(Targets.Version)", "^(.+?)(\.0+)$", "$1"))" />
</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("%(Targets.Version)", "^(.+?)(\.0+)$", "$1"))" />
通过
<VersionNumber Include="$([System.Text.RegularExpressions.Regex]::Replace("%(Targets.Version)", "^([^\.]+)\.([^\.]+)(.*)$", "$1.$2"))" />
我不知道为什么,但Brent Arias的宏对我不起作用(@(VersionNumber)
总是为空):(。.Net6 VS2022。最终我使用了稍微修改过的版本:
<Target Name="GetVersion" AfterTargets="PostBuildEvent">
<GetAssemblyIdentity AssemblyFiles="$(TargetPath)">
<Output TaskParameter="Assemblies" ItemName="AssemblyInfo" />
</GetAssemblyIdentity>
<PropertyGroup>
<VersionInfo>%(AssemblyInfo.Version)</VersionInfo>
</PropertyGroup>
<!--And use it after like any other variable:-->
<Message Text="VersionInfo = $(VersionInfo)" Importance="high" />
</Target>
除非我漏掉了什么,否则这个问题很简单。将以下内容放入您的预构建或后构建脚本中:
FOR /F delims^=^"^ tokens^=2 %%i in ('findstr /b /c:"[assembly: AssemblyVersion(" $(ProjectDir)\Properties\AssemblyInfo.cs') do (set version=%%i)
echo Version: %version%
我认为你可以做的最好的事情就是查看 MSBuild 和 MsBuild 扩展包,你应该能够编辑解决方案文件以便在后期构建事件发生时写入到你的测试文件中。
如果这太复杂,那么你可以简单地创建一个小程序,检查你输出目录中的所有程序集,并在后期构建时执行它,你可以使用变量名传递输出目录... 例如,在后期构建事件中...
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;
}
}
}
}
你也可以传递文本文件的位置...
根据我的理解...
你需要一个用于构建后事件的生成器。
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();
}
}
}
第二步:将其用作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"
需要注意的是,使用现代化的 (VS2017+) .csproj 格式和 VS2022 版本,可以直接使用原始帖子中的 $(AssemblyVersion)
。
我已经开始添加一个单独的项目来构建最后版本,并在该项目中添加一个后期构建事件来运行自身。然后我只需在其中以编程方式执行我的后期构建步骤。
这样做可以使这些工作变得更加容易。然后,您只需检查您想要的任何程序集的程序集属性即可。到目前为止,它的工作非常出色。