Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .github/workflows/ci/ppc64-be-build.sh
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ docker run --rm --platform linux/ppc64 \
-e FPC_TARBALL_TARGET \
-e MAKE_BUILD_BACKEND \
-e MAKE_PACKAGE_SCOPE \
-e MAKE_RUN_BENCHMARK \
-e LAZARUS_BRANCH \
-e LAZARUS_REPO \
-e CI_DEBUG \
Expand Down
167 changes: 153 additions & 14 deletions .github/workflows/make.pas
Original file line number Diff line number Diff line change
Expand Up @@ -224,23 +224,32 @@ TMakeRunner = class
FTargetOs: string;
FErrorCount: Integer;
FUseColor: Boolean;
// Selected via MAKE_RUN_BENCHMARK (defaults to False when unset). When true,
// builds and runs console benchmark projects under BenchmarkTargetFolder
// after the test suite completes.
FRunBenchmark: Boolean;
FGraph: TPackageGraph;
function ParseBackendEnv: TBuildBackend;
function ParsePackageScopeEnv: TPackageScope;
function ParseBoolEnv(const AName: string; ADefault: Boolean): Boolean;
function ParseDefinesEnv: TStringList;
procedure InitEnvironment;
procedure UpdateSubmodules;
procedure InstallDependencies;
procedure BuildDiscoveredPackagesFpc;
function CollectProjectRequiredNames: TStringList;
procedure CollectRequiredNamesFromDirectory(const ADirectory: string;
AResult: TStringList);
procedure BuildAllProjects;
procedure RunBenchmarkProjects;
procedure BuildGuiProject(const ALpiPath: string);
function BuildProject(const ALpiPath: string): string;
function BuildProjectWithLazbuild(const APath: string): string;
function BuildProjectWithFpc(const APath: string): string;
function ExtractBinaryFromBuildLog(const AOutput, AFallback: string): string;
function IsGUIProject(const ALpiPath: string): Boolean;
function IsTestProject(const ALpiPath: string): Boolean;
function IsBenchmarkProject(const ALpiPath: string): Boolean;
procedure RunTestProject(const APath: string);
procedure RunSampleProject(const APath: string);
procedure InitSslForDownloads;
Expand All @@ -267,6 +276,7 @@ TMakeRunner = class
AStreamToStderr: Boolean; out AOutput: string): Boolean; overload;
function RepoRoot: string;
function TargetDirectory: string;
function BenchmarkDirectory: string;
procedure ForEachLpkInDir(const ARoot: string; ACallback: TLpkPathProc);
procedure RunBuiltBinary(const ABinaryPath: string;
const AArgs: array of string; const AFailMessage: string);
Expand Down Expand Up @@ -295,7 +305,8 @@ TMakeRunner = class
// ---------------------------------------------------------------------------

const
Target: string = 'HashLib.Tests';
TestTargetFolder: string = 'HashLib.Tests';
BenchmarkTargetFolder: string = 'HashLib.Benchmark';

CSI_Reset = #27'[0m';
CSI_Red = #27'[31m';
Expand Down Expand Up @@ -510,7 +521,8 @@ class function TLazXml.CollectFileSourceDirs(const AContent, APkgDir, ATargetCpu

class function TLazXml.ParseCompilerOptions(const AContent: string): TLazCompilerOptions;
var
Block: string;
Block, AfterProjectOptions: string;
P: Integer;
begin
Result.CompilerMode := 'delphi';
Result.OptLevel := '2';
Expand All @@ -520,7 +532,18 @@ class function TLazXml.ParseCompilerOptions(const AContent: string): TLazCompile
Result.UnitPaths := '';
Result.UnitOutputDirTemplate := 'lib\$(TargetCPU)-$(TargetOS)';

Block := ExtractBlock(AContent, 'CompilerOptions');
// Lazarus stores the active compiler options in a root-level <CompilerOptions>
// block (after </ProjectOptions>). BuildModes can contain additional
// <CompilerOptions> sections; prefer the root block when present.
Block := '';
P := Pos('</ProjectOptions>', AContent);
if P > 0 then
begin
AfterProjectOptions := Copy(AContent, P, Length(AContent) - P + 1);
Block := ExtractBlock(AfterProjectOptions, 'CompilerOptions');
end;
if Block = '' then
Block := ExtractBlock(AContent, 'CompilerOptions');
if Block = '' then
Exit;

Expand Down Expand Up @@ -1330,6 +1353,7 @@ constructor TMakeRunner.Create;
FBackendResolved := False;
FPackageScope := TPackageScope.Required;
FErrorCount := 0;
FRunBenchmark := False;
// Honor the NO_COLOR convention (https://no-color.org): any value disables
// ANSI colors. GitHub Actions renders ANSI in its log viewer, so default on.
FUseColor := GetEnvironmentVariable('NO_COLOR') = '';
Expand Down Expand Up @@ -1417,6 +1441,20 @@ function TMakeRunner.ParsePackageScopeEnv: TPackageScope;
raise Exception.CreateFmt('unknown MAKE_PACKAGE_SCOPE: "%s"', [Env]);
end;

function TMakeRunner.ParseBoolEnv(const AName: string; ADefault: Boolean): Boolean;
var
Env: string;
begin
Env := LowerCase(Trim(GetEnvironmentVariable(AName)));
if Env = '' then
Exit(ADefault);
if (Env = '1') or (Env = 'true') or (Env = 'yes') then
Exit(True);
if (Env = '0') or (Env = 'false') or (Env = 'no') then
Exit(False);
raise Exception.CreateFmt('unknown %s: "%s"', [AName, Env]);
end;

// Parse MAKE_DEFINES into a deduped list of conditional-define names. Accepts a
// space/comma/semicolon-separated list; each entry must be a valid identifier
// ([A-Za-z_][A-Za-z0-9_]*) so it is a safe -d argument in either backend.
Expand Down Expand Up @@ -1601,7 +1639,7 @@ function TMakeRunner.RepoRoot: string;
Candidate := Seeds[I];
while Candidate <> '' do
begin
DemoDir := IncludeTrailingPathDelimiter(ConcatPaths([Candidate, Target]));
DemoDir := IncludeTrailingPathDelimiter(ConcatPaths([Candidate, TestTargetFolder]));
if DirectoryExists(DemoDir) then
Exit(ExcludeTrailingPathDelimiter(Candidate));
Parent := ExpandFileName(IncludeTrailingPathDelimiter(Candidate) + '..');
Expand All @@ -1615,7 +1653,12 @@ function TMakeRunner.RepoRoot: string;

function TMakeRunner.TargetDirectory: string;
begin
Result := IncludeTrailingPathDelimiter(ConcatPaths([RepoRoot, Target]));
Result := IncludeTrailingPathDelimiter(ConcatPaths([RepoRoot, TestTargetFolder]));
end;

function TMakeRunner.BenchmarkDirectory: string;
begin
Result := IncludeTrailingPathDelimiter(ConcatPaths([RepoRoot, BenchmarkTargetFolder]));
end;

procedure TMakeRunner.ForEachLpkInDir(const ARoot: string;
Expand Down Expand Up @@ -1690,6 +1733,12 @@ procedure TMakeRunner.InitEnvironment;
Log(CSI_Yellow, 'defines: ' + FDefines.DelimitedText)
else
Log(CSI_Yellow, 'defines: (none)');

FRunBenchmark := ParseBoolEnv('MAKE_RUN_BENCHMARK', False);
if FRunBenchmark then
Log(CSI_Yellow, 'run benchmark: true')
else
Log(CSI_Yellow, 'run benchmark: false');
end;

procedure TMakeRunner.UpdateSubmodules;
Expand Down Expand Up @@ -1993,20 +2042,17 @@ procedure TMakeRunner.BuildDiscoveredPackagesFpc;
end;
end;

// Union of RequiredPackages across the buildable (non-GUI) projects under the
// target directory. Drives the fpc backend's 'required' scope so it compiles
// only the dependency closure those projects need.
function TMakeRunner.CollectProjectRequiredNames: TStringList;
procedure TMakeRunner.CollectRequiredNamesFromDirectory(const ADirectory: string;
AResult: TStringList);
var
List: TStringList;
Each: string;
Proj: TLpiProject;
I: Integer;
begin
Result := TStringList.Create;
Result.Sorted := True;
Result.Duplicates := dupIgnore;
List := TProjectFiles.FindAll(TargetDirectory, '*.lpi');
if not DirectoryExists(ADirectory) then
Exit;
List := TProjectFiles.FindAll(ADirectory, '*.lpi');
try
for Each in List do
begin
Expand All @@ -2016,7 +2062,7 @@ function TMakeRunner.CollectProjectRequiredNames: TStringList;
try
if Proj.IsValid then
for I := 0 to Proj.RequiredPackageNames.Count - 1 do
Result.Add(Proj.RequiredPackageNames[I]);
AResult.Add(Proj.RequiredPackageNames[I]);
finally
Proj.Free;
end;
Expand All @@ -2026,6 +2072,19 @@ function TMakeRunner.CollectProjectRequiredNames: TStringList;
end;
end;

// Union of RequiredPackages across the buildable (non-GUI) projects under the
// test and (when enabled) benchmark directories. Drives the fpc backend's
// 'required' scope so it compiles only the dependency closure those projects need.
function TMakeRunner.CollectProjectRequiredNames: TStringList;
begin
Result := TStringList.Create;
Result.Sorted := True;
Result.Duplicates := dupIgnore;
CollectRequiredNamesFromDirectory(TargetDirectory, Result);
if FRunBenchmark then
CollectRequiredNamesFromDirectory(BenchmarkDirectory, Result);
end;

function TMakeRunner.ExtractBinaryFromBuildLog(const AOutput,
AFallback: string): string;
var
Expand Down Expand Up @@ -2190,6 +2249,26 @@ function TMakeRunner.IsTestProject(const ALpiPath: string): Boolean;
Result := Pos('consoletestrunner', Content) > 0;
end;

function TMakeRunner.IsBenchmarkProject(const ALpiPath: string): Boolean;
var
LprPath, Content, ProjectBaseName: string;
begin
Result := False;
if not SameText(ExtractFileExt(ALpiPath), '.lpi') then
Exit;

ProjectBaseName := ChangeFileExt(ExtractFileName(ALpiPath), '');
if Pos('BenchmarkConsole', ProjectBaseName) > 0 then
Exit(True);

LprPath := ChangeFileExt(ALpiPath, '.lpr');
if not FileExists(LprPath) then
Exit;

Content := TLazXml.ReadFile(LprPath);
Result := Pos('TPerformanceBenchmark.Run', Content) > 0;
end;

procedure TMakeRunner.RunTestProject(const APath: string);
var
BinaryPath: string;
Expand Down Expand Up @@ -2271,13 +2350,73 @@ procedure TMakeRunner.BuildAllProjects;
end;
end;

procedure TMakeRunner.RunBenchmarkProjects;
var
List: TStringList;
Each, BinaryPath: string;
begin
if not FRunBenchmark then
begin
Log(CSI_Yellow, 'benchmarks: skipped (MAKE_RUN_BENCHMARK=false)');
Exit;
end;

if not DirectoryExists(BenchmarkDirectory) then
begin
IncError;
Log(CSI_Red, 'benchmark directory missing: ' + BenchmarkDirectory);
Exit;
end;

Log(CSI_Cyan, 'using benchmark directory: ' + BenchmarkDirectory);
List := TProjectFiles.FindAll(BenchmarkDirectory, '*.lpi');
try
for Each in List do
begin
if IsGUIProject(Each) then
begin
if not LclSupported then
begin
Log(CSI_Yellow, 'skip GUI benchmark project ' + Each);
Continue;
end;
BuildGuiProject(Each);
Continue;
end;

if not IsBenchmarkProject(Each) then
begin
Log(CSI_Yellow, 'skip non-benchmark project ' + Each);
Continue;
end;

BinaryPath := BuildProject(Each);
if BinaryPath = '' then
Continue;
try
Log(CSI_Yellow, 'run benchmark ' + BinaryPath);
RunBuiltBinary(BinaryPath, [], 'benchmark failed: ' + BinaryPath);
except
on E: Exception do
begin
IncError;
Log(CSI_Red, E.ClassName + ': ' + E.Message);
end;
end;
end;
finally
List.Free;
end;
end;

function TMakeRunner.Execute: Integer;
begin
InitEnvironment;
Log(CSI_Cyan, 'using target directory: ' + TargetDirectory);
UpdateSubmodules;
InstallDependencies;
BuildAllProjects;
RunBenchmarkProjects;
ReportSummary;
Result := FErrorCount;
end;
Expand Down
13 changes: 9 additions & 4 deletions .github/workflows/make.yml
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,9 @@ env:
# single: MAKE_DEFINES: WITH_GTK2_IM
# multiple: MAKE_DEFINES: 'WITH_GTK2_IM DEBUG_LOG USE_CTHREADS'
MAKE_DEFINES: ''
# When true, make.pas builds and runs console benchmarks after the test suite.
# Override per job below, like MAKE_BUILD_BACKEND.
MAKE_RUN_BENCHMARK: false
# Set by the workflow_dispatch "debug" toggle; '1' enables `set -x` tracing in
# install-fpc-lazarus.sh, '0' otherwise. Forwarded into the sandboxed jobs below.
CI_DEBUG: ${{ github.event.inputs.debug == 'true' && '1' || '0' }}
Expand Down Expand Up @@ -192,6 +195,7 @@ jobs:
LAZARUS_REPO: ${{ env.LAZARUS_REPO }}
MAKE_BUILD_BACKEND: ${{ env.MAKE_BUILD_BACKEND }}
MAKE_PACKAGE_SCOPE: ${{ env.MAKE_PACKAGE_SCOPE }}
MAKE_RUN_BENCHMARK: ${{ env.MAKE_RUN_BENCHMARK }}
CI_DEBUG: ${{ env.CI_DEBUG }}
run: bash .github/workflows/ci/arm32-run.sh

Expand All @@ -217,6 +221,7 @@ jobs:
FPC_TARBALL_TARGET: ${{ fromJSON(needs.setup.outputs.target_map)['linux-powerpc64-be'].fpc_tarball_target }}
MAKE_BUILD_BACKEND: ${{ env.MAKE_BUILD_BACKEND }}
MAKE_PACKAGE_SCOPE: ${{ env.MAKE_PACKAGE_SCOPE }}
MAKE_RUN_BENCHMARK: ${{ env.MAKE_RUN_BENCHMARK }}
run: bash .github/workflows/ci/ppc64-be-build.sh

freebsd:
Expand All @@ -238,7 +243,7 @@ jobs:
FPC_TARBALL_TARGET: ${{ fromJSON(needs.setup.outputs.target_map)['freebsd'].fpc_tarball_target }}
FREEBSD_INSTALL_MODE: interim
with:
envs: FPC_VERSION FPC_TARGET FPC_TARBALL_TARGET LAZARUS_BRANCH LAZARUS_REPO MAKE_BUILD_BACKEND MAKE_PACKAGE_SCOPE FREEBSD_INSTALL_MODE CI_DEBUG
envs: FPC_VERSION FPC_TARGET FPC_TARBALL_TARGET LAZARUS_BRANCH LAZARUS_REPO MAKE_BUILD_BACKEND MAKE_PACKAGE_SCOPE MAKE_RUN_BENCHMARK FREEBSD_INSTALL_MODE CI_DEBUG
release: "15.0"
usesh: true
prepare: sh .github/workflows/ci/vm-freebsd-prepare.sh
Expand All @@ -262,7 +267,7 @@ jobs:
FPC_TARGET: ${{ fromJSON(needs.setup.outputs.target_map)['netbsd'].fpc_target }}
FPC_TARBALL_TARGET: ${{ fromJSON(needs.setup.outputs.target_map)['netbsd'].fpc_tarball_target }}
with:
envs: FPC_VERSION LAZARUS_BRANCH LAZARUS_REPO MAKE_BUILD_BACKEND MAKE_PACKAGE_SCOPE FPC_TARGET FPC_TARBALL_TARGET CI_DEBUG
envs: FPC_VERSION LAZARUS_BRANCH LAZARUS_REPO MAKE_BUILD_BACKEND MAKE_PACKAGE_SCOPE MAKE_RUN_BENCHMARK FPC_TARGET FPC_TARBALL_TARGET CI_DEBUG
prepare: sh .github/workflows/ci/vm-netbsd-prepare.sh
run: bash .github/workflows/ci/vm-run-shared.sh

Expand All @@ -285,7 +290,7 @@ jobs:
FPC_TARBALL_TARGET: ${{ fromJSON(needs.setup.outputs.target_map)['dragonflybsd'].fpc_tarball_target }}
LD_LIBRARY_PATH_EXTRA: /usr/local/lib
with:
envs: FPC_VERSION LAZARUS_BRANCH LAZARUS_REPO MAKE_BUILD_BACKEND MAKE_PACKAGE_SCOPE FPC_TARGET FPC_TARBALL_TARGET LD_LIBRARY_PATH_EXTRA CI_DEBUG
envs: FPC_VERSION LAZARUS_BRANCH LAZARUS_REPO MAKE_BUILD_BACKEND MAKE_PACKAGE_SCOPE MAKE_RUN_BENCHMARK FPC_TARGET FPC_TARBALL_TARGET LD_LIBRARY_PATH_EXTRA CI_DEBUG
usesh: true
prepare: sh .github/workflows/ci/vm-dragonfly-prepare.sh
run: bash .github/workflows/ci/vm-run-shared.sh
Expand All @@ -308,7 +313,7 @@ jobs:
FPC_TARGET: ${{ fromJSON(needs.setup.outputs.target_map)['solaris'].fpc_target }}
FPC_TARBALL_TARGET: ${{ fromJSON(needs.setup.outputs.target_map)['solaris'].fpc_tarball_target }}
with:
envs: FPC_VERSION LAZARUS_BRANCH LAZARUS_REPO MAKE_BUILD_BACKEND MAKE_PACKAGE_SCOPE FPC_TARGET FPC_TARBALL_TARGET CI_DEBUG
envs: FPC_VERSION LAZARUS_BRANCH LAZARUS_REPO MAKE_BUILD_BACKEND MAKE_PACKAGE_SCOPE MAKE_RUN_BENCHMARK FPC_TARGET FPC_TARBALL_TARGET CI_DEBUG
release: "11.4-gcc"
usesh: true
prepare: sh .github/workflows/ci/vm-solaris-prepare.sh
Expand Down
Loading