给.Net项目编译的程序集加入版本号的方式有许多种,包括:

1. 默认的方式,在每个项目的AssemblyInfo.cs文件中指定版本号:

 // Version information for an assembly consists of the following four values:
//
// Major Version
// Minor Version
// Build Number
// Revision
//
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]

2. 使用IDE的插件方式定制版本号增加的方式:

从上图中可以看到,这个插件可以通过根据时间信息来生产每一位的版本号

3. 通过T4模板在每次编译解决方案时更新项目的版本号

T4模板是一种在设计阶段帮助开发人员按照一定规则生产代码的机制,在IDE中可以添加:

具体的介绍可以参考MSDN上面的:
http://msdn.microsoft.com/en-us/library/bb126445(v=VS.100).aspx

我们在这里使用T4模板的目的就是让它根据当前的subversion上的revision number生产版本号

using System.Reflection;

[assembly: AssemblyVersion("1.0.0.7162")]
[assembly: AssemblyFileVersion("1.0.0.7162")]

其中最后一位是当前的SvnRevisionNumber

具体操作方法如下:

(1)为了使用T4模板,需要安装Visual Studio 2010 SP1 SDK和Visual Studio 2010 SP1 Visualization and Modeling SDK

Visual Studio 2010 SP1 SDK的下载地址:
http://visualstudiogallery.msdn.microsoft.com/25622469-19d8-4959-8e5c-4025d1c9183d

Visual Studio 2010 SP1 Visualization and Modeling SDK的下载地址:
http://www.microsoft.com/en-us/download/details.aspx?id=23025

(2)在需要共享同一个版本号的解决方案中新增一个新的项目

(3)编辑这个项目文件,加入如下内容:

(4)在这个新项目中添加一个T4模板文件

(5)编辑这个扩展名为tt的模板, 内容如下:

 <#@ template hostspecific="true" language="C#" #>
<#@ output extension=".cs" #>
<#@ Assembly name="E:\trunk\src\BuildVersion\SSvnEx-1.7002.1998\SharpSvn\SharpSvn.dll" #>
<#@ import namespace="System.IO" #>
<#@ import namespace="SharpSvn" #>
<#
string RevisionNumber = String.Empty;
SvnInfoEventArgs args;
SvnPathTarget target = SvnPathTarget.FromString(@"E:\trunk\src");
try
{
new SvnClient().GetInfo(target, out args);
RevisionNumber = args.LastChangeRevision.ToString();
}
catch (SvnWorkingCopyPathNotFoundException)
{
RevisionNumber = "";
}
#>
//
// This code was generated by a tool. Any changes made manually will be lost
// the next time this code is regenerated.
// using System.Reflection; [assembly: AssemblyVersion("1.0.0.<#= RevisionNumber #>")]
[assembly: AssemblyFileVersion("1.0.0.<#= RevisionNumber #>")]

上面的代码使用SharpSvn类库来读取指定目录下的svn信息,并将LastChangeRevision作为最后一位的版本号。


注:
这里的<#@ Assembly name="E:\trunk\src\BuildVersion\SSvnEx-1.7002.1998\SharpSvn\SharpSvn.dll" #>
引用外部dll时,T4模板不能使用相对路径

可以使用如下5中方式指定程序集的引用:

a. 将T4需要引用的模板加入到GAC中,之后在T4中就可以直接写上程序集的名字了:
<#@ Assembly name="SharpSvn.dll" #>

b. 使用绝对路径指定程序集名字:
<#@ Assembly name="E:\trunk\src\BuildVersion\SSvnEx-1.7002.1998\SharpSvn\SharpSvn.dll" #>

c. 将程序集拷贝到Visual Studio的 "Public Assemblies Folder"中,之后在T4中就可以直接写上程序集的名字了:
<#@ Assembly name="SharpSvn.dll" #>

d. 使用一个环境变量来代替绝对路径:
<#@ Assembly name="%mylib%\SharpSvn.dll" #>

e. 使用Visual Studio Macro来构建程序集路径:
<#@ Assembly name="$(SolutionDir)lib\SharpSvn.dll" #>

(6)编译这个包含T4模板的项目,或者保存这个模板文件,都会触发T4生成一个cs文件,内容如下:

(7)在其他项目中添加这个生成的cs文件,以link的方式添加:

(8)注释掉这些项目原有的版本信息:

到此为止,所有引用了这个BuildVersion.cs文件的项目都会根据这个文件的内容生产版本号了,而这个版本号是来自于指定svn路径下的revision number。

4. 通过定制一个MSBuild Task,在每次编译时更新版本号信息。
MSBuild提供对复杂编译任务的可定制,我们通过继承代表一次编译任务的Task类,并重写其Execute方法来定制自己的编译任务。

(1)首先,我们需要建立一个Class Library类型的项目,并引用下面的程序集:

(2)继承Task类,并实现自己的Execute方法:

下面的代码通过读取含有版本号信息的cs文件,使用正则表达式解析出四位版本号,并实现我们自己的版本号增加逻辑,这里的逻辑是根据Jenkins的环境变量,将版本号第三位置为Jenkins的BuildNumber,将第四位置为SVN Revison号。
注:这里在处理来自svn的revision number时,当其大于UInt16.MaxValue时,我们舍弃其最高位,因为Assembly Version的值需要在0~UInt16.MaxValue之间。

 using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Build.Utilities;
using System.IO;
using System.Text.RegularExpressions; namespace VersionInfoBuildTask
{
public class AutoIncrementTask : Task
{
private const string AssemlyVersionPattern =
@"\[assembly: AssemblyVersion\(\""(\d{1}).(\d{1}).(\d{1,}).(\d{1,})""\)\]"; private const string AssemlyFileVersionPattern =
@"\[assembly: AssemblyFileVersion\(\""(\d{1}).(\d{1}).(\d{1,}).(\d{1,})""\)\]"; private const string AssemblyInformationalVersionPattern =
@"\[assembly: AssemblyInformationalVersion\(\""(\d{1}).(\d{1}).(\d{1,}).(\d{1,})""\)\]"; public string AssemblyInfoPath { get; set; } public override bool Execute()
{
try
{
if (String.IsNullOrEmpty(AssemblyInfoPath))
throw new ArgumentException("AssemblyInfoPath must have a value"); string[] content = File.ReadAllLines(AssemblyInfoPath, Encoding.Default);
var rxForAssemlyVersion = new Regex(AssemlyVersionPattern);
var rxForFileVersion = new Regex(AssemlyFileVersionPattern);
var rxForInfoVersion = new Regex(AssemblyInformationalVersionPattern); var newContent = new List<string>();
content.ToList().ForEach(line =>
{
if (rxForAssemlyVersion.IsMatch(line))
{
line = VersionMatcherForAssemlyVersion(rxForAssemlyVersion.Match(line));
Console.Out.WriteLine("Match result for assembly version is: " + line);
}
else if (rxForFileVersion.IsMatch(line))
{
line = VersionMatcherForFileVersion(rxForFileVersion.Match(line));
Console.Out.WriteLine("Match result for file version is: " + line);
}
else if (rxForInfoVersion.IsMatch(line))
{
line = VersionMatcherForInfoVersion(rxForInfoVersion.Match(line));
Console.Out.WriteLine("Match result for info version is: " + line);
}
else
{
Console.Out.WriteLine(line + " didn't match!!");
}
newContent.Add(line);
}); File.WriteAllLines(AssemblyInfoPath, newContent.ToArray());
}
catch (Exception ex)
{
Console.Out.WriteLine(ex);
return false;
} return true;
} private string VersionMatcherForFileVersion(Match match)
{
int major = int.Parse(match.Groups[].Value);
int minor = int.Parse(match.Groups[].Value);
int build = int.Parse(match.Groups[].Value);
string revision = match.Groups[].Value; Console.WriteLine("AutoIncrement Assembly {0}",
Path.GetFileName(AssemblyInfoPath));
Console.WriteLine("Current matched version: {0}.{1}.{2}.{3}",
major, minor, build, revision); string buildNumber = Environment.GetEnvironmentVariable("BUILD_NUMBER");
if (buildNumber == null) buildNumber = ""; string revisionNumber = Environment.GetEnvironmentVariable("SVN_REVISION");
if (revisionNumber == null) revisionNumber = "";
if (long.Parse(revisionNumber) > UInt16.MaxValue)
{
Console.WriteLine("The revision number is too big");
long rev = long.Parse(revisionNumber);
rev = rev % ;
revisionNumber = rev.ToString();
} Console.WriteLine("Incremented to version: {0}.{1}.{2}.{3}",
major, minor, build, revision); string result = match.Result
("[assembly: AssemblyFileVersion(\"$1.$2.{0}.{1}\")]");
return String.Format(result, buildNumber, revisionNumber);
}
}
}

(3)准备一个proj文件,这个文件是运行msbuild时需要指定的,主要是告知msbuild实现定制任务的程序集在哪里,和定制任务的输入文件在哪里:

 <?xml version="1.0" encoding="utf-8" ?>
<Project ToolsVersion="4.0" DefaultTargets="IncrementBuild"
xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<UsingTask AssemblyFile="VersionInfoBuildTask.dll" TaskName="AutoIncrementTask" />
<Target Name="IncrementBuild">
<AutoIncrementTask AssemblyInfoPath="SharedAssemblyInfo.cs" />
</Target>
</Project>

(4)准备一个包含程序集版本信息的文件,这里的SharedAssemblyInfo.cs就是将要传给定制来任务处理的文件,内容如下:

(5)将SharedAssemblyInfo.cs文件加入到解决方案中,其中的所有项目都使用Add as link方式加入这个文件,以达到共享版本信息的目的 :

(6)将编译好的VersionInfoBuildTask.dll文件,与这个proj文件都放到Jenkins的workspace目录中:

并使用下面的命令来运行这个定制的Task:
msbuild BuildTask.proj /t:IncrementBuild

运行之后,查看SharedAssemblyInfo.cs的内容,发现版本号已经发生了改变:

好了,下面就可以使用msbuild来编译整个解决方案了,之后我们查看编译出来的程序集的版本号:

版本号最后两位对应Jenkins上的build number和revision number(舍去最高位),这样做是方便查询每个版本所对应的代码变更信息:

.Net项目版本号的生成的更多相关文章

  1. javaweb项目运行时生成的Servers项目作用

    在javaweb项目中,看到有一个Servers的项目,发现每新增一个项目,就会在Servers项目中新生成一些对应的项目文件. 如图所示: 每个项目都有对应的文件.文件的结构图如下: 解释一:Ser ...

  2. 从0搭建vue3组件库:自动化发布、管理版本号、生成 changelog、tag

    今天看到一篇文章中提到了一个好用的工具release-it.刚好可以用在我正在开发的vue3组件库.纸上得来终觉浅,绝知此事要躬行,说干就干,下面就介绍如何将release-it应用到实际项目中,让组 ...

  3. Web Api 多项目文档生成之SwaggerUI

    SwaggerUI 可以生成不错的文档,但默认只能作用于单个api 项目,研究了一下源码发现只需修改一下SwaggerConfig.cs文件即可支持多API项目 1.使用生成脚本把xml文件复制到AP ...

  4. 循序渐进开发WinForm项目(1) --数据库设计和项目框架的生成

    随笔背景:在很多时候,很多入门不久的朋友都会问我:我是从其他语言转到C#开发的,有没有一些基础性的资料给我们学习学习呢,你的框架感觉一下太大了,希望有个循序渐进的教程或者视频来学习就好了. 其实也许我 ...

  5. C#dll版本号默认生成规则

    原文:C#dll版本号默认生成规则 1.版本号自动生成方法 只需把 AssemblyInfo.cs文件中的[assembly: AssemblyVersion("1.0.0.0") ...

  6. eclipse或adt-bundle创建的android项目没有自动生成MainActivity.java和activity_main.xml等文件解决办法

    以前我电脑一直以来都是用的eclipse3.7来开发android项目的,创建android项目也能正常生成MainActivity.java和activity_main.xml等文件.后来不知道什么 ...

  7. 新版本ADT创建Android项目无法自动生成R文件解决办法

    本人使用的是ADT是Version 23.0.2,支持Android 6.0之后的系统环境,最高版本23,在创建Android项目的时候,每次创建项目选择“Compile With”低于6.0版本的时 ...

  8. 查找python项目依赖并生成requirements.txt——pipreqs 真是很好用啊

    查找python项目依赖并生成requirements.txt 转自:http://blog.csdn.net/orangleliu/article/details/60958525 一起开发项目的时 ...

  9. JAVA - SpringBoot项目引用generator生成 Mybatis文件

    JAVA - SpringBoot项目引用generator生成 Mybatis文件  在spring官网https://start.spring.io/自动生成springboot项目,这里选择项目 ...

随机推荐

  1. 关于ios越狱开发的那些事

    也许吧,每每接触某些新东西的时候,都有点犯晕吧,这不是应该要的. 第一次接触ios越狱开发,也是这样吧.这篇主要是从无到有的说一下ios越狱的开发,网上很多的教程大部门都比较旧了吧,放在新设备上总是出 ...

  2. Hibernate-Criteria Queries

    1.实例 接口org.hibernate.Criteria针对特殊持久层类进行查询,Sesion是Criteria的工厂: Criteria crit = sess.createCriteria(Ca ...

  3. Linux服务器偶尔无法访问问题

    最近上了一台web服务器(本地包含mysql服务器),在运行一段时间发现服务器偶尔会无法访问, 包括mysql,ftp以及ssh等都无法响应,但是已经连接上的ssh不受任何影响,在查看系统log时, ...

  4. ORACLE创建、修改、删除序列

    ORACLE没有象SQL SERVER中一样的自增加字段,要实现只能通过SEQUENCE来实现. 1.创建序列语法:(需要CREATE SEQUENCE系统权限) CREATE SEQUENCE 序列 ...

  5. RabbitMQ链接不上异常

    链接代码 项目启动报的异常 本地main方法链接报的异常 网上查询原因 问题说明及解决方案: 网上原因很多,最终原因都是连接不到数据库造成的. 1.查看防火墙 2.tomcat端口是否屏蔽 3.查看连 ...

  6. 聊聊Dataguard的三种保护模式实验(上)

    Data Guard是Oracle高可用性HA的重要解决方案.针对不同的系统保护需求,DG提供了三种不同类型的保护模式(Protection Mode),分别为:最大保护(Maximum Protec ...

  7. js记录用户在网站的浏览记录和停留时间

    by weber开发者 from http://weber.pub/ 本文地址: http://weber.pub/js记录用户行为浏览记录和停留时间/163.html 问题 公司想统计一个用户从进入 ...

  8. [转载]C++异常机制的实现方式和开销分析

    原文章网址:http://baiy.cn/doc/cpp/inside_exception.htm C++异常机制的实现方式和开销分析 白杨 http://baiy.cn 在我几年前开始写<C+ ...

  9. RedHat 5 配置CentOS yum 更新源

    YUM是Redhat Linux在线安装更新及软件的工具,但是这是RHEL5的收费功能,如果没有购买Redhat的服务时不能使用RHEL5的更新源的,会提示注册. 由于CentOS是从Redhat演化 ...

  10. (转)Visual Studio原生开发的10个调试技巧(二)

    我以前关于Visual Studio调试技巧的文章引起了大家很大的兴趣,以至于我决定分享更多调试的知识.以下的列表中你可以看到写原生开发的调试技巧(接着以前的文章来编号).这些技巧可以应用在VS200 ...