3

I have an assembly that uses the System.AddIn assembly attributes:

[AddIn("Foobar", Version = "1.2.3.4")]
public class Foobar {
...

I normally maintain version information within the Assembly Information in the project properties - in the Assembly version and File version fields.

enter image description here

Is there any magic constant or compile-time constant that I could use to keep the attribute version in-sync with my assembly or file version?

This looks like a possible fallback option, if not: Is it possible to get assembly info at compile time without reflection?

Lauren Rutledge
  • 1,195
  • 5
  • 18
  • 27
jhilgeman
  • 1,543
  • 10
  • 27

1 Answers1

2

Ok, here's a convoluted workaround.

Change your class to a partial class. Put all your logic in one main file. In a separate file, decorate your class with the attributes you want. See Can I define properties in partial classes, then mark them with attributes in another partial class?

Except, make the second class a T4 template.

Something like the following:

Foobar.cs

public partial class Foobar {
    // regular code
    ...
}

FoobarAttributes.tt (syntax highlighting is wrong here)

<#@ template debug="true" hostspecific="true" language="C#" #>
<#@ output extension=".cs" #>
<#@ import namespace="System.IO" #>
<#@ import namespace="System.Text.RegularExpressions" #>
<#
int major = 1;
int minor = 0;
int revision = 1;
int build = 1;

    try
    {
        // Code here is copied from a template in the Properties 
        // folder that auto-increments the build version, so that's the file location  
        string currentDirectory = Path.GetDirectoryName(Host.TemplateFile);
        string assemblyInfo = File.ReadAllText(Path.Combine(currentDirectory,"AssemblyInfo.cs"));
        Regex pattern = new Regex("AssemblyVersion\\(\"\\d+\\.\\d+\\.(?<revision>\\d+)\\.(?<build>\\d+)\"\\)");
        MatchCollection matches = pattern.Matches(assemblyInfo);
        revision = Convert.ToInt32(matches[0].Groups["revision"].Value);
        build = Convert.ToInt32(matches[0].Groups["build"].Value) + (incBuild?1:0);
    }
    catch(Exception)
    { }
#>
[AddIn("Foobar", Version = "<#= this.major #>.<#= this.minor #>.<#= this.revision #>.<#= this.build #>")]
public partial class Foobar
{
    // ...
}

Set up an event to compile the template on build. Now you can write C# code into the T4 to extract/modify text from other files to give your attribute the correct version info (see above for some example code).

I can't find the SO answer I pulled this from (some time ago), but I'm getting the transform to run from a build event

"%CommonProgramFiles(x86)%\microsoft shared\TextTemplating\$(VisualStudioVersion)\TextTransform.exe" -a !!build!true "$(ProjectDir)Properties\AssemblyInfo.tt"

but see Get Visual Studio to run a T4 Template on every build for some similar ideas.

I'm not sure this is the right solution, and in general I'd recommend against T4 due to the complexity it adds, but there you go.

edit: The partial class post linked above applies to properties, you should just be able to add attributes without creating a metadata class for the partial class.

BurnsBA
  • 4,347
  • 27
  • 39
  • 1
    So I'd agree with you that the template approach is a little convoluted, but it does do what I asked for, even though it's not really a simple compile-time constant of some sort. That said, I'll probably go back with the fallback approach for simplicity. Thanks for the innovative approach, though! Marked this as the answer in case the fallback option doesn't work for someone else in the future. – jhilgeman Aug 21 '18 at 21:43