libFPC is a Delphi library that embeds the FreePascal Compiler (FPC), enabling your applications to compile Pascal source code at runtime. With libFPC, you can dynamically generate EXEs, DLLs, or other binary outputs without invoking external toolchains or requiring any manual steps from users.
Whether you're building a plugin architecture, scripting engine, code playground, or runtime compiler toolchainβlibFPC puts FreePascalβs power directly into your application.
- π οΈ Runtime compilation of Object Pascal source code using FPC
- βοΈ Generate EXEs, DLLs, or custom binaries from within your Delphi app
- π True native compilation with full control over the build process
- π§ Supports build modes, version info, output paths, icons, and more via inline project directives
- β‘Can output EXE/DLL also to memory for in-memory dynamic code generation and execution
- 𧩠Ideal for scripting, plugin frameworks, live coding environments, and automation tools
Getting started with libFPC
is quick and easy:
- Download
libFPC
from the github. You can also fork the repo if you wish to make contributions. - Add
libFPC
to youruses
clause. - Start Coding! libFPC brings the power of the FreePascal compiler into the hands of your application.
β Tested using Delphi 12.3, Windows 11, Version 24H2
β οΈ Note: Compilation will fail if the project is located in a folder with spaces in its path. Please ensure the full path to the project directory does not contain any spaces.
Hereβs how to compile a Pascal source file into an executable from your Delphi code:
var
LlibFPC: TLibFPC;
begin
// Create an instance of TLibFPC
LlibFPC := TLibFPC.Create();
try
// Assign the Pascal source file to compile
LlibFPC.SetProjectSource(psFile, 'test.pas');
// Compile the file and display the result
if LlibFPC.Compile() then
WriteLn('Success!')
else
WriteLn('Failed!');
finally
LlibFPC.Free();
end;
end;
Here's how to compile a Pascal source file to a EXE in memory and run it:
var
LlibFPC: TLibFPC;
LExitCode: DWORD;
begin
// Create an instance of TLibFPC
LlibFPC := TLibFPC.Create();
try
// Assign the Pascal source file to compile
LlibFPC.SetProjectSource(psFile, 'test.pas');
// Set the output path to the in-memory cache directory
LlibFPC.SetOutputPathToCacheDir();
// Compile the file and check for success
if LlibFPC.Compile() then
begin
WriteLn('Created EXE...'); // Notify EXE creation
// Attempt to run EXE from cache with argument '7'
WriteLn('Running EXE...');
if LlibFPC.RunEXE('7', LExitCode) then
begin
WriteLn('Ran EXE, ExitCode: ', LExitCode); // Output the resulting exit code
end;
end
else
begin
WriteLn('Failed!'); // Notify compile failure
end;
finally
// Free the instance to release resources
LlibFPC.Free();
end;
end;
Here's how to compile a Pascal source file to a DLL in memory and load it:
var
LlibFPC: TLibFPC;
LHandle: THandle;
Test: procedure();
begin
// Create an instance of TLibFPC
LlibFPC := TLibFPC.Create();
try
// Assign the Pascal source file to compile
LlibFPC.SetProjectSource(psFile, 'test.pas');
// Set the output path to the in-memory cache directory
LlibFPC.SetOutputPathToCacheDir();
// Compile the file and check for success
if LlibFPC.Compile() then
begin
WriteLn('Created DLL...'); // Notify DLL creation
// Attempt to load the DLL from cache
LHandle := LlibFPC.LoadDLL();
if LHandle <> 0 then
begin
WriteLn('Loading DLL...'); // Notify DLL load
// Resolve the 'Test' exported procedure
Test1 := GetProcAddress(LHandle, 'Test');
if Assigned(Test1) then
begin
WriteLn('Extracted and running export...'); // Notify success
Test1(); // Execute the export
end;
FreeLibrary(LHandle); // Unload the DLL after use
WriteLn('Unloaded DLL...');
end;
end
else
begin
WriteLn('Failed!'); // Notify compile failure
end;
finally
// Free the instance to release resources
LlibFPC.Free();
end;
Here's how to compile and use libFPC to compile code from a string:
const
CCode =
'''
library source;
uses
sysutils,
windows;
procedure Test01();
begin
MessageBox(0, 'This is exported routine Test01()', 'DLL compiled from code in string', MB_OK);
end;
exports
Test01;
end.
''';
var
LlibFPC: TLibFPC;
LHandle: THandle;
Test1: procedure();
begin
// Create an instance of TLibFPC
LlibFPC := TLibFPC.Create();
try
// Assign the Pascal source string to compile
LlibFPC.SetProjectSource(psString, CCode);
// Set the output path to the in-memory cache directory
LlibFPC.SetOutputPathToCacheDir();
// Compile the file and check for success
if LlibFPC.Compile() then
begin
WriteLn('Created DLL...'); // Notify DLL creation
// Attempt to load the DLL from cache
LHandle := LlibFPC.LoadDLL();
if LHandle <> 0 then
begin
WriteLn('Loading DLL...'); // Notify DLL load
// Resolve the 'Test01' exported procedure
Test1 := GetProcAddress(LHandle, 'Test01');
if Assigned(Test1) then
begin
WriteLn('Extracted and running export...'); // Notify success
Test1(); // Execute the export
end;
FreeLibrary(LHandle); // Unload the DLL after use
WriteLn('Unloaded DLL...');
end;
end
else
begin
WriteLn('Failed!'); // Notify compile failure
end;
finally
// Free the instance to release resources
LlibFPC.Free();
end;
Pause(); // Wait for user input before exit
end;
Here's how to do interop between the host and in-memory DLL using interfaces:
type
{ IContext }
IContext = interface
['{867E4D05-FC90-4D03-A981-E3FD82EF1154}']
function GetVersion(): WideString; stdcall;
function GetDescription(): WideString; stdcall;
procedure Test1(const AValue: Int32); stdcall;
procedure Test2(const AValue: WideString); stdcall;
end;
{ TContext }
TContext = class(TNoRefCountObject, IContext)
public
function GetVersion(): WideString; stdcall;
function GetDescription(): WideString; stdcall;
procedure Test1(const AValue: Int32); stdcall;
procedure Test2(const AValue: WideString); stdcall;
end;
{ TContext }
function TContext.GetVersion(): WideString;
begin
Result := '1.0.0';
end;
function TContext.GetDescription(): WideString;
begin
Result := 'Testing interop between host and DLL';
end;
procedure TContext.Test1(const AValue: Int32); stdcall;
begin
MessageBox(0, PWideChar(Format('Int32: %d', [AValue])), 'Host EXE', MB_OK);
end;
procedure TContext.Test2(const AValue: WideString); stdcall;
begin
MessageBox(0, PWideChar(Format('WideString: %s', [AValue])), 'Host EXE', MB_OK);
end;
procedure Interop();
const
CCode =
'''
library source;
uses
sysutils,
windows;
type
{ IContext }
IContext = interface
['{867E4D05-FC90-4D03-A981-E3FD82EF1154}']
function GetVersion(): WideString; stdcall;
function GetDescription(): WideString; stdcall;
procedure Test1(const AValue: Int32); stdcall;
procedure Test2(const AValue: WideString); stdcall;
end;
procedure Run(const AContext: IContext); stdcall;
begin
MessageBoxW(0, PWideChar(UnicodeFormat('Version: %s', [AContext.GetVersion()])), 'Context DLL', MB_OK);
AContext.Test1(2025);
AContext.Test2('This is a string from Context DLL');
end;
exports
Run;
end.
''';
var
LlibFPC: TLibFPC;
LHandle: THandle;
LContext: TContext;
Run: procedure(const AContext: IContext); stdcall;
begin
LContext := TContext.Create(); // Create interface implementation
try
// Create an instance of TLibFPC
LlibFPC := TLibFPC.Create();
try
// Assign the Pascal source string to compile
LlibFPC.SetProjectSource(psString, CCode);
// Set the output path to the in-memory cache directory
LlibFPC.SetOutputPathToCacheDir();
// Disable debug mode for this build
LlibFPC.SetDebugMode(False);
// Compile the file and check for success
if LlibFPC.Compile() then
begin
WriteLn('Created DLL...'); // Notify DLL creation
// Attempt to load the DLL from cache
LHandle := LlibFPC.LoadDLL();
if LHandle <> 0 then
begin
WriteLn('Loading DLL...'); // Notify DLL load
// Resolve the 'Run' exported procedure
Run := GetProcAddress(LHandle, 'Run');
if Assigned(Run) then
begin
WriteLn('Extracted and running export...'); // Notify success
Run(LContext); // Call DLL with host interface
end;
FreeLibrary(LHandle); // Unload the DLL
WriteLn('Unloaded DLL...');
end;
end
else
begin
WriteLn('Failed!'); // Notify compile failure
end;
finally
LlibFPC.Free(); // Release compiler instance
end;
finally
LContext.Free(); // Release interface instance
end;
end;
Here's how to compile and use libFPC as a command-line utility:
var
LlibFPC: TlibFPC;
begin
// Create an instance of TlibFPC
LlibFPC := TlibFPC.Create();
try
// Invoke the CLI method to show command-line help or perform CLI action
// This example should be run from the commandline.
LlibFPC.CLI();
finally
// Free the instance to release memory and resources
LlibFPC.Free();
end;
end;
libFPC supports special inline directives in the main Pascal source file to control project settings, compilation behavior, and output metadata. These must appear at the top of the file, in the format shown below:
{==================== [PROJECT DIRECTIVES] =================================}
{@APPTYPE CONSOLE} // CONSOLE|GUI
{@OUTPUTPATH ".\"}
{.@EXEICON ".\main.ico"} // remove "." before @, set path to icon
{@SEARCHPATH ".\"} // path1;path2;path3 seperated by ";"
{@BUILDCONFIG DEBUG} // DEBUG|RELEASE
{@ADDVERSIONINFO NO} // YES|NO
{@MAJORVER 1} // valid numerical value 0-n
{@MINORVER 0} // valid numerical value 0-n
{@PATCHVER 0} // valid numerical value 0-n
{@PRODUCTNAME "Project Name"}
{@DESCRIPTION "Your Project"}
{@COMPANYNAME "Your Company"}
{@COPYRIGHT "Copyright Β© 2025-present Your Companyβ’"}
{===========================================================================}
program test;
uses
sysutils;
var
i: integer;
begin
for i := 1 to 20 do
writeln(i);
end.
These directives allow you to define everything from application type and versioning to icon resources and search pathsβensuring clean and controlled builds right from your source code.
- π Report Issues: GitHub Issue Tracker
- π¬ Join the Community: Forum | Discord
- π Learn Delphi: Learn Delphi
We welcome contributions to libFPC! π
- π Report Bugs β Help improve
libFPC
by submitting issues. - β¨ Suggest Features β Share ideas to enhance its functionality.
- π§ Submit Pull Requests β Improve the codebase and add features.
libFPC is distributed under the BSD-3-Clause License, allowing redistribution and modification in both source and binary forms. See the LICENSE for details.
Your support keeps libFPC evolving! If you find this library useful, please consider sponsoring the project. Every contribution helps drive future enhancements and innovations.
- β Star the repo β Show your appreciation.
- π’ Share with your network β Spread the word.
- π Report bugs β Help improve
libFPC
. - π§ Submit fixes β Contribute by fixing issues.
- π‘ Suggest features β Help shape its future.
π Every contribution makes a difference β thank you for being part of the journey!
π₯ libFPC β FreePascal in your pocket!