MSBuild: By Example—Formatting Your Output
Microsoft .NET Framework, ASP.NET, Visual C# (CSharp, C Sharp, C-Sharp) Developer Training, Visual Studio
| CSharp-Online.NET:Articles |
| Visual Studio Articles |
| © 2006 Hashimi & Hashimi |
Formatting Your Output
As you use MSBuild to build your projects, you may want to format your output for increased readability or for other reasons. MSBuild uses the % character to show the beginning of an escaped character. The % character is followed by the ASCII character code for the desired character. You can find a complete reference for these codes in the MSDN Help documentation. For example, if you would like to place a carriage return line feed (\r\n) in your text, use the %0D%0A code. For example, consider the following FormatNewLine target, which is in the MSBuildEx.csproj file:
<Target Name="FormatNewline"> <Message Text="FirstLine%0D%0ASecondLine" /> </Target>
To execute this target, you call MSBuild with the following at the command line: >msbuild MetaDataEx.csproj /t:FormatNewLine. Figure 3-2 shows the output from this target.
![]()
Figure 3-2. MSBuild-formatted output
From Figure 3-2 you can see that the new line was successfully inserted into the output for this text. Table 3-2 lists some ASCII character codes that you may find useful when creating your project files.
Table 3-2. Useful ASCII Character Code Escape Values
| Character | ASCII Escape Value |
| Carriage return | %0D
|
| Line feed | %0A
|
| New line | %0D%0A
|
| Tab | %09
|
| Space | %20
|
| Quotes (") | %22
|
| Apostrophe (') | %27
|
| Ampersand (&) | %26
|
| Percent sign (%) | %25
|
In some circumstances when you are using a vector value, you may want to change the delimiter when using the Message task. Refer to the following target:
<Target Name="ShowFiles"> <Message Text="MDFormOther files:" /> <!-- Uses standard ; delimiter --> <Message Text="@(MDFormOther->'%(Filename)')" /> <!-- Uses , delimiter instead--> <Message Text="@(MDFormOther->'%(Filename)', ',')" /> </Target>
The <Message Text="@(MDFormOther->'%(Filename)')" /> call will print the values for the files included with the default delimiter, which is a semicolon. If you want a different delimiter, you can specify it as an argument. For example, the <Message Text="@(MDFormOther->'%(Filename)', ',')" /> call changes the delimiter to a comma. You can also use this feature to format your output! For example, if you want to align all the filenames, you can specify the delimiter to be a new line. Add the following message invocation to the target:
<Message Text="@(MDFormOther->'%(Filename)', '%0D%0A')" />
This invocation is specifying that a new line should delimit all the entries that will be included in the list. The target will look like the following snippet now:
<Target Name="ShowFiles"> <Message Text="MDFormOther files:" /> <!-- Uses standard ; delimiter --> <Message Text="Default delimiter" /> <Message Text="@(MDFormOther->'%(Filename)')" /> <!-- Uses , delimiter instead--> <Message Text="Comma delimiter" /> <Message Text="@(MDFormOther->'%(Filename)', ',')" /> <!-- This lines up the filenames --> <Message Text="Align on new lines"/> <Message Text="@(MDFormOther->'%(Filename)', '%0D%0A')" /> </Target>
When you execute this task, you will get the result shown in Figure 3-3.
![]()
Figure 3-3. Output from ShowFiles with custom delimiter
I’m sure you noticed that this output is still not very readable. Now you’ll add a few features to make it better. First, we will show how to add new lines to separate the sections of this output. To add a new line, insert <Message Text="%0D%0A"/> where desired. Second, we’ll show how to align all the elements under their headings with tabs. To do this, you can add a tab before the result for the first two targets. But for the last target, you will also have to add a tab to the delimiter. The final target should look like the following:
<Target Name="ShowFilesFinal"> <Message Text="MDFormOther files:%0D%0A" /> <!-- Uses standard ; delimiter --> <Message Text="Default delimiter" /> <Message Text="%09@(MDFormOther->'%(Filename)')" /> <Message Text="%0D%0A"/> <!-- Uses , delimiter instead--> <Message Text="Comma delimiter" /> <Message Text="%09@(MDFormOther->'%(Filename)', ',')" /> <Message Text="%0D%0A"/> <!-- This lines up the filenames --> <Message Text="Align on new lines"/> <Message Text="%09@(MDFormOther->'%(Filename)', '%0D%0A%09;')" /> <Message Text="%0D%0A"/> </Target>
Figure 3-4 shows the output from executing this new target.
![]()
Figure 3-4. Output from message with formatting
You can see that the output from this target execution is much more readable than the previous invocations.
Now you have successfully made the output much easier to read, but what have you done to the readability of the build file itself? Sifting through all the ASCII values is not only nonintuitive but is distracting. What can you do to avoid this problem? You may have guessed—you can keep these values inside properties. In a few cases, this method doesn’t work as expected, such as when you are placing whitespace-related items inside the properties. But you can get around that. We will skip covering those issues for now, however, in order to examine the other issues first. Refer to the following properties:
<PropertyGroup> <AT_SIGN>%40</AT_SIGN> <PERCENT_SIGN>%25</PERCENT_SIGN> <DOUBLE_QUOTE>%22</DOUBLE_QUOTE> <SINGLE_QUOTE>%27</SINGLE_QUOTE> <CR>%0D</CR> <LF>%0A</LF> <!-- New line items removed --> </PropertyGroup>
From these properties you can see that the ASCII values for each character in the previous table have been supplied in the appropriate property. Now you will examine how you can use these properties in your build files. Refer to the following target:
<Target Name="ShowAsciiProps"> <Message Text="At sign: $(AT_SIGN)" /> <Message Text="Percent: $(PERCENT_SIGN)"/> <Message Text="Double $(DOUBLE_QUOTE)Quote$(DOUBLE_QUOTE)" /> <Message Text="Single $(SINGLE_QUOTE)Quote$(SINGLE_QUOTE)"/> </Target>
You can execute this target at the command line with >msbuild MetaDataEx.csproj /t:ShowAsciiProps. Figure 3-5 shows the output from this target.
![]()
Figure 3-5. Output from the ShowUnicodeProps target
So, now you can embed ASCII values inside the project file while maintaining its readability. Now we’ll cover the tricky situations. Since you have seen how to use escaped characters, we will show how you can align MSBuild output using them.
In the previous example where you placed the ASCII values inside properties, we skipped over how to deal with the whitespace characters. Here is the remainder of PropertyGroup that was truncated previously and that contains working versions of the values:
<PropertyGroup> <TAB>%09</TAB> <HARD_NEW_LINE>%0A%0D%0C%08</HARD_NEW_LINE> <SOFT_NEW_LINE>%0A%20%08</SOFT_NEW_LINE> </PropertyGroup>
If you look at the definition for TAB, you’ll see that the %09 character is the ASCII character code value for the tab character. This defines two variations for the new line: a HARD_NEW_LINE and a SOFT_NEW_LINE. The SOFT_NEW_LINE is the one that acts as a new line embedded inside your text as ASCII. The SOFT_NEW_LINE will drop down a line and start at the same horizontal position as the start of the previous line. The HARD_NEW_LINE will drop down a line and start at the beginning of the next line. Let’s see what they look like. Here is a target to test these new values:
<Target Name="ShowAsciiWhiteSpaceProps"> <Message Text="Space$(SPACE)Here"/> <Message Text="Space Here"/> <Message Text=" "/> <Message Text="A Tab$(TAB)Example"/> <Message Text="A Tab%09Example"/> <Message Text=" "/> <Message Text="(soft)New$(SOFT_NEW_LINE)Line"/> <Message Text=" "/> <Message Text="(hard)New$(HARD_NEW_LINE)Line"/> <Message Text=" "/> <Message Text="New%0D%0A;Line"/> </Target>
In the ShowAsciiWhiteSpaceProps target, first the test messages are displayed, and the last message contains the ASCII code embedded within the text. You can invoke this target by executing the following at the command line: >msbuild MetaDataEx.csproj /t:ShowAsciiWhiteSpaceProps. Figure 3-6 shows the result of this execution.
![]()
Figure 3-6. Output from the ShowUnicodeWhiteSpaceProps target
Make a note of the difference between a SOFT_NEW_LINE and a HARD_NEW_LINE before you choose to use either. You’ll most likely want to stick to the SOFT_NEW_LINE.
With these tricks you should be able to format your output for simple situations. If you need more fine-grained control over how the output is being returned, you may have to implement your own task to conduct the formatting for you. Because creating a task is a fairly simple process, if you are not able to format your output after a few minutes of trying, just create a new task to do it for you! Another option is to write a custom logger to perform this for you.
|

