Disparity between IDE build and MSBuild build

While converting a product from VS2008 to VS2010, I stumbled across a disparity between building in the IDE and building from the command line using MSBuild. Here was the scenario: I had a csproj (let’s call it MyLibrary) that had a Target task that generated the CS file that the default Build task consumed. Then I had another project (let’s call this one MyLibraryStub) that contained a reference to MyLibrary.csproj and stubbed it out for test purposes. I had a solution file that contained just these two project. The solution built fine in the IDE and even built fine when I run MSBuild against the solution file. However, I stumbled into this error when I ran MSBuild against MyLibraryStub.csproj:

CSC : fatal error CS0009: Metadata file ‘c:TFSMyLibraryMyLibrary.cs’ could not be opened — ‘File is corrupt.’ [C:MyLibraryStubMyLibraryStub.csproj]

This was initially confusing and posts on the internet were misleading. Looking closer at the output of the MSBuild I spotted something fishy about the parameters being passed to CSC.EXE. It had a reference to my CS file!

c:WindowsMicrosoft.NETFrameworkv4.0.30319Csc.exe /noconfig /nowarn:1701,1702
/nostdlib+ /errorreport:prompt /warn:4 /doc:MyLibraryStub.xml /define:DEBUG;TRACE
/reference:C:TFSMyLibraryobjAny CPUDebugMyLibrary.dll
/reference:C:TFSMyLibraryMyLibrary.cs

. . .

Now I understood the error message: the CS file was being passed in as an assembly and of course, it would have no metadata. My next step was to look at the Target in MyLibrary.csproj that was creating this file. And that’s when I spotted the problem. Here is the build target:

<Target Name=”CreateCSFile” Inputs=”@(MyInputFiles)” Outputs=”$(MyLibraryCSFileOutput)” >
<Exec Command=”$(MyCodeGenExe) /c:$(MyLibraryClassName) /o:$(MyLibraryCSFileOutput) . . . ” />
</Target>

This Target uses the Inputs and Outputs to control when the Target gets built. If the Inputs are older than the Outputs, the Target is skipped. That’s also the problem. Apparently, MSBuild was passing the Outputs of ALL Targets from MyLibrary.csproj to MyLibraryStub.csproj. The IDE does not and MSBuild does not when you build the solution file. This only occurs when you build the MyLibraryStub.csproj.

The solution to this problem was actually very simple. You can control what the ‘output’ of a Target is. In my case, the Target was generating source code so it had no ‘real’ output. I simply added the Return attribute with an empty string like this:

<Target Name=”CreateCSFile” Inputs=”@(MyInputFiles)” Outputs=”$(MyLibraryCSFileOutput)” Returns=”” >
<Exec Command=”$(MyCodeGenExe) /c:$(MyLibraryClassName) /o:$(MyLibraryCSFileOutput) . . . ” />
</Target>

Presto, problem solved. MyLibrary.cs no longer gets passed to CSC in building MyLibraryStub.csproj. I have notified the MSBuild team. In my mind, it should build the same in IDE and in building the CSProj from the command line. We’ll see if they agree. J

Happy Coding!

Advertisement

About JohnHowell

I am a professional software developer with over 20 years of experience. I currently specialize in Microsoft technologies such as VS, TFS, C#, VB.Net, WCF, WPF, WW, etc.
This entry was posted in Uncategorized. Bookmark the permalink.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s