How can I define a user macro path property to a stable location, for multiple solutions and projects? - visual-studio

I want to use Visual Studio's property pages to define some commonly-used paths as user macros - e.g., to the location of third-party libraries, or the base directory of the repository.
However, I'm only managing to define user macros either:
Using an absolute path -- obviously wrong
Relative to the project directory
Relative to the solution directory.
None of those work for me, since our codebase has multiple VS solutions and some projects are shared between solutions.
For example, if I have the following structure:
$(BASE_DIR)/dirA/Solution1/Project1
$(BASE_DIR)/dirB/dirC/Solution2/Project2
$(BASE_DIR)/ThirdPartyLibs
... and I want to define a $(ThirdPartyLibDir) macro. Or, heck, a $(BASE_DIR) macro.
Let's say Solution 1 has only Project 1, and Solution 2 includes Project 1 and Project 2. In this case, I can't define these macros relative to $(SolutionDir), nor to $(ProjectDir).
Is there any way I can define such a macro?
(If there were a macro for getting the location of a property page file, I could put a .props file in $(BASE_DIR), and define the macros that way. It doesn't look like that exists.)

One way to achieve this is to do the following -
Create one property sheet per solution and create a user macro BASE_DIR inside it with a relative path (....\ etc.).
Create a common property sheet that contains the actual library locations etc. based on the BASE_DIR macro. Add this sheet to the projects too.
This allows you to specify include directories and other "common" properties using the $(BASE_DIR) macro in the common props file. The solution-specific property sheet provides the $(BASE_DIR) value relative to the projects in the solution.

Related

Visual C++ 2010: Including boost in several projects in a solution?

I'm using the boost library in several projects in my Visual C++ 2010 solution. What I'm currently doing is modifying each project's properties by setting:
Properties->Configuration Properties->C/C++->General->Additional Include Directories to include the boost directory C:\boost\boost_1_47
Properties->Configuration Properties->Linker->General->Additional Library Directories to include the boost lib directory C:\boost\boost_1_47\lib
Now that I'm upgrading my boost version to 1.51 I realize I'm violating DRY by specifying this information more than once (i.e., once for each project). I want to define the boost info in a single place.
After researching it seems like Property Sheets are a good solution. I've read about property sheet inheritance but I don't see how that's useful because it seems that if you want to add a project-specific include directory then you will have to set the Additional Include Directories in the properties for that specific project which will then override the inherited property sheet which defines the boost include directory. If I am wrong about this please correct me.
So my next thought is to create a single property sheet called GlobalMacros.prop and define a user macro something like $(BoostDir) and then add this property sheet to each project. Then I can use the macro in each project's properties when I'm defining include directories and library directories. However, when creating a macro there is an option "set this macro as an environment variable in the build environment" and I am not sure what that does or if I should set it.
Overall I want to know what is the best way to reduce repeated configuration definitions for common settings?
I'm actually the developer that originally implemented property sheets in Visual C++ back in VS 2005 (although I'm not responsible for the mess that is the VC++ project properties dialog). Caveat: I stopped working on VC++ after 2005, so below may not be entirely accurate for your version.
In the Property Manager, you should be able to multiselect all of your project configurations in all projects and add a new property sheet. This will automatically inherit those project configurations from the same property sheet. In that property sheet, set the boost include and library directories directly or use a macro for BoostVersion and BoostDir and use those macros in the property sheet's properties.
Provided that the project configurations have "inherit from parent or project defaults" checked (this controls inserting $(Inherit) vs $(NoInherit) in 2005/2008 and %(<propertyname>) in 2010/2012) for those properties, you should see any project-specific properties set in the project configurations prepended, by default, to the values in the property sheet.
You can confirm this by going to a project and checking the compiler's "Command Line" tab to see if the switches are what you expect.
Edit: note that there are two types of properties in VC++: "single-value" and "multi-value". An example of a single-value property is the compiler's warning level. The value on the command line for a single-value property comes from the first place in the property inheritance chain that specifies it, starting with the project configuration, then the property sheets, then the built-in default in the project system. An example of a multi-value property is the compiler's include directories. Multi-value properties get concatenated with their parent property sheets unless $(NoInherit) is specified (2005/2008) or %(<propertyname>) is not specified (2010/2012). Thus, by default, you should see the boost paths showing up in your project settings provided they have intentionally not inherited the property from the property sheet.

VS2010, MSBuild and Environment Variables

I have a question about MSBuild.exe and Environment Variables. First, the development environment:
The code is mostly C++ with some C#. There are over 5,000 classes, 340 projects and 200 solutions arranged in a deep source tree. There is a solution at the root of the tree and at various other points in the tree. We use TFS and maintain multiple active branches for a series of future releases. Each developer uses a local view to modify and test code. Some developers work on multiple branches concurrently. Sometimes developers build solutions from different TFS branches concurrently.
We use about 70 environment variables for locating shared header files, libraries, etc. In VS2008 we used a file with the same base name as the solution file and an extension of .slnenv for defining the environment variables. All variables are defined relative to the base of the source tree. This .slnenv file is read by a custom VS2008 AddIn that creates Environment Variables in the VS2008 process space, i.e.
MyProjectDir=$(SolutionDir)\..\..
MyRoot=$(MyProjectDir)\..\..
MyInstallDir=$(MyRoot)\Install
MySourceDir=$(MyRoot)\Source
MyUnmanagedSourceDir=$(MySourceDir)\My\Unmanaged
MyIncludeDirs=$(MyProjectDir);$(MyUnmanagedSourceDir)
This AddIn does not work correctly with VS2010 because MSBuild does not inherit the environment variables that are created after the solution is loaded.
My question is how do I get these environment variables to MSBuild? I have found two methods that work, but are not as convenient as the AddIn we had been using.
VS2010 can be started with a command script that first sets Environment Variables in the process space and then starts VS2010. MSBuild does inherit these Environment Variables. This is unsatisfactory because the scripts would need to be customized for the various points where a solution can be loaded.
The second method I have tried is defining the Environment Variables as Properties in property sheets and .vcxproj files. We have a base property sheet that all .vcxproj files load. In that property sheet:
<PropertyGroup>
<MyRoot>$(MyProjectDir)\..\..</MyRoot>
<MyInstallDir>$(MyRoot)\Install</MyInstallDir>
<MySourceDir>$(MyRoot)\Source</MySourceDir>
<MyUnmanagedSourceDir>$(MySourceDir)\My\Unmanaged</MyUnmanagedSourceDir>
<MyIncludeDirs>$(MyProjectDir);$(MyUnmanagedSourceDir)</MyIncludeDirs>
</PropertyGroup>
Then I can define the base directory in each .vcxproj file:
<PropertyGroup>
<MyProjectDir>..\..</MyProjectDir>
</PropertyGroup>
This method uses relative path names where the AddIn resolved all Environment Variables to absolute path names. I'd rather not have to edit 340 .vcxproj files where the definition of "MyProjectDir" would vary depending on how far from the source root the project file exists. So far I've tried this method in just one project.
I have tried to modify the AddIn to create Properties rather than Environment Variables. I tried using ENV2.get_Properties(), but that seems to work only for Properties that are defined in VS2010, not for Properties I've defined.
Thank you,
Dan Kary
After discussion via email, we figured what were Dan needs and following message helps him with question issue. He agrees that this could be helpfull to other SO members:
Aha, I get it now.
As I did understand from that topic - you really need all that stuff only for build step (paths for include clause, paths to built tool to resolve dll\exe dependency), and no regular developer activities depends on it.
It's good, cause we don't need to fight VStudio about not reinitializing env vars after first load. We just need somehow edit and implement your solution for huge amount of proj files ;) Task become much easier :D
Small disclaimer - I'm writing this on the road and have no access to fully fledged dev env to check and bulletproof all further recomendations, so take my apologizes in advance for any mistakes I will make ;)
I would like to bring your attention to some msbuild features:
http://msdn.microsoft.com/en-us/library/ms164309.aspx
You have predefined properties, that available for all scripts and projects, it looks like this will help you solve first issue - absolute paths instead of relative. This is small excerpt from the link for further reference
$(MSBuildProjectDirectory) -
The absolute path of the directory where the project file is located, for example, C:\MyCompany\MyProduct.
$(MSBuildProjectFile) -
The complete file name of the project file, including the file name extension, for example,
MyApp.proj.
$(MSBuildProjectExtension)
The file name extension of the project file, including the period, for example, .proj.
$(MSBuildProjectFullPath)
The absolute path and complete file name of the project file, for example, C:\MyCompany\MyProduct\MyApp.proj.
$(MSBuildProjectName)
The file name of the project file without the file name extension, for example, MyApp.
So you should be able to use this defined properties in your Property sheets.
Now, with msbuild 4.0 toolset (toolset defined by attribute ToolsVersion="" on the root script element) there is interesting trick
Microsoft.Common.Targets define 2 variables to conditionally import custom msbuild script before or after itself
CustomBeforeMicrosoftCommonTargets and CustomAfterMicrosoftCommonTargets
You can use it the following way ( I think it will possible help you to avoid editing of all proj files or at least - reduce editing to very simplified copy-paste part)
Define it using relative path to include your property sheet files
<CustomBeforeMicrosoftCommonTargets>$(MSBuildProjectFile)\ConcreteProjectCustomProperties.propz</CustomBeforeMicrosoftCommonTargets>
Take a note that CustomBeforeMicrosoftCommonTargets should be global scoped property to be inherited by all secondary
And if you properly define your ConcreteProjectCustomProperties.propz I think you could achieve what you need.
"Properly" is that you will include there your global properties file (with relative or absolute path) and then define all your project-level properties.
NB:
Also take a note that later defined properties not available for reference to early defined
Your example defines $(MyIncludeDirs) with reference to $(MyProjectDir), but $(MyProjectDir) declared and defined a bit later.
It could be due to "exampling" and fast churning that code, but if you define your vars same way in production - it could lead to subtle error.
I hope I got your problem properly and my explanations will help you to quickly move all your properties from custom add-on to simpler and native msbuild script ;)

Including directories in VC++ when working from multiple computers

In Visual Studio 2010, how to I set up my VC++ import directories so that I can build my project from multiple machines? For example, my project requires the use of a graphics library that's installed on both of the machines I'm using, but located on different paths.
I imagine the answer is some sort of macro, but I'm new to Visual Studio, so I'm not sure if my intuitions are correct.
I'm transitioning from the Matlab environment, where you can set the path directly per machine, rather than per project. Does Visual Studio have such functionality, or is everything at the level of project properties?
You can indeed use macros to set your include and lib directories on a per machine basis.
if you have your files in your project directory you can use:
$(SolutionDir)\ This will point to the full path of your solution directory root
$(ProjectDir)\ This will point to the full path of your project directory root.
You can place your graphics library in your project directory and point to it like:
$(ProjectDir)\graphicslib, for example, or any other place you want to put it in your solution.
More info is located here http://msdn.microsoft.com/en-us/library/c02as0cs.aspx
use a hierarchy of property sheets, or use a tool like CMake to generate project files for you (as it should automatically find all the libs/includes).
For the property sheets: add the same property sheet to each project. Withing that property sheet, include other property sheets with predefined names. Then on each computer you work, you will have to provide those 'per-machine' propert sheets. For example:
main.vsprops -> include by every project
|- matlab.vsprops -> sets the lib/includes for matlab runtime
|- opengl.vsprops -> sets the lib/include for opengl
Now in your versioning system, you provide a default for matlab.vsprops and opengl.vsprops that contains suitable dfeault values and a batch file to create them. This way you can checkout from the VCS, run a single file, edit the vsprops and start coding without too much hassle. For example:
main.vsprops -> versioned
matlab.vsprops.def -> versioned, contains defaults
opengl.vsprops.def -> versioned, contains defaults
create_vsprops.bat -> versioned, copies *.vsprops.def to *.vsprops

How can one globally define a variable in a visual studio 2010 solution?

Is there any way one can define for a solution a global variable accessable by any project file or project property sheet included in the solution?
One variable is enough if that can define a path for a property sheet that every project can include.
This is desirably for a solution.
An environment variable would be ok though less desirable.
** (visual studion 2010)
Poking at it again, I can't see how to set a user defined $(xxx) vartiable in a project - yes you can in a property sheet, but In a project I have reference to the $(ProjectDir) -
The problem is I want to define a $(tree of projects root) that any project that references a specific poject can access and the root will be relative to that project.
An alternative would be if a property sheet can have access to its own path and define the project collection root (of a tree of related projects with common resources) relative to that.
It seems every one building a big system with VS has the same problem.
I don't know your use case but it doesn't sound like the best idea. This will tie all of your projects together very tightly, which violates good OO design. Microsoft hasn't included any solution-level properties for this reason. However, if you really want to do something like this, you have two ways (at least) to go about it.
The first way, and the way I would recommend against, would be to store the variable in one of your projects and make it public. Then all of your projects could access it as long as they include the project as a reference.
The second way, and the way that will retain some OO principles, would be to create a config file that all projects looked at for the variable. This could be an XML file, a CSV file, or even a simple text file. If you wanted to get more complicated, you could point to a database location since this is one of the roles of a database.

How do I create a Visual Studio project template that includes linked files?

In Visual Studio 2010, I want to create a project template that includes links to two files that should exist on the system. One of them is a common AssemblyInfo.cs file. Another is the strong name key file (*.snk).
I need these references to be relative, because each developer's workspace will be set up differently. Is it possible for the project template to somehow figure out where these files reside in each developer's environment?
From reading about templates, it sound like they're pretty static so I wonder if tricks can be done to do something like this. If nothing else, I can add bogus references that will cause compilation errors and force the developer to hook these files in. But if I can do it for them, that would be better.
You should set the CreateInPlace property to true in the vstemplate. The documentation says
Specifies whether to create the project and perform parameter replacement in the specified location, or perform parameter replacement in a temporary location and then save the project to the specified location.
If you want relative paths to work, you need the parameter replacement to occur in the place where you're creating the project, not in a temporary location.
Microsoft have confirmed that this is a bug with the extensibility model and project templates. I managed to get round this by using IWizard. I added the following code in the RunFinished method of my IWizard implementation:
//Get linked file directory
string coreDir = Path.GetDirectoryName(MyProject.FullName);
//Data folder
ProjectItem propertiesProjectItem = slSharedProject.ProjectItems.Cast<ProjectItem>().Where(p => p.Name == "Data").First();
propertiesProjectItem.ProjectItems.AddFromFile(coreDir + #"\Service\TheFileIWantToLink.cs");
This code links in a copy of TheFileIWantToLink.cs file to my shared Silverlight project (slSharedProject).
You could try to integrate a Wizard into your Project Template and set the Paths to the linked files. If i remember right you don't have to create an User Inteface; you only have to specify the correct replacements in the replacementsDictionary and VS will replace the values in your Template File. See this or this for further information.

Resources