Friday, May 22, 2015

XUnit and MSBuild

Recently I needed to execute xUnit tests with MSBuild, so I've spent some time for creating a MSBuild project running the tests. Luckily xUnit already have MSBuild tasks so I just needed to hook it up.
I wanted to search for all xUnit unit test dlls inside a folder and run the tests there. So I'm searching for xunit.core.dll file to get all the folders eventually containing such dlls and then search all these folders for a pattern - *.Tests.dll. When the tests dlls are found xunit task is run for all of them. So here's the result .proj file:
<?xml version="1.0" encoding="utf-8"?>
<Project
    DefaultTargets="Test"
    xmlns="http://schemas.microsoft.com/developer/msbuild/2003">

    <UsingTask
      AssemblyFile="xunit.runner.msbuild.dll"
      TaskName="Xunit.Runner.MSBuild.xunit"/>

    <UsingTask TaskName="GetAssemblies" TaskFactory="CodeTaskFactory" AssemblyFile="$(MSBuildToolsPath)\Microsoft.Build.Tasks.v4.0.dll">
        <ParameterGroup>
            <Path ParameterType="System.String" Required="true" />
            <XUnitFileName ParameterType="System.String" Required="false"/>
            <TestsSearchPattern ParameterType="System.String" Required="false"/>
            <Assemblies ParameterType="System.String[]" Output="true" />
        </ParameterGroup>
        <Task>
            <Code Type="Fragment" Language="cs">
                <![CDATA[
                    var xUnitFileName = XUnitFileName ?? "xunit.core.dll";
                    var testsSearchPattern = TestsSearchPattern ?? "*.Tests.dll";
                
                    // get all the directories containing xUnit dlls
                    var directories = System.IO.Directory.GetFiles(Path, xUnitFileName, System.IO.SearchOption.AllDirectories)
                        .Select(file => System.IO.Path.GetDirectoryName(file));

                    var assembliesList = new List<string>();

                    foreach(string directory in directories)
                    {
                        // get all test dlls from the given paths
                        assembliesList.AddRange(System.IO.Directory.GetFiles(directory, testsSearchPattern, System.IO.SearchOption.TopDirectoryOnly));
                    }

                    Assemblies = assembliesList.ToArray();
                ]]>
            </Code>
        </Task>
    </UsingTask>

    <Target Name="Test">
        <GetAssemblies Path="$(BuildRoot).">
            <Output PropertyName="TestAssemblies" TaskParameter="Assemblies"/>
        </GetAssemblies>
        <xunit Assemblies="$(TestAssemblies)" />
    </Target>
</Project>

Friday, May 15, 2015

Git History tool

This is a small tool getting history for a path in a given local git repository and exporting it using RazorTemplates

The tool get all the changes between revisions or to a revision of a local git repository and pass the commits to a Razor template which then outputs the result in a given file. The commits can be filtered by a given pat in the repository. 

UsageGitHistory.App.exe [-gitRepo] [-templateFile] [-outputFile] [-endRevision ] [-gitInstallationFolder ] [-includeMerges] [-pageTitle ] [-pathInRepo ] [-startRevision ] 

To use the tool you can pass the following arguments:

  • gitRepo(or just r) - The local path to the git repository 
  • templateFile(or just t) - The path to the Razor template file that will be used for tansforming the list of commits 
  • outputFile(or just o) - The path to the file where the result content will be saved 
  • startRevision(or r or revision) - This argument is optional and is needed if you want to get the changes in a range or to a revision 
  • endRevision(or just er) - Optional argument that specifies the end revision 
  • includeMerges(or just im) - Whether or not the merge commits to be included in the output 
  • gitInstallationFolder(or just gi) - The path to the git executable if it is not set in the Path environment variable 
  • pathInRepo(or just p) - The relative path inside the local repository where you want to get the changes for 
  • pageTitle(or just pt) - This can be used for presentation purposes only. Can be passed to the command line and then output in the result 
You can find the tool on github here. Example usage(example.bat) and razor(example.razor) files inside the project folder.

Friday, April 24, 2015

ConfigurationTransformations Nuget

As in my recent work I had to deal with some configuration transformations from C# code I've created a nuget that can be used if you want to apply a transformation programmatically. To do that you can install the ConfigurationTransformation package from the official nuget repository.
In order to use it you can call the following from the package manager console:
PM> Install-Package ConfigurationTransformations

and then just create an instance of the ConfigTransformer class to apply the transformation.
ConfigTransformer configTransformer = new ConfigTransformer();
string sourceFile = @"C:\temp\web.config";
string transformationFile = @"C:\temp\web.Release.config";
string destinationFile = @"C:\temp\resultweb.config";
TransformationResult transformationResult = configTransformer.ApplyTransformation(sourceFile, transformationFile, destinationFile);
If you don't specify the destination file the source file content will be changed with the result transformated content.

The other option is to get the result content without saving it to a file:
ConfigTransformer configTransformer = new ConfigTransformer();
string sourceFile = @"C:\temp\web.config";
string transformationFile = @"C:\temp\web.Release.config";
string transformedContent = configTransformer.GetTransformedFileContent(sourceFile, transformationFile);

Tuesday, April 21, 2015

Small helper tool for configuration transformations

In my daily work I often use configuration transformations for different settings for the different environment and often I need to add a new transformation for a new environment. So I've decided to create a small tool that can automate adding new transformations to all the projects in a solution. In addition to this I've added functionality for applying config transformations as this can be useful if you want to test the transformations from command line. Here's a short description of the tool:

TransformHelper

Small tool helping with the usage of the configuration transformations

It has two

  • Add - passing solution, existing transformation and the new transformation the tool will go through all the projects in the solution and check for configuration files having the existing transformation and will add new transformation copying the files from the existing
  • Remove - passing solution, and existing transformation the tool will go through all the projects in the solution and check for configuration files having the existing transformation and will delete them
  • Apply - passing source file, transformation file and optional target file (if not passed the source file content will be replaced)
  • ApplySLN - passing solution, and existing transformation the tool will go through all the projects in the solution and apply the transformation on the original file
Usage
  • Add mode (you may not specify the mode here as the Add mode is the default behavior)
TransformHelper.exe [-mode add] -solution C:\Work\MySolution.sln -existing Dev -new Live

This will check all the projects in the solution for files like [fileName].Dev.config and will add the corresponding [fileName].Live.config files copying the content from the existing ones
  • Remove mode
TransformHelper.exe -mode remove -solution C:\Work\MySolution.sln -existing Dev

This will check all the projects in the solution for files like [fileName].Dev.config and will delete them and remove them from the project
  • Apply mode
TransformHelper.exe -mode apply -source "C:\Work\MySolution\WebProject\web.config" -transformFile "C:\Work\MySolution\WebProject\web.Dev.config" -target "C:\Temp\result.web.config"

This will apply the transformation file C:\Work\MySolution\WebProject\web.Dev.config on C:\Work\MySolution\WebProject\web.config and the result content will be saved in C:\Temp\result.web.config file
  • ApplySLN mode
TransformHelper.exe -mode applySLN -solution C:\Work\MySolution.sln -existing Dev

This will check all the projects in the solution for files like [fileName].Dev.config and if there are files like [fileName].config as well in the project they will be transformed using the Dev transformations

You can find the tool on github here

Wednesday, January 14, 2015

Online GUID converter

Sometimes I need to convert GUIDs without dashes to GUIDs with dashes or vice versa, so I've created this small page to help me with the conversion.

or

Tuesday, September 17, 2013

How to build a specific projects from a solution with MSBuild

Sometimes you want to build only a single project, or a list of projects from a solution without building the whole solution with MSBuild.

To do so, you can use custom build targets. You need to add a custom build target for every project you want to build containing the relative path in solution structure of the project (.csproj/.vproj).
Another thing you need to have in mind is that the dots(.) in the project name should be replaced with underscores(_). You can clean or rebuild adding :Clean, :Rebuild at the end of the target.

However if you can check all the MSBuild targets used from the solution. As you may know, when running MSBuild command against a solution file in-memory it is converted to an actual MSBuild project named [SolutionName].sln.metaproj. You can add an environment variable named msbuildemitsolution and set its value to 1 and run MSBuild from command line. Another option is to add /p:msbuildemitsolution=1 to the passed parameters to the build. This will generate the .metaproj file where you can find all the needed targets.

Lets illustrate all the above with a simple example.
Here is a simple project structure:

 
 
 
If we want to build Project.Four and rebuild ProjectTwo we need to call MSBuild with the following arguments:
 
msbuild BuildSpecificProjectsFromASolution.sln /t:"Folder2\Project_Four";"ProjectTwo:Rebuild"

 
You can find the sample solution here.

Tuesday, December 18, 2012

Using WIX with managed custom action

WIX is a great toolset for creating installers. In most of the cases when you need an installer you need some custom logic to be executed. It's great that WIX supports managed custom actions. Anyway it wasn't so easy for me to make it work, so I want to share my experience.
I will demonstrate it using the WIX Visual Studio add-in (version v3.7.1217.0).

We will create WIX Setup project and C# Custom Action Project.
We will add a dummy text file in the setup project to be used as installation content and will change a little bit the auto created Product.wxs file.
<?xml version="1.0" encoding="UTF-8"?>
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi">
  <Product Id="*" Name="WixWithCustomAction" Language="1033" Version="1.0.0.0" Manufacturer="Trifonov" UpgradeCode="60468a7d-6485-4e7e-bf82-503213bc43a8">
    <Package InstallerVersion="200" Compressed="yes" InstallScope="perMachine" />

    <Media Id='1' Cabinet='Dummy.cab' EmbedCab='yes' />

    <Directory Id='TARGETDIR' Name='SourceDir'>
      <Directory Id='ProgramFilesFolder'>
        <Directory Id='WixWithCustomAction' Name='WixWithCustomAction'>
          <Component Id="DummyContent" Guid="ba9028ae-0d3b-4b66-8560-f53330736265">
            <!-- Add the dummy file as content. -->
            <File Id="DummyFile" KeyPath="yes" Source="Dummy.txt" Vital="yes" />
          </Component>
        </Directory>
      </Directory>
    </Directory>

    <Feature Id="Complete" Title="WixWithCustomAction" Level="1">
      <ComponentRef Id='DummyContent' />
    </Feature>
  </Product>
</Wix>
That's how our solution looks like:

If we build the WixWithCustomAction project, WixWithCustomAction.msi will be created. If we run it WixWithCustomAction folder will be created in program files with Dummy.txt file inside.
But now we want to add a custom action which will create a file in C:\Temp folder. We will use the MyCustomActionProject for this. Let's change the CustomAction class a little bit:
using Microsoft.Deployment.WindowsInstaller;
using System.IO;

namespace MyCustomActionProject
{
    public class CustomActions
    {
        [CustomAction]
        public static ActionResult MyCustomAcion(Session session)
        {
            session.Log("Executing MyCustomAcion");

            File.CreateText(@"c:\temp\installed.txt");

            return ActionResult.Success;
        }
    }
}

Now we just need to call this custom action from the installer. To do this we will add a reference to this project in the setup project.
Now let's add the custom action in the Product.wxs file.
Adding the project as a reference allows as to use these variables.
But adding custom action is a little bit complicated. After building the MyCustomActionProject.dll file we will need a call to MakeSfxCA.exe and sfxca.dll in your installed WiX toolset as the dll need a reference to Microsoft.Deployment.WindowsInstaller.dll and has CustomAction.config attached. Calling the MakeSfxCA.exe tool will package the project output to MyCustomActionProject.CA.dll(here you can find some additional information about this).
As we use "C# Custom Action Project" there is an import added to $(MSBuildExtensionsPath)\Microsoft\WiX\v3.x\Wix.CA.targets file which will create this package on build. To check this you can build the custom action project and see the output:
So the custom action in the Product.wxs needs to reference the .CA.dll file. Thats why we cannot use
$(var.MyCustomActionProject.TargetPath) as a source for the custom action binary, but we will have to construct the source path like this:
$(var.MyCustomActionProject.TargetDir)$(var.MyCustomActionProject.TargetName).CA.dll
The other option is not to use the project reference but add the full path to the custom action output.
So we will add the following rows to the wxs file
<!--The source file should be the MyCustomActionProject.CA.dll file, that's why it's constructed this way-->
<Binary Id='CustomActionBinary' SourceFile='$(var.MyCustomActionProject.TargetDir)$(var.MyCustomActionProject.TargetName).CA.dll' />

<!--The DllEntry must be the name of the method to be called from the custom action project, in our case - MyCustomActionMethod 
http://wix.tramontana.co.hu/tutorial/events-and-actions/at-a-later-stage 
The Execute attribute will specify the deferred status of our custom action.
And finally, HideTarget will allow us to disable logging the parameteres passed to this custom action if security considerations so dictate.-->
<CustomAction Id='CustomActionId' BinaryKey='CustomActionBinary' DllEntry='MyCustomActionMethod' Execute="deferred" HideTarget="yes"/>

<InstallExecuteSequence>
  <!--We want to call the custom action before the install finalizes-->
  <Custom Action='CustomActionId' Before='InstallFinalize'/>
</InstallExecuteSequence>
And that's it. Now if we build the setup project and run the created msi installer, c:\temp\installed.txt will be created as a part of the installation process.

You can find the solution file here - WixWithCustomAction.zip