tags:

views:

573

answers:

2

I have a simple Word to Pdf converter as an MSBuild Task. The task takes Word files (ITaskItems) as input and Pdf files (ITaskItems) as output. The script uses a Target transform for conversion:

<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003" ToolsVersion="3.5">
  <UsingTask AssemblyFile="$(MSBuildExtensionsPath)\MyTasks.dll" TaskName="MyTasks.DocToPdf" />
  <Target Name="Build" DependsOnTargets="Convert" />
  <Target Name="Convert"
          Inputs="@(WordDocuments)"
          Outputs="@(WordDocuments->'%(FileName).pdf')">
    <DocToPdf Inputs="@(WordDocuments)"
              Outputs="%(FileName).pdf">
      <Output TaskParameter="ConvertedFiles" ItemName="PdfDocuments" />
    </DocToPdf>
  </Target>
  <ItemGroup>
    <WordDocuments Include="One.doc" />
    <WordDocuments Include="SubDir\Two.doc" />

    <WordDocuments Include="**\*.doc" />
  </ItemGroup>
</Project>

What's happening is that SubDir\Two.doc gets converted on every incremental build, One.doc does not (ie MSBuild correctly skips that file because it was already converted). If I use the recursive items spec (the third one above), I get the same behaviour (ie. One.doc only gets converted if the PDF is out of date or missing, but all documents in subdirectories always get converted regardless).

What am I doing wrong here?

A: 

I found the problem. It turns out that I had some logic in the Task that would turn any relative path specified for a PDF file into an absolute path. Once I removed that and changed the script to this:

  <Target Name="Convert"
          Inputs="@(WordDocuments)"
          Outputs="@(WordDocuments->'%(RelativeDir)%(FileName).pdf')">
    <DocToPdf Inputs="%(WordDocuments.Identity)"
              Outputs="%(RelativeDir)%(FileName).pdf">
      <Output TaskParameter="ConvertedFiles" ItemName="PdfDocuments" />
    </DocToPdf>
  </Target>

I got the behaviour I expected.

Wayne
A: 

Here's my example of a task that performs incremental builds on items found recursively through subdirectories:

  <Target Name="Build" Inputs="@(RequestTextFiles)" Outputs="@(RequestTextFiles -> '%(Rootdir)%(Directory)%(Filename).out')">

    <DoSomething SourceFiles="@(RequestTextFiles)" />

  </Target>

This maps 1:1 with an input file, and an output file with the same name, that outputs to the same path with a different extension, namely 'out' in this case.

Adam