We found significant scalability problems with solutions consisting of as few as 10 VB projects with entangled references among them. In those cases, compile time for each project grows exponentially towards the end of the build.
We have a solution written in VB.NET 7.1, consisting of 9 modules and 305 project files.
NAnt version 0.85 takes so much (several hours) to build our solution that we have never had it complete the task.
By debugging NAnt, we found the cause of the poor performance to be ProjectReferenceBase.GetAssemblyReferences(), which is called to gather the command line parameters for VBC.
For Visual Basic projects, GetAssemblyReferences resorts to a very costly and repetitive recursive search:
public override StringCollection GetAssemblyReferences(string solutionConfiguration) {
StringCollection assemblyReferences = null;
// check if parent is a VB.NET project
if (typeof(VBProject).IsAssignableFrom(Parent.GetType())) {
assemblyReferences = Project.GetAssemblyReferences(solutionConfiguration);
}
else
{
assemblyReferences = new StringCollection();
}
...
// return assembly references
return assemblyReferences;
}
Once we removed the recursive call, NAnt correctly built our solution in only 16 minutes (compared to several hours), so the improvement was evident:
public override StringCollection GetAssemblyReferences(string solutionConfiguration) {
StringCollection assemblyReferences = new StringCollection();
...
// return assembly references
return assemblyReferences;
}
The method comments contain remarks explaining that NAnt developers added this special case on purpose, because they found that Visual Studio has a hack that lets VB project compile a project without references to its direct dependencies.
All things considered, We have two suggestions to the current state of this method:
1. As implemented, the recursive call (headed by an “if (typeof(VBProject).IsAssignableFrom(Parent.GetType()))” statement) violates encapsulation, since it should be implemented as a VB specific class and not be asking for the identity of the Project type from another class.
2. Much more important than the previous point, the assumption that VB.NET projects do not require direct references to compile-time dependencies is uncertain. Even if you could hit a rare scenario in which this behavior is triggered, it would be preferable to fail the compilation of the project with a descriptive error message, hinting the developer to add the appropriate references to the project, than to incur in a huge performance penalty for everyone else trying to use NAnt with Visual Basic.
Actually, we cannot reproduce the described behavior on either VS 2003, or VS 2005, or VS codename Orcas.
We have been wondering how this was verified by NAnt developers both on VS 2003 and VS 2005. We speculate that there could be a confusion of run-time dependencies with compile-time dependencies. It is clear that NAnt concern is build-time, not run-time dependencies.
As we have to deal with large real-world VB.NET solutions, we need NAnt Solution Task to be scalable, and it does not bother us if we have to add a "correct" reference to a project.
Thank you.
Diego Vega & Hugo Martinez Rosa
CAM Informática S.A.
http://www.caminf.com
ProjectReferenceBase with special cased GetAssemblyReferences removed
Logged In: YES
user_id=1806965
Originator: YES
We have done our own testing, and here it is what we have tried:
Initially we create a solution with 3 projects:
ClassLibrary1:
Public Class Class1
public a as Integer
End Class
ClassLibrary2 (references project ClassLibrary1):
Public Class Class2
inherits ClassLibrary1.Class1
End Class
ConsoleApplication1 (references project ClassLibrary2):
Sub Main()
Dim b As new ClassLibrary2.Class2
End Sub
This will compile within Visual Studio:
------ Rebuild All started: Project: ConsoleApplication1, Configuration: Debug .NET ------
Preparing resources...
Updating references...
Performing main compilation...
Building satellite assemblies...
---------------------- Done ----------------------
Rebuild All: 3 succeeded, 0 failed, 0 skipped
As well as with NAnt with a task solution:
NAnt 0.85 (Build 0.85.2478.0; release; 10/14/2006)
Copyright (C) 2001-2006 Gerry Shaw
http://nant.sourceforge.net
Buildfile: file:///W:/AFCNET/unitesting/ClassLibrary1/all.build
Target framework: Microsoft .NET Framework 2.0
[property] Target framework changed to "Microsoft .NET Framework 1.1".
[echo] Base build directory = W:\AFCNET\unitesting\ClassLibrary1
[solution] Starting solution build.
[solution] Building 'ClassLibrary1' [Debug] ...
[solution] Building 'ClassLibrary2' [Debug] ...
[solution] Building 'ConsoleApplication1' [Debug] ...
BUILD SUCCEEDED
Total time: 0.5 seconds.
However, as soon as we change the ClassLibrary1 references in ClassLibrary2 to “Copy Local=False”, the program will compile but not run. This is because ClassLibrary1 is a run-time dependency for ConsoleApplication1, but not a compile-time dependency.
Moreover, if I add the following line to ConsoleApplication1:
Sub Main()
Dim b As new ClassLibrary2.Class2
b.a=1
End Sub
Visual Studio will fail the build with this error:
w:\AFCNET\unitesting\ClassLibrary1\ConsoleApplication1\Module1.vb(5): 'a' is declared in project 'ClassLibrary1.dll', which is not referenced by project 'ConsoleApplication1.exe'.
And the command line compiler (invoked by NAnt) will fail with this error:
W:\AFCNET\unitesting\ClassLibrary1\ConsoleApplication1\Module1.vb(4) : error BC30007: Reference required to assembly 'ClassLibrary1' containing the base class 'ClassLibrary1.Class1'. Add one to your project.
If you look carefully, the error from NAnt refers to line 4 (Dim statement) while VS refers to line 5 (the assignment). However, the message displayed from the command line compiler is perfectly clear and suggests the right solution to the developer.