Referencing macros on msbuild (12.0) command line property assignment - msbuild

I am curious, is it possible to reference a macro on a command line property assignment for MSBuild?
E.g:
msbuild.exe MySolution.sln /p:CustomBeforeMicrosoftCSharpTargets="$(SolutionDir)\custom.targets"
Would this also work when specified as "MSBuildArguments" from an "Edit Build Definition"/"Queue New Build" from Visual Studio connected to TFS?
E.g:
/p:CustomBeforeMicrosoftCSharpTargets="$(SolutionDir)\custom.targets"
Because it doesn't appear to be importing these targets for me. But the targets file is definitely there, alongside the solution, in the build workspace.
I don't want to have to specify an absolute path. Not sure how working with relative paths is meant to work here, can't find any advice on the internet, and debugging it is quite difficult, as it is called on a build agent using a workflow. The workflow logging is definitely reporting it is calling MSBuild with these arguments, but nowhere in the verbose logging output can I see it is making reference to the CustomBeforeMicrosoftCSharpTargets target, or calling it.
EDIT
I wrote a little test build project buildme.proj to further my understanding.
<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<SetMe>NotInTheSandbox</SetMe>
</PropertyGroup>
<PropertyGroup>
<SomeMacroValue>c:\Sandbox\BuildTest</SomeMacroValue>
</PropertyGroup>
<PropertyGroup>
<AlreadySet>$(SomeMacroValue)\InTheSandbox</AlreadySet>
</PropertyGroup>
<Target Name="Build">
<Message Text="I am building!" />
<Message Text="Some macro value: $(SomeMacroValue)" />
<Message Text="$(SetMe)" />
<Message Text="$(AlreadySet)" />
</Target>
</Project>
When I execute with the command:
msbuild buildme.proj /p:SetMe="$(SomeMacroValue)\StillNotInSandbox"
I get the following output:
Microsoft (R) Build Engine version 12.0.31101.0
[Microsoft .NET Framework, version 4.0.30319.42000]
Copyright (C) Microsoft Corporation. All rights reserved.
Build started 10/12/2015 22:12:08.
Project "C:\Sandbox\BuildTest\buildme.proj" on node 1 (default targets).
Build:
I am building!
Some macro value: c:\Sandbox\BuildTest
$(SomeMacroValue)\StillNotInSandbox
c:\Sandbox\BuildTest\InTheSandbox
Done Building Project "C:\Sandbox\BuildTest\buildme.proj" (default targets).
Build succeeded.
0 Warning(s)
0 Error(s)
Time Elapsed 00:00:00.02
So clearly, it is not behaving how I expected: The macro identifier appears in the output message text.
Is there a solution to this?

A "macro" like $(SolutionDir) exists only in VisualStudio and VS passes the value to MSBuild.
Instead MSBuild makes Environment variables available as properties, so a batch file like this
set SomeMacroValue=foo
msbuild buildme.proj /p:SetMe="$(SomeMacroValue)\StillNotInSandbox"
is probably what you are looking for.
And you can set environment variables per-user or per-machine (Control Panel\All Control Panel Items\System Advanced System Settings, Environment variables).

Related

Inherit Parent MsBuild file

I have a project structure that looks like this:
Parent
-- ChildProjects1
-- ChildProjects2
-- ChildProjects3
I have an msbuild file under each ChildProjects node that builds the relevant projects, creates zip files, tags them in subversion etc
However most of this logic is common between ChildProjects. I'm wondering if I can refactor this common logic to sit in another msbuild file at the parent level and have each child inherit this?
Any ideas on this appreciated.
You can put common Targets inside an a file that you include using the following syntax, you will also see it in your proj files:
<Import Project="path to file.targets"/>
Things to note:
The convention is to use a .targets extension but it doesn't matter.
Where you place the import is important depending on if you want to be able to override properties or targets in the imported targets file.
Targets are not like methods, you cannot call them more than once but you can influence the order in which they are called.
If you require reusable chunks that you want to call multiple times create a custom task library but check out the MSBuildExtensionPack first to see if it has what you want.
Call Target
In relation to the question about CallTarget. CallTarget will invoke the specified target(s) the same way that DependsOnTargets, BeforeTargets and AfterTargets do. The target will only be run if it has not already been run. See the following example:
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003" DefaultTargets="Foo">
<Target Name="Foo" DependsOnTargets="Bar">
<Message Text="Foo" />
<CallTarget Targets="Bar" />
<CallTarget Targets="Bar" />
</Target>
<Target Name="Bar" AfterTargets="Foo" BeforeTargets="Foo">
<Message Text="Bar" />
</Target>
</Project>
Which will output the following:
Microsoft (R) Build Engine version 4.0.30319.17929
[Microsoft .NET Framework, version 4.0.30319.18449]
Copyright (C) Microsoft Corporation. All rights reserved.
Build started 24/02/2014 12:06:59.
Project "D:\Dev\Test.proj" on node 1 (default targets).
Bar:
Bar
Foo:
Foo
Done Building Project "D:\Dev\Test.proj" (default targets).
Build succeeded.
0 Warning(s)
0 Error(s)
Time Elapsed 00:00:00.69

Running XUnit tests with TeamCity using MSBuild

I am trying to get TeamCity to run XUnit tests as part of the build process. So I created a separate file - MyProject.msbuild - living in the same folder as the .sln file, which looks like this:
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<UsingTask AssemblyFile="$(MSBuildProjectDirectory)\..\bin\xunit.net\xunit.runner.msbuild.dll" TaskName="Xunit.Runner.MSBuild.xunit"
/>
<Target Name="Build">
<MSBuild Projects="MyProject.sln" Targets="Build" Properties="Configuration=Release">
<xunit Assembly="MyProject.Utility.Tests\bin\Release\MyProject.Utility.Tests.dll" />
</MSBuild>
</Target>
</Project>
However, no matter what I do, VS2010 hates me having the element inside the element. If I run MSBuild on the file, it tells me a little bit more:
P:\MyProject\src>c:\Windows\Microsoft.NET\Framework64\v4.0.30319\MSBuild.exe MyProject.msbuild /tv:4.0 /v:d
Microsoft (R) Build Engine Version 4.0.30319.1
[Microsoft .NET Framework, Version 4.0.30319.225]
Copyright (C) Microsoft Corporation 2007. All rights reserved.
Build started 08.11.2011 21:08:46.
Project "P:\MyProject\src\MyProject.msbuild" on node 1 (default targets).
Building with tools version "4.0".
P:\MyProject\src\MyProject.msbuild(8,9): error MSB4067: The element <xunit> beneath element <MSBuild> is unrecognized.
Done Building Project "P:\MyProject\src\MyProject.msbuild" (default targets) -- FAILED.
Build FAILED.
"P:\MyProject\src\MyProject.msbuild" (default target) (1) ->
P:\MyProject\src\MyProject.msbuild(8,9): error MSB4067: The element <xunit> beneath element <MSBuild> is unrecognized.
0 Warning(s)
1 Error(s)
Time Elapsed 00:00:00.01
So my current guess is that it doesn't successfully load the xunit.runner.msbuild.dll somehow - or I have done something else strange.
However, I would think that if it couldn't load xunit.runner.msbuild.dll, it would tell me about it. I made sure the file is not blocked (by unpacking the xunit distribution with 7zip).
Any ideas what I can do to get MSBuild to swallow my build file and run the tests?
You don't want to nest the calls, try this:
<Target Name="Build">
<MSBuild Projects="MyProject.sln" Targets="Build" Properties="Configuration=Release">
</MSBuild>
<xunit Assembly="MyProject.Utility.Tests\bin\Release\MyProject.Utility.Tests.dll" />
</Target>
The items within a target are executed in sequence.

How to change Assembly Version Number using AssemblyInfoTask?

I am trying to automate the process for setting the Version for all DLL's, after spending some time I came to know the AssemblyInfo Task with which it can most likely be achieved.
So I went ahead and installed it, specifically version 1.0.51130.0.
After Installing, I manually added the Import Tag (by unloading the each project) of AssemblyInfoTask in .cspoj files (the solution has more than 35 proj files).
<Import Project="$(MSBuildExtensionsPath)\Microsoft\AssemblyInfoTask\Microsoft.VersionNumber.Targets"/>
Next I modified the Microsoft.VersionNUmber.Target file which will be installed in path: C:\Program Files\MSBuild\Microsoft\AssemblyInfoTask, and I modified the following section:
<!-- Properties for controlling the Assembly Version -->
<PropertyGroup>
<AssemblyMajorVersion>4</AssemblyMajorVersion>
<AssemblyMinorVersion>0</AssemblyMinorVersion>
<AssemblyBuildNumber></AssemblyBuildNumber>
<AssemblyRevision></AssemblyRevision>
<AssemblyBuildNumberType>DateString</AssemblyBuildNumberType>
<AssemblyBuildNumberFormat>01MMdd</AssemblyBuildNumberFormat>
<AssemblyRevisionType>AutoIncrement</AssemblyRevisionType>
<AssemblyRevisionFormat>00</AssemblyRevisionFormat>
</PropertyGroup>
<!-- Properties for controlling the Assembly File Version -->
<PropertyGroup>
<AssemblyFileMajorVersion>4</AssemblyFileMajorVersion>
<AssemblyFileMinorVersion>0</AssemblyFileMinorVersion>
<AssemblyFileBuildNumber></AssemblyFileBuildNumber>
<AssemblyFileRevision></AssemblyFileRevision>
<AssemblyFileBuildNumberType>DateString</AssemblyFileBuildNumberType>
<AssemblyFileBuildNumberFormat>01MMdd</AssemblyFileBuildNumberFormat>
<AssemblyFileRevisionType>AutoIncrement</AssemblyFileRevisionType>
<AssemblyFileRevisionFormat>00</AssemblyFileRevisionFormat>
</PropertyGroup>
Next I set the assemblyInfo.cs file's version to 1.0.0.0 in every project. Finally I saved and close it, reopened solution, and built. It works like a champ!
Now what want is to customize the Version to 4.0.1053.1 where 10 is the part of year indicator which is 2010 and 53 denotes the week number, at last 1 denotes revision number.
How to achieve this using the AssemblyInfo Task? I came across several posts that a new version of AssemblyInfo Task is available in Build Extension Pack.
I have installed the MSBuild Extension Pack December 2010 and its version is MSBuild Extension Pack 4.0.2.0 Installer
First.. use a globalassemblyinfo.cs that is linked from each project.
Add its as linked file to each project.
This means you update one file, not 30+ assemblyinfo files...then:
use MSBuild.Community.Tasks....
Then call
<AssemblyInfo CodeLanguage="CS"
OutputFile="$(VersionFile)"
AssemblyCompany="Company"
AssemblyProduct="Product"
AssemblyCopyright="Copyright © Company 2011"
ComVisible="false"
AssemblyVersion="$(BUILD_NUMBER)"
AssemblyFileVersion="$(BUILD_NUMBER)" />
Assuming you have something like:
<Import Project=".\tasks\MSBuild.Community.Tasks.Targets"/>
I do this in Jenkins by having a package build that is parameterised using the List Subversion Tags parameter type. The Subversion tag must follow the version number format (major.minor.revision.build), e.g. tags/2.0.0.1. The tag name is then assigned to a Jenkins parameter, e.g. $VERSION becomes 2.0.0.1
I use the WriteLinesToFile msbuild task to write out the assembly attribute to a second file alongside the PropertyInfo.cs called VersionInfo.cs. As checked in to source control, this just contains a default version number:
// Do not change this. The version is set on package builds only by setting the AsmVersion MSBuild property
[assembly: System.Reflection.AssemblyVersion("0.0.0.0")]
The package build on the build server passes in the version via the AsmVersion parameter:
/p:AsmVersion=$VERSION
The .csproj file is modified to have a BeforeBuild target (Visual Studio creates a commented out one for you):
<Target Name="BeforeBuild">
<WriteLinesToFile
Condition=" '$(AsmVersion)' != '' " File="Properties\VersionInfo.cs"
Overwrite="True"
Lines="[assembly: System.Reflection.AssemblyVersion("$(AsmVersion)")] // Generated by build" />
</Target>
When building in Visual Studio, or without passing in the AsmVersion, your assembly will have a default version of 0.0.0.0. When building in the package build, you will get your desired build number.
As #bruceboughton proposed, you can easily generate a version assembly file during compilation without using MSBuild.Community.Tasks library:
<PropertyGroup>
<Version>0.0.0</Version>
<InformationalVersion>0.0.0-dev~commithash</InformationalVersion>
<VersionFileName>$(BaseIntermediateOutputPath)Version.cs</VersionFileName>
</PropertyGroup>
<Target Name="GenerateVersionFile" BeforeTargets="BeforeBuild">
<WriteLinesToFile
File="$(VersionFileName)"
Overwrite="True"
Lines="
[assembly: System.Reflection.AssemblyVersion("$(Version)")]
[assembly: System.Reflection.AssemblyFileVersion("$(Version)")]
[assembly: System.Reflection.AssemblyInformationalVersion("$(InformationalVersion)")]" />
<ItemGroup>
<Compile Include="$(VersionFileName)" />
</ItemGroup>
</Target>
Remove definitions of the parameters you specify in the generated file from Properties\AssemblyInfo.cs file.
After that you can specify version by adding a parameter to the msbuild:
msbuild /property:Version=1.2.3 /property:InformationalVersion=1.2.3-dev~commithash .\SolutionFile.sln
Update for .NET Core style .csproj files: If you've come upon this question after having transitioned to the new .csproj format used by .NET Core, you can just set the Version property (no need to to bother with MSBuild tasks).
How I finally got this to work MSBuild version 12 (VS 2013).
Used Nuget to get MSBuildTasks Community package
Edited my .csproj file and added a path to the import the package:
<Import Project="..\packages\MSBuildTasks.1.5.0.235\build\MSBuildTasks.targets" Condition="Exists('..\packages\MSBuildTasks.1.5.0.235\build\MSBuildTasks.target')"/>
Figured out the Regex to change just the Revision number in the AssemblyInfo.cs file:
(?<=AssemblyFileVersion\("[0-9]\.[0-9]\.[0-9]\.)(\*)
which is not XML compatible, so has to be changed to:
(?<=AssemblyFileVersion\("[0-9]\.[0-9]\.[0-9]\.)(\*)
Uncommented the <Target Name="BeforeBuild"> section and added the following:
<Target Name="BeforeBuild">
<FileUpdate Files="properties\AssemblyInfo.cs"
Regex="(?<=AssemblyFileVersion\("[0-9]\.[0-9]\.[0-9]\.)(\*)"
ReplacementText="$(Revision)" />
</Target>
When running MSBuild added the "Revision" property to the command line e.g.
msbuild.exe myProject.csproj /t:Build /p:Configuration=Release;Revision=12345

Specifying assembly version of all projects within a web deployment wdproj script

I have a .wdproj Web Deployment Project created with VS2010 that contains references to other class libraries, like this:
<Project ToolsVersion="4.0" DefaultTargets="Build"
xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ProjectReference Include="..\Path\Proj1.csproj">
<Project>{GUID-HERE}</Project>
<Name>Proj1</Name>
</ProjectReference>
<ProjectReference Include="..\Path\Proj2.csproj">
<Project>{GUID-HERE}</Project>
<Name>Proj2</Name>
</ProjectReference>
There are lots of these. I want to be able to run msbuild /t:Rebuild /p:Configuration=Release and have all the assemblies of all the included projects compiled at a specified version. Nothing fancy just static like 2.5.6.0 and specified once in the wdproj file. I dont want to open 30 files manually.
I have looked at MSBuild Community Task and MSBuildExtension Pack and can not get anything to work. The build runs ok without errors.
Anyone have an example of how this can be done?
This is an attempt with MSBuild Extensions (adapted from the sample included) that doesn't work:
<Import Project="$(MSBuildExtensionsPath)\ExtensionPack\4.0\MSBuild.ExtensionPack.VersionNumber.targets"/>
<Target Name="Build">
<MSBuild.ExtensionPack.Framework.AssemblyInfo
ComVisible="true"
AssemblyInfoFiles="VersionInfo.cs"
AssemblyFileMajorVersion="2"
AssemblyFileMinorVersion="5"
AssemblyFileBuildNumber="6"
AssemblyFileRevision="0"
/>
</Target>
MSBuild is definately looking at the MSBuild.ExtensionPack.Framework.AssemblyInfo element because if the attribute names are incorrect the build will fail. This builds ok but none of the versions on the referenced assemblies are changed. The version numbers on the ASP.NET page assemblies from the website are all 0.0.0.0.
Are you maybe missing to specify the CodeLanguage and OutputFile attributes?
I think the AssemblyInfo task is intended to generate (replace) a source file prior to compiling.

MSBuild.Community.Tasks.Attrib on x64 machine failing

I'm having an issue with the Attrib task from the MSBuild Community Tasks Project when running on a 64 bit build machine.
I've put together this small test project to show what the problem is:
<Project ToolsVersion="3.5" DefaultTargets="Build"
xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath32)\MSBuildCommunityTasks\MSBuild.Community.Tasks.Targets"/>
<Target Name="PrintProperties">
<Message Text="MSBuildCommunityTasksPath: $(MSBuildCommunityTasksPath)"/>
<Message Text="MSBuildCommunityTasksLib: $(MSBuildCommunityTasksLib)"/>
<Message Text="MSBuildNodeCount: $(MSBuildNodeCount)"/>
<Message Text="MSBuildExtensionsPath: $(MSBuildExtensionsPath)"/>
<Message Text="MSBuildExtensionsPath32: $(MSBuildExtensionsPath32)"/>
<Message Text="MSBuildProjectDirectoryNoRoot: $(MSBuildProjectDirectoryNoRoot)"/>
<Message Text="MSBuildToolsPath: $(MSBuildToolsPath)"/>
<Message Text="MSBuildToolsVersion: $(MSBuildToolsVersion)"/>
<Message Text="MSBuildBinPath: $(MSBuildBinPath)"/>
<Message Text="MSBuildExtensionsPath: $(MSBuildExtensionsPath)"/>
<Message Text="MSBuildProjectDefaultTargets: $(MSBuildProjectDefaultTargets)"/>
<Message Text="MSBuildProjectDirectory: $(MSBuildProjectDirectory)"/>
<Message Text="MSBuildProjectExtension: $(MSBuildProjectExtension)"/>
<Message Text="MSBuildProjectFile: $(MSBuildProjectFile)"/>
<Message Text="MSBuildProjectFullPath: $(MSBuildProjectFullPath)"/>
<Message Text="MSBuildProjectName: $(MSBuildProjectName)"/>
<Message Text="MSBuildStartupDirectory: $(MSBuildStartupDirectory)"/>
</Target>
<Target Name="TestAttrib" DependsOnTargets="PrintProperties">
<Attrib Files="Test.txt" ReadOnly="false" />
</Target>
</Project>
when I attempt to build this project with MSBuild using TestAttrib as the Target I get the following results
C:>msbuild "C:_Source Code\Test.vbproj" /t:TestAttrib
Microsoft (R) Build Engine Version 3.5.30729.1
[Microsoft .NET Framework, Version 2.0.50727.4016]
Copyright (C) Microsoft Corporation 2007. All rights reserved.
Build started 7/09/2009 2:50:12 PM.
Project "C:_Source Code\Test.vbproj" on node 0 (TestAttrib target(s)).
MSBuildCommunityTasksLib: C:\Program Files\MSBuild\MSBuildCommunityTasks\MSBu
ild.Community.Tasks.dll
MSBuildNodeCount: 1
MSBuildExtensionsPath: C:\Program Files\MSBuild
MSBuildExtensionsPath32: C:\Program Files (x86)\MSBuild
MSBuildProjectDirectoryNoRoot: _Source Code
MSBuildToolsPath: c:\Windows\Microsoft.NET\Framework64\v3.5
MSBuildToolsVersion: 3.5
MSBuildBinPath: c:\Windows\Microsoft.NET\Framework64\v3.5
MSBuildExtensionsPath: C:\Program Files\MSBuild
MSBuildProjectDefaultTargets: Build
MSBuildProjectDirectory: C:_Source Code
MSBuildProjectExtension: .vbproj
MSBuildProjectFile: Test.vbproj
MSBuildProjectFullPath: C:_Source Code\Test.vbproj
MSBuildProjectName: Test
MSBuildStartupDirectory: C:\
C:_Source Code\Test.vbproj(26,5): error MSB4062: The "MSBuild.Community.Tasks.Attrib" task could not be loaded from the assembly C:\Program Files\MSBuild\MSBuildCommunityTasks\MSBuild.Community.Tasks.dll. Could not load file or assembly 'file:///C:\Program Files\MSBuild\MSBuildCommunityTasks\MSBuild.Community.Tasks.dll' or one of its dependencies. The system cannot find the file specified. Confirm that the declaration is correct, and that the assembly and all its dependencies are available.
Done Building Project "C:_Source Code\Test.vbproj" (TestAttrib target(s)) -- FAILED.
Build FAILED.
"C:_Source Code\Test.vbproj" (TestAttrib target) (1) ->(TestAttrib target) -> C:_Source Code\Test.vbproj(26,5): error MSB4062: The "MSBuild.Community.Tasks.Attrib" task could not be loaded from the assembly C:\Program Files\MSBuild\MSBuildCommunityTasks\MSBuild.Community.Tasks.dll. Could not load file or assembly 'file:///C:\Program Files\MSBuild\MSBuildCommunityTasks\MSBuild.Community.Tasks.dll' or one of its dependencies. The system cannot find the file specified. Confirm that the declaration is correct, and that the assembly and all its dependencies are available.
0 Warning(s)
1 Error(s)
Time Elapsed 00:00:00.05
Why is the MSBuild searching for the Attrib task in C:\Program Files\MSBuild\MSBuildCommunityTasks\MSBuild.Community.Tasks.dll when I've explicitly imported the tasks using the (MSBuildExtensionsPath32) variable?
I seem to have fixed the issue by editing line 6 of "C:\Program Files (x86)\MSBuild\MSBuildCommunityTasks\MSBuild.Community.Tasks.Targets"
Originally it was:
<MSBuildCommunityTasksPath Condition="'$(MSBuildCommunityTasksPath)' == ''">$(MSBuildExtensionsPath)\MSBuildCommunityTasks</MSBuildCommunityTasksPath>
and I altered it to:
<MSBuildCommunityTasksPath Condition="'$(MSBuildCommunityTasksPath)' == ''">$(MSBuildExtensionsPath32)\MSBuildCommunityTasks</MSBuildCommunityTasksPath>
note the change of $(MSBuildExtensionsPath) to $(MSBuildExtensionsPath32)
While this seems to have sorted out my issue for now, I'm not sure why I had to edit the MSBuild.Community.Tasks.Targets file in the first place - I assumed the installer would have made sure the file was correct. So perhaps editing the MSBuild.Community.Tasks.Targets file isn't the best idea in the world, so be careful if you decide to follow my lead.
This is obviously far too late an answer, but I thought I would add to this in case anyone else is having this problem. Instead of editing the MSBuild.Community.Tasks.Targets file, you can just define the MSBuildCommunityTasksPath property in a property group in your build file. For example, I have something like this at the top of my build script:
<PropertyGroup>
<ToolsDirectory>$(MSBuildProjectDirectory)\tools</ToolsDirectory>
<MSBuildCommunityTasksPath>$(ToolsDirectory)\MSBuildCommunityTasks</MSBuildCommunityTasksPath>
</PropertyGroup>
I then store the MSBuildCommunityTasks folder in a tools directory in source control. It means that other folk can pull out the source and build it straight away without installing anything.
Cheers,
Adam
This is very out of date help. The ms build process has changed since this ticket, best to follow the instructions here https://github.com/loresoft/msbuildtasks
For my project, I used a Build folder in my solution and in the solution I create a MSBuildTasks Folder where I copy the latests MBBuild.Community.Task files in the Zip file release. and put the following lines in my msbuild file
<PropertyGroup>
<MSBuildCommunityTasksPath>.\MSBuildTasks</MSBuildCommunityTasksPath>
</PropertyGroup>
<Import Project=".\MSBuildTasks\MSBuild.Community.Tasks.Targets" />