kingerja2 / MSBuildProjectCreator

A fluent API for creating MSBuild projects for use by unit tests

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Overview

Build Status NuGet NuGet

This class library is a fluent interface for generating MSBuild projects and NuGet package repositories. Its primarily for unit tests that need MSBuild projects to do their testing.

Example

You want to test a custom MSBuild task that you are building so your unit tests need to generate a project that you can build with MSBuild. The following code would generate the necessary project:

ProjectCreator creator = ProjectCreator.Create("test.proj")
    .UsingTaskAssemblyFile("MyTask", pathToMyTaskAssembly)
    .ItemInclude("CustomItem", "abc")
    .Property("CustomProperty", "value")
    .Target("Build")
    .Task(
        name: "MyTask",
        parameters: new Dictionary<string, string>
        {
            { "MyProperty", "$(CustomProperty)" },
            { "Items", "@(CustomItem)" }
        });

The resulting project would look like this:

<?xml version="1.0" encoding="utf-8"?>
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
  <UsingTask TaskName="MyTask" AssemblyFile="..." />
  <ItemGroup>
    <CustomItem Include="abc" />
  </ItemGroup>
  <PropertyGroup>
    <CustomProperty>value</CustomProperty>
  </PropertyGroup>
  <Target Name="Build">
    <MyTask MyProperty="$(CustomProperty)" Items="@(CustomItem)" />
  </Target>
</Project>

Building Projects

Use the TryBuild methods to build your projects. TryBuild returns a BuildOutput object which captures the build output for you.

This example creates a project that logs a message, executes TryBuild, and asserts some conditions against the build output.

ProjectCreator.Create()
    .TaskMessage("Hello, World!")
    .TryBuild(out bool success, out BuildOutput log);

Assert.True(success);
Assert.Equal("Hello, World!", log.Messages.Single().Message);

Extensibility

You can extend the ProjectCreator class by adding extension methods.

Custom Item Type

You can implement a method that adds a known item type with metadata. It is recommended that you prefix the method with Item so it is included alphabetically with other methods that add items. Your method should call the ProjectCreator.ItemInclude method with the custom metadata.

public static class ExtensionsMethods
{
    public static ProjectCreator ItemMyCustomType(this ProjectCreator creator, string include, string param1, string param2, string condition = null)
    {
        return creator.ItemInclude(
            "MyCustomType",
            include,
            null,
            new Dictionary<string, string>
            {
                { "Metadata1", param1 },
                { "Metadata2", param2 }
            },
            condition);
    }
}

The above extension method would add the following item:

<ItemGroup>
  <MyCustomType Include="X">
    <Metadata1>Y</Metadata1>
    <Metadata2>Y</Metadata2>
  </MyCustomType>
</ItemGroup>

Templates

Several project templates are included for convenience purposes. One template is the SDK-style C# project:

using Microsoft.Build;
using Microsoft.Build.Utilities.ProjectCreation;

namespace MyApplication
{
    public static void Main(string[] args)
    {
        ProjectRootElement project = ProjectCreator.Templates.SdkCsproj("project1.csproj");
    }
}

In the above example, the generated project looks like this:

<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <TargetFramework>netstandard2.0</TargetFramework>
  </PropertyGroup>
</Project>

Custom Templates

You can create your own templates by adding extension methods.

Your extension methods should extend the ProjectCreatorTemplates class so they are available as methods to the ProjectCreator.Templates property.

public static class ExtensionMethods
{
    public ProjectCreator LogsMessage(this ProjectCreatorTemplates template, string text, string path = null, MessageImportance ? importance = null, string condition = null)
    {
        return ProjectCreator.Create(path)
            .TaskMessage(text, importance, condition);
    }
}

The above extension method can be called like this:

using Microsoft.Build;
using Microsoft.Build.Utilities.ProjectCreation;

namespace MyApplication
{
    public static void Main(string[] args)
    {
        ProjectRootElement project = ProjectCreator.Templates.LogsMessage("Hello, World!");
    }
}

And the resulting project would look like this:

<?xml version="1.0" encoding="utf-8"?>
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
  <Target Name="Build">
    <Message Text="Hello, World!" />
  </Target>
</Project>

Package Repositories

NuGet and MSBuild are very tightly coupled and a lot of times you need packages available when building projects.

Example

Create a package repository with a package that supports two target frameworks:

PackageRepository.Create(rootPath)
    .Package("MyPackage", "1.2.3", out PackageIdentify package)
        .Library("net472")
        .Library("netstandard2.0");

The resulting package would have a lib\net472\MyPackage.dll and lib\netstandard2.0\MyPackage.dll class library. This allows you to restore and build projects that consume the packages

PackageRepository.Create(rootPath)
    .Package("MyPackage", "1.0.0", out PackageIdentify package)
        .Library("netstandard2.0");

ProjectCreator.Templates.SdkCsproj()
    .ItemPackageReference(package)
    .Save(Path.Combine(rootPath, "ClassLibraryA", "ClassLibraryA.csproj"))
    .TryBuild(restore: true, out bool result, out BuildOutput buildOutput);

The result would be a project that references the MyPackage package and would restore and build accordingly.

About

A fluent API for creating MSBuild projects for use by unit tests

License:MIT License


Languages

Language:C# 100.0%