如何自动将版本号插入 AssemblyName

Posted

技术标签:

【中文标题】如何自动将版本号插入 AssemblyName【英文标题】:How to automatically insert version number into AssemblyName 【发布时间】:2014-09-24 16:11:04 【问题描述】:

我正在尝试以这个问题为基础:

Reading a single value from a file in MSBuild

我的目标是在一个地方放置多个项目中使用的版本号,并且我还希望其中一个项目的 DLL 文件名中包含版本号的一部分。

根据上面的问题,我已经拿到了第一部分,但是我在第二部分遇到了困难,希望得到一些指导。

在我的解决方案中,我设置了一个名为 Version.txt 的纯文本文件,其中仅包含我的完整版本号:

1.1.0.0

在我的两个项目中,我打开了他们的 AssemblyInfo.cs 文件并删除了 AssemblyVersion 和 AssemblyFileVersion 项,然后修改了两个项目以在单独的文件中生成它们,如上述问题中所述。

<ItemGroup>
    <VersionFile Include="..\Version.txt" />
</ItemGroup>
<Target Name="BeforeBuild">
    <ReadLinesFromFile File="@(VersionFile)">
        <Output TaskParameter="Lines" PropertyName="VersionNumber" />
    </ReadLinesFromFile>
    <Delete Files="Properties\Version.cs" />
    <WriteLinesToFile File="Properties\Version.cs" Lines="using System.Reflection%3B&#xD;&#xA;&#xD;&#xA;[assembly: AssemblyVersion("$(VersionNumber)")]&#xD;&#xA;[assembly: AssemblyFileVersion("$(VersionNumber)")]" />
</Target>

现在,当我构建时,我会为每个项目生成一个 Properties\Version.cs 文件,该文件用于构建 EXE/DLL,并在其文件属性中显示为“1.1.0.0”。这正是我想要的。

对于 DLL,我想将程序集命名为“filename.v1.1.dll”,其中“1.1”来自上面 Version.txt 中的前两个组件。我对 Version.txt 的格式很灵活,只要我能在 EXE/DLL 属性中获得完整的“1.1.0.0”,在 DLL 文件名中获得“1.1”。

为了试试这个,我修改了 DLL 的 csproj 文件:

<RootNamespace>dllfile</RootNamespace>
<AssemblyName>dllfile.v$(VersionNumber)</AssemblyName>

当然,这会在文件名中插入完整的版本号,这是我不想要的。

有人对如何进行有任何提示吗?

谢谢。

编辑:通过将以下内容添加到我的 .csproj BeforeBuild 目标中,我已经能够提取版本号的主要/次要组件:

<ReadLinesFromFile File="@(VersionFile)">
    <Output TaskParameter="Lines" PropertyName="VersionNumber" />
</ReadLinesFromFile>
<PropertyGroup>
    <VersionNumberFirstDotIndex>$(VersionNumber.IndexOf('.'))</VersionNumberFirstDotIndex>
    <VersionNumberMajorStart>0</VersionNumberMajorStart>
    <VersionNumberMajorLen>$(VersionNumberFirstDotIndex)</VersionNumberMajorLen>
    <VersionNumberMinorStart>$([MsBuild]::Add(1, $(VersionNumberFirstDotIndex)))</VersionNumberMinorStart>
    <VersionNumberSecondDotIndex>$(VersionNumber.IndexOf('.', $(VersionNumberMinorStart)))</VersionNumberSecondDotIndex>
    <VersionNumberMinorLen>$([MSBuild]::Subtract($([MSBuild]::Subtract($(VersionNumberSecondDotIndex), $(VersionNumberFirstDotIndex))), 1))</VersionNumberMinorLen>
    <VersionNumberMajor>$(VersionNumber.Substring($(VersionNumberMajorStart), $(VersionNumberMajorLen)))</VersionNumberMajor>
    <VersionNumberMinor>$(VersionNumber.Substring($(VersionNumberMinorStart), $(VersionNumberMinorLen)))</VersionNumberMinor>
    <VersionNumberShort>$(VersionNumberMajor).$(VersionNumberMinor)</VersionNumberShort>
</PropertyGroup>
<Message Text="DEBUG1 VersionNumberFull=$(VersionNumber)" Importance="High" />
<Message Text="DEBUG2 VersionNumberAbbrev=$(VersionNumberShort)" Importance="High" />
<Delete Files="Properties\Version.cs" />
<WriteLinesToFile File="Properties\Version.cs" Lines="using System.Reflection%3B&#xD;&#xA;&#xD;&#xA;[assembly: AssemblyVersion(&quot;$(VersionNumber)&quot;)]&#xD;&#xA;[assembly: AssemblyFileVersion(&quot;$(VersionNumber)&quot;)]" />

我现在唯一缺少的是如何将此 VersionNumberShort 放入 DLL 文件名中。除非有人有更好的主意,否则我可以采纳 Peter 的建议并使用 Move 任务:

<Target Name="AfterBuild">
    <Move SourceFiles="$(OutputPath)$(AssemblyName).pdb" DestinationFiles="$(OutputPath)$(AssemblyName).v$(VersionNumberShort).pdb" />
    <Move SourceFiles="$(OutputPath)$(AssemblyName).dll" DestinationFiles="$(OutputPath)$(AssemblyName).v$(VersionNumberShort).dll" />
</Target>
<Target Name="AfterClean" DependsOnTargets="Common">
    <Delete Files="$(OutputPath)$(AssemblyName).v$(VersionNumberShort).pdb" ContinueOnError="true" />
    <Delete Files="$(OutputPath)$(AssemblyName).v$(VersionNumberShort).dll" ContinueOnError="true" />
</Target>

由于我需要与以前相同的属性定义,我将上面的 sn-p 移动到“通用”目标中,并在此处显示的构建和清理任务中引用它。

彼得 - 如果您想将您的评论作为答案,我会接受。

谢谢!

编辑:按照 jdlugosz 的回答,我尝试在我的任务中设置 AssemblyName。不幸的是,根据顶部列出的原始示例,这似乎仍然没有任何效果:

<Target Name="BeforeBuild">
    ...
    <WriteLinesToFile ... />
    <PropertyGroup>
      <AssemblyName>dllfile.v$(VersionNumber)</AssemblyName>
    </PropertyGroup>
</Target>

我尝试通过 Visual Studio 开发人员命令提示符使用 MSBuild 运行它:

msbuild /target:clean projfile.csproj
msbuild /verbosity:diag projfile.csproj > out.txt

在此之前,我将我的 csproj 文件顶部和“重新定义”中的 重命名为独特的名称,以便于搜索(例如“dllfileoriginal”与“dllfilemodified”)。

查看输出日志,我找不到任何对修改文本的引用;输出中到处都是 dllfileoriginal。

在 WriteLinesToFile 任务之后,似乎构建了以下目标:

IncrementalClean(已完成) 构建后事件 CoreBuild 构建后 构建

其中没有引用任何一个 DLL 名称。

目前看来,这仍然是我最好的选择。

【问题讨论】:

为什么不在目标或 AfterBuild 目标的末尾添加一个 元素来重命名二进制文件? @PeterRitchie - 如果程序集已签名,它将不起作用。另外,问题是关于构建新路径,而不是什么时候这样做...... 为什么要程序集名称中的版本?你从中得到了什么好处? 在我们的案例中,我们发现需要使用同一个 DLL 的多个版本来部署我们的软件。例如,假设 DLL A 和 B 都依赖于 DLL X 版本 1.0。然后我们生成 DLL X 的 1.1 版,我们开始更新 DLL B 以使用 X 1.1,但 A 仍然使用 X 1.0,没有更新计划。在这种情况下,我们需要同时使用 A、B、X 1.0 和 X 1.1 部署最终产品。无论如何,这不是我们可以控制的;它就是这样儿的。我想做的就是把这个数字集中到一个地方。谢谢。 添加链接的 AssemblyVersionl.cs 会不起作用吗? 【参考方案1】:

目标名称显示在 IDE 属性页编辑器的“配置属性”选项卡下的“常规”页上。我自己没有方便的工具来为您查找名称,但是您可以通过将 IDE 中的空白更改为 XXXX 之类的内容并保存来完成。然后在版本控制commit reviewer中查看diff,看看Property的名字是什么。在这种情况下,然后编辑该行以将 XXXX 更改为 $(OutputPath)$(AssemblyName).v$(VersionNumberShort)

哦,查看FormatVersion 任务,这可能会有所帮助。我认为也有一些预制任务可以操纵类似于您所展示的版本程序集。

我正在为版本做的是通过#defines 将片段作为/D 命令行参数传递。我猜你在 C# 中没有这个,IIRC。

【讨论】:

恐怕我找不到任何常规选项卡。我查看了项目的“属性”页面以及 Visual Studio 2012 中的“属性”窗格。FormatVersion 看起来会根据 MSDN 页面将数字附加在一起,而不是将它们分开。我也在考虑定义;我相信 C# 确实支持将它们传入,但我不需要在源文件中对它们做任何事情,因为我已经使用 WriteLinesToFile 生成了 Version.cs 文件,但这似乎是另一个好主意。谢谢。 @jia103 我在上面的答案中澄清了位置。 感谢您的更新。那个屏幕看起来很熟悉;看起来您使用的是不同版本的 Visual Studio;我在 2012 年,我没有看到。我在 .csproj 文件中也没有看到类似的东西;我所拥有的只是 。 也许&lt;AssemblyName&gt; 是您需要的?尝试将其更改为 XXX,看看是否会出现在您期望的位置。 嗯, 的问题是我已经尝试在其中插入属性:dllname.v$(VersionNumber)。这样做的问题是 VersionNumber 似乎直到后来才获得值。在解决这个问题时,它显然仍然是空的,最终我得到了一个名为 dllname.v.dll 的 DLL,这不是我想要的。到目前为止,它仍然看起来像 是我唯一的方法,它可能不适用于已签名的程序集。【参考方案2】:

这对我有用,它解决了在构建时将版本信息字符串附加到文件名的看似简单的问题。

首先,构建后事件:

(右键单击项目 -> 属性 -> 构建事件 -> 编辑构建后...)

$(TargetPath) "version" > $(TargetDir)text.txt
set /p version= <$(TargetDir)text.txt
copy $(TargetPath) $(TargetDir)$(TargetName)_%version%.exe
del $(TargetDir)text.txt

现在,诀窍: 重载 sub main 以返回版本信息,并在刚刚构建的 exe 的构建后事件中调用它。

这是 F# 中的一个示例:

[<EntryPoint>]
let main argv = 

    let version = argv.Length = 1 && argv.[0] = "version"
    if version then
        let version = System.Reflection.Assembly.GetExecutingAssembly().GetName().Version.ToString()
        do stdout.WriteLine(version)
        do stdout.Flush()
    else 
        try
        //...

上面的构建后事件

1) 使用“版本”参数调用新建的 exe,并将输出写入 txt 文件

2) 将文本文件内容读入局部变量

3) 通过添加版本信息重命名新建的exe

3) 复制新建的 exe 并在名称中添加版本信息

4) 清理临时文件

*将“移动”更改为“复制”,以便 Visual Studio 仍然可以 F5 项目

【讨论】:

以上是关于如何自动将版本号插入 AssemblyName的主要内容,如果未能解决你的问题,请参考以下文章

如何自动将内部版本号添加到 Xcode ios 存档

DevOps: 自动部署语义化版本实操

如何拥有自动递增的构建版本号(KDevelop)?

如何在 Azure Pipelines 中自动增加 Xamarin Android 内部版本号和版本号?

DevOps: 自动与手动部署语义化版本(Semantic Versioning)实操

自动修改js版本号