Search code examples
pascalprogram-entry-pointfpcscriptedmain

Can Pascal units be compiled as executables?


I like my libraries to double as executables. The desired behavior is:

$ ./scriptedmain
Main: The meaning of life is: 42
$ ./test
Test: The meaning of life is: 42

How can I:

  • Get scriptedmain.p to compile into a scriptedmain binary?
  • Prevent test.p from running the code that's in scriptedmain.p's begin/end section?

scriptedmain.p:

unit ScriptedMain;
    interface

    function MeaningOfLife () : integer;

    implementation

    function MeaningOfLife () : integer;
    begin
        MeaningOfLife := 42
    end;
begin
    write('Main: The meaning of life is: ');
    writeln(MeaningOfLife())
end.

When I compile scriptedmain.p with fpc scriptedmain.p, no executable is created, because Pascal detects that it's a unit. But I want it to be an executable in addition to a library.

$ ./scriptedmain
-bash: ./scriptedmain: No such file or directory

test.p:

program Test;
uses
    ScriptedMain;
begin
    write('Test: The meaning of life is: ');
    writeln(MeaningOfLife())
end.

When I compile test.p with fpc test.p, the resulting executable combines the two begin/end declarations (NOT the desired behavior).

$ ./test 
Main: The meaning of life is: 42
Test: The meaning of life is: 42

Solution

  • Thanks to Ager and Zhirov in the Free Pascal mailing list, I was able to construct a working scripted main example with minimal hacks. Also posted on RosettaCode.

    Makefile:

    all: scriptedmain
    
    scriptedmain: scriptedmain.pas
        fpc -dscriptedmain scriptedmain.pas
    
    test: test.pas scriptedmain
        fpc test.pas
    
    clean:
        -rm test
        -rm scriptedmain
        -rm *.o
        -rm *.ppu
    

    scriptedmain.pas:

    {$IFDEF scriptedmain}
    program ScriptedMain;
    {$ELSE}
    unit ScriptedMain;
    interface
    function MeaningOfLife () : integer;
    implementation
    {$ENDIF}
        function MeaningOfLife () : integer;
        begin
            MeaningOfLife := 42
        end;
    {$IFDEF scriptedmain}
    begin
        write('Main: The meaning of life is: ');
        writeln(MeaningOfLife())
    {$ENDIF}
    end.
    

    test.pas:

    program Test;
    uses
        ScriptedMain;
    begin
        write('Test: The meaning of life is: ');
        writeln(MeaningOfLife())
    end.
    

    Example:

    $ make
    $ ./scriptedmain 
    Main: The meaning of life is: 42
    $ make test
    $ ./test 
    Test: The meaning of life is: 42