24

I have an MSBuild script which compiles my existing solution but I'd like to change some properties of one of the projects within the solution at compile-time, including but not limited to AssemblyProduct and AssemblyTitle.

Here's a snippet of my build script:

  <Target Name="Compile" >
 <MSBuild Projects="..\MySolution.sln" 
             Properties="Configuration=MyReleaseConfig;Platform=x86" />
  </Target>

I've got one main executable and several DLLs that are compiled. I am aware of the MSBuild Extension Pack and I suspect it might help me to get to where I need to be, although I'm not sure how to proceed.

Can I selectively change AssemblyInfo properties at build time?

Charlie Salts
  • 13,109
  • 7
  • 49
  • 78
  • Out of curiosity, why would you want to do that? I'd be interested in getting to know a real-world scenario. – Raheel Khan Jul 02 '13 at 05:48
  • @RaheelKhan: Automated build scripts outside of Visual Studio. Such a script sets a specific version number, performs a build, generates an installer, and optionally uploads the finished installer (complete with product name and version number in the filename) to the Internet. If you have 12 variants of the same product (each with a different product name) that you update every few days, having a one-click solution for each is the way to go. Consider too that running such a script can be done by someone who isn't a programmer. – Charlie Salts Jul 04 '13 at 00:01

3 Answers3

28

You're on the right track with the MSBuild Extension Pack.

I find the easiest way to conditionally generate the assembly details at build time is to add an "AssemblyVersion" target directly to my .csproj file(s) that require an updated AssemblyInfo file. You can add the target directly to each csproj file that requires an updated AssemblyInfo file, or as I prefer to do it, create a custom targets file with the AssemblyVersion target and have each csproj file include your custom targets file.

Either way you likely want to use the MSBuild Extension Pack or the MSBuild Community Tasks to use their respective AssemblyInfo task.

Here's some code from our build scripts:

<!-- Import the AssemblyInfo task -->
<Import Project="$(MSBuildCommunityTasksPath)\MSBuild.Community.Tasks.Targets"/>

<!-- Overriding the Microsoft.CSharp.targets target dependency chain -->
<!-- Call our custom AssemblyVersion target before build, even from VS -->
<PropertyGroup>
    <BuildDependsOn>
        AssemblyVersion;
        $(BuildDependsOn)
    </BuildDependsOn>
</PropertyGroup>

<ItemGroup>
    <AssemblyVersionFiles Include="$(MSBuildProjectDirectory)\Properties\AssemblyInfo.cs"/>
</ItemGroup>

<Target Name="AssemblyVersion"
                Inputs="@(AssemblyVersionFiles)"
                Outputs="UpdatedAssemblyVersionFiles">
    <Attrib Files="%(AssemblyVersionFiles.FullPath)"
                    Normal="true"/>
    <AssemblyInfo
        CodeLanguage="CS"
        OutputFile="%(AssemblyVersionFiles.FullPath)"
        AssemblyCompany="$(CompanyName)"
        AssemblyCopyright="Copyright $(CompanyName), All rights reserved."
        AssemblyVersion="$(Version)"
        AssemblyFileVersion="$(Version)">
        <Output TaskParameter="OutputFile"
                        ItemName="UpdatedAssemblyVersionFiles"/>
    </AssemblyInfo>
</Target>
Manushin Igor
  • 3,398
  • 1
  • 26
  • 40
Sneal
  • 2,546
  • 20
  • 22
  • Hmmm.. this *looks* like what I need. I'll try to give it a go. – Charlie Salts Sep 01 '10 at 23:56
  • Overiding the target dependency chain added some noise in VS, every time I build it asks if I want to load the updated AssemblyInfo.cs file which is kind of annoying. As a workaround, I used `` instead. As expected, the alerts went away in VS and MSBuild on the CLI still picks up the changes. Great! – Hovis Biddle Jul 17 '14 at 20:22
  • Thank you for the great answer. I recommend against modifying the dependency chain for `Build`, however, since `BeforeBuild` is there for that (or if you want, you can do `BeforeTargets="BeforeBuild"`). Also, the `Inputs` and `Outputs` attributes aren't quite used correctly - those attributes are used to check if the Target should be run or not (might as well leave it empty for this one), so you might get behaviour where it isn't run at times. Again, thanks for the perfect solution! – makhdumi Mar 17 '15 at 20:00
20

Sneal's answer was very helpful, but I'd like to show what I actually ended up doing. Instead of editing csproj files (there are several) I instead added tasks to my build script. Here's a snippet:

    <PropertyGroup>  
        <ProductName>MyApp</ProductName>
        <CompanyName>MyCompany</CompanyName>
        <Major>1</Major>
        <Minor>0</Minor>
        <Build>0</Build>
        <Revision>0</Revision>
    </PropertyGroup> 

    <ItemGroup>
        <AssemblyVersionFiles Include="..\MyMainProject\Properties\AssemblyInfo.cs"/>
    </ItemGroup>

<Target Name="AssemblyVersionMAIN" Inputs="@(AssemblyVersionFiles)" Outputs="UpdatedAssemblyVersionFiles">
        <Attrib Files="%(AssemblyVersionFiles.FullPath)" Normal="true"/>
        <AssemblyInfo
            CodeLanguage="CS"
            OutputFile="%(AssemblyVersionFiles.FullPath)"
            AssemblyProduct="$(ProductName)"
            AssemblyTitle="$(ProductName)"
            AssemblyCompany="$(CompanyName)"
            AssemblyCopyright="© $(CompanyName) 2010"
            AssemblyVersion="$(Major).$(Minor).$(Build).$(Revision)"
            AssemblyFileVersion="$(Major).$(Minor).$(Build).$(Revision)"
            AssemblyInformationalVersion="$(Major).$(Minor).$(Build).$(Revision)">
            <Output TaskParameter="OutputFile" ItemName="UpdatedAssemblyVersionFiles"/>
        </AssemblyInfo>
    </Target>

 <Target Name="Compile" DependsOnTargets="AssemblyVersionMAIN">
      <MSBuild Projects="..\MySolution.sln"     
               Properties="Configuration=Release;Platform=x86;Optimize=true" />
 </Target>

Then, I can override my variables from the command line, or a batch script, like so:

set MAJ=1
set MIN=2
set BLD=3
set REV=4

msbuild buildScript.xml /t:Compile /p:Major=%MAJ% /p:Minor=%MIN% /p:Build=%BLD% /p:Revision=%REV%
Charlie Salts
  • 13,109
  • 7
  • 49
  • 78
  • Very interesting... thanks as command line build is directly what I am working with too. – DRapp Apr 01 '16 at 16:10
-1
<Target Name="SetVersion">
<ItemGroup>
  <AssemblyInfoFiles  Include="$(TargetDir)\**\AssemblyInfo.cs"/>
</ItemGroup>

<Message Text="change the Version number for:"/>
<Message Text="%(AssemblyInfoFiles.FullPath)"/>

<MSbuild.ExtensionPack.Framework.AssemblyInfo 
 AssemblyInfoFiles="@(AssemblyInfoFiles)"
                                              AssemblyTitle="newTitle"
                                              AssemblyMajorVersion="2"
                                              AssemblyMinorVersion="0"/>

 </Target>