Showing posts with label MSBuild. Show all posts
Showing posts with label MSBuild. Show all posts

Sunday, March 7, 2010

MSBuild: Passing arrays between targets

In MSBuild, if you have a target that operates on a list of items of a specific type, and you want to call that operation several times with different lists, here is a way to do that using the DependsOnTargets attribute:

<Target Name="UserInterface">
  <MSBuild Projects="$(MSBuildProjectFile)" Targets="Service"
    Properties="ServiceDependsOn=PersonRepository-1" />
  <MSBuild Projects="$(MSBuildProjectFile)" Targets="Service"
    Properties="ServiceDependsOn=PersonRepository-2" />
</Target>

<Target Name="Service" DependsOnTargets="$(ServiceDependsOn)">
  <Message Text="Persons: @(Persons->'%(Name) (%(Identity))')" />
</Target>

<Target Name="PersonRepository-1">
  <ItemGroup>
    <Persons Include="Al"><Name>Al Omaha</Name></Persons>
    <Persons Include="Ben"><Name>Ben Patterson</Name></Persons>
  </ItemGroup>
</Target>

<Target Name="PersonRepository-2">
  <ItemGroup>
    <Persons Include="Adrian"><Name>Adrian Quist</Name></Persons>
    <Persons Include="Britta"><Name>Britta Ruud</Name></Persons>
  </ItemGroup>
</Target>


and here is sample output:

Project "C:\Projects\MSBuildItems.xml" on node 0 (default targets).
Project "C:\Projects\MSBuildItems.xml" (1) is building "C:\Projects\MSBuildItems.xml" (1:2) on node 0 (Service target(s)).
 Persons: Al Omaha (Al);Ben Patterson (Ben)
Done Building Project "C:\Projects\MSBuildItems.xml"
(Service target(s)).

Project "C:\Projects\MSBuildItems.xml" (1) is building "C:\Projects\MSBuildItems.xml" (1:3) on node 0 (Service target(s)).
 Persons: Adrian Quist (Adrian);Britta Ruud (Britta)
Done Building Project "C:\Projects\MSBuildItems.xml"
(Service target(s)).

Done Building Project "C:\Projects\MSBuildItems.xml" (default targets).

MSBuild with dependency injection

I am about to start refactoring a MSBuild file with around 1000 lines of xml, and I find that the targets are heavily dependent on each other.

Maybe dependency injection can help here?!

In MSBuild this could look something like this:

UserInterface.xml:
<Import Project="ApplicationService.xml" />
<Import Project="Repository.xml" />
(or <Import Project="FakeRepository.xml" />)
<Target Name="UserInterface-Action">
  <CallTarget Targets="ApplicationService-Action" />
</Target>


ApplicationService.xml:
<Target Name="ApplicationService-Action">
  <CallTarget Targets="IRepository-Action" />
</Target>


Repository.xml:
<Target Name="IRepository-Action">
  <Message Text="Repository action" />
</Target>


FakeRepository.xml:
<Target Name="IRepository-Action">
  <Message Text="Fake repository action" />
</Target>


For comparison, here is the corresponding structure in C#:
public class UserInterface
{
  private readonly IRepository repository;

  public UserInterface(IRepository repository)
  {
    this.repository = repository;
  }

  public void Action()
  {
    var service = new ApplicationService(this.repository);
    service.Action();
  }
}

public class ApplicationService
{
  private readonly IRepository repository;

  public ApplicationService(IRepository repository)
  {
    this.repository = repository;
  }
  
  public void Action()
  {
    this.repository.Action();
  }
}

public interface IRepository
{
  void Action();
}

public class Repository : IRepository
{
  public void Action()
  {
    System.Console.WriteLine("Repository action");
  }
}

public class FakeRepository : IRepository
{
  public void Action()
  {
    System.Console.WriteLine("Fake repository action");
  }
}