MSBuild: By Example—Introducing Well-Known Metadata

Microsoft .NET Framework, ASP.NET, Visual C# (CSharp, C Sharp, C-Sharp) Developer Training, Visual Studio


Jump to: navigation, search
CSharp-Online.NET:Articles
Visual Studio Articles

MSBuild: By Example

© 2006 Hashimi & Hashimi

Introducing Well-Known Metadata

When using MSBuild, you have two primary ways to pass data to tasks and targets; those are through properties and through items. A property is a key/value pair, and an item is typically a reference to a file. For example, when your project is compiled, the Compile item is evaluated to determine which files should be included. Using items for files over properties has some advantages; one of these advantages is that items can have metadata attached to them. The following is a sample of an item declaration from the Microsoft.Common.targets file:

<ItemGroup>
  <AppConfigFileDestination 
      Include=
      "$(OutDir)$(TargetFileName).config"/>
</ItemGroup>

In the previous declaration, the item AppConfigFileDestination is being defined, and its value is specified in the Include attribute. This attribute is using two properties, OutDir and TargetFileName, to create the name of the config file. An example of its value is bin\debug\WindowsApplication1.exe.config.

When you specify which files are included in the item declaration, you can include multiple files by separating them with semicolons. Another way to include multiple files is to use expressions that include wildcards. You can use three wildcard elements with MSBuild: ?, *, and **. You can use ? to replace a single character with any character. For example, the include declaration Include="c?r.cs" would include the files car.cs, cbr.cs, ccr.cs, and so on. The * element can replace any location with zero or more characters. To change the previous example, Include="c*r.cs" would include car.cs, caar.cs, cr.cs, colorer.cs, and so on. The ** notation tells MSBuild to search the directories recursively for the pattern. For example, Include="src\**\*.cs" would include all the files under the src directory with .cs extensions.

When you include an item in your MSBuild project file, it may seem you are simply adding a text entry, but what you don’t see is what’s happening behind the scenes. When you add an item, you’re adding a rich object to your project, and you get some information for free!

For this example, we will use MetaDataEx.csproj. This is a simple Windows Forms application, similar to the one contained in Chapter 2. Table 3-1 describes the metadata that is automatically set when your project file is loaded; this is well-known metadata.

Table 3-1. An Item’s Well-Known Metadata

Metadata Name Description
Identity Value for the item specified in the Include attribute.
Filename Filename for this item, not including the extension.
Extension File extension for this item.
FullPath Full path of this item including the filename.
RelativeDir Path to this item relative to the current working directory.
RootDir Root directory to which this item belongs.
RecursiveDir Used for items that were created using wildcards. This would be the directory that replaces the wildcard(s) statements that determine the directory.
Directory The directory of this item.
AccessedTime Last time this item was accessed.
CreatedTime Time the item was created.
ModifiedTime Time this item was modified.

The following is a target that demonstrates how to use well-known metadata:

<ItemGroup>
    <MDForm Include="MetaDataFrm.cs">
        <Author>
            <Name>Sayed Ibrahim Hashimi</Name>
            <Email>sayed.hashimi@gmail.com</Email>
        </Author>
    </MDForm>
    <MDFormOther Include="..\..\**\MSBuild1\*.cs">
        <Author>
            <Name>Sayed Y. Hashimi</Name>
            <Email>hashimi_sayed@gmail.com</Email>
        </Author>
    </MDFormOther>
</ItemGroup>
<Target Name="ShowWellKnownMD">
    <Message Text="Normal: @(MDForm)" />
    <Message Text="FullPath: @(MDForm->'%(FullPath)') " />
    <Message Text="RootDir: @(MDForm->'%(RootDir)')" />
    <Message Text="Filename: @(MDForm->'%(Filename)')" />
    <Message Text="Extension: @(MDForm->'%(Extension)')" />
    <Message Text="RelativeDir: @(MDForm->'%(RelativeDir)')" />
    <Message Text="Directory: @(MDForm->'%(Directory)')" />
    <Message Text="RecusriveDir: @(MDForm->'%(RecursiveDir)')" />
    <Message Text="Identity: @(MDForm->'%(Identity)')" />
    <Message Text="ModifiedTime: @(MDForm->'%(ModifiedTime)')" />
    <Message Text="CreatedTime: @(MDForm->'%(CreatedTime)')" />
    <Message Text="AccessedTime: @(MDForm->'%(AccessedTime)')" />
 
    <Message Text="%0D%0A;--------------"/>
    <Message Text="Recursive dir [MDFormOther]: "/>
    <Message Text="%09@(MDFormOther->'%(Filename)~CCC
           %09%(RecursiveDir)', '%0D%0A%09;')"/>
 
    <Message Text="%0D%0A;Relative dir [MDFormOther]: "/>
    <Message Text="%09;@(MDFormOther->'%(Filename)~CCC
               %09;%(RelativeDir)', '%0D%0A%09;')"/>
</Target> 

In this snippet from the MetaDataEx project file, an ItemGroup contains the item declarations upon which this target will act. This ItemGroup defines two items. One of these items, MDForm, includes only a single file and is explicitly defined. This item will provide the results of many of the metadata queries in this sample. The other item contains many files and has been defined using wildcards. This item will demonstrate how to use the RecursiveDir and RelativeDir metadata values. This file also includes some formatting of the output; for further information regarding formatting, see the "Formatting Your Output" section. To invoke this target on your project, you will invoke MSBuild by executing the following at the command line: >msbuild MetaDataEx.csproj /t:ShowWellKnownMD. Figure 3-1 shows the output from this target.


Image:6528f0301.jpg
Figure 3-1. Well-known metadata output


In Figure 3-1 you can see that MSBuild was able to resolve the items to the actual files on disk. If you are creating a new task that acts upon a file, you most likely will want to pass the task the FullPath value of the item. This will ensure that your task is dealing with the same file as your MSBuild project file. For example, if you want to copy IntermediateAssembly to another location, then you can use a declaration similar to the following one:

<Copy SourceFiles="@(IntermediateAssembly->'%(FullPath)')"
    DestinationFiles=~CCC
"@(IntermediateAssembly->'$(Destination)\%(Filename)%(Extension)')"/> 

This will copy the IntermediateAssembly to the Destination location and preserve the filename and extension. It is a best practice to use the FullPath for items as inputs to the tasks to ensure that no other file can be used in its place. When you are creating your MSBuild targets and tasks, it is helpful to remember what metadata is available to you out of the box and to remember how you can use it effectively.


Previous_Page_.gif Next_Page_.gif

Personal tools