Monday, September 26, 2011

MSBuild Properties in Delphi

From Delphi 2007 forward the Delphi IDE relies on MSBuild to manage project dependencies, compiler options, etc. If you're a Delphi developer you are likely familar with the $(..) notation used for accessing values exposed by the build system. With a little digging you'll discover some of these values are environment variables and that you can modify them or create your own. If you take a closer look you'll see there are other values accessed using $(..) that are not environment variables.

The $(..) notation comes from the make syntax. It is used for referencing variables defined in a make script. Prior versions of Delphi relied of Borland's variation of make. MSBuild (as well was most other xml based build scripts) inherited certain design ideas from make. The $(..) notation is one of them. In MSBuild variables are called properties. Delphi's .dproj files are simply MSBuild files with a little customization.

MSBuild treats environment variables and property values within a build script the same. This is why you can access environment variables using $(..). You can also create enviroment varibles that override properties in a project file. So what properties are defined?

Take a look at BDS\Bin\*.Targets. Every .dproj file you create includes one or more of these .targets files. Every option passed to the compiler has an associated property that can be accessed at various stages of the build process. You can take advantage of these for setting the output path for generated files and writing pre and post build scripts. Be careful not to modify the supplied targets files. You are free to define your own targets files and incorporate them into your projects. I may write a separate article about custom targets at a later time.

Example
Its common to want debug and release artifacts to output to separate folders. You can edit each configuration to output to ".\debug" and ".\release" or you can just edit the base configuration instead:

Output directory = $(Config)

Now any configuration inheriting from the base configuration will use the Config property when writing the executable to disk. You can create additional configurations that inherit from Base and they will override the Config property with their name.

Tuesday, August 30, 2011

The "With" Statement

Regardless of which language you use the controversial with statement's intended purpose is to reduce repetitive typing. Some developers think its the greatest thing since sliced bread. Others are certain it was created by Satan. Let's examine a few languages that support it.

Pascal

The with keyword was present in the original Pascal language, developed in 1968-70 and published in Acta Informatica, Vol. 1 in 1971. I've found no description of a with statement in Wirth's prior language projects or in any of the languages which influenced Pascal's design. Though admittedly, my research into its origins is limited. I'll assume for the moment that it was an original idea.
The with statement was originally intended to only be used with record variables. Here's a modification of a sample record from the original language description.

type
  Date = record
    day: 1..31;
    month: 1..12;
    year: 0..9999;
end;

var
 today: Date;
The two following blocks are equivalent:
begin
  with today do
  begin
    day := 25;
    month := 12;
    year := 2011;
  end;
end;

begin
  today.day := 25;
  today.month := 12;
  today.year := 2011;
end;

Object oriented descendants of Pascal added support for classes and interfaces. The main criticism of with is that it introduces the possibility of ambiguous references:
var  day: integer;  
  today: Date;

begin
  with today do
  begin
    day := 25;
    month := 12;
    year := 2011;
  end;
end;


Which day is being assigned to? Intuition may lead you to think that it is the one within the scope of the date record and you'd be right, at least in this case. However, when this block is in the body of an object method, which references multiple other objects in the same with statement it isn't so clear.

Where things really fall apart is when you are referencing a local variable from within the body of a with statement and a field or property with the same name is added to the definition of the object or record referenced by the with statement. Suddenly code that was working flawlessly fails and all you did was add a field to a class definition. Heck, you haven't even referenced the new field in your code yet... or so you think.

So that's Pascal's implementation. I would recommend you avoid using it whenever possible and use great care if you find it necessary to use it. What other languages support the with statement? Let's take a look.

VisualBasic


With today
  .day = 25
  .month = 12
  .year = 2011
End With

Here we see dot notation preceding the fields. This makes it apparent which identifiers are fields and which are not. This syntax was carried forward into VB.NET as well. There is still the possibility of ambiguity if you write a nested with statement. According to the documentation the compiler would only see the fields of the object referenced by the inner with statement until the statement's end. A novice developer could easily overlook this.

JavaScript

JavaScript's with statement functions more or less the same as Pascal's and JS developer's opinions of it are just as polarized as Pascal developer's. ECMAScript 5th Edition "Strict" mode forbids its use and many implementations now emit a warning that it has been deprecated and/or suggest an alternative.

D

Digital Mars' D language supports a with statement with behavior similar to Pascal's with one notable exception: local identifiers and identifiers within the scope of the with statement cannot have the same name. This results in a compilation error, effectively preventing the ambiguity caused by the introduction of new object members.


struct Date
{
    int day;
    int month;
    int year;
}

void main()
{
    int day;
    Date today;
    with (today)
    {
        day++;  // error, shadows the local day declaration
    }
}

Languages with similar features

These languages have similar features but the purpose and/or syntax is too different for them to be consider the same as with.
  • Smalltalk cascades - multiple messages can be sent to the same object separated by a semicolon
  • C#
    • using statement - encapsulates the more verbose try..except..finally syntax when working with implementors of IDisposable
    • object and collection initializers - a more concise syntax for setting object properties at creation time
  • Python with statement - similar to C#'s using statement, it encapsulates the try...except...finally pattern into a less verbose syntax.

Fluent Interfaces are another programming style that can be used to accomplish the same goal as with statements. They use varying combinations of method chaining, nested functions and object scoping along with careful method naming to produce concise, readable statements. The Date record could be rewritten as:
type
  Date = class
  private
    FDay: 1..31;
    FMonth: 1..12;
    FYear: 0..9999;
  public
    function Day(Day: integer): Date;
    function Month(Month: integer): Date;
    function Year(Year: integer): Date;
  end;

function Date.Day(Day: integer): Date;
begin
  self.FDay := Day;
  Result := Self;
end;

function Date.Month(Month: integer): Date;
begin
  self.FMonth := Month;
  Result := Self;
end;

function Date.Year(Year: integer): Date;
begin
  self.FYear := Year;
  Result := Self;
end;

Then in place of the with statement you could write:
begin
  today := Date.Create
               .Day(25)
               .Month(12)
               .Year(2012);
end.