We got a question today on our internal discussion alias about the behaviour of wildcard expansions in project files. It went roughly like this:

I have the following target declared in my project file:

<Target Name="MyTarget" Inputs="$(MyInputs)" Outputs="$(MyOutputDirectory)\*">

However, when I build I get the following output:

Output file "d:\Sources\*" does not exist.

Why?

This is because of how wildcards are processed by the MSBuild engine. Rajeev replied with the list of where wildcards are expanded:

  • The Include attribute of a statically declared item: <MyItem Include="*.txt" />
  • The Exclude attribute of a statically declared item: <MyItem Include="*.txt" Exclude="a*.txt"/>
  • The Include attribute of the CreateItem task: <CreateItem Include="*.txt"/>
  • The Exclude attribute of the CreateItem task: <CreateItem Include="*.txt" Exclude="a*.txt"/>

Do do what the original poster wants you have to create an itemgroup first that has the wildcard in it, and then use that itemgroup as the parameter to the Output attribute.

Jeff replied a second time to the thread to provide more background on why it works this way:

Target outputs are useful to MSBuild when deciding whether or not a Target is up to date and hence whether or not it should be built.  MSBuild decides a Target is up to date if each of the files in the Outputs list is newer than each of the files in the Inputs list. In particular, if any file in the Outputs list does not exist, the Target is built.

Of course, a wildcard will always describe exactly what’s on disk when it’s evaluated - rather than what should be on disk once the Target has executed.  If we supported wildcards in the actual targets input and output tags this would break the up to date check.

Consider this example:

Inputs=x.cs,y.cs,z.cs, each with file change times of 1/1/2000 12:00
Outputs=x.pdb, with file change times of 1/1/2001 12:00 (constructed with a wildcard in the output tag if we supported it)

Now if x.dll is produced by this target, but had been deleted from disk, MSBuild would mistakenly skip this target because the wildcard didn’t include it and each of the files the wildcard included are coincidentally newer than those in the inputs...

Now you know :)

[ Author: Neil Enns ]