tags:

views:

650

answers:

4

Now, I'm writing VS 2008 Macro for replace Assembly version in AssemblyInfo.cs file. From MSDN, Assembly version must be wrote by using the following pattern.

major.minor[.build[.revision]]

Example

  • 1.0
  • 1.0.1234
  • 1.0.1234.0

I need to dynamically generate build number for 'AssemblyInfo.cs' file and use Regular Expression for replace old build number with new generated build number.

Do you have any Regular Expression for solving this question? Moreover, build number must not be contained in commented statement like below code. Finally, don't forget to check your regex for inline comment.

Don't replace any commented build number

//[assembly: AssemblyVersion("0.1.0.0")]

/*[assembly: AssemblyVersion("0.1.0.0")]*/

/*
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
[assembly: ComVisible(false)]
[assembly: AssemblyVersion("0.1.0.0")]
 */

Replace build number that are not commented

[assembly: AssemblyVersion("0.1.0.0")] // inline comment
/* inline comment */ [assembly: AssemblyVersion("0.1.0.0")]
[assembly: /*inline comment*/AssemblyVersion("0.1.0.0")]

Hint.

Please try your regex at Online Regular Expression Testing Tool

+2  A: 

This is somewhat crude, but you could do the following.

Search for:

^{\[assembly\: :w\(\"0\.1\.}\*

Replace with:

\1####

Where #### is your replacement string.

This regex work as follows:

  1. It starts by searching for lines beginning with \[assembly\: ,(^ indicates the beginning fo a line, backslashes escape special characters) followed by...
  2. ...some alphabetic identifier :w, followed by...
  3. ...an opening brace \(, followed by...
  4. ...The beginning of the version string, in quotes \"0\.1\., finally followed by...
  5. ...an asterisk \*.
  6. Steps 1-4 are captured as the first tagged expression using the curly braces { } surrounding them.
  7. The replacement string drops the tagged expression verbatim, so that it's not harmed with: \1, followed by your replacement string, some ####.

Commented lines are ignored as they do not start with [assembly: .Subsequent in-line comments are left untouched as they are not captured by the regex.

If this isn't exactly what you need, it's fairly straightforward to experiment with the regex to capture and/or replace different parts of the line.

Oren Trutner
This regex doesn't correct because I need to detect comment of this attribute. But your regex can't detect it.
Soul_Master
Can you clarify? do you want the asterisk replaced in the comment or not? If you want the replacement to occur inside the commented lines, you could remove the ^ character from the beginning of the regex. That will remove the requirement that the match occurs at the beginning of a line.
Oren Trutner
Please read my rewrite question.
Soul_Master
A: 

I doubt using regular expressions will do you much good here. While it could be possible to formulate an expression that matches "uncommented" assembly version attributes it will be hard to maintain and understand.

You are making it very very hard on yourself with the syntax that you present. What about enforcing a coding standard on your AssemblyInfo.cs file that says that lines should always be commented out with a beginning // and forbid inline comments? Then it should be easy enough to parse it using a StreamReader.

If you can't do that then there's only one parser who's guaranteed to handle all of your edge cases and that's the C# compiler. How about just compiling your assembly and then reflecting it to detect the version number?

var asm = Assembly.LoadFile("foo.dll");
var version = Assembly.GetExecutingAssembly().GetName().Version;

If you're simply interested in incrementing your build number you should have a look at this question: Can I automatically increment the file build version when using Visual Studio?

Markus Olsson
Soul_Master
A: 

I just find answer for my question. But answer is very very complicate & very long regex. By the way, I use this syntax only 1 time per solution. So, It doesn't affect overall performance. Please look at my complete source code.

Module EnvironmentEvents.vb

Public Module EnvironmentEvents
    Private Sub BuildEvents_OnBuildBegin(ByVal Scope As EnvDTE.vsBuildScope, ByVal Action As EnvDTE.vsBuildAction) Handles BuildEvents.OnBuildBegin
        If DTE.Solution.FullName.EndsWith(Path.DirectorySeparatorChar & "[Solution File Name]") Then
            If Scope = vsBuildScope.vsBuildScopeSolution And Action = vsBuildAction.vsBuildActionRebuildAll Then
                AutoGenerateBuildNumber()
            End If
        End If
    End Sub
End Module

Module AssemblyInfoHelp.vb

Public Module AssemblyInfoHelper

    ReadOnly AssemblyInfoPath As String = Path.Combine("Common", "GlobalAssemblyInfo.cs")

    Sub AutoGenerateBuildNumber()

        'Declear required variables
        Dim solutionPath As String = Path.GetDirectoryName(DTE.Solution.Properties.Item("Path").Value)
        Dim globalAssemblyPath As String = Path.Combine(solutionPath, AssemblyInfoPath)
        Dim globalAssemblyContent As String = ReadFileContent(globalAssemblyPath)
        Dim rVersionAttribute As Regex = New Regex("\[[\s]*(\/\*[\s\S]*?\*\/)?[\s]*assembly[\s]*(\/\*[\s\S]*?\*\/)?[\s]*:[\s]*(\/\*[\s\S]*?\*\/)?[\s]*AssemblyVersion[\s]*(\/\*[\s\S]*?\*\/)?[\s]*\([\s]*(\/\*[\s\S]*?\*\/)?[\s]*\""([0-9]+)\.([0-9]+)(.([0-9]+))?(.([0-9]+))?\""[\s]*(\/\*[\s\S]*?\*\/)?[\s]*\)[\s]*(\/\*[\s\S]*?\*\/)?[\s]*\]")
        Dim rVersionInfoAttribute As Regex = New Regex("\[[\s]*(\/\*[\s\S]*?\*\/)?[\s]*assembly[\s]*(\/\*[\s\S]*?\*\/)?[\s]*:[\s]*(\/\*[\s\S]*?\*\/)?[\s]*AssemblyInformationalVersion[\s]*(\/\*[\s\S]*?\*\/)?[\s]*\([\s]*(\/\*[\s\S]*?\*\/)?[\s]*\""([0-9]+)\.([0-9]+)(.([0-9]+))?[\s]*([^\s]*)[\s]*(\([\s]*Build[\s]*([0-9]+)[\s]*\))?\""[\s]*(\/\*[\s\S]*?\*\/)?[\s]*\)[\s]*(\/\*[\s\S]*?\*\/)?[\s]*\]")

        'Find Version Attribute for Updating Build Number
        Dim mVersionAttributes As MatchCollection = rVersionAttribute.Matches(globalAssemblyContent)
        Dim mVersionAttribute As Match = GetFirstUnCommentedMatch(mVersionAttributes, globalAssemblyContent)
        Dim gBuildNumber As Group = mVersionAttribute.Groups(9)
        Dim newBuildNumber As String

        'Replace Version Attribute for Updating Build Number
        If (gBuildNumber.Success) Then
            newBuildNumber = GenerateBuildNumber(gBuildNumber.Value)
            globalAssemblyContent = globalAssemblyContent.Substring(0, gBuildNumber.Index) + newBuildNumber + globalAssemblyContent.Substring(gBuildNumber.Index + gBuildNumber.Length)
        End If

        'Find Version Info Attribute for Updating Build Number
        Dim mVersionInfoAttributes As MatchCollection = rVersionInfoAttribute.Matches(globalAssemblyContent)
        Dim mVersionInfoAttribute As Match = GetFirstUnCommentedMatch(mVersionInfoAttributes, globalAssemblyContent)
        Dim gBuildNumber2 As Group = mVersionInfoAttribute.Groups(12)

        'Replace Version Info Attribute for Updating Build Number
        If (gBuildNumber2.Success) Then
            If String.IsNullOrEmpty(newBuildNumber) Then
                newBuildNumber = GenerateBuildNumber(gBuildNumber2.Value)
            End If

            globalAssemblyContent = globalAssemblyContent.Substring(0, gBuildNumber2.Index) + newBuildNumber + globalAssemblyContent.Substring(gBuildNumber2.Index + gBuildNumber2.Length)
        End If

        WriteFileContent(globalAssemblyPath, globalAssemblyContent)

    End Sub

    Function GenerateBuildNumber(Optional ByVal oldBuildNumber As String = "0") As String

        oldBuildNumber = Int16.Parse(oldBuildNumber) + 1

        Return oldBuildNumber

    End Function

    Private Function GetFirstUnCommentedMatch(ByRef mc As MatchCollection, ByVal content As String) As Match
        Dim rSingleLineComment As Regex = New Regex("\/\/.*$")
        Dim rMultiLineComment As Regex = New Regex("\/\*[\s\S]*?\*\/")

        Dim mSingleLineComments As MatchCollection = rSingleLineComment.Matches(content)
        Dim mMultiLineComments As MatchCollection = rMultiLineComment.Matches(content)

        For Each m As Match In mc
            If m.Success Then
                For Each singleLine As Match In mSingleLineComments
                    If singleLine.Success Then
                        If m.Index >= singleLine.Index And m.Index + m.Length <= singleLine.Index + singleLine.Length Then
                            GoTo NextAttribute
                        End If
                    End If
                Next

                For Each multiLine As Match In mMultiLineComments
                    If multiLine.Success Then
                        If m.Index >= multiLine.Index And m.Index + m.Length <= multiLine.Index + multiLine.Length Then
                            GoTo NextAttribute
                        End If
                    End If
                Next

                Return m
            End If
NextAttribute:
        Next

        Return Nothing

    End Function

End Module

Thanks you every body

PS. Special Thank to [RegExr: Online Regular Expression Testing Tool][1]. The best online regex tool which I have ever been played. [1]: http://gskinner.com/RegExr/

Soul_Master
A: 

You can achieve same effect much more easily, by downloading and installing MS Build Extension Pack and adding following line at the top of your .csproj file:

<Import Project="$(MSBuildExtensionsPath)\ExtensionPack\MSBuild.ExtensionPack.VersionNumber.targets"/>

This will automatically use current date (MMdd) as the build number, and increment the revision number for you. Now, to override minor and major versions, which are set to 1.0 by default, just add following anywhere in the .csproj file:

<PropertyGroup>
 <AssemblyMajorVersion>2</AssemblyMajorVersion>
 <AssemblyFileMajorVersion>1</AssemblyFileMajorVersion> 
</PropertyGroup>

You can further customize how build number and revision are generated, and even set company, copyright etc. by setting other properties, see this page for the list of properties.

zvolkov
Soul_Master
Actually, it does generate build and revision numbers. I update the answer, check it out.
zvolkov
Is it possible to change auto generate rule?
Soul_Master
Yes, what do you want to do?
zvolkov
Please look at my final answer.
Soul_Master