MSBuild Syntax and the IDE
Changing over to MSBuild as the project file format and build engine has some subtle changes. I just want to give a quick tour of the main concepts and terms so you'll be better able to work with the system - and better able to understand the new features we'll be describing over the next few weeks.
If you'd like to read this along with your project file, you can open it inside VS - just right-click on the project node in the solution explorer and select "Unload Project". Then right-click it again and select "Edit <my project>".
Definitions
Property: A simple name-value pairing that is global in scope to the project.
<CharacterSet>Unicode</CharacterSet>
Property Group: Simple XML notation to group together properties.
<PropertyGroup ...>
<ConfigurationType>Application</ConfigurationType>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
Okay - I'm cheating a bit here...a Property Group element can also have some attributes:
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
Condition is basically an IF statement - if the property values Configuration and Platform are equal to Release and Win32, then this property group is active. Think of it like a #define. Label is just a search marker we use to help us find the right locations in a project file - we'll get into that in a later post. Note that just about every MSBuild element can have conditions. I'll tend to delete them from this post to keep the XML simple.
Also note that property groups don’t impact the properties – except in the case of conditions – whether a property is in one property group or another is really about human readability.
Item: An entry of some sort of item - files are the most common. Items have two key attributes: ItemType and Include (think name & value). In this example, we have a ClCompile Item that points to the relative path of the file. The ItemType is how we distinguish a file consumed by CL.exe versus RC.exe. We don't rely on file extensions - it's all down to the ItemType.
<ClCompile Include="ConsoleApp.cpp" />
ItemGroup: Similar to PropertyGroup. Also can have conditions and labels, but you won't often see them in a VS generated project for source files.
<ItemGroup>
<ClCompile Include="ConsoleApp.cpp" />
<ClCompile Include="stdafx.cpp" />
</ItemGroup>
Similar to PropertyGroups, ItemGroups are for your convience – which group an item belongs to doesn’t really matter. You can even mix different ItemTypes in the same group if you want.
One very important difference between Items and Properties – when you specify the same property name later in the project file, its value becomes the new value.
<PropertyGroup ...>
<WholeProgramOptimization>true</WholeProgramOptimization>
<WholeProgramOptimization>false</WholeProgramOptimization>
</PropertyGroup>
Thus optimization is now false. However, Items are cumulative. Thus
<ItemGroup>
<ClCompile Include="ConsoleApp.cpp" />
<ClCompile Include="stdafx.cpp" />
</ItemGroup>
Means you have two items. Even if the Include values are the same, you still have two items.
Item Metadata: Think of these as properties that apply to a specific Item.
<ClCompile Include="stdafx.cpp">
<PrecompiledHeader>Create</PrecompiledHeader>
</ClCompile>
ItemDefinition: Consider these the templates for Item Metadata. You can specify an ItemDefinition - such as for ClCompile.PrecompiledHeader - that automatically applies to all items of that type, unless they override it locally.
<ItemDefinitionGroup>
<ClCompile>
<WarningLevel>Level3</WarningLevel>
</ClCompile>
</ItemDefinitionGroup>
Now all ClCompile items, such as the stdafx.cpp in the previous example, have WarningLevel set to 3.
Imports: Just like #includes - they bring in other MSBuild files. The extensions of these files are more for humans (like .h) - the Import doesn't care.
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
Now, just because we don’t care about their extensions doesn’t mean we don’t have a convention. You’ll find .props are files that contain property values and are imported at the top of the file. At the bottom of the project file you’ll find .targets imports. We aren’t really getting into this aspect in this post – but consider targets and tasks as the verbs of your build system. This is where things like Build, Rebuild, and Clean would be defined.
Evaluation Order
Okay, now that we have these concepts, let's talk about one of the big changes from VCBuild to MSBuild - evaluation order. MSBuild is what we call a linear evaluation model - this means that you start at the top of the file and work your way down.
If I have the following (where $() is used to refer to a property):
<PropertyGroup ...>
<A>1</A>
<B>$(A)</B>
<A>2</A>
</PropertyGroup>
The final result of B is 1 for MSBuild - you just follow the logic like a program. However, VCBuild is a late evaluation model, which results in B being equal to 2 - VCBuild keeps the string values until the very end in a large table and then evaluates everything.
This might sound like not much of a deal, but it has caused numerous issues for us as we ported from VCBuild to MSBuild - and if you are porting your projects to Dev10 - it might become an issue for you as well. We try to fix it up where we can, but watch for it in your conversion warning messages.
Referencing Elements
Okay - whew - finally at the part I wanted to talk about: How to work with these bits in the IDE. The way to access project properties is the same as in VS2008 - just right click on the project or file you are interested in and select Properties.
Notice how the highlighted properties above are using MSBuild property values (the $ is the indication of an MSBuild property) in their own definitions. This is pretty straight-forward. To help you out, however, we can display what properties are available to you when setting another property. For example, if you want to set Output Directory, you can select the drown-down arrow, select edit and then press the Macros button.
We calculate what all the macro values are at the point Output Directory would be evaluated so you don't have to worry about it. The same also applies to ItemMetadata.
Notice here how the project is pre-pending some values to itself - and notice how the % symbol is used instead of $ . This shows that you are working with ItemDefinitions - and if you bring up the macro editor window again, you'll be able to pick from not just properties, but also any ItemDefinition metadata defined for your ItemType (in this case, ClCompile).
Lastly - see the checkbox at the top of the screenshot. As I said above, PreprocessorDefinitions inherits from values defined before it as is evident from the MSBuild syntax displayed. But you don't need to do that yourself (although you are free to). We provide a checkbox to turn on or off the automatic appending of the inherited value.
Summary
So that is some basic behind-the-scenes on what is going on with your project and its properties. Notice we haven't talked about where these inherited values are coming from - that will come in a future post about property sheets and a walk-through of the MSBuild import structure we use for VC++ projects.
Comments
Anonymous
June 15, 2009
This is just a quick announcement about some Visual Studio team blogs that might be really worth checkingAnonymous
July 21, 2009
Introducing such a topic you'd like to congratulate you've let us know. Have good work