SharpGIS

#GIS from a .NET developer's perspective

Fixing Visual Studio’s auto generated code

I usually have a zero-tolerance when it comes to build warnings. While granted often the warnings are benign, having a lot will very often hide the important ones. Sometimes I even set the option “treat warnings as errors” to help me enforce this. When you’re building a library, you should also add XML doc comments to your classes and members, so you get full intellisense support in the projects you’re using it from. Just remember to check off the following check box:

image

This also have the benefit of giving you warnings for missing doc comments on public members, so you remember to write proper doc. However if you’re building for Windows Store or Phone, there’s a good chance you’ll see these four warnings now:

image

These are coming from code auto-generated by the compiler which exposes a set of public classes. You can even see these in intellisense:

image

This is a major issue in my mind. First of all it introduces warnings in code you didn’t write, and it pollutes your class library with methods that are not meant to be used. Here’s what this auto-generated class looks like:

image

The fix seems simple: Just go in and add the doc comments, and problem is solved, right? Not really. The problem is any changes you make to this file is overwritten every time you build. So we need to tweak the file right after it’s being generated, but right before it’s being compiled, but who’s that fast?

Enter: The Build Task

We can use a build task to do exactly that, and tell Visual Studio to let us know that its about to compile the code and fix it ourselves.

First create a new empty Windows Class Library, and add the following two references:

image

Next add the following class and compile:

XamlTypeInfoBuildTask.cs

using Microsoft.Build.Framework;
using Microsoft.Build.Utilities;
using System;

namespace XamlBuildTasks
{
    public class XamlTypeInfoBuildTask : Task
    {
        private const string XamlTypeInfoFileName = "XamlTypeInfo.g.cs";

        [Required]
        public string IntermediateOutputPath { get; set; }       
       
        public override bool Execute()
        {
            string filename = IntermediateOutputPath + XamlTypeInfoFileName;
            if (!System.IO.File.Exists(filename))
                return false;
            string code = System.IO.File.ReadAllText(filename);

            if (code.StartsWith("#pragma warning disable 1591")) //Already modified
                return true;
            int idx = code.IndexOf("[System.CodeDom.Compiler.GeneratedCodeAttribute");
            if (idx < 0)
                return false;
            string insert = "[System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never)]\n";
            code = "#pragma warning disable 1591\n" + code.Substring(0, idx) + insert + code.Substring(idx) +
                "#pragma warning restore 1591\n";
            System.IO.File.WriteAllText(filename, code);
            return true;
        }
    }
}

What does this class do? Simple: It has one property: The folder where the intermediate files including XamlTypeInfo.g.cs is. It then opens the file to be modified, and first injects “#pragma warning disable 1591” at the header which disables doc warnings, and re-enables it again at the bottom. At the same time we hide the class from intellisense, by setting the EditorBrowsable attribute on the class. This doesn’t really remove the class from the assembly – it just tells Visual Studio to skip showing this for intellisense.

To use this build-task, we need to add a little bit to the project file that causes this issue. We’ll first create a .targets file with the parameters for buildtask. Place this next to the compiled DLL (and use the name of the dll where highlighted):

XamlTypeInfoBuildTask.targets

<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
  <UsingTask TaskName="XamlTypeInfoBuildTask"
            AssemblyFile="$(MSBuildThisFileDirectory)XamlBuildTask.dll" />
  <Target Name="MyBuildTask" AfterTargets="MarkupCompilePass2" BeforeTargets="CoreCompile">
    <XamlTypeInfoBuildTask IntermediateOutputPath="$(IntermediateOutputPath)" />
  </Target>
</Project>

This tells the project to run this before the compilation after the markup has been processed. It also sets the IntermediateOutputPath property on our build task, so the build task can find the file.

Now the last step is to reference this .targets file from the csproj file. Open the csproj file up in notepad, and scroll down to the <Import…/> tags, and add the following (remember to modify the highlighted paths to where the targets file is:

  <Import Project="..\..\..\build\XamlTypeInfoBuildTask.targets" Condition="Exists('..\..\..\build\XamlTypeInfoBuildTask.targets')" />

Now when we build, we get a nice pretty build with no warnings:

image

And here’s all the auto-generated classes, gone from intellisense:

image

Making all this simpler

Naturally this is a bit of a pain having to set up over and over again. So unless you ever have to do a similar build task, you can forget everything you just read (sorry Smile), and just use the Nuget package I created that’ll do all this for you.

Simply add a reference to this nuget package, and the build task dll is downloaded and the .targets file auto-referenced.

And if you want the source code? It’s all available here on Github: https://github.com/dotMorten/XamlTypeInfoBuildTask

Enjoy!