Using MSBuild Property Sheets

Overview

Just like the regular VC++ projects, MSBuild-based VisualGDB projects store various settings (e.g. include directories) separately for each configuration: This enables greater flexibility, however makes common settings redundant, if they need to be reused across multiple projects and configurations. This page explains how to use MSBuild property sheets to eliminate the redundancy.

Property sheets are collections of MSBuild properties that can be referenced by project files. The properties from them will be automatically merged into the project, as if they were defined for each related configuration explicitly.

You can create and manage property sheets using the View->Other Windows->Property Manager command: There you can create new property sheets, or reference existing ones:Note that due to the limitations of the Visual Studio GUI, the property sheets referenced by VisualGDB projects will appear empty, however you will still be able to edit them manually:

Property Sheet Semantics

Property sheets are very similar to C/C++ header files defining preprocessor macros. Just like you would #include a header file that #define-s several macros, you can import a property sheet using the <Import> element in the .vcxproj file:

 <Import Project="SamplePropertySheet.props" />

Below is the most basic example of a property sheet defining just 1 variable:

<?xml version="1.0" encoding="utf-8"?> 
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
  <PropertyGroup>
	<MyMacroName>TEST_MACRO</MyMacroName>
  </PropertyGroup>
</Project>

You can then refer to the MyMacroName variable across your project properties:Note that if you use the Property Manager to reference property sheets, it will make the <Import> elements conditional to each configuration/platform:

<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|VisualGDB'">
   <Import Project="SamplePropertySheet.props" />
</ImportGroup>

We generally advise replacing them with unconditional <Import> elements and not grouping them with <ImportGroup>.

Property Sheet Order

You can import multiple property sheets by inserting multiple <Import> elements in the .vcxproj file. The <PropertyGroup> elements from the property sheets will then be applied in the order they were imported, possibly overwriting the previous definitions.

If you would like multiple property sheets to add values to the same variable, consider using the syntax shown below:

<PropertyGroup>
  <MyMacroName>$(MyMacroName);TEST_MACRO2</MyMacroName>
</PropertyGroup>

This will append “;TEST_MACRO2” to the MyMacroName variable instead of replacing it. Note that if the .vcxproj file defines MyMacroName as well, it may overwrite the value from the property sheet, if the .vcxproj definition is located after the <Import> element.

You can also construct a property sheet that will only set a variable if it was not set before:

<PropertyGroup Condition="'$(MyMacroName)' == ''">
  <MyMacroName>TEST_MACRO2</MyMacroName>
</PropertyGroup>

Conditions can also be set on individual assignments:

<PropertyGroup>
  <MyMacroName Condition="'$(MyMacroName)' == ''">TEST_MACRO2</MyMacroName>
</PropertyGroup>

The <PropertyGroup> elements are evaluated one-by-one in the order they are declared (where <Import>-s are expanded similar to #include-s). If you are troubleshooting weird property sheet errors, make sure you understand the order of the property sheet evaluation in your project.

<PropertyGroup> vs <ItemDefinitionGroup>

Some properties in MSBuild projects (e.g. include directories) can be overridden for individual files. These properties are not defined using <PropertyGroup>. Instead they use  <ItemDefinitionGroup> to declare the default values and can then override them inside <ItemGroup>:

  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|VisualGDB'">
    <ClCompile>
      <PreprocessorDefinitions>%(ClCompile.PreprocessorDefinitions);DEBUG=1</PreprocessorDefinitions>
    </ClCompile>
  </ItemDefinitionGroup>
  <ItemGroup>
    <ClCompile Include="PropertySheetDemo.cpp">
      <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Debug|VisualGDB'">%(ClCompile.PreprocessorDefinitions);EXTRA_MACRO_FOR_JUST_THIS_FILE</PreprocessorDefinitions>
    </ClCompile>
  </ItemGroup>

In this example, the project added the DEBUG=1 macro to all source files, and the EXTRA_MACRO_FOR_JUST_THIS_FILE to PropertySheetDemo.cpp.

If you would like to add a certain macro to all source files via a property sheet, you can do it as shown below:

<?xml version="1.0" encoding="utf-8"?> 
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
  <ItemDefinitionGroup>
    <ClCompile>
      <PreprocessorDefinitions>%(ClCompile.PreprocessorDefinitions);MACRO_FROM_PROPERTY_SHEET</PreprocessorDefinitions>
    </ClCompile>
  </ItemDefinitionGroup>
</Project>

Custom Targets

Property sheets can also declare custom targets. E.g. you can add a check that will trigger an error if a certain variable is missing:

<?xml version="1.0" encoding="utf-8"?> 
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
	<Target Condition="'$(MyVariable)' == ''" Name="MyCustomTarget">
		<Error Text="You forgot to set MyVariable"/>
	</Target>
 
	<PropertyGroup>
		<BuildDependsOn>$(BuildDependsOn);MyCustomTarget</BuildDependsOn>
	</PropertyGroup>
</Project>

Note that the MSBuild targets are executed after loading all property sheets. Hence the check for MyVariable will be done after ALL property sheets have been loaded, even the ones imported after the current one.

Targets can include many supported MSBuild tasks, e.g. <Exec>:

	<Target Condition="'$(MyVariable)' == ''" Name="MyCustomTarget">
		<Exec Command="$(WINDIR)\system32\cmd.exe /c echo test"/>
	</Target>

Adding the task to the BuildDependsOn variable will make it run just before the Build task defined in <VisualGDB directory>\MSBuild\SysprogsPlatform.targets:

<Target Name="Build" DependsOnTargets="<...>;$(BuildDependsOn);CommitRemoteBuild;ExecutePostBuildActions"/>

Unless the target is referenced by any other target via its DependsOnTargets attribute, it will never get executed.

Recommended Use

If you have a large solution containing multiple projects, we recommend locating the commonly set properties in the .vcxproj files (<PropertyGroup> or <ItemDefinitionGroup> elements) and moving them to a property sheet file referenced from all the projects. This will allow conveniently changing them in just one place.

In fact, VisualGDB uses this structure for embedded projects. It moves all device-specific parameters to a <mcu family>.props file and references it from the project. You can read more about the embedded project layout here.