Menu

#42 Solution task scalability for VB projects

Release
open
nobody
Tasks (29)
5
2007-06-11
2007-06-11
diegovega
No

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

Discussion

  • diegovega

    diegovega - 2007-06-11

    ProjectReferenceBase with special cased GetAssemblyReferences removed

     
  • diegovega

    diegovega - 2007-06-11

    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.

     

Log in to post a comment.