Saturday, 2 April 2011

Including generated files as input for a project

Sometimes you may have a project in your solution who's sole purpose is to generate files that need to be referenced by a second project. If the files are fixed, then it's just a matter of adding a link to them in the second project, but what about when you don't know at build time how many files will be generated?

It turns out that MSBuild can handle this situation. For example, given the following solution:

image

Note that we've added a reference for the Generate project to the IncludeProjectOutputs project. We've also set 'Copy Local' to false as the only reason for the reference is to enforce building the projects in the right order (we don't actually refer to the Generate assembly within the IncludeProjectOutputs code).

So that the executable output from the generator project is run, add the following to the generator project's project file:

  <Target Name="AfterBuild">
    <Message Text="After build execute $(TargetPath)" Importance="high" />
    <Exec Command="$(TargetPath)" />
  </Target>

In the project that should be including the generated files, add the following:

    <Target Name="IncludeGenerated" BeforeTargets="ResolveAssemblyReferences">
        <ItemGroup>
            <Content Include="$(ProjectDir)..\Generate\*.txt" >
                <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
            </Content>
        </ItemGroup>
    </Target>

In this instance, we're including all .txt files from the ..\Generate folder. Because we define the <Content /> item, they're added as content (equivalent to the 'Content' build action). I haven't tested it, but conceivably you could also add to <Compile/> or <References/> too.

The ability to add ItemGroup elements within a Target element was added in .NET 3.5. The advantage of this is that the filenames matching the pattern of the Include attribute are evaluated in the execution phase. This contrasts with normal ItemGroup elements, which are assigned values during the evaluation phase of the build. See MSBuild Items for more details.

Building this project gives the following result:

Microsoft (R) Build Engine Version 4.0.30319.1
[Microsoft .NET Framework, Version 4.0.30319.431]
Copyright (C) Microsoft Corporation 2007. All rights reserved.

Build started 2/04/2011 8:49:43 AM.
Project "C:\dev\Sandbox\IncludeProjectOutputs\IncludeProjectOutputs.sln" on node 1 (default targets).
ValidateSolutionConfiguration:
  Building solution configuration "Debug|Mixed Platforms".
Project "C:\dev\Sandbox\IncludeProjectOutputs\IncludeProjectOutputs.sln" (1) is building "C:\dev\Sandbox\IncludeProjectOutputs\IncludeProjectOutputs\IncludeProjectOutputs.csproj" (2) on node 1 (default targets).
Project "C:\dev\Sandbox\IncludeProjectOutputs\IncludeProjectOutputs\IncludeProjectOutputs.csproj" (2) is building "C:\dev\Sandbox\IncludeProjectOutputs\Generate\Generate.csproj" (3) on node 1 (default targets).
ResolveAssemblyReferences:
  A TargetFramework profile exclusion list will be generated.
GenerateTargetFrameworkMonikerAttribute:
Skipping target "GenerateTargetFrameworkMonikerAttribute" because all output files are up-to-date with respect to the input files.
CoreCompile:
Skipping target "CoreCompile" because all output files are up-to-date with respect to the input files.
_CopyOutOfDateSourceItemsToOutputDirectory:
Skipping target "_CopyOutOfDateSourceItemsToOutputDirectory" because all output files are up-to-date with respect to the input files.
CopyFilesToOutputDirectory:
  Generate -> C:\dev\Sandbox\IncludeProjectOutputs\Generate\bin\Debug\Generate.exe
AfterBuild:
  After build execute C:\dev\Sandbox\IncludeProjectOutputs\Generate\bin\Debug\Generate.exe
  C:\dev\Sandbox\IncludeProjectOutputs\Generate\bin\Debug\Generate.exe
  Generated file
Done Building Project "C:\dev\Sandbox\IncludeProjectOutputs\Generate\Generate.csproj" (default targets).
ResolveAssemblyReferences:
  A TargetFramework profile exclusion list will be generated.
GenerateTargetFrameworkMonikerAttribute:
Skipping target "GenerateTargetFrameworkMonikerAttribute" because all output files are up-to-date with respect to the input files.
CoreCompile:
Skipping target "CoreCompile" because all output files are up-to-date with respect to the input files.
_CopyOutOfDateSourceItemsToOutputDirectory:
  Copying file from "C:\dev\Sandbox\IncludeProjectOutputs\Generate\stuff.txt" to "bin\Debug\stuff.txt".
CopyFilesToOutputDirectory:
  IncludeProjectOutputs -> C:\dev\Sandbox\IncludeProjectOutputs\IncludeProjectOutputs\bin\Debug\IncludeProjectOutputs.exe
Done Building Project "C:\dev\Sandbox\IncludeProjectOutputs\IncludeProjectOutputs\IncludeProjectOutputs.csproj" (default targets).
Done Building Project "C:\dev\Sandbox\IncludeProjectOutputs\IncludeProjectOutputs.sln" (default targets).

Build succeeded.
    0 Warning(s)
    0 Error(s)

Time Elapsed 00:00:00.20

No comments: