Chris Pfohl
Chris Pfohl

Reputation: 19074

Can MSBuild Alter its own project file?

I have written a custom task that builds files by creating a parallel file with a different extension.

When MSBuild goes and creates such a file I'd like to add it to the project file itself. I'd also like to nest the built file under the source (with DependentUpon).

Can MSBuild do this? Will it need to reload the project? Can I automate all that?

The following is my .targets file that gets installed by NuGet when my package is added:

<UsingTask TaskName="PreCompiler" AssemblyFile="..\tools\Compiler.dll">
</UsingTask>

<UsingTask TaskName="GenerateDependencies" AssemblyFile="..\tools\Compiler.dll">
</UsingTask>

<PropertyGroup>
  <BuildDependsOn>
    $(BuildDependsOn);
    BuildCoffeeFiles;
    BuildLessFiles
  </BuildDependsOn>
  <ContentDir>Content\</ContentDir>
</PropertyGroup>

<ItemGroup Condition="'$(BuildingInsideVisualStudio)'=='true'">
  <AvailableItemName Include="CoffeeScript" />
  <AvailableItemName Include="Less" />
</ItemGroup>

<Target Name="GenerateCoffeeDependencies">
  <GenerateDependencies Include="@(CoffeeScript->'%(FullPath)')">
    <Output TaskParameter="Dependencies" ItemName="InputCoffeeFiles"/>
  </GenerateDependencies>
  <ItemGroup>
    <InputCoffeeFiles
        Include="@(InputCoffeeFiles->Distinct())"
        KeepDuplicates='false' />
  </ItemGroup>
</Target>

<Target Name="BuildCoffeeFiles"
        DependsOnTargets="GenerateCoffeeDependencies"
        Inputs="@(InputCoffeeFiles)"
        Outputs="@(CoffeeScript->'%(RelativeDir)%(Filename).js')">
  <PreCompiler Include="@(CoffeeScript->'%(FullPath)')" />
</Target>

<Target Name="GenerateLessDependencies">
  <GenerateDependencies Include="@(Less->'%(FullPath)')">
    <Output TaskParameter="Dependencies" ItemName="InputLessFiles"/>
  </GenerateDependencies>
  <ItemGroup>
    <InputLessFiles
        Include="@(InputLessFiles->Distinct())"
        KeepDuplicates='false' />
  </ItemGroup>
</Target>

<Target Name="BuildLessFiles"
        DependsOnTargets="GenerateLessDependencies"
        Inputs="@(InputLessFiles)"
        Outputs="@(Less->'%(RelativeDir)%(Filename).css')">
  <PreCompiler Include="@(Less->'%(FullPath)')" />
</Target>

Upvotes: 0

Views: 404

Answers (1)

Marlos
Marlos

Reputation: 1965

You can apply a XslTransformation msbuild task in your project, before you build it, adding the required file.

This is the sample transformation file:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0"
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
  xmlns:ms="http://schemas.microsoft.com/developer/msbuild/2003">

    <xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>

    <!-- Identity. -->
    <xsl:template match="@* | node()">
      <xsl:copy>
        <xsl:apply-templates select="@* | node()" />
      </xsl:copy>
    </xsl:template>

    <xsl:template match="//ms:Compile[@Include='Program.cs']">
      <xsl:copy>
        <xsl:apply-templates select="@* | node()" />
      </xsl:copy>
      <xsl:element name="DependentUpon"
         namespace="http://schemas.microsoft.com/developer/msbuild/2003">
           <xsl:text>Output.cs</xsl:text>
      </xsl:element>
    </xsl:template>

</xsl:stylesheet>

And here is the msbuild target sample file:

<Project DefaultTargets="Build"
 xmlns="http://schemas.microsoft.com/developer/msbuild/2003" ToolsVersion="4.0">

    <Target Name="Build">
        <XslTransformation
           XmlInputPaths="ConsoleApplication1.csproj"
           OutputPaths="ConsoleApplication1.transformed.csproj"
           XslInputPath="proj.xslt">
        </XslTransformation>

        <MSBuild Projects="ConsoleApplication1.transformed.csproj" Targets="Build" />
    </Target>
</Project>

You will need to build with .NET 4.0 at least. For this specify the ToolsVersion in msbuild file.

Upvotes: 1

Related Questions