Generating a list of Biml Scripts and Descriptions



A C# script that finds all .biml files in a directory and pulls the Description Summary annotation if available.

published 03.28.13

last updated 03.28.13



using System;
using System.Windows.Forms;
using System.IO;
using System.Text.RegularExpressions;
using System.Text;

class Script
    /// <summary>
    /// This script will write a Text file containing all the Biml Script Locations/Names/Descriptions.
    /// The Description is pulled from an annotation in the format
    /// <#@ annotation annotationtype="Description" tag="Summary" text="<Description>" #>
    /// </summary>
    /// <param name="args">The args.</param>
	static public void Main(string[] args)
        // Location to dump the output.
        var outputPath = @"C:\Temp\BimlScriptDocumentation.txt";

        // Set up the location where the Biml Scripts live.
        var edw3Trunk = Environment.GetEnvironmentVariable("BFG_BI_EDW3_TRUNK", EnvironmentVariableTarget.Machine);
        var bimlScriptPath = Path.Combine(edw3Trunk, "Framework", "BimlScripts");
        StringBuilder output = new StringBuilder();

        // Output the table header
        output.AppendFormat("||{0}||{1}||{2}||\r\n", "Path", "Script", "Description");
        // Loop over all the Biml files
        foreach(var file in Directory.GetFiles(bimlScriptPath, "*.biml", SearchOption.AllDirectories))
            string relativePath = Path.GetDirectoryName(file).Remove(0, bimlScriptPath.Length+1);
            string scriptName = Path.GetFileNameWithoutExtension(file);
            string scriptContents = File.ReadAllText(file);
            string scriptDocumentation = " "; // Important!  Required for the table to format correctly.

            // Get the first "Description" annotation in the file (if one exists).
            string annotationRegex = "<\\#@(?:\\s+)annotation(?:\\s+)annotationtype=\"Description\"(?:\\s+)tag=\"Summary\"(?:\\s+)text=\"(.*?)\"(?:\\s+)\\#>";
            RegexOptions options = ((RegexOptions.IgnorePatternWhitespace | RegexOptions.Multiline) | RegexOptions.IgnoreCase);
            Regex reg = new Regex(annotationRegex, options);

            var groupCollection = reg.Match(scriptContents).Groups;

            if (groupCollection.Count >= 2)
                scriptDocumentation = groupCollection[1].Value.ToString();

            // Output the table row
            output.AppendFormat("|{0}|{1}|{2}|\r\n", relativePath, scriptName, scriptDocumentation);

        // Write the output to a file; it's hard to get from the console.
        if (File.Exists(outputPath))

        File.WriteAllText(outputPath, output.ToString());


Our team has created several dozen Biml Scripts over the last few months... enough that it is tough to know exactly what we have. We use the following C# to enumerate the scripts (stored in a common location and included in all of our projects) and generate a Confluence wiki table. You'll have to modify this to adjust the output / path but it might be helpful to someone.

This assumes scripts have a Description annotation in the format <#@ annotation annotationtype="Description" tag="Summary" text="" #>

You are not authorized to comment. A verification email has been sent to your email address. Please verify your account.



Paul S. Waters

3:58pm 10.31.13

Nice script David.

After making the edits for my environment, I was not able to run the script without the following error:

C:\WINDOWS\system32>echo off C# Script execution engine. Version Copyright (C) 2004-2013 Oleg Shilo.

Error: Specified file could not be executed.

System.ArgumentOutOfRangeException: Index and count must refer to a location wit hin the string. Parameter name: count at System.String.Remove(Int32 startIndex, Int32 count) at Script.Main(String[] args) Press any key to continue . . .

To resolve the issue I edited the following line from: string relativePath = Path.GetDirectoryName(file).Remove(0, bimlScriptPath.Length+1); To: string relativePath = Path.GetDirectoryName(file).Substring(0, bimlScriptPath.Length);

It ends up getting the absolute path, but for my case that is fine.