Getting Started
Requirements
- Delphi 12+ (Athens) — required for multi-line string literals (
'''), which five shipped units use - Windows — Win32 and Win64. No package declares a Linux target
Installation
DAF is distributed as source: a set of Delphi packages (.dpk). Every release on GitHub carries it as a zip, Source code (zip) under the release’s Assets: https://github.com/Delphi-DAF/dafce/releases. Unzip it anywhere, a path with spaces included, and run everything below from that folder. Or clone the repo:
git clone https://github.com/Delphi-DAF/dafce.gitBuilding the packages
Every project takes its output paths from src/paths.optset, which derives all of them from PRJ_OUT_DIR. BDS.bat sets that variable from its own location, so the IDE is started through it:
BDS startThat opens RAD Studio with the project’s environment and loads src/DAFGroup.groupproj. An IDE started any other way leaves PRJ_OUT_DIR undefined, and every output path is then built from an empty value. BDS inspect prints what is set; the project’s own variables live in project.cmd, and per-machine overrides go in a %COMPUTERNAME%.project.cmd beside it.
BDS.bat keeps each folder’s IDE profile under HKCU\Software\Embarcadero\DAF\<folder name>. Deleting the folder leaves that key behind, and reg delete "HKCU\Software\Embarcadero\DAF\<folder name>" /f removes it.
From the command line there is nothing to set up — both scripts call BDS.bat themselves:
buildall.bat all
runtests.bat allWith no options, buildall.bat builds the framework in Debug and Release, then the samples and the specs in Debug. These options build only part of it, and they combine:
| Option | Builds |
|---|---|
--framework |
the framework, in Debug and Release |
--samples |
the framework in Debug, then the samples |
--specs |
the framework in Debug, then the specs |
runtests.bat runs every suite the specs group declares. Both take Win32, Win64 or all, and buildall.bat also takes --clean, which deletes out/ first.
Choosing the Delphi version
project.cmd selects Delphi 13 (PRJ_IDE=D370). Delphi 12 is PRJ_IDE=D290, and either of these selects it without editing project.cmd:
- a
%COMPUTERNAME%.project.cmdnext toproject.cmd, holding the lineset "PRJ_IDE=D290"; set "PRJ_IDE=D290"on its own line in the console, before runningbuildall.bat.
With Delphi 12, buildall.bat --framework --samples builds the framework and the samples. The specs of the VCL module need Delphi 13, so --specs, and buildall.bat with no options, end in BUILD FAILED.
When the version asked for is not installed, BDS.bat builds nothing, exits with code 2 and lists the installed versions with the PRJ_IDE of each.
Using what is built
Add out\Win32\Release\lib or out\Win64\Release\lib to your project’s search path, and the DAF units your program uses compile against it. out\<Platform>\Debug\lib holds the Debug build of the same units, for debugging.
Using the source directly
Adding the src/[Module]/ paths you need to your own project’s search path compiles those units with your project’s output paths, so none of the above applies. Every module is self-contained.
Your first DAF application
This is the QuickStart sample, verbatim — it is the file the compiler reads, and a check compares the two, so what you copy from here is what builds:
program QuickStart;
{$APPTYPE CONSOLE}
uses
Daf.Extensions.DependencyInjection,
Daf.CommandLine,
Daf.Configuration.CommandLine,
Daf.Cli;
// El ejemplo de entrada de una herramienta de linea de comandos: el builder, la accion de la raiz
// escrita en el sitio, los argumentos que recibe la gramatica, y el codigo de salida que devuelve
// esa accion.
//
// QuickStart -> 0, saluda
// QuickStart --help -> 0, la ayuda
//
// Como se anade un servicio esta en el sample ServiceWiring, que es el nivel siguiente.
begin
var B := TCliApplication.CreateBuilder(CommandLineConfig.ProcessArgs);
B.SetAction(
function(const Services: IServiceProvider; const Parsed: IParseResult): Integer
begin
Writeln('Hola desde DAF');
Result := TCliExit.Ok;
end);
B.ParseArgs(CommandLineConfig.ProcessArgs);
ExitCode := B.Build.Run;
end.Three things about the shape, all of them easy to get wrong:
- The builder is held in a variable, not chained, and registration happens where it is written: there is no deferred
ConfigureServicescallback.TCliApplication.CreateBuilderreturns anICliApplicationBuilder, whoseBuildreturns theICliApplicationwhoseRunis the exit code. - The arguments go to two calls, and each one feeds one reader:
CreateBuilderadds them to the configuration, andParseArgshands them to the grammar, which answers--helpwith no option declared. WithoutParseArgs, the grammar parses an empty invocation. Daf.Extensions.DependencyInjectionis in theusesalthough nothing is registered: the signature of the action namesIServiceProvider, and Delphi needs the type visible to write it.
Adding a service
The ServiceWiring sample is the same program with one service registered and resolved:
program ServiceWiring;
{$APPTYPE CONSOLE}
{$STRONGLINKTYPES ON}
uses
System.SysUtils,
Daf.Extensions.DependencyInjection,
Daf.CommandLine,
Daf.Configuration.CommandLine,
Daf.Cli,
ServiceWiring.Greeter in 'ServiceWiring.Greeter.pas';
// Como entra un servicio en una herramienta de linea de comandos: se registra en el builder, que
// ya esta vivo, y la accion lo resuelve del proveedor que recibe.
//
// ServiceWiring -> 0, saluda a traves del servicio
begin
var B := TCliApplication.CreateBuilder(CommandLineConfig.ProcessArgs);
B.Services.AddSingleton<IGreeter, TGreeter>;
B.SetAction(
function(const Services: IServiceProvider; const Parsed: IParseResult): Integer
begin
Writeln(Services.GetRequiredService<IGreeter>.Greet);
Result := TCliExit.Ok;
end);
B.ParseArgs(CommandLineConfig.ProcessArgs);
ExitCode := B.Build.Run;
end.Servicesanswers before anything is built, soAddSingletontakes effect where it is written, and the action declared withSetActionresolves from the provider it receives.- The service lives in its own unit. A type declared in the
.dpris not in theinterfacesection of any unit, and resolving it fails at run time withENonPublicType. That is also why the program carries{$STRONGLINKTYPES ON}: without it the linker drops the RTTI the container resolves by.
There is a second, lower-level entry point — THostBuilder from the Hosting module — for when you want the host without the application layer around it, with Hosting/samples/HostQuickStart as its own entry example. The application family guide says what each one gives you.
Module map
| If you need… | Use module |
|---|---|
| DI container | DependencyInjection |
| App lifecycle | Hosting |
| Config files / env vars | Configuration |
| Structured logging | Logging |
| Commands / queries | MediatR |
| BDD specs / tests | MiniSpec |
| NLog-style logging | NNLog |
| Smart pointers / RTTI | Commons |
| HTTP server / routing | Web |
Next steps
- Read the Architecture overview
- Browse the Module reference
- Check the
samples/folder in the repo for runnable examples