Fast Solution Build 2.20

An Add-in for Visual Studio .NET

September 30, 2003

New versions of Fast Solution Build may be found on http://workspacewhiz.com/ in the Other Add-ins section.

Introduction

The transition from Visual C++ 6 to Visual Studio .NET resulted in multi-project solutions building quite a bit slower than their Visual C++ 6 counterpart workspaces.  This appears due to very slow visual "dependency checking" for every project of the solution.  This add-in is a solution to that problem.

Fast Solution Build emulates the Visual C++ 6 build style, in that it only builds those projects with changes.  It builds on the concepts from the original Fast Project Build VBScript macro, while providing more ease of use and error checking.  In addition, the Run (Debug) command is now available.

New in Version 2.20 is support for incremental linking a multi-project solution.  Visual C++ does not seem to support incremental linking when a file in a library project has changed.  In fact, when examining the output after turning on the verbose linker output, it appears to be nearly identical to a full link.  Fast Solution Build enables incremental linking support for all projects in the solution.  It is amazing how quickly links perform when they are incremental.

See the Known Issues and Known Bugs sections for additional details around the conditions where multi-project incremental linking works.

Please note: Fast Solution Build is unable to support fast dependency checking for any other .NET language due to limitations in the Visual Studio.NET automation interface.  It is highly recommended a makefile system is employed to speed up your builds.

Features

Installation

The installer will ask whether multi-project incremental linking should be turned on by default.  The answer is solely based on the needs of your project.  There should be no problem turning on the incremental linking support.  A project or configuration in a project can turn off incremental linking support as needed.

Please note: Fast Solution Build's multi-project incremental linking support is only available if the project has incremental linking turned on in the first place.

Setting Up Keyboard Bindings

Under normal circumstances, it is okay to accept the keyboard binding defaults offered when Visual Studio .NET starts up for the first time after installing Fast Solution Build.  These defaults correspond to the Visual C++ 6 equivalents of the commands.

In the event the defaults can't be accepted, the following instructions may be followed for setting up your own key bindings.

  1. If desired, you may install a key binding for the macro:
    1. Go to Tools->Options->Keyboard.
    2. If the keyboard mapping scheme has never had a custom copy made, press the Save As button and name your key bindings.
    3. In Show commands containing:, type FastSolutionBuild.
    4. Click on FastProjectBuild.Connect.BuildActiveProject.
    5. Go to Press shortcut key(s).
    6. Press F7 (or your desired key).
    7. Click Assign.
    8. Click on FastProjectBuild.Connect.DebugActiveProject.
    9. Go to Press shortcut key(s).
    10. Press F5 (or your desired key).
    11. Click Assign.
    12. Click OK.

Usage

The bold project in the Solution Explorer is the top-level project built by the Fast Solution Build macro.  To make a different project the "startup" project, right click on the desired project and choose Set as StartUp Project.

Run Fast Solution Build's BuildActiveProject or DebugActiveProject commands from the Tools menu or press the keyboard key assigned to the add-in commands.

Enabling Multi-Project Incremental Linking

There are two ways to enable multi-project incremental linking.  The first involves setting Fast Solution Build to always attempt a multi-project incremental link.  Choose Yes to the installation dialog asking whether multi-project incremental linking should be on by default, or navigate in the registry to the key HKEY_CURRENT_USER\Software\Fast Solution Build and change the DefaultIncrementalLink value to 1.

If multi-project incremental linking is not on by default, a special file enabling the incremental link needs to exist in the same directory as the main executable or DLL .vcproj.  This file is named MainExecutableProject.vcprojFSB file.  If this file is empty, support for multi-project incremental linking is turned on.

The .vcprojFSB file can be populated with configuration information.  The first line must be:

	VERSION=1

The only understood key at this point is IncrementalLink.  This key may be followed by a true or a false, dependent on whether incremental linking should be on.

	IncrementalLink=true

If a key is to be accepted only for a given solution configuration, the configuration name followed by a colon may precede the key.

	Release:IncrementalLink=false

The first time you link with Fast Solution Build's BuildActiveProject command, a full link will be performed.  Thereafter, a faster incremental link will be used.  Note that if you ever use Visual Studio .NET's Build commands, the multi-project incremental link will not work.

When linking, a file called MainExecutableProject.vcproj.rsp is created.  It is purposely not destroyed after the build finishes.  This file contains all the .obj files to perform the executable's link.  This list can, at your discretion, be inserted into the Command Line Linker options in Visual Studio .NET.

Technical Details

Fast Solution Build is a C++/ATL enhanced version of the VBScript Fast Project Build macro.  It demonstrates many add-in concepts learned while developing the Workspace Whiz add-in for Visual Studio .NET.  Most importantly, it illustrates the steps I have discovered to solidly run an add-in.

Fast Solution Build is a dual add-in, working simultaneously under Visual Studio.NET 2002 and 2003.  It does so through a lot of internal code duplication, and while this isn't the most desirable implementation, it is very effective.  The binary distribution of Fast Solution Build consists of one .dll and a redirecting link executable.

First, Fast Solution Build is fully capable of installing and uninstalling itself through just a regsvr32 call.  Especially when debugging, it is far more convenient to not run an installer for add-in installations and uninstallations.  In AddIn.cpp, DllRegisterServer() handles registering the server and hooking up the add-in's registry entries.  The critical registry keys are at HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\7.0\AddIns\FastSolutionBuild.Connect and  HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\7.0\PreloadAddinState (in Visual Studio 7.1, the 7.0 in the registry key is merely changed to 7.1).   The PreloadAddinState key tells Visual Studio .NET to force creation of the add-in commands.  Usually, VS .NET caches the add-in commands and toolbar entries.  Unfortunately, it doesn't do a very good job of it and often loses the settings for them.  Also, separate registry keys are stored down per 7.0 and 7.1 build for default incremental linking support.

DllUnregisterServer() illustrates a couple important functions.  First, the add-in registry keys are removed, including GUIDs, typelibs, etc.  Finally, Fast Solution Build actually connects with the VS .NET DTE COM object and removes its registered commands.  It does this for both the VisualStudio.DTE.7 and VisualStudio.DTE.7.1 ProgIDs.  If the add-in created a toolbar (Fast Solution Build just adds commands to the Tools menu), this is an appropriate place to remove the toolbar, too.

The CConnect::OnConnection() code is quite a bit different from the boilerplate AppWizard generated code.  It performs the following steps:

  1. The version of Visual Studio is discovered.  This version is used to execute two nearly identical code paths, one for VS2002 and one for VS2003.
  2. m_pDTE and m_pAddInInstance are set to NULL.  While developing Workspace Whiz, VS .NET called the OnConnection() function more than once without calling OnDisconnection().  Setting these values to NULL ensures double OnConnection() calls don't crash.
  3. A test is made to see if this was launched from the command-line.  This doesn't work for all command-line cases, but it covers some of them.
  4. During the execution of the function, large blocks of code are wrapped in try-catch blocks.  When launching from a command-line build, requesting the CommandBars object, for instance, results in Visual Studio throwing an exception, instead of returning a proper error code.  A similar thing happens when trying to add a named command to the Commands object.  Even though you can successfully retrieve the Commands object, called AddNamedCommand() also causes Visual Studio to throw an exception.
  5. The Tools menu is scanned for the presence of Fast Solution Build commands.  If they aren't there, it recreates them.  Some VS .NET crashes don't save out command icon information and don't bother calling OnConnection() again with a ConnectMode of 5 (which causes recreation of the toolbar items and add-in commands).  This check force recreates the commands, making the assumption they aren't there.
  6. Again, in Workspace Whiz, cases were seen where events weren't properly unregistered, due to OnDisconnection() not being called.  Whole VS .NET crashes would result.  Fast Solution Build unregisters the event handlers before it registers them.

Fast Solution Build implements two commands, BuildActiveProject and DebugActiveProjectCConnect::Exec() identifies the proper command and routes accordingly.

In a nutshell, BuildActiveProject works by performing the following steps.  The whole process is actually a lot more complex than this, though.

  1. If a build is in progress, the command instantly exits.  There is no point in trying to perform any build operations when the build is already going.
  2. If the application is currently being debugged, a dialog box pops up asking the user if they want to stop debugging.  If the answer is no, BuildActiveProject exits.
  3. Like VC6, all files are saved before the build.
  4. The Build output window pane is obtained through the function GetOutputWindowPane().  It is cleared and some informational build text is displayed.
  5. If any of the solution, solution build, build dependency, or other EnvDTE objects can't be retrieved, BuildActiveProject exits with an error.
  6. The startup projects are obtained.  The startup projects are attached to a CComSafeArray.  If you attach a SAFEARRAY to a CComSafeArray and don't intend it to be destroyed, be sure to detach it or CComSafeArray will destroy it!
  7. All the dependencies begin to recurse.
  8. The solution context's "should build project" setting is used to ignore any projects the developer turned off for the solution configuration.
  9. All required dependencies are traversed.
  10. The proper configuration and platform is retrieved for the project's context.
  11. If any of the children dependencies built, then a build is automatically forced for the parent projects.
  12. As the projects are traversed, a list of .obj files are generated based on the VCFileConfiguration object.  The list of .obj files are critical for the incremental linker to work.  By providing the list of .obj files before the .lib files, the linker can successfully incrementally link.
  13. When a linkable project is reached and its build completes:
    1. The .vcprojFSB file is read.
    2. The list of .obj files are written to the file ProjectName.vcproj.rsp.
    3. Fast Solution Build temporarily modifies the current platform's Executable Directories to put the Fast Solution Build install directory first.
    4. When Visual Studio .NET launches LINK.exe, it runs Fast Solution Build's copy.
    5. Fast Solution Build's LINK.exe prepends the linker response file ProjectName.vcproj.rsp onto the Linker command line.  It looks up the location of Visual Studio .NET's LINK.exe and executes it.
      1. It is necessary for Fast Solution Build to provide its own LINK.exe, because the IDE provides no way of prepending linker response files onto the command-line.
    6. When the build is finished, the old Executable Directories property is restored, making it appear as if Fast Solution Build never made any modifications.
  14. Finally, the VCConfiguration.UpToDate property is obtained.  This is truly where all the magic happens.  It seems as if the Build Solution command ignores the UpToDate property and calls Build all the time.  In fact, without the check for UpToDate in Fast Solution Build, the macro behaves no different than Build Solution!

RunActiveProject expands on BuildActiveProject by doing the following extra items:

  1. If the debugger is active, no solution build will be performed.  However, the command Debug.Start is fired, to simulate the Debug menu's Continue behavior when stepping or at a breakpoint.
  2. The active project and all its updated dependencies are built.

Finally, Fast Solution Build wedges itself into a couple BuildEvents.  When OnBuildProjConfigDone is triggered, Fast Solution Build checks for any errors during the current project's build.  If it finds any, it aborts the rest of the build through the Build.Cancel command.  When OnBuildDone is triggered and no build errors occurred, Debug.Start is called, causing the debugger to become active.

Known Issues

The following "issues" aren't actually issues at all.  In fact, if an executable application didn't have any library projects and all files were in the master project, these same issues would occur during the executable link.  They are described here so a better understanding may be had of the causes.

Known Bugs

Conclusion

Please report any comments, bugs, or fixes to jjensen@workspacewhiz.com.

Thanks,
Joshua Jensen
Author, Fast Solution Build
http://workspacewhiz.com/

Edit History

30 Sep 2003 - Version 2.20

20 Mar 2003 - Version 2.10

10 Feb 2003 - Version 2.03

4 Feb 2003 - Version 2.02

5 Dec 2002 - Version 2.01

4 Dec 2002 - Version 2.00