MSBuild pattern matching for test dlls - msbuild

I have a CreateItem task to match unit test assembly(s):
<CreateItem Include="%(Solution.RelativeDir)**\bin\$(Configuration)\*Test*.dll">
<Output ItemName="TestDlls" TaskParameter="Include"/>
</CreateItem>
This matching dll(s) like:
Company.Product.Component1.Test.dll
Company.Product.Component2.UnitTests.dll
Company.Product.UnitTestFramework1.dll
Company.Product.UnitTestFramework2.dll
My question is how to change the matching pattern to select only unit test (that have Test, Tests, UnitTest or UnitTests naming convention) but no framework dll(s).

Just add Test.dll and Tests.dll incrementally. The following would do what you want:
<CreateItem Include="%(Solution.RelativeDir)**\bin\$(Configuration)\*Test.dll">
<Output ItemName="TestDlls" TaskParameter="Include"/>
</CreateItem>
<CreateItem Include="%(Solution.RelativeDir)**\bin\$(Configuration)\*Tests.dll">
<Output ItemName="TestDlls" TaskParameter="Include"/>
</CreateItem>
Alternatively, you can add use your original item definition, and after that remove those files that match framework pattern. E.g.:
<CreateItem Include="%(Solution.RelativeDir)**\bin\$(Configuration)\*Test*.dll">
<Output ItemName="TestDlls" TaskParameter="Include"/>
</CreateItem>
<CreateItem Remove="*Framework*.dll" />

Related

How can I name the output from my WiX Installer project based on GitVersion properties?

I've recently started using GitVersion to version my assemblies, and I love it!
I like to generate a .msi file that reflects the version of the product being built. Hitherto, I was using this in my .wixproj file:
<!-- [TPL] name the output file to include the version from theLocalServer assembly -->
<Target Name="BeforeBuild">
<GetAssemblyIdentity AssemblyFiles="$(SolutionDir)BuildOutput\$(Configuration)\TA.DigitalDomeworks.Server.exe">
<Output TaskParameter="Assemblies" ItemName="AssemblyVersions" />
</GetAssemblyIdentity>
<CreateProperty Value="$(OutputName).%(AssemblyVersions.Version)">
<Output TaskParameter="Value" PropertyName="TargetName" />
</CreateProperty>
<CreateProperty Value="$(TargetName)$(TargetExt)">
<Output TaskParameter="Value" PropertyName="TargetFileName" />
</CreateProperty>
<CreateProperty Value="$(TargetDir)$(TargetFileName)">
<Output TaskParameter="Value" PropertyName="TargetPath" />
</CreateProperty>
</Target>
This produces an output file with a name like:
TA.DigitalDomeworks.Installer.7.1.0.3.msi
I found this solution from this answer, which references this blog post. The 7.1.0.3 comes from the assembly version of the main assembly in the build, which in turn is being versioned by GitVersion during its own build.
However, what I'd really like is to use the FullSemVer property, which can be seen here:
C:\Users\Tim\source\repos\TA.DigitalDomeworks [release/7.1 ↑1 +0 ~1 -0 !]> gitversion
{
"Major":7,
"Minor":1,
"Patch":0,
"PreReleaseTag":"beta.3",
"PreReleaseTagWithDash":"-beta.3",
"PreReleaseLabel":"beta",
"PreReleaseNumber":3,
"BuildMetaData":"",
"BuildMetaDataPadded":"",
"FullBuildMetaData":"Branch.release/7.1.Sha.77fa2c96ed9b0f5ab162d07052ef094e8ccfc8c5",
"MajorMinorPatch":"7.1.0",
"SemVer":"7.1.0-beta.3",
"LegacySemVer":"7.1.0-beta3",
"LegacySemVerPadded":"7.1.0-beta0003",
"AssemblySemVer":"7.1.0.3",
"FullSemVer":"7.1.0-beta.3",
"InformationalVersion":"7.1.0-beta.3+Branch.release/7.1.Sha.77fa2c96ed9b0f5ab162d07052ef094e8ccfc8c5",
"BranchName":"release/7.1",
"Sha":"77fa2c96ed9b0f5ab162d07052ef094e8ccfc8c5",
"NuGetVersionV2":"7.1.0-beta0003",
"NuGetVersion":"7.1.0-beta0003",
"CommitsSinceVersionSource":3,
"CommitsSinceVersionSourcePadded":"0003",
"CommitDate":"2018-09-10"
}
So the final filename I'd like to see is:
TA.DigitalDomeworks.Installer-7.1.0-Beta.3.msi
In the GitVersion documentation, it say that I need to make sure the GitVersion.GetVersion build task has executed, after which I should be able to obtain the full SemVer from a build property called $(GitVersion_FullSemVer).
However, I'm not sure how to achieve that in a WiX project, since installing the GitVersionTask NuGet package doesn't seem to do anything (in C# projects, everything just magically works). If I could get to the point where I can execute the GitVersion.GetVersion task, then I think I can see my way clear to getting the output name I want.
Has anyone got this working? Or can anyone offer any advice on how to approach this?
I figured it out. I was on the right lines to start with. There's no need to manually execute GitVersion.GetVersion, this is handled correctly by MSBuild once the GitVersionTask NuGet package is installed in the WiX project. The reason it wasn't working is because I was trying to use GitVersion's properties before they had been set.
So, first thing is to create a couple of properties that disable some unwanted GitVersion actions:
<PropertyGroup>
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
<WriteVersionInfoToBuildLog>false</WriteVersionInfoToBuildLog>
<GenerateGitVersionInformation>false</GenerateGitVersionInformation>
</PropertyGroup>
We can't simply change the OutputName property because that is set before any tasks (including GitVersion tasks) execute, so if we try to use the GitVersion properties in there, they will be empty/unset because the GitVersion targets haven't executed yet. Instead, we have to put some logic into a build target, the obvious candidate being the BeforeBuild target that is usually found at the end of the project file:
<Target Name="BeforeBuild" AfterTargets="GetVersion">
<CreateProperty Value="$(OutputName).$(GitVersion_SemVer)">
<Output TaskParameter="Value" PropertyName="TargetName" />
</CreateProperty>
<CreateProperty Value="$(TargetName)$(TargetExt)">
<Output TaskParameter="Value" PropertyName="TargetFileName" />
</CreateProperty>
<CreateProperty Value="$(TargetDir)$(TargetFileName)">
<Output TaskParameter="Value" PropertyName="TargetPath" />
</CreateProperty>
</Target>
Here we use the OutputName property as a base name and augmented it with the desired GitVersion properties, to create the TargetName, TargetFileName and TargetPath properties that will be used in the rest of the build process. This works because the BeforeBuild task is guaranteed to execute before any output file is generated, so all the Target* properties will be set before then.
Let's see if this solves your problem
My approach goes thus:
Add the following to your main assembly project, not the wix project
<Target Name="WriteVersionToFile" AfterTargets="GetVersion">
<WriteLinesToFile File="$(OutputPath)version.txt" Lines="$(GitVersion_FullSemVer)" Overwrite="true" Encoding="Unicode" />
</Target>
Then replace your BeforeBuild target with this sample in your wix project
<Target Name="BeforeBuild">
<ReadLinesFromFile File="$(SolutionDir)BuildOutput\$(Configuration)\version.txt">
<Output TaskParameter="Lines" ItemName="Version" />
</ReadLinesFromFile>
<CreateProperty Value="$(OutputName).$(Version)">
<Output TaskParameter="Value" PropertyName="TargetName" />
</CreateProperty>
<CreateProperty Value="$(TargetName)$(TargetExt)">
<Output TaskParameter="Value" PropertyName="TargetFileName" />
</CreateProperty>
<CreateProperty Value="$(TargetDir)$(TargetFileName)">
<Output TaskParameter="Value" PropertyName="TargetPath" />
</CreateProperty>
</Target>
The solution adds the GitVersion_FullSemVer property to a text file after the GetVersionTask is run. This data is then used in the wix project for versioning the msi file.

NUnit Extension Pack task stop after failed tests

I am running msbuild nunit task from extension pack that has 1 test which fails:
<Target Name="Tests">
<MSBuild.ExtensionPack.CodeQuality.NUnit
Assemblies="$(DropsDir)\$(Configuration)\$(TestPrj)\$(TestPrj).dll"
ToolPath="$(NUnitPath)"
ContinueOnError="False">
<Output TaskParameter="Total" PropertyName="ResultTotal"/>
<Output TaskParameter="NotRun" PropertyName="ResultNotRun"/>
<Output TaskParameter="Failures" PropertyName="ResultFailures"/>
<Output TaskParameter="Errors" PropertyName="ResultErrors"/>
<Output TaskParameter="Inconclusive" PropertyName="ResultInconclusive"/>
<Output TaskParameter="Ignored" PropertyName="ResultIgnored"/>
<Output TaskParameter="Skipped" PropertyName="ResultSkipped"/>
<Output TaskParameter="Invalid" PropertyName="ResultInvalid"/>
</MSBuild.ExtensionPack.CodeQuality.NUnit>
</Target>
output:
How can I prevent the next target to be executed? "Zip-Projects" ?
I am using MSBuild.Extension.Pack.March.2015.zip and framework 4.0
I solved it using an Error task and reading both output variables ResultErrors and ResultFailures.
<Error Condition="$(ResultErrors) > 0 Or $(ResultFailures) > 0" Text="Unit Tests didn't pass *****" />
You don't mention what version of the MSBuildExtensionPack you're using, but looking at the source for Trunk (Line 278) It looks like you need to specify The FailOnFailures Property in order to get their failure detection to work.
Therefore
<Target Name="Tests">
<MSBuild.ExtensionPack.CodeQuality.NUnit
Assemblies="$(DropsDir)\$(Configuration)\$(TestPrj)\$(TestPrj).dll"
ToolPath="$(NUnitPath)"
FailOnFailures="True"
ContinueOnError="False">
<Output TaskParameter="Total" PropertyName="ResultTotal"/>
<Output TaskParameter="NotRun" PropertyName="ResultNotRun"/>
<Output TaskParameter="Failures" PropertyName="ResultFailures"/>
<Output TaskParameter="Errors" PropertyName="ResultErrors"/>
<Output TaskParameter="Inconclusive" PropertyName="ResultInconclusive"/>
<Output TaskParameter="Ignored" PropertyName="ResultIgnored"/>
<Output TaskParameter="Skipped" PropertyName="ResultSkipped"/>
<Output TaskParameter="Invalid" PropertyName="ResultInvalid"/>
</MSBuild.ExtensionPack.CodeQuality.NUnit>
</Target>

msbuild output remove assemblies

I have some msbuild code that looks something like this:
<Target Name="Build">
<MSBuild
Projects="#(UnitTestProject)"
Properties="$(BuildProperties)">
<Output TaskParameter="TargetOutputs" ItemName="TestAssembly" />
</MSBuild>
</Target>
<Target Name="Test" DependsOnTargets="Build">
<ItemGroup>
<TestAssembly Remove="*.Example.dll" />
</ItemGroup>
<xunit Assemblies="#(TestAssembly)" />
</Target>
So I am building all of my unit test projects and caputuring the built dll's using the Output task on the TargetOutputs parameter. The problem is that one of the projects is calling a task that outputs some dll's that I don't want to actually run xunit against.
What's weird though is that the Remove="*.Example.dll" appears to not have any affect at all and xunit is trying to test the assembly anyway.
Why is Remove not working?
Actually I think I figured it out. It appears that the problem resides in the way the relative path is resolved in ItemGroups in the Target vs. outside of a Target. I need to be a little more explicit with my path and then it works. Basically I did this to get it to work:
<Target Name="Build">
<MSBuild
Projects="#(UnitTestProject)"
Properties="$(BuildProperties)">
<Output TaskParameter="TargetOutputs" ItemName="UnitTestOutput" />
</MSBuild>
<ItemGroup>
<TestAssembly Include="#(UnitTestOutput)" Exclude="$(RootTestPath)\**\*.Example.dll" />
</Target>
<Target Name="Test" DependsOnTargets="Build">
<xunit Assemblies="#(TestAssembly)" />
</Target>

MSBuild Batching on Three Independent Variables

I have been writing a build system based on MSBuild, and am to the end of the project where I need to essentially run the one msbuild file 88 times by batching over three variables:
Configuration = Debug; Beta; Release; Evaluation
Platform = x86; x64
Language = CN; CS; DE; EN; ES; FR; IT; JP; KO; PL; TW
I want to build:
"Debug x86 CN", "Debug x86 CS", ... "Debug x86 TW"
"Debug x64 CN", ...
I can, of course, define 88 of these:
<ItemGroup>
<ToBuild Include="Debug_x86_CN">
<Configuration>Debug</Configuration>
<Platform>x86</Platform>
<Language>EN</Language>
</ToBuild>
<ItemGroup>
And then batch based on metadata. But what a drag! Can I create the 88 permutations in code, so adding a language is as easy as adding three characters to an ItemGroup:
<ItemGroup>
<AllConfigurations Include="Beta; Release; Evaluation;"/>
<AllPlatforms Include="x86; x64" />
<AllLanguages Include="CN; CS; DE; EN; ES; FR; IT; JP; KO; PL; TW" />
</ItemGroup>
Thanks to Anders Ljusberg for posting an answer to this years ago. The solution is to use the CreateItem task to combine the individual ItemGroups into one ItemGroup. The cross product of each item needs to be done one at a time into a new ItemGroup (in this case _Config_X_Language and _Config_X_Language_X_Platform) to prevent empty metadata from leaking in (if you try to reuse _Config_X_Language, you'll get items with empty Platform, n addition to the platforms in $(Platform).
<ItemGroup>
<Configuration Include="Beta; Release; Evaluation;"/>
<Platform Include="x86; x64" />
<Language Include="CN; CS; DE; EN; ES; FR; IT; JP; KO; PL; TW" />
</ItemGroup>
<!-- Create an ItemGroup that is the cross product of Configuration and Language: -->
<CreateItem Include="#(Configuration)" AdditionalMetadata="Language=%(Language.Identity);" >
<Output ItemName="_Config_X_Language" TaskParameter="Include"/>
</CreateItem>
<!-- Create another ItemGroup that is the cross product of _Configuration_X_Language and Platform: -->
<CreateItem Include="#(_Config_X_Language)" AdditionalMetadata="Platform=%(Platform.Identity);" >
<Output ItemName="_Config_X_Language_X_Platform" TaskParameter="Include"/>
</CreateItem>
<!-- Task batch over each unique set of metadata on AllBuilds -->
<MSBuild Projects="myproject.msbuild"
Properties="Configuration=%(_Config_X_Language_X_Platform.Identity);
Platform=%(_Config_X_Language_X_Platform.Platform);
Language=%(_Config_X_Language_X_Platform.Language);"
Targets="MyTarget"
BuildInParallel="true" />
It is a good idea to combine the individual ItemGroups into one ItemGroup.
However, when using this method MSBuild builds projects ignoring attribute BuildInParallel (all projects building consistently).
So you need something to supplement script:
<ItemGroup>
<Configuration Include="Beta; Release; Evaluation;"/>
<Platform Include="x86; x64" />
<Language Include="CN; CS; DE; EN; ES; FR; IT; JP; KO; PL; TW" />
</ItemGroup>
<!-- Create an ItemGroup that is the cross product of Configuration and Language: -->
<CreateItem Include="#(Configuration)" AdditionalMetadata="Language=%(Language.Identity);" >
<Output ItemName="_Config_X_Language" TaskParameter="Include"/>
</CreateItem>
<!-- Create another ItemGroup that is the cross product of _Configuration_X_Language and Platform: -->
<CreateItem Include="#(_Config_X_Language)" AdditionalMetadata="Platform=%(Platform.Identity);" >
<Output ItemName="_Config_X_Language_X_Platform" TaskParameter="Include"/>
</CreateItem>
<!--Creating groups for projects to build-->
<!--In the attribute AdditionalMetadata sign '=' is changed to '%3D', and the sign ';'' changed to '%3B'-->
<CreateItem Include="myproject.msbuild" AdditionalMetadata="Properties=
Configuration%3D%(_Config_X_Language_X_Platform.Identity)%3B
Platform%3D%(_Config_X_Language_X_Platform.Platform)%3B
Language%3D%(_Config_X_Language_X_Platform.Language)" >
<Output ItemName="ProjectToBuild" TaskParameter="Include"/>
</CreateItem>
<!--Task batch build all projects parallel -->
<MSBuild Projects="#(ProjectToBuild)"
Targets="MyTarget"
BuildInParallel="true"
StopOnFirstFailure="true" />

How to get the last part of $(MSBuildProjectDirectory)

I can't figure out how to get the last part of $(MSBuildProjectDirectory).
For example, if the value was "c:\development\projects\project_branch" then I want just the last part "project_branch".
How can I do this?
In 4.0+ you can use Property Functions to do this in one line.
In this case for example
$([System.IO.Path]::GetDirectoryName($(MSBuildProjectDirectory)))
or you could use a String function.
<Project DefaultTargets="BuildAll" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Target Name="GetMSBuildProjectLocalDirectory">
<CreateItem Include="$(MSBuildProjectDirectory)">
<Output ItemName="MSBuildProjectDirectoryMeta" TaskParameter="Include"/>
</CreateItem>
<CreateProperty Value="%(MSBuildProjectDirectoryMeta.Filename)">
<Output PropertyName="LocalDirectory" TaskParameter="Value"/>
</CreateProperty>
</Target>
<Target Name="BuildAll" DependsOnTargets="GetMSBuildProjectLocalDirectory">
<Message Text="$(LocalDirectory)" />
</Target>
</Project>
If you're following best practice, then your project directory will have the same name as your project file. Therefore, you should be able to use:
$(MSBuildProjectName)