Rash thoughts about .NET, C#, F# and Dynamics NAV.


"Every solution will only lead to new problems."

Wednesday, 14. July 2010


"Fake – F# Make" 1.40.5.0 released – Bugfixes for CruiseControl.NET

Filed under: F#,FAKE - F# Make — Steffen Forkmann at 9:15 Uhr

Today I released “FAKE – F# Make” version 1.40.5.0. This release fixes some issues with CruiseControl.NET compatibility and improves the MSBuild task.

Important links:

Changes for CruiseControl.NET

Daniel Nauck created a FAKE task for CC.Net. This task allows a much easier configuration of FAKE. Please download the latest CC.NET build if you want to use it.

Daniel also created a FAKE build project on his server which showed us some compatibility issues:

@Daniel: Thank you very much for helping me on this CruiseControl.NET stuff. I really appreciate this.

MSBuild task changes

The MSBuild task in FAKE gets a sequence of project files and compiles them in the given order. This might be slow if you have lots of dependent projects. Then MSBuild might analyze the dependencies over and over again. To fix this issue I currently see two possible solutions:

  1. Generate a temporary solution file and use this for compilation.
  2. Analyze the given projects and remove all dependent projects from the list.

FAKE 1.40.5.0 implements the second idea. If you have a better idea please contact me.

Tags: , , ,

Sunday, 11. July 2010


“Fake – F# Make” 1.33.0 released

Filed under: F#,FAKE - F# Make — Steffen Forkmann at 15:11 Uhr

Yesterday I released “FAKE – F# Make” version 1.33.0. This release has lots of small bug fixes and a couple of new features.

Important links:

Git helpers -Fake.Git.dll

Git is a distributed revision control system with an emphasis on speed. Git was initially designed and developed by Linus Torvalds for Linux kernel development. Every Git working directory is a full-fledged repository with complete history and full revision tracking capabilities, not dependent on network access or a central server.

[Wikipedia]

In the last couple of months I worked on small helper library for controlling Git. This library is now released as part of “FAKE – F# Make”. You can find the source code at http://github.com/forki/FAKE/tree/master/src/app/Fake.Git/.

Features:

  • Repository handling
    • init, clone
  • Submodules
    • init, clone, information about submodules
  • Branches
    • checkout, create, delete, merge, rebase, tag, pull, push, reset, commit, …
  • SHA1 calculation
Documentation generation with James Gregory’s docu tool

What’s a docu? A documentation generator for .Net that isn’t complicated, awkward, or difficult to use. Given an assembly and the XML that’s generated by Visual Studio, docu can produce an entire website of documentation with a single command.

[docu website]

Fake comes bundled with James Gregory’s documentations generator “docu”, which converts XML-Documentation comments into HTML files. All you need to do is downloading a template and calling the docu task inside your build script:

!+ (buildDir + "*.dll")
     |> Scan
     |> Docu (fun p ->
         {p with
             ToolPath = docuPath + "docu.exe"
             TemplatesPath = templatesSrcDir
             OutputPath = docsDir })

Since FAKE builds its own documentation with docu I started to add more (and hopefully better) XML doc comments. My plan is to describe more and more of the internal FAKE functions in the next releases. An updated HTML-document (generated via a docu task) can be found at http://www.navision-blog.de/fake/.

Side by side specification

For Test-driven development (TDD) it’s sometimes nice to have the specifications next to the implementation files since the specs are considered as documentation.

By using a tool like VSCommands it is possible to group the implementation with the specs (see also http://gist.github.com/457248).

This “side by side specification” makes TDD a lot easier but of course we don’t want to deploy the specification classes and the test data.

Side by side specification ==> After "RemoveTestFromProject"

FAKE has a new feature which automatically removes all specification files and test framework references according to a given convention:

Target "BuildApp" (fun _ –>
      !+ @"src\app\**\*.csproj"
         |> Scan
         |> Seq.map (
             RemoveTestsFromProject
                 AllNUnitReferences      // a default references convention
                 AllSpecAndTestDataFiles // a default file convention
                 )
         |> MSBuildRelease buildDir "Build"
         |> Log "AppBuild-Output: "
)

The conventions are simple functions and can be customized e.g.:

/// All Spec.cs or Spec.fs files and all files containing TestData
let AllSpecAndTestDataFiles elementName (s:string) =
     s.EndsWith "Specs.cs" ||
       s.EndsWith "Specs.fs" ||
       (elementName = "Content" && s.Contains "TestData")
Miscellaneous
  • SQL Server helpers are moved to Fake.SQL.dll
    • Additional functions for attaching and detaching databases.
  • FileHelper.CopyCached function was added
    • Copies the files from a cache folder. If the files are not cached or the original files have a different write time the cache will be refreshed.
  • EnvironmentHelper.environVarOrDefault added
    • Retrieves the environment variable or a given default.
  • Fixed Issue 3: toRelativePath calculates paths with ..\..\ if needed
  • Added a build time report to the build output.
  • XPathReplace and XMLPoke tasks added.
    • Replaces text in an XML file at the location specified by an XPath expression.
  • ILMerge task added
  • Windows Installer XML (WiX) task added
Tags: , , , , , , , ,

Wednesday, 7. July 2010


Strange bug in Dynamics NAV client

Filed under: Dynamics NAV 2009,Navision — Steffen Forkmann at 7:34 Uhr

My colleague Mathias Meissner found a strange bug in the Dynamics NAV Client (occurs at least in the native clients for NAV 403, 501 and 601). It seems the following code creates a strange memory allocation issue. (Download sample)

OBJECT Codeunit 99500 NAV Bug
{
  OBJECT-PROPERTIES
  {
    Date=17.06.10;
    Time=14:30:30;
    Modified=Yes;
    Version List=;
  }
  PROPERTIES
  {
    OnRun=BEGIN
            TestMiddleCase;  // comment this line out and ReverseString works!
            TestReverseString;
          END;

  }
  CODE
  {

    PROCEDURE TestMiddleCase@5128500();
    BEGIN
      MiddleCase('fooo AnD bar');
    END;

    PROCEDURE TestReverseString@5128502();
    VAR
      l_String@5128500 : Text[1024];
      l_Result@5128503 : Integer;
    BEGIN
      l_String :=
        '1101101111011011110110111101101111011011110110111101101111011011' +
        '1101101111011011110110111101101111011011110110111101101111011011' +
        '1101101111011011110110111101101111011011110110111101101111011011' +
        '1101101111011011110110111101101111011011110110111101101111011011' +
        '1101101111011011110110111101101111011011110110111101101111011011' +
        '1101101111011011110110111101101111011011110110111101101111011011' +
        '1101101111011011110110111101101111011011110110111101101111011011' +
        '1101101111011011110110111101101111011011110110111101101111011011';

      IF STRLEN(l_String) <> 512 THEN
        ERROR('Wrong strlen');

      l_Result := TestReverseStringSub(l_String);

      IF l_Result <> 512 THEN
        ERROR('Error: Actual: ' + FORMAT(l_Result) + ' ' + ' Expected: 512');
    END;

    PROCEDURE TestReverseStringSub@5128521(p_String@5128500 : Text[1024]) r_Int : Integer;
    VAR
      l_StringMgt@5128501 : Codeunit 5128519;
    BEGIN
      p_String := ReverseString(p_String);
      EXIT(STRLEN(p_String));
    END;

    PROCEDURE ReverseString@5128509(p_In@5128502 : Text[1024]) Result : Text[1024];
    VAR
      i@5128500 : Integer;
      l_Length@5128501 : Integer;
    BEGIN
      l_Length := STRLEN(p_In) + 1;

      FOR i := 1 TO l_Length - 1 DO
        Result[i] := p_In[l_Length-i];
    END;

    PROCEDURE MiddleCase@1000000000(p_StringToConvert@1000000000 : Text[250]) ConvertedString : Text[250];
    BEGIN
      ConvertedString :=
        UPPERCASE(COPYSTR(p_StringToConvert, 1, 1)) +
        LOWERCASE(COPYSTR(p_StringToConvert, 2));
    END;

    BEGIN
    END.
  }
}

Since we don’t do anything evil here my only advice is to initialize every return parameter with a default value. This seems to fix the bug.


    PROCEDURE ReverseString@5128509(p_In@5128502 : Text[1024]) Result : Text[1024];
    VAR
      i@5128500 : Integer;
      l_Length@5128501 : Integer;
    BEGIN
      Result := ''; // Init return value – this fixes the issue!
      l_Length := STRLEN(p_In) + 1;

      FOR i := 1 TO l_Length - 1 DO
        Result[i] := p_In[l_Length-i];
    END;

Interestingly I didn’t find a smaller sample which reproduces this memory issue.

Tags: ,