|
Visual Studio 2022 uses a different path since it is 64-bit. Using the `DevEnvDir` MSBuild environment variable is the more bulletproof way to specify the location of TextTransform.exe now.
set textTemplatingPath="$(DevEnvDir)\TextTransform.exe"
%textTemplatingPath% "$(ProjectDir)AssemblyFileVersion.tt"
|
|
|
|
|
I have a requirement where I have to update the Assembly version/Assembly File version only when the Class library Project is built in Release mode and not in debug mode. Any suggestions on how i acn achieve the same?
|
|
|
|
|
<#@ template language="C#" hostspecific="True" #>
<#@ import namespace="System.IO" #>
<#@ assembly name="System.Core" #>
// This code was generated by a tool. Any changes made manually will be lost
// the next time this code is regenerated.
// see also http://bennor.github.io/AutoT4MVC/
using System.Reflection;
[assembly: AssemblyVersion("<#= this.Version #>")]
[assembly: AssemblyFileVersion("<#= this.Version #>")]
[assembly: AssemblyCopyright("Copyright © <your company="" name=""> 2006 - <#= this.Year #>")]
<#+
private string version;
public string Version
{
get
{
if (version == null)
{
// read the current version from the file in the solution directory
var path = this.Host.ResolveAssemblyReference("$(SolutionDir)");
string full = Path.Combine(path, "install_version.txt");
string contents = File.ReadAllText(full);
string[] parts = contents.Split('.');
int build;
if (int.TryParse(parts[3], out build))
{
// increment the build number
parts[3] = (build + 1).ToString();
}
version = string.Join(".", parts);
// write the new version number back to the file
File.WriteAllText(full, version);
}
return version;
}
}
public string Year = DateTime.Now.ToString("yyyy");
#>
|
|
|
|
|
Great article easy and effetive, thanks!!
|
|
|
|
|
Hi this is a great solution, but how do you then read the new version numbers.
when you try (VB)
Dim Assemble = System.Reflection.Assembly.GetExecutingAssembly
Dim version As String
version = Assemble.GetName().Version.ToString
it returns a number such as 2.1.4487.18154 but not the information
in the AssemblyFileVerson.cs file which is
[assembly: AssemblyFileVersion("2.1.0.39")]
thanks
|
|
|
|
|
|
|
Good idea, well explained, and as you said good start with t4 (though not new for me) -And, as others said, a good solution for automatic versioning without addins or tools etc. -> 5ed!
|
|
|
|
|
In order to change AssemblyVersion , i write a new Template:
<#@ template language="C#" hostSpecific="True"#>
<#@ output extension="cs" #>
<#@ import namespace="System.IO" #>
<#@ import namespace="System.Text" #>
<#
string assemblyVersion="0.0.0.0";
string fileVersion="0.0.0.0";
try
{
using(var f = File.OpenText(Host.ResolvePath("AssemblyFileVersion.cs")))
{
assemblyVersion = f.ReadLine().Replace("//","");
fileVersion = f.ReadLine().Replace("//","");
}
}catch
{
}
assemblyVersion=IncreaseVersion(assemblyVersion);
fileVersion=IncreaseVersion(fileVersion);
#>
using System.Reflection;
[assembly: AssemblyVersion("<#= assemblyVersion #>")]
[assembly: AssemblyFileVersion("<#= fileVersion #>")]
<#+
string IncreaseVersion(string version)
{
string[] tokens = version.Split('.');
int lastIndex = tokens.Length - 1;
int revision = int.Parse(tokens[lastIndex]);
tokens[lastIndex] = (++revision).ToString();
return string.Join(".", tokens);
}
#>
----
A little pitty , after assembly version attributes updated , we can not see it from "Properties-Assembly Information" (compiled exe or dll's version are correct). But this method regenerating fileversion cs file ,and not invoks ide's document changed event . So before next building ,if no changes of document detected , it seems ide will not build again , then file version will not increase .
To let version number be displayed in project property dialog .I tried to using IO.File change AssemblyInfo.cs , and succeeded (code as following), but when next building , even other files not changed , fileversion still increased .Maybe we should delete AssemblyInfo.cs , and then use AssemblyInfo.tt to regenerate it .
However , thanks, Bruno . You tell us a good , easy , hand-free ,addin-free way to manage file version .
-----codes of change assemblyinfo.cs---
<#+
void UpdateAssemblyInfo()
{
const string assembVersionToken = "[assembly: AssemblyVersion";
const string fileVersionToken = "[assembly: AssemblyFileVersion";
string assemblyVersion = "0.0.0.0";
string fileVersion = "0.0.0.0";
var path = Host.ResolvePath("Properties\\AssemblyInfo.cs");
var lines = new StringBuilder();
var stream = File.OpenRead(path);
var reader = new StreamReader(stream);
while (!reader.EndOfStream)
{
var line = reader.ReadLine();
if (line.StartsWith(assembVersionToken ))
{
assemblyVersion = line.Substring(28, 7);
}
else if (line.StartsWith(fileVersionToken))
{
fileVersion = line.Substring(32, 7);
}
else
{
lines.AppendLine(line);
}
}
stream.Close();
lines.AppendLine(string.Format("[assembly: AssemblyVersion(\"{0}\")]", IncreaseVersion(assemblyVersion)));
lines.AppendLine(string.Format("[assembly: AssemblyFileVersion(\"{0}\")]", IncreaseVersion(fileVersion)));
File.WriteAllText(path, lines.ToString());
}
#>
|
|
|
|
|
Thank you for your contribution
Yeah well I tried to keep it as simple as possible, at a "novice" level.
Anyway, if "no changes are detected" probably that means that there are no changes in your assembly too.. so why would you need a new file version number?
|
|
|
|
|
Yes , It should be so , but when sharing your this great idea , one of my friend not familar with IDE asked to me : Wether will Version increase if building with no changes in document . So for sure , i did a test .
I really like this idea , and now I added it to vs Template(File-Export Template) and Reshaper file template as :
<#@ template language="C#" hostSpecific="True"#>
<#@ output extension="cs" #>
<#@ import namespace="System.IO" #>
<#@ import namespace="System.Text" #>
<#
#>
<#
string assemblyVersion="0.0.0.0";
string fileVersion="0.0.0.0";
try
{
using(var f = File.OpenText(Host.ResolvePath("AssemblyFileVersion.cs")))
{
assemblyVersion = f.ReadLine().Replace("//","");
fileVersion = f.ReadLine().Replace("//","");
}
}catch
{
}
GetAssemblyInfo(ref assemblyVersion,ref fileVersion);
assemblyVersion=IncreaseVersion(assemblyVersion);
fileVersion=IncreaseVersion(fileVersion);
#>
using System.Reflection;
[assembly: AssemblyVersion("<#= assemblyVersion #>")]
[assembly: AssemblyFileVersion("<#= fileVersion #>")]
<#+
string IncreaseVersion(string version)
{
string[] tokens = version.Split('.');
int lastIndex = tokens.Length - 1;
int revision = int.Parse(tokens[lastIndex]);
tokens[lastIndex] = (++revision).ToString();
return string.Join(".", tokens);
}
#>
<#+
void GetAssemblyInfo(ref string assemblyVersion,ref string fileVersion)
{
const string assembVersionToken = "[assembly: AssemblyVersion";
const string fileVersionToken = "[assembly: AssemblyFileVersion";
bool shouldUpdate=false;
var path = string.Empty;
try
{
path=Host.ResolvePath("Properties\\AssemblyInfo.cs");
}
catch
{
return;
}
var lines = new StringBuilder();
var stream = File.OpenRead(path);
var reader = new StreamReader(stream);
while (!reader.EndOfStream)
{
var line = reader.ReadLine();
if (line.StartsWith(assembVersionToken ))
{
assemblyVersion = line.Substring(28, 7);
shouldUpdate=true;
}
else if (line.StartsWith(fileVersionToken))
{
fileVersion = line.Substring(32, 7);
shouldUpdate=true;
}
else
{
lines.AppendLine(line);
}
}
stream.Close();
if(shouldUpdate)
{
File.WriteAllText(path, lines.ToString());
}
}
#>
|
|
|
|
|
Ok that's a nice T4 exercise.
But you should remember that if you need to autoincrement BOTH AssemblyVersion and AssemblyFileVersion, this can be done automatically by using the '*' as shown below:
[assembly: AssemblyVersion("1.0.*")]
Also, there's one thing I don't understand.
You're reading AssemblyVersion and AssemblyFileVersion from a file located in Properties\AssemblyVersion.cs
Then you're generating your AssemblyFileVersion.cs
it looks like both of your files will have the two attributes AsseblyVersion and AssemblyFileVersion.
Maybe it works, I didn't test it, but it looks like a bit confusing to me
|
|
|
|
|
Before building , template will read versions info from proerties\assemblyversion.cs , when assemblyversion.cs's content(without assemblyversionattribute and assemblyfileversionattribute) be cached .
if GetAssemblyInfo method find the two attributes existed in assemblyversion.cs , assemblyversion.cs will be replaced by cached contents , then that two attributes disappears .
Because all above happened in pre-building , so building will not be affected .
i am just a little lazy and just not want to copy projects' attribute in assemblyfileversion.cs , so let template do it .
|
|
|
|
|
I can't figure it out but sounds interesting... would you please be so kind to share a working sample with me?
bruno.tagliapietra AT gmail.com
|
|
|
|
|
I did tests in vs2010 and 2012 , And find that :
1, In vs10 will throw an error as "duplicate ..." at the first time , then i click "no" . Next time of building , no error thrown , for AssemblyInfo.cs's two attributes have been removed .
2, In vs12 no error thrown at the first time.
3, For .tt(with customtool property set as TextTemplatingFileGenerator) runs on design time , in vs10, if you first using Ctrl+C in template file , forcing which to run once and removing two attributes in AssemblyInfo.cs , we can avoid error appearing .
4, This method may cause extral one version increase , for it changes AssemblyInfo.cs .
---
It seems that my email can not be sent to you . Email box sended back email and told :host gmail-smtp-in.l.google.com[173.194.79.27] said: 552 5.7.0 guidelines. hr1si577976pbc.234 - gsmtp (in reply to end of DATA command)
Maybe i can replay your email : cp2516@qq.com
|
|
|
|
|
Hi Chen.
The interesting solution you found was suffering some pattern-matching errors... try this in VS 2010.
This could be further improved, of course.
<#@ template language="C#" hostSpecific="True"#>
<#@ output extension="cs" #>
<#@ import namespace="System.IO" #>
<#@ import namespace="System.Text" #>
<#@ import namespace="System.Text.RegularExpressions" #>
<#
#>
<#
string assemblyVersion="1.0.0.0";
string fileVersion="1.0.0.0";
Regex pattern = new Regex(@"[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+");
ReadFromAssemblyInfoAndOptionallyDelete(ref assemblyVersion,ref fileVersion, pattern, "AssemblyFileVersion.cs", false);
ReadFromAssemblyInfoAndOptionallyDelete(ref assemblyVersion,ref fileVersion, pattern, "Properties\\AssemblyInfo.cs", true);
assemblyVersion=IncreaseRevision(assemblyVersion);
fileVersion=IncreaseRevision(fileVersion);
#>
using System.Reflection;
[assembly: AssemblyVersion("<#= assemblyVersion #>")]
[assembly: AssemblyFileVersion("<#= fileVersion #>")]
<#+
string IncreaseRevision(string version)
{
string[] tokens = version.Split('.');
int lastIndex = tokens.Length - 1;
int revision = int.Parse(tokens[lastIndex]);
tokens[lastIndex] = (++revision).ToString();
return string.Join(".", tokens);
}
#>
<#+
void ReadFromAssemblyInfoAndOptionallyDelete(ref string assemblyVersion,ref string fileVersion, Regex pattern, string path, bool delete)
{
const string assembVersionToken = "[assembly: AssemblyVersion";
const string fileVersionToken = "[assembly: AssemblyFileVersion";
bool shouldUpdate=false;
try
{
path=Host.ResolvePath(path);
}
catch
{
return;
}
var lines = new StringBuilder();
using(var stream = File.OpenRead(path))
{
var reader = new StreamReader(stream);
while (!reader.EndOfStream)
{
var line = reader.ReadLine();
if (line.Contains(assembVersionToken ))
{
assemblyVersion = pattern.Match(line).ToString();
shouldUpdate=true;
}
else if (line.Contains(fileVersionToken))
{
fileVersion = pattern.Match(line).ToString();
shouldUpdate=true;
}
else
{
lines.AppendLine(line);
}
}
}
if(shouldUpdate && delete)
{
File.WriteAllText(path, lines.ToString());
}
}
#>
|
|
|
|
|
Good Tip!!!
I am familiar with T4 and build event , but never thought they could be used as you said .
-------
Now I ran your demo , it could build successfully . But when i did as you directed , it could't . IDE gived me 9009 error .
I compared batch commands in demo and in readme.txt , maybe you should modify them as following:
set commonprogramfiles32=%CommonProgramFiles%
if "%commonprogramfiles32%"=="" set commonprogramfiles32=%CommonProgramFiles(x86)%
"%commonprogramfiles32%\Microsoft Shared\TextTemplating\$(VisualStudioVersion)\texttransform.exe" "$(ProjectDir)AssemblyFileVersion.tt"
modified 25-Sep-13 2:09am.
|
|
|
|
|
Sorry, I don't get it.
In both 64 and 32 bit environment
%CommonProgramFiles% has a value.
so, in a 64 bit environment the instruction you proposed,
set commonprogramfiles32=%CommonProgramFiles%
sets commonprogramfiles32 value to
C:\Program Files\Common Files .
But the path that we need is
C:\Program Files (x86)\Common Files\Microsoft Shared\TextTemplating\ , and in that way build is going to fail.
I ask you: could you please write down here the full path of your TextTransform.exe file?
|
|
|
|
|
You demo is fine . But After creating a new test project directed by steps in article , before building ,error 9009 appeared in errorlist . Then i found there is something wrong in batch command .
In article(step 1) you set commonprogramfiles32="%CommonProgramFiles%", so when call texttransform.exe using "%commonprogramfiles32%\Microsoft Shared\..." , the path becomes ""C:\Program Files (x86)\Common Files"\Microsoft shared\...." ----i think it's the reason the batch command could not work .
(Forgive my poor english ~ )
modified 25-Sep-13 3:58am.
|
|
|
|
|
Ok I redownload my own demo project, the mistake was I initially set a CommonFiles32, but in the end I was referring to CommonFiles(x86).
Then I tried to fix it but I stumbled on a "double quote problem".
So I came out with this solution, that should work on 32 and 64 bit systems
set textTemplatingPath="%CommonProgramFiles(x86)%\Microsoft Shared\TextTemplating\$(VisualStudioVersion)\texttransform.exe"
if %textTemplatingPath%=="\Microsoft Shared\TextTemplating\$(VisualStudioVersion)\texttransform.exe" set textTemplatingPath="%CommonProgramFiles%\Microsoft Shared\TextTemplating\$(VisualStudioVersion)\texttransform.exe"
%textTemplatingPath% "$(ProjectDir)AssemblyFileVersion.tt"
Ok now I'll edit the article and reupload a working, correct example.
Thanks
|
|
|
|
|
Really good tip. I'm currently doing something similar in the process template xaml definition. I'll refactor using a t4 template, it's a really good idea. Nice and clean
|
|
|
|
|
|
I didn't know of T4 Text Templates and its potential.
I generally prefer manually increment the assembly file version, but your method is a smart solution, for example, in Sharepoint development where the deployment not always is done correctly (so you can verify if the right dlls version are deployed).
I also found useful these two great Jeremy Jameson's articles:
Shared Assembly Info in Visual Studio projects[^]
Best Practices for .NET Assembly Versioning[^]
Luca Ritossa
|
|
|
|
|
|
Simple, yet very interesting and useful.
|
|
|
|
|