01 November 2009

Visual Studio 2010: Property Sheets and C++ Directories

I love external libraries.  A good library (one that is well-tested and interoperable) literally saves years of development time.  Since this post revolves around C++, I'm going to mention Boost as one of the most helpful libraries ever made.  Visual Studio provides a fairly obtuse (but working) system for referencing libraries on a per-user, per-system basis, accessable from the Tools > Options menu:

It doesn't look the most user-friendly and it's a little bit painful to get to (especially the first time), but it got the job done.  Theoretically, you only go into this menu (or you write a script to do it for you) once per library and never touch it again.

Naturally, when I began testing Visual Studio 2010, the first thing I wanted to do was set up my include and library paths.  So, I opened the options dialog only to find that Microsoft saw fit to remove this feature.  My first reaction was: "WHAT? WHY?" and I went on a hunt to see where they had moved that option to (because it is basically impossible to live without).  It turns out they had moved -- to the property page of the project (just right-click the project and open properties):

This is basically an extension (duplicate?) to the Visual Studio 200x options for the C++ compiler settings, which let you specify more directories to search through when looking for header files (a similar option exists for the linker in "Additional Library Directories"):

But before you start adding references to "C:\Path\To\Library" in all of your projects, let me tell you about a feature of Visual C++ called "Property Pages."  Personally, I have not seen them used very much in various projects for a number of reasons, the number one being that they are quite difficult to find, number two being that their visibility is not enabled by default, number three being that you do not have to know about them to get compiling in VC++ and lastly because their functionality is a little odd.

How to Use Property Sheets
These instructions will work with Visual Studio 2005, 2008 and 2010 (and probably beyond).

Anyway, the first thing you need to do is turn on the Property Manager toolbox.  Do this by hitting "Property Manager" under the "View" menu.  The toolbox will probably attach to your Solution Explorer, but it could just appear in the middle of the screen.  In either case, you should see a tree list of your projects, which have a cross-joined list of all solution configurations and platforms as children, which have a collection of settings sheets:

Okay, let's add a property sheet that defines the symbol _DEBUG and add it to all the debug configurations.  Right-click on any debug configuration and select "Add New Project Property Sheet."

If you are planning on using this sheet in multiple projects, it is a good idea to put the file in a directory that is not project-specific.  I made a directory called "Properties" in the solution directory to store all my property sheets.  A special note: At the time of writing, Visual Studio will give you an exception if you try to make a new property sheet in a directory that does not exist (error reported to Microsoft Connect), so you have to make the folder in Explorer or something first.

Open the newly-created file by double clicking and you should see a window that looks almost identical to what you would see if you opened up the "Properties" dialog from the Solution Explorer, save that there are a million options to configure.  The reason there are so many options to configure is because property sheets are not tied to any project, so all configurable options show up in the GUI.

Go into C++ > Preprocessor settings, add "_DEBUG" to the definition list and click OK.  Now you can add this .vsprops file to multiple projects.  You can actually share the .vsprops files between Visual Studio 2005 and Visual Studio 2008, if you are planning on maintaining two solutions for two separate consumer groups.  Unfortunately, Visual Studio 2010 creates a .props file, which is completely different XML and cannot be shared with previous versions of the IDE (but it can convert them).  A fun quirk is that you have to right-click on the individual sheet to explicitly save them (do not worry if you forget to do this, you will be asked when you exit the IDE).

Okay, so making a whole file and screwing around with all this stuff just to define another preprocessor macro is pretty pointless.  However, you can see that you have complete control over all the properties of VC++ projects, which means you could make another one for release that defines specific optimization settings for every project or a general setting that tells all projects to output to some shared output and intermediate files (I use a variation of "$(SolutionDir)/bin/$(ConfigurationName)/$(PlatformName)/$(ProjectName)" and "$(SolutionDir)/obj/$(ConfigurationName)/$(PlatformName)/$(ProjectName)").  Property sheets let you consolidate shared settings to arbitrary levels of granularity.

So if you have two property sheets, one which sets the output directory to "$(SolutionDir)/bin/$(ConfigurationName)/$(PlatformName)" and one that sets it to "$(SolutionDir)/bin/$(ConfigurationName)-$(PlatformName)", what is the value of $(OutDir)?  The answer is whichever one was evaluated last.  You can tell the order of evaluation by the order the sheets are listed in the Property Manager toolbox.  Evaluation occurs in a bottom-up order, so those sheets on top are evaluated last.

Back to the origninal problem: How can we use property sheets to make up for the lack of global VC++ Directories?  Even if we make a property sheet that adds a reference for #includes to "F:\Applications\Dev\boost_1_40_0", what happens when another developer has their Boost library in "C:\SDKs\boost1.40.0"?  Project property pages have not solved this problem, since changes to the file would get propagated over source control and screw up the settings for everyone else.  Wouldn't it be nice if there was a user-specific property page that was defined outside of the project?

In Visual Studio 2010 there is such a file: Microsoft.Cpp.Win32.user, which exists somewhere in your user application settings.  The good part about this change is that settings now reside in an overridable format.  If your project uses a modified version of some library, the project property sheet can add a search path to "$(SolutionDir)/ModifiedLibrary", which will be searched before the user files.  Granted, the change significantly changes the configuration system (in a way that takes a while to explain), but I think it's for the best.  There are also still some quirks with how adding different configurations will propagate certain sheets, but its still in beta, so hopefully they will be smoothed out.