Extending Cake – creating a Cake extension method

There may come a time when cake’s existing commands do not do everything you want, so being that the cake DSL is basically C# we can easily create our own code.

In this post I’m going to create a DLL which is pretty standard but we’ll make it so we can fit into the cake ecosystem by making it an alias, this will make it simple to use and also give us access to the ICakeContext for logging etc..

First let’s see how it’ll be used inside cake

#r "./tools/SvnVersioning.dll" 

Task("Version")
    .Description("Generates a version based upon current SVN checkin")
    .Does(() =>
{
    // svnPath can be null if svn.exe is accessible via path
    var svnPath = @"C:\svn\svn-win32-1.6.0\bin\";
    var current = MakeAbsolute(Directory("."));
    var version = GetCurrentVersion(svnPath, current.FullPath);
    Information("Version: {0}", version);
});

Now if we create a class library (named SvnVersioning as uses in the above script).

We need to add a reference to Cake.Core to allows us access to the ICakeContext and cake attributes. Here’s the code for the GetCurrentVersion method (again, as can be seen in use in the script above)

public static class SvnVersionAliases
{
   [CakeMethodAlias]
   public static string GetCurrentVersion(this ICakeContext context, string svnPath, string path)
   {
      var REVISION_HEADING = "Last Changed Rev:";
      var revision = "10101";
      try
      {
         context.Log.Information("Solution path {0}", path);
         context.Log.Information("Starting svn {0}", svnPath);

         var svn = "svn.exe";
         if (!String.IsNullOrEmpty(svnPath))
         {
            svn = svnPath + @"\" + svn;
         }

         var proc = new Process();
         var procInfo = new ProcessStartInfo(svn)
         {
            CreateNoWindow = true,
            UseShellExecute = false,
            RedirectStandardOutput = true,
            Arguments = "info " + path
         };

         proc.StartInfo = procInfo;
         proc.Start();
         var output = proc.StandardOutput.ReadLine();

         while (!String.IsNullOrEmpty(output))
         {
            if (output.StartsWith(REVISION_HEADING))
            {
               revision = output.Replace(REVISION_HEADING, String.Empty).Trim();
            }
            output = proc.StandardOutput.ReadLine();
         }

         //revision is restricted to 65535 - and svn is already past this!
         //so mod by 10000!
         revision = (Convert.ToInt32(revision) % 10000).ToString();
         return revision;
      }
      catch (Exception ex)
      {
         context.Log.Information(ex.Message);
         return revision;
      }
   }
}

The CakeMethodAlias simply marks the code as a method alias, we can also add CakeAliasCategoryAttribute on method of class which is used for documentations of methods/properties. The CakeNamespaceImportAttribute is used to “hint” about additional namespaces that are required by the alias.