Potential

May 27, 2008 at 1:45 AM

Firstly I would just like to say what a good idea BuildCop is and how I can see alot of potential benefits from using it. The flip side is that I'm dealing with a number of requirements that cannot be meet with the way certain aspects have been implemented in this first release.

I'm dealing at the moment with a moderate to large multi solution build using team foundation server that I need to find ways to speed up. This is where BuildCop will help as a pre compile analysis tool to avoid the cost of a compile based on silly mistakes in project file settings. Used in this way BuildCop compliments Code Analysis, FxCop, NDepend as oppose to directly competes with these tools. However as mentioned they are some aspects that make it difficult to incorporate into a TFS team build.

1) The call to BuildCopEngine requires an instance of BuildCopConfiguration which is a System.Configuration.ConfigurationSection derived class. I'm not overly familiar with the Configuration namespace but there appears to be no way to externalise the Xml associated with the BuildCopConfiguration from the actual configuration file required by the console runner. This really bites when trying to call BuildCopEngine from a custom MSBuild (or even NAnt) task as required to incorporate BuildCop into a CI build. You either have to put the configuration section xml  into the configuration file of msbuild or nant itself or you have to ship an exe (for example the console exe) that exists purely so that you can obtain the configuration section to supply the engine. Then because your running in app domain of nant or msbuild you with need to put the build cop dll in the GAC or in the probing path of msbuild/nant so that BuildCopConfiguration can be instantiated. I'm kinda hopping I've missed something obvious so here is the code from a custom MSBuildTask I've attempted to write.

        public override bool Execute()
        {
            if (!File.Exists(ExePath)) throw new FileNotFoundException("ExePath does not exist!", ExePath);

            System.Configuration.Configuration configuration = ConfigurationManager.OpenExeConfiguration(ExePath);
            ConfigurationSection section = configuration.GetSection("buildCopConfiguration");
            BuildCopConfiguration buildConfiguration = section as BuildCopConfiguration;
            BuildCopReport buildCopReport = BuildCopEngine.Execute(buildConfiguration);

            foreach (BuildGroupReport groupReport in buildCopReport.BuildGroupReports)
            {
                foreach (BuildFileReport fileReport in groupReport.BuildFileReports)
                {
                    foreach (LogEntry logEntry in fileReport.FindLogEntries(LogLevel.Error))
                    {
                        Log.LogError(string.Format("{0} - {1}", fileReport.FileName, logEntry.Detail));
                    }
                    foreach (LogEntry logEntry in fileReport.FindLogEntries(LogLevel.Warning))
                    {
                        Log.LogWarning(string.Format("{0} - {1}", fileReport.FileName, logEntry.Detail));
                    }
                }
            }

            return true;
        }


2) In the above example I'm iterating the build report to produce the require msbuild logging. If the BuildEngine accepted report formatters registration programmatically then this could all be written in a report formatter that got instantiated with the msbuild logger and then passed to the engine. At the moment there is no way to do this as report formatter instantiation is encapsulated in the engine.

3) The rules implementation all looks solid but is highly dependant on the required details being supplied by the BuildFile class. The Microsoft.Build.BuildEngine.Project class can be instantiated and loaded to provide all this information for the rules to be evaluated against so it does raise the question why maintain your own equivalent class that at the moment is not as rich (e.g. All project files must be valid msbuild project files so why not use msbuild to parse it.)

Again I can see alot of potential in BuildCop to solve some of the build issues I'm dealing with but some aspects of the implementation are causing me some difficulties. Can you see anything that I've missed that would help me out?

Cheers

Coordinator
Jun 30, 2008 at 3:38 PM
Hi there,

First of all sorry for not responding sooner, but I've been moving house and we still have a lot of work so I'm a bit slow on my "personal projects"... Anyway, I really appreciate the feedback you gave here, you certainly have some good points that I'll address one by one below, but I do want to add that someone else has also been writing an MSBuild task that would enable BuildCop to be run during a daily build scenario for example. Shame on me for not thinking of it myself but I just conceived it as a simple command line tool at first. I definitely agree, however, that BuildCop should be available as an MSBuild task itself as well, so stay tuned for more (although maybe not in the very near future...)!

1) It's not very easy to discover in the System.Configuration namespace but it is in fact possible to return a configuration instance from an external XML configuration file with the code below. I'll add an overload to the next release however that will make this accessible at once by just passing the file name. But for now, you can use this:

System.Configuration.ExeConfigurationFileMap fileMap = new System.Configuration.ExeConfigurationFileMap();
fileMap.ExeConfigFilename = @"<path to config file>";
System.Configuration.Configuration configFile = System.Configuration.ConfigurationManager.OpenMappedExeConfiguration(fileMap, ConfigurationUserLevel.None);
BuildCopConfiguration buildCopConfig = configFile.GetSection(BuildCopConfiguration.BuildCopConfigurationSectionName) as BuildCopConfiguration;

2) The other MSBuild implementation I talked about has the same problem, so I'll definitely work on a clean solution. But in the mean time, you can register your own MSBuild-enabled formatter in the configuration file as well of course...

3) I didn't (and still don't) want to take a dependency on MSBuild assemblies for reasons of simplicity and versioning: I don't want to drag in a big assembly (and furthermore some specific version of it), just to get access to a simple XML file. If you have a rule implementation that would like to use the Project class to do parsing and provide you all the details that MSBuild cares about, you can reference the assembly and intantiate the Project class yourself with the filename of the BuildFile object. Furthermore, I've tried the Project class approach but it requires MSBuild to be installed on the machine (which I don't want to require) and when loading the project file it will try to parse it in a non-forgiving way. For example, it will try to resolve the referenced targets files (e.g. Microsoft.CSharp.targets) and thrown an exception if it can't find the file. Aggain, I want to avoid that since I don't want to require MSBuild or any of the referenced targets files to be installed. I need it to be light and simple, so that's why I'm just reading the XML myself in a custom class.

I hope this clarifies some things, let me know if you have additional questions/input/remarks or if you want to help work on the MSBuild integration!

Cheers,

Jelle