07 November 2006

MSBuild Can't Find Secondary References

Hi All and welcome to another installment of "Shouting at the Wall!", I'm your host Steve St.Jean.

On today's show we are going to shout at the wall that is Microsoft's "Decider of things to be fixed."  I have no idea who this person actually is, so I'll blame the whole company.  Here's the story...

Problem: You have an Application "A" that uses Class "Foo" which resides in Assembly "B".  Class "Foo" implements Interface "IFoo" which resides in Assembly "C".  So Application "A" holds a direct reference to Assembly "B" and Assembly "B" holds a direct reference to Assembly "C", but Application "A" does not hold any reference to Assembly "C". 

Click thumbnail for full-sized image 

 When you bring all three of these projects into a Visual Studio 2005 solution and build them, the compilation runs without error, but when you run the same solution through MSBuild, you find that the build fails with an exception stating that the build cannot find a the reference to Assembly "C".

error BC30009: Reference required to assembly "C, Version=, Culture=neutral, PublicKeyToken=2c72757e0bcb9887' containing the implemented interface 'IFoo'. Add one to your project.

Issue: The cause of this problem is that since Application "A" doesn't have a direct reference to Assembly "C", MSBuild cannot find the Interface "IFoo" that it needs to complete Class "Foo".  What I am seeing is that MSBuild will start resolving references for Application "A"; it finds that Assembly "B" is referenced, so it reads the project file for Assembly "B" and finds where the its binary was compiled.  This binary is copied into Application "A"'s \bin folder and then Application "A" is compiled.  Unfortunately, MSBuild doesn't have the functionality to read Assembly "B"'s project file to find its dependencies, so we get this error.  To get Application "A" to build, you need to add a reference to Assembly "C" even though none of its classes are used directly by the application.

The reason that the Visual Studio 2005 IDE can build this kind of solution is that it can figure out where Assembly "C"'s binary resides prior to compilation of Application "A".  I have spoken with Microsoft Premier Support on this subject.  They have stated that Visual Studio's behavior is a "side-effect" of other processing that the IDE has to do.

I asked Microsoft to fix the problem with MSBuild so that it walks the dependency tree within the Solution to find the dependencies correctly.  The result would be that MSBuild behaves like the Visual Studio IDE.

Ok, now that I've laid out what the problem is, let me give you the response that Microsoft Support gave me when I asked for MSBuild to be fixed.  After they spent some time looking into the issue, I was informed that they would be fixing the problem in a future release.  The "fix" they gave was that they would "modify" the Visual Studio IDE to behave like MSBuild does.  The VS IDE would throw the same error BC30009 in this situation. 

Gripe:  I am not happy with the answer that Microsoft Support passed to me from the dev team.  I don't think that selecting the "easier" fix is necessarily the best way to fix this problem.  My dev leads have stated to me that they don't think that having to add direct references to secondary assemblies is a good idea.  The big problem is that Microsoft build a cool little feature into the VS 2005 IDE labeled "Remove unused references".  After you get your build up and running, all you need to have that build come crashing down around you is to have a conscientious developer that runs this feature. 

I'd prefer to see MSBuild "upgraded" to support looking through the solution's projects to find the references rather than "downgrading"  the VS 2005 IDE.  More than any other reason though, I believe that fixing MSBuild is the "right" thing to do.  So I'd like to give a great big "DUH" to the person or persons at Microsoft that came up with this "fix".  I don't think that this decision is bad enough to get an article on "The Daily WTF", but it's close.


I've attached the test solution that I sent to Microsoft Support to illustrate the issue.  If you want to see what I've been complaining about, just extract it and compile it in the IDE, then create a standard Team Build for it.  The VS IDE will build it but the Team Build will fail. 

Attachment:  MSBuildBadRefs.zip


Benjy said...

Rather awful behavior from MSBuild and from MS too. So now they dumb down the IDE cos they couldnt smarten MSBuild.!! Im still in .NET 1.1 and using Nant. WHat do you suggest ? stay with NAnt? im not keen to move to a lesser platform really...


Steven St Jean said...

While I'm not happy with Microsoft's present plans, I'm hopeful that putting this out in a public forum might allow Microsoft to see that the community would rather that it be fixed in MSBuild instead of "fixed" in the IDE.

As far as what I'd recommend...Anything that you use will still have to call MSBuild, so until this is fixed you'll have to add the secondary references to your "Application A" to get it toubuild correctly in your build or CI process. Just be sure to let your devs know that they shouldn't use the "Cleanup unused references" button.

Another recommendation, let Microsoft know that you are unhappy with their planned solution.

Anonymous said...

we have a team of about 10 users with a build of about 50 solutions. we usually get this only when there's a reference with the Specific Version flag set to true, and that is only in the IDE, not in MSBuild. The way we have our Team Build set up is that all non-VS05 references live in one folder that Team Build has access to and we have an AdditionReferencePath pointed to that folder. it just works!

Anonymous said...

Not the same issue, but kind of related...when building an application supporting the pluggable architectures model, such as a Spring.NET app or using the providers model MSFT provides, you wind up with a design where assembly a in your diagram is completely decoupled from / doesn't know about assembly b at compilation time (since you program to interfaces), and can only find it at run time. That's an issue that you have to accept with that design, not a big deal to me. Your issue you mention is though.

I would expect more from MSFT than what they've given to you as a solution and feel a little nervous about what that will mean to future VS.NET versions. But at least today I don't have to worry about that. I am researching whether to ditch nant and go with MSBuild, but the stories I'm hearing and contradictions regarding MSBuild are making me wonder if there are any benefits to it over nant versus pain.

Did you ever see Faisal Mohamood's comment here, http://forums.microsoft.com/MSDN/ShowPost.aspx?PostID=1616&SiteID=1, that MSBuild "delivers a platform for build", unlike nant? This is a perfect contradiction - they want to make it "the platform" by supporting "build input/output analysis" but then they want to dummy it down and make it harder to work with (from what you're saying).

Mitch Denny said...

I think MS has made the right decision here. If they tried to automate it it could lead to you having a lack of control over which version of the assembly you compile against.

Lets say you have Assembly A depending on B v1, and C depending on B v2 - when it walked the dependency graph - which one would it choose. The latest one? How would it tell - a lot of component vendors can be sloppy with versions.

Shadowin said...

I can't make it compile from the IDE without a direct reference to the assembly containing IFoo. I get the following error:

Error 1 The type 'ClassLibrary2.IFoo' is defined in an assembly that is not referenced. You must add a reference to assembly 'ClassLibrary2, Version=, Culture=neutral, PublicKeyToken=null'. C:\Projects
\WindowsApplication1\WindowsApplication1\Form1.cs 21 13 WindowsApplication1

jeff said...

You should submit this issue through the feedback center for Visual Studio. That way we could all help vote on it and hopefully get some attention.

I agree with you all the way, except in my tests I have certain assemblies that seem to work properly and others that do not. The results are consistent however there isn't any clear explaination of why certain "secondary" referenced assemblies are copied and others are not. BTW, in my case I have everything set as COpy Local so I know that is not the issue.

Cisjokey said...

I don't get your point.

If Project A uses Project B and Project B uses a public interface from Project C;

In the world where I live, I have to reference Project C in Project A? (Otherwise you would never recieve the type infos and so on of this Interface)?.

As I can see (I never had issues like you) I would rather say the IDE is broken or does to mutch.

Anyway, you can define a compile Order (which needs a lidle manual work) to ensure the not reference assembly C is built. But I really don't understand what you expect from msbuild to find?

Anonymous said...

Can you tell if there is any way the MSBuild can report for unused refernce files and projects

Steven St Jean said...

You could write an MSBuild custom task to do this, but it is not a basic feature of MSBuild.

Post a Comment