rtl-unification: move E2E test helpers off blaise_rtl.a (source-built RTL)
The E2E test helpers linked Blaise programs against the shipped blaise_rtl.a. With the archive on its way out (RTL-unification Stage 3), migrate them to the source-built RTL: - cp.test.e2e.base gains LinkWithRTL: assembles the program object, builds the RTL from source via scripts/build-rtl-objects.sh (--exclude-defined-by drops the RTL units the whole-program assembly already inlined), and links the loose objects. Its five cc-link sites and the ToolchainAvailable gate now use it / the RTL source instead of the archive. - New cp.test.rtllink unit factors the same link helper for the standalone helpers (publishedrtti, proctypes_ofobject, attributes) that lower programs themselves rather than via the base class. - cp.test.cli and cp.test.assembler drive an external blaise binary that source-builds its own RTL, so their gates now require the binary + RTL source, not the archive. - cp.test.linker's TestArchive_ParsesRTL kept its archive-parser coverage (including the GNU long-name member) by building a throwaway .a from the source-built objects with ar — a test-time-only ar use, not a product one. build-rtl-objects.sh resolves its RTL source relative to its own location (so it works from any CWD — the fixpoint scripts run from the project root, the test runner from compiler/), caches a per-object .syms list to keep repeated --exclude-defined-by calls cheap, and skips rebuilding fresh objects. Full suite green (3829 tests) on both QBE- and native-built runners; the previously-skipped TInternalAsmE2ETests / TCLIContractTests and the archive parser test now run.
This commit is contained in:
parent
406a8c303c
commit
773d773008
|
|
@ -83,7 +83,6 @@ type
|
|||
FCompiler: string;
|
||||
FRTLPath: string;
|
||||
FStdlibPath: string;
|
||||
FRTL: string;
|
||||
FScratch: string;
|
||||
FCounter: Integer;
|
||||
function ProjectRoot: string;
|
||||
|
|
@ -752,7 +751,6 @@ begin
|
|||
FCompiler := '/tmp/fp_blaise3';
|
||||
FRTLPath := ProjectRoot() + 'compiler/src/main/pascal';
|
||||
FStdlibPath := ProjectRoot() + 'stdlib/src/main/pascal';
|
||||
FRTL := ProjectRoot() + 'compiler/target/blaise_rtl.a';
|
||||
FScratch := ProjectRoot() + 'compiler/target/asm_scratch/';
|
||||
ForceDirectories(FScratch);
|
||||
FCounter := 0;
|
||||
|
|
@ -763,12 +761,15 @@ var
|
|||
SrcFile, OutFile, CompOut, RunOut: string;
|
||||
Rc: Integer;
|
||||
begin
|
||||
Result := FileExists(FCompiler) and FileExists(FRTL);
|
||||
{ The compiler binary source-builds the RTL itself (no blaise_rtl.a); only
|
||||
the binary and the RTL source need to be present. }
|
||||
Result := FileExists(FCompiler)
|
||||
and FileExists(FRTLPath + '/runtime.arc.pas');
|
||||
if (not Result) and (not GInternalAsmSkipNoted) then
|
||||
begin
|
||||
GInternalAsmSkipNoted := True;
|
||||
WriteLn(StdErr, 'note: TInternalAsmE2ETests skipped — compiler binary "',
|
||||
FCompiler, '" or RTL "', FRTL, '" not found ',
|
||||
FCompiler, '" or RTL source not found ',
|
||||
'(set BLAISE_QBE_COMPILER to a QBE-backend blaise binary to run them)');
|
||||
Exit;
|
||||
end;
|
||||
|
|
|
|||
|
|
@ -20,7 +20,8 @@ interface
|
|||
|
||||
uses
|
||||
Classes, SysUtils, Process, blaise.testing,
|
||||
uLexer, uParser, uAST, uSymbolTable, uSemantic, blaise.codegen.qbe;
|
||||
uLexer, uParser, uAST, uSymbolTable, uSemantic, blaise.codegen.qbe,
|
||||
cp.test.rtllink;
|
||||
|
||||
function ProjectRootAttr: string;
|
||||
function RunCmdAttr(const AExe: string; const AArgs: array of string): Integer;
|
||||
|
|
@ -154,7 +155,7 @@ function TCustomAttributeTests.CompileAndRun(const ASrc: string): string;
|
|||
var
|
||||
IR: string;
|
||||
Root: string;
|
||||
QBE, RTL, Scratch: string;
|
||||
QBE, Scratch: string;
|
||||
IRFile, AsmFile, BinFile: string;
|
||||
Lst: TStringList;
|
||||
Proc: TProcess;
|
||||
|
|
@ -163,8 +164,7 @@ begin
|
|||
Result := '';
|
||||
Root := ProjectRootAttr();
|
||||
QBE := Root + 'vendor/qbe/qbe';
|
||||
RTL := Root + 'compiler/target/blaise_rtl.a';
|
||||
if not (FileExists(QBE) and FileExists(RTL)) then
|
||||
if not RTLLinkToolchainAvailable(Root) then
|
||||
begin
|
||||
Result := '<toolchain-missing>';
|
||||
Exit;
|
||||
|
|
@ -190,7 +190,7 @@ begin
|
|||
Exit;
|
||||
end;
|
||||
|
||||
if RunCmdAttr('cc', ['-o', BinFile, AsmFile, RTL]) <> 0 then
|
||||
if LinkProgramWithRTL(Root, AsmFile, BinFile) <> 0 then
|
||||
begin
|
||||
Result := '<link-failed>';
|
||||
Exit;
|
||||
|
|
|
|||
|
|
@ -35,7 +35,6 @@ type
|
|||
FCompiler: string;
|
||||
FRTLPath: string;
|
||||
FStdlibPath: string;
|
||||
FRTL: string;
|
||||
FScratch: string;
|
||||
FCounter: Integer;
|
||||
function ProjectRoot: string;
|
||||
|
|
@ -144,7 +143,6 @@ begin
|
|||
FCompiler := '/tmp/fp_blaise2';
|
||||
FRTLPath := ProjectRoot() + 'compiler/src/main/pascal';
|
||||
FStdlibPath := ProjectRoot() + 'stdlib/src/main/pascal';
|
||||
FRTL := ProjectRoot() + 'compiler/target/blaise_rtl.a';
|
||||
FScratch := ProjectRoot() + 'compiler/target/cli_scratch/';
|
||||
ForceDirectories(FScratch);
|
||||
FCounter := 0;
|
||||
|
|
@ -155,12 +153,15 @@ var
|
|||
Out_: string;
|
||||
EC: Integer;
|
||||
begin
|
||||
Result := FileExists(FCompiler) and FileExists(FRTL);
|
||||
{ The compiler binary source-builds the RTL itself (no blaise_rtl.a); only
|
||||
the binary and the RTL source need to be present. }
|
||||
Result := FileExists(FCompiler)
|
||||
and FileExists(FRTLPath + '/runtime.arc.pas');
|
||||
if (not Result) and (not GCLISkipNoted) then
|
||||
begin
|
||||
GCLISkipNoted := True;
|
||||
WriteLn(StdErr, 'note: TCLIContractTests skipped — compiler binary "',
|
||||
FCompiler, '" or RTL "', FRTL, '" not found ',
|
||||
FCompiler, '" or RTL source not found ',
|
||||
'(set BLAISE_QBE_COMPILER to a QBE-backend blaise binary to run them)');
|
||||
Exit;
|
||||
end;
|
||||
|
|
|
|||
|
|
@ -34,7 +34,6 @@ type
|
|||
TE2ETestCase = class(TTestCase)
|
||||
private
|
||||
FQBE: string;
|
||||
FRTL: string;
|
||||
FRTLUnitPath: string;
|
||||
FStdlibUnitPath: string;
|
||||
FScratch: string;
|
||||
|
|
@ -43,6 +42,13 @@ type
|
|||
function RunProc(const AExe: string; const AArgs: array of string;
|
||||
out AStdout: string): Integer;
|
||||
function RunProcNoArgs(const AExe: string; out AStdout: string): Integer;
|
||||
{ Link an assembled program (AAsmFile) into ABinFile against the RTL. The
|
||||
RTL is built from source by scripts/build-rtl-objects.sh (no blaise_rtl.a
|
||||
archive); --exclude-defined-by drops the RTL objects the whole-program
|
||||
assembly already inlines, so the loose objects do not double-define.
|
||||
Returns the cc exit code; AStdout carries any tool output. }
|
||||
function LinkWithRTL(const AAsmFile, ABinFile: string;
|
||||
out AStdout: string): Integer;
|
||||
protected
|
||||
function ToolchainAvailable(): Boolean;
|
||||
function ValgrindAvailable(): Boolean;
|
||||
|
|
@ -169,7 +175,11 @@ end;
|
|||
|
||||
function TE2ETestCase.ToolchainAvailable(): Boolean;
|
||||
begin
|
||||
Result := FileExists(FQBE) and FileExists(FRTL)
|
||||
{ Need the QBE assembler, the compiler binary (build-rtl-objects.sh drives it
|
||||
to source-build the RTL), and the RTL source. No blaise_rtl.a archive. }
|
||||
Result := FileExists(FQBE)
|
||||
and FileExists(ProjectRoot() + 'compiler/target/blaise')
|
||||
and FileExists(ProjectRoot() + 'compiler/src/main/pascal/runtime.arc.pas')
|
||||
end;
|
||||
|
||||
function TE2ETestCase.ValgrindAvailable(): Boolean;
|
||||
|
|
@ -186,9 +196,6 @@ begin
|
|||
FQBE := GetEnvironmentVariable('BLAISE_QBE');
|
||||
if FQBE = '' then
|
||||
FQBE := ProjectRoot() + 'vendor/qbe/qbe';
|
||||
FRTL := GetEnvironmentVariable('BLAISE_RTL');
|
||||
if FRTL = '' then
|
||||
FRTL := ProjectRoot() + 'runtime/target/blaise_rtl.a';
|
||||
{ RTL units (runtime.*, rtl.platform.*) now live in the compiler's own source
|
||||
tree after the RTL-unification move; the old runtime/src/main/pascal is empty. }
|
||||
FRTLUnitPath := ProjectRoot() + 'compiler/src/main/pascal';
|
||||
|
|
@ -228,6 +235,62 @@ begin
|
|||
end
|
||||
end;
|
||||
|
||||
function TE2ETestCase.LinkWithRTL(const AAsmFile, ABinFile: string;
|
||||
out AStdout: string): Integer;
|
||||
var
|
||||
ProgObj, ObjDir, Compiler, ScriptOut: string;
|
||||
Objs: TStringList;
|
||||
Proc: TProcess;
|
||||
I: Integer;
|
||||
begin
|
||||
{ 1. Assemble the program to an object so build-rtl-objects.sh can see which
|
||||
RTL symbols it already defines (it inlines the RTL units it uses). }
|
||||
ProgObj := AAsmFile + '.o';
|
||||
Result := RunProc('cc', ['-c', '-o', ProgObj, AAsmFile], AStdout);
|
||||
if Result <> 0 then Exit;
|
||||
|
||||
{ 2. Build the RTL objects from source, excluding the units the program
|
||||
already inlined. Compiler binary is the freshly-built compiler/target. }
|
||||
Compiler := ProjectRoot() + 'compiler/target/blaise';
|
||||
ObjDir := IncludeTrailingPathDelimiter(FScratch) + 'rtlobj';
|
||||
Result := RunProc(ProjectRoot() + 'scripts/build-rtl-objects.sh',
|
||||
[Compiler, ObjDir, '--exclude-defined-by', ProgObj],
|
||||
ScriptOut);
|
||||
if Result <> 0 then
|
||||
begin
|
||||
AStdout := 'build-rtl-objects failed: ' + ScriptOut;
|
||||
Exit;
|
||||
end;
|
||||
|
||||
{ 3. Link: cc -o Bin ProgObj <rtl objects> -lm -lpthread. Build the TProcess
|
||||
directly so the object list (variable length) can be appended. }
|
||||
Objs := TStringList.Create();
|
||||
Proc := TProcess.Create(nil);
|
||||
try
|
||||
Objs.Text := ScriptOut;
|
||||
Proc.Executable := 'cc';
|
||||
Proc.Parameters.Add('-o');
|
||||
Proc.Parameters.Add(ABinFile);
|
||||
Proc.Parameters.Add(ProgObj);
|
||||
for I := 0 to Objs.Count - 1 do
|
||||
if Trim(Objs.Strings[I]) <> '' then
|
||||
Proc.Parameters.Add(Trim(Objs.Strings[I]));
|
||||
Proc.Parameters.Add('-lm');
|
||||
Proc.Parameters.Add('-lpthread');
|
||||
Proc.Execute();
|
||||
AStdout := '';
|
||||
repeat
|
||||
ScriptOut := Proc.ReadOutput();
|
||||
AStdout := AStdout + ScriptOut
|
||||
until (ScriptOut = '') and not Proc.Running;
|
||||
Proc.WaitOnExit();
|
||||
Result := Proc.ExitCode;
|
||||
finally
|
||||
Proc.Free();
|
||||
Objs.Free();
|
||||
end;
|
||||
end;
|
||||
|
||||
function TE2ETestCase.RunProcNoArgs(const AExe: string;
|
||||
out AStdout: string): Integer;
|
||||
var
|
||||
|
|
@ -316,7 +379,7 @@ begin
|
|||
Rc := RunProc(FQBE, ['-o', AsmFile, IRFile], ToolOut);
|
||||
if Rc <> 0 then begin AStdout := 'qbe failed: ' + ToolOut; AExitCode := Rc; Exit end
|
||||
end;
|
||||
Rc := RunProc('cc', ['-o', BinFile, AsmFile, FRTL, '-lm', '-lpthread'], ToolOut);
|
||||
Rc := LinkWithRTL(AsmFile, BinFile, ToolOut);
|
||||
if Rc <> 0 then begin AStdout := 'cc failed: ' + ToolOut; AExitCode := Rc; Exit end;
|
||||
AExitCode := RunProcNoArgs(BinFile, AStdout);
|
||||
Result := True
|
||||
|
|
@ -428,7 +491,7 @@ begin
|
|||
WriteFile(IRFile, IR);
|
||||
Rc := RunProc(FQBE, ['-o', AsmFile, IRFile], ToolOut);
|
||||
if Rc <> 0 then begin AStdout := 'qbe failed: ' + ToolOut; AExitCode := Rc; Exit end;
|
||||
Rc := RunProc('cc', ['-o', BinFile, AsmFile, FRTL, '-lm', '-lpthread'], ToolOut);
|
||||
Rc := LinkWithRTL(AsmFile, BinFile, ToolOut);
|
||||
if Rc <> 0 then begin AStdout := 'cc failed: ' + ToolOut; AExitCode := Rc; Exit end;
|
||||
AExitCode := RunProc(BinFile, AExtraArgs, AStdout);
|
||||
Result := True
|
||||
|
|
@ -472,7 +535,7 @@ begin
|
|||
WriteFile(IRFile, IR);
|
||||
Rc := RunProc(FQBE, ['-o', AsmFile, IRFile], ToolOut);
|
||||
if Rc <> 0 then Exit;
|
||||
Rc := RunProc('cc', ['-o', BinFile, AsmFile, FRTL, '-lm', '-lpthread'], ToolOut);
|
||||
Rc := LinkWithRTL(AsmFile, BinFile, ToolOut);
|
||||
if Rc <> 0 then Exit;
|
||||
|
||||
Rc := RunProc('valgrind',
|
||||
|
|
@ -664,7 +727,7 @@ begin
|
|||
Rc := RunProc(FQBE, ['-o', AsmFile, IRFile], ToolOut);
|
||||
if Rc <> 0 then begin AStdout := 'qbe failed: ' + ToolOut; AExitCode := Rc; Exit end
|
||||
end;
|
||||
Rc := RunProc('cc', ['-o', BinFile, AsmFile, FRTL, '-lm', '-lpthread'], ToolOut);
|
||||
Rc := LinkWithRTL(AsmFile, BinFile, ToolOut);
|
||||
if Rc <> 0 then begin AStdout := 'cc failed: ' + ToolOut; AExitCode := Rc; Exit end;
|
||||
AExitCode := RunProcNoArgs(BinFile, AStdout);
|
||||
Result := True
|
||||
|
|
@ -764,7 +827,7 @@ begin
|
|||
Rc := RunProc(FQBE, ['-o', AsmFile, IRFile], ToolOut);
|
||||
if Rc <> 0 then begin AStdout := 'qbe failed: ' + ToolOut; AExitCode := Rc; Exit end
|
||||
end;
|
||||
Rc := RunProc('cc', ['-o', BinFile, AsmFile, FRTL, '-lm', '-lpthread'], ToolOut);
|
||||
Rc := LinkWithRTL(AsmFile, BinFile, ToolOut);
|
||||
if Rc <> 0 then begin AStdout := 'cc failed: ' + ToolOut; AExitCode := Rc; Exit end;
|
||||
AExitCode := RunProcNoArgs(BinFile, AStdout);
|
||||
Result := True
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ unit cp.test.linker;
|
|||
interface
|
||||
|
||||
uses
|
||||
SysUtils, process, blaise.testing, Generics.Collections,
|
||||
classes, SysUtils, process, blaise.testing, Generics.Collections,
|
||||
blaise.elfreader, blaise.linker.elf, blaise.assembler.x86_64;
|
||||
|
||||
type
|
||||
|
|
@ -330,22 +330,82 @@ end;
|
|||
|
||||
procedure TElfReaderTests.TestArchive_ParsesRTL;
|
||||
var
|
||||
RTLPath: string;
|
||||
Root, ObjDir, ArPath, ScriptOut: string;
|
||||
Compiler: string;
|
||||
Members: TList<TArchiveMember>;
|
||||
ObjList: TStringList;
|
||||
Proc: TProcess;
|
||||
Chunk: string;
|
||||
I: Integer;
|
||||
M: TArchiveMember;
|
||||
Obj: TElfObjectFile;
|
||||
SawLongName: Boolean;
|
||||
begin
|
||||
RTLPath := ProjectRoot() + 'compiler/target/blaise_rtl.a';
|
||||
if not FileExists(RTLPath) then
|
||||
{ The shipped blaise_rtl.a is gone (RTL-unification Stage 3). To still
|
||||
exercise the archive parser — including the GNU long-name (//) table on the
|
||||
>15-char member rtl.platform.layout.linux.o — build the RTL objects from
|
||||
source and bundle them into a THROWAWAY .a with `ar` just for this test.
|
||||
`ar` is a test-time dependency only; the product/bootstrap no longer use it. }
|
||||
Root := ProjectRoot();
|
||||
Compiler := Root + 'compiler/target/blaise';
|
||||
if (not FileExists(Compiler))
|
||||
or (not FileExists(Root + 'compiler/src/main/pascal/runtime.arc.pas')) then
|
||||
begin
|
||||
Ignore('<toolchain-missing>');
|
||||
Exit;
|
||||
end;
|
||||
ObjDir := Root + 'compiler/target/test-archive-rtl';
|
||||
ScriptOut := '';
|
||||
Proc := TProcess.Create(nil);
|
||||
try
|
||||
Proc.Executable := Root + 'scripts/build-rtl-objects.sh';
|
||||
Proc.Parameters.Add(Compiler);
|
||||
Proc.Parameters.Add(ObjDir);
|
||||
Proc.Parameters.Add('--with-startup');
|
||||
Proc.Execute();
|
||||
repeat
|
||||
Chunk := Proc.ReadOutput();
|
||||
ScriptOut := ScriptOut + Chunk
|
||||
until (Chunk = '') and not Proc.Running;
|
||||
Proc.WaitOnExit();
|
||||
if Proc.ExitCode <> 0 then
|
||||
begin
|
||||
Ignore('<toolchain-missing>');
|
||||
Exit;
|
||||
end;
|
||||
finally
|
||||
Proc.Free();
|
||||
end;
|
||||
|
||||
{ Bundle the built objects into a temp archive with ar. Split the script's
|
||||
stdout (one object path per line) via TStringList. }
|
||||
ArPath := IncludeTrailingPathDelimiter(ObjDir) + 'rtl_test.a';
|
||||
DeleteFile(ArPath);
|
||||
ObjList := TStringList.Create();
|
||||
Proc := TProcess.Create(nil);
|
||||
try
|
||||
ObjList.Text := ScriptOut;
|
||||
Proc.Executable := 'ar';
|
||||
Proc.Parameters.Add('rcs');
|
||||
Proc.Parameters.Add(ArPath);
|
||||
for I := 0 to ObjList.Count - 1 do
|
||||
if Trim(ObjList.Strings[I]) <> '' then
|
||||
Proc.Parameters.Add(Trim(ObjList.Strings[I]));
|
||||
Proc.Execute();
|
||||
Proc.WaitOnExit();
|
||||
if (Proc.ExitCode <> 0) or (not FileExists(ArPath)) then
|
||||
begin
|
||||
Ignore('<toolchain-missing>');
|
||||
Exit;
|
||||
end;
|
||||
finally
|
||||
Proc.Free();
|
||||
ObjList.Free();
|
||||
end;
|
||||
|
||||
Members := TList<TArchiveMember>.Create();
|
||||
try
|
||||
ReadArchiveFile(RTLPath, Members);
|
||||
ReadArchiveFile(ArPath, Members);
|
||||
AssertTrue('expected several RTL members, got '
|
||||
+ IntToStr(Members.Count), Members.Count >= 5);
|
||||
{ A member whose name exceeds 15 chars exercises the GNU long-name (//)
|
||||
|
|
|
|||
|
|
@ -20,7 +20,8 @@ interface
|
|||
|
||||
uses
|
||||
Classes, SysUtils, Process, blaise.testing,
|
||||
uLexer, uParser, uAST, uSymbolTable, uSemantic, blaise.codegen.qbe;
|
||||
uLexer, uParser, uAST, uSymbolTable, uSemantic, blaise.codegen.qbe,
|
||||
cp.test.rtllink;
|
||||
|
||||
function ProjectRootOFO: string;
|
||||
function RunCmdOFO(const AExe: string; const AArgs: array of string): Integer;
|
||||
|
|
@ -157,7 +158,7 @@ function TProcTypesOfObjectTests.CompileAndRun(const ASrc: string): string;
|
|||
var
|
||||
IR: string;
|
||||
Root: string;
|
||||
QBE, RTL, Scratch: string;
|
||||
QBE, Scratch: string;
|
||||
IRFile, AsmFile, BinFile: string;
|
||||
Lst: TStringList;
|
||||
Proc: TProcess;
|
||||
|
|
@ -166,8 +167,7 @@ begin
|
|||
Result := '';
|
||||
Root := ProjectRootOFO();
|
||||
QBE := Root + 'vendor/qbe/qbe';
|
||||
RTL := Root + 'compiler/target/blaise_rtl.a';
|
||||
if not (FileExists(QBE) and FileExists(RTL)) then
|
||||
if not RTLLinkToolchainAvailable(Root) then
|
||||
begin
|
||||
Result := '<toolchain-missing>';
|
||||
Exit;
|
||||
|
|
@ -193,7 +193,7 @@ begin
|
|||
Exit;
|
||||
end;
|
||||
|
||||
if RunCmdOFO('cc', ['-o', BinFile, AsmFile, RTL]) <> 0 then
|
||||
if LinkProgramWithRTL(Root, AsmFile, BinFile) <> 0 then
|
||||
begin
|
||||
Result := '<link-failed>';
|
||||
Exit;
|
||||
|
|
|
|||
|
|
@ -21,7 +21,8 @@ interface
|
|||
|
||||
uses
|
||||
Classes, SysUtils, Process, blaise.testing,
|
||||
uLexer, uParser, uAST, uSymbolTable, uSemantic, blaise.codegen.qbe;
|
||||
uLexer, uParser, uAST, uSymbolTable, uSemantic, blaise.codegen.qbe,
|
||||
cp.test.rtllink;
|
||||
|
||||
function ProjectRootRTTI: string;
|
||||
function RunCmd(const AExe: string; const AArgs: array of string): Integer;
|
||||
|
|
@ -152,7 +153,7 @@ function TPublishedRTTITests.CompileAndRun(const ASrc: string): string;
|
|||
var
|
||||
IR: string;
|
||||
Root: string;
|
||||
QBE, RTL, Scratch: string;
|
||||
QBE, Scratch: string;
|
||||
IRFile, AsmFile, BinFile: string;
|
||||
Lst: TStringList;
|
||||
Proc: TProcess;
|
||||
|
|
@ -161,8 +162,7 @@ begin
|
|||
Result := '';
|
||||
Root := ProjectRootRTTI();
|
||||
QBE := Root + 'vendor/qbe/qbe';
|
||||
RTL := Root + 'compiler/target/blaise_rtl.a';
|
||||
if not (FileExists(QBE) and FileExists(RTL)) then
|
||||
if not RTLLinkToolchainAvailable(Root) then
|
||||
begin
|
||||
Result := '<toolchain-missing>';
|
||||
Exit;
|
||||
|
|
@ -188,7 +188,7 @@ begin
|
|||
Exit;
|
||||
end;
|
||||
|
||||
if RunCmd('cc', ['-o', BinFile, AsmFile, RTL]) <> 0 then
|
||||
if LinkProgramWithRTL(Root, AsmFile, BinFile) <> 0 then
|
||||
begin
|
||||
Result := '<link-failed>';
|
||||
Exit;
|
||||
|
|
|
|||
117
compiler/src/test/pascal/cp.test.rtllink.pas
Normal file
117
compiler/src/test/pascal/cp.test.rtllink.pas
Normal file
|
|
@ -0,0 +1,117 @@
|
|||
{
|
||||
Blaise - An Object Pascal Compiler
|
||||
Copyright (c) 2026 Graeme Geldenhuys
|
||||
SPDX-License-Identifier: Apache-2.0 WITH Swift-exception
|
||||
Licensed under the Apache License v2.0 with Runtime Library Exception.
|
||||
See LICENSE file in the project root for full license terms.
|
||||
}
|
||||
|
||||
unit cp.test.rtllink;
|
||||
|
||||
{ Shared RTL link helper for the standalone E2E test helpers (those that lower a
|
||||
program to assembly themselves and link it, rather than via cp.test.e2e.base).
|
||||
|
||||
The RTL is built from source by scripts/build-rtl-objects.sh — there is no
|
||||
blaise_rtl.a archive (RTL-unification Stage 3). A whole-program assembly dump
|
||||
inlines the RTL units the program uses, so --exclude-defined-by drops the RTL
|
||||
objects whose symbols the program already defines, avoiding double-definition. }
|
||||
|
||||
interface
|
||||
|
||||
{ Link AAsmFile (assembled program, .s) into ABinFile against the RTL.
|
||||
AProjectRoot must end with a path delimiter. Returns the cc exit code; 0 on
|
||||
success. Non-zero (and a negative value for a failed RTL build) on failure. }
|
||||
function LinkProgramWithRTL(const AProjectRoot, AAsmFile,
|
||||
ABinFile: string): Integer;
|
||||
|
||||
{ True when the toolchain needed by LinkProgramWithRTL is present: the QBE
|
||||
assembler, the compiler binary, and the RTL source. }
|
||||
function RTLLinkToolchainAvailable(const AProjectRoot: string): Boolean;
|
||||
|
||||
implementation
|
||||
|
||||
uses
|
||||
classes, sysutils, process;
|
||||
|
||||
function RTLLinkToolchainAvailable(const AProjectRoot: string): Boolean;
|
||||
begin
|
||||
Result := FileExists(AProjectRoot + 'vendor/qbe/qbe')
|
||||
and FileExists(AProjectRoot + 'compiler/target/blaise')
|
||||
and FileExists(AProjectRoot + 'compiler/src/main/pascal/runtime.arc.pas');
|
||||
end;
|
||||
|
||||
function RunCapture(const AExe: string; const AArgs: array of string;
|
||||
out AOut: string): Integer;
|
||||
var
|
||||
Proc: TProcess;
|
||||
I: Integer;
|
||||
Chunk: string;
|
||||
begin
|
||||
Proc := TProcess.Create(nil);
|
||||
try
|
||||
Proc.Executable := AExe;
|
||||
for I := 0 to High(AArgs) do
|
||||
Proc.Parameters.Add(AArgs[I]);
|
||||
Proc.Execute();
|
||||
AOut := '';
|
||||
repeat
|
||||
Chunk := Proc.ReadOutput();
|
||||
AOut := AOut + Chunk
|
||||
until (Chunk = '') and not Proc.Running;
|
||||
Proc.WaitOnExit();
|
||||
Result := Proc.ExitCode
|
||||
finally
|
||||
Proc.Free()
|
||||
end
|
||||
end;
|
||||
|
||||
function LinkProgramWithRTL(const AProjectRoot, AAsmFile,
|
||||
ABinFile: string): Integer;
|
||||
var
|
||||
ProgObj, ObjDir, Compiler, ScriptOut: string;
|
||||
Objs: TStringList;
|
||||
Proc: TProcess;
|
||||
I: Integer;
|
||||
begin
|
||||
{ Assemble the program so the RTL build can see which RTL symbols it inlined. }
|
||||
ProgObj := AAsmFile + '.o';
|
||||
Result := RunCapture('cc', ['-c', '-o', ProgObj, AAsmFile], ScriptOut);
|
||||
if Result <> 0 then Exit;
|
||||
|
||||
Compiler := AProjectRoot + 'compiler/target/blaise';
|
||||
ObjDir := ExtractFilePath(AAsmFile) + 'rtlobj';
|
||||
Result := RunCapture(AProjectRoot + 'scripts/build-rtl-objects.sh',
|
||||
[Compiler, ObjDir, '--exclude-defined-by', ProgObj],
|
||||
ScriptOut);
|
||||
if Result <> 0 then
|
||||
begin
|
||||
Result := -1;
|
||||
Exit;
|
||||
end;
|
||||
|
||||
Objs := TStringList.Create();
|
||||
Proc := TProcess.Create(nil);
|
||||
try
|
||||
Objs.Text := ScriptOut;
|
||||
Proc.Executable := 'cc';
|
||||
Proc.Parameters.Add('-o');
|
||||
Proc.Parameters.Add(ABinFile);
|
||||
Proc.Parameters.Add(ProgObj);
|
||||
for I := 0 to Objs.Count - 1 do
|
||||
if Trim(Objs.Strings[I]) <> '' then
|
||||
Proc.Parameters.Add(Trim(Objs.Strings[I]));
|
||||
Proc.Parameters.Add('-lm');
|
||||
Proc.Parameters.Add('-lpthread');
|
||||
Proc.Execute();
|
||||
repeat
|
||||
ScriptOut := Proc.ReadOutput();
|
||||
until (ScriptOut = '') and not Proc.Running;
|
||||
Proc.WaitOnExit();
|
||||
Result := Proc.ExitCode;
|
||||
finally
|
||||
Proc.Free();
|
||||
Objs.Free();
|
||||
end;
|
||||
end;
|
||||
|
||||
end.
|
||||
|
|
@ -53,7 +53,15 @@ if [ ! -x "$BLAISE" ]; then
|
|||
exit 2
|
||||
fi
|
||||
|
||||
SRC=compiler/src/main/pascal
|
||||
# Resolve the RTL source relative to THIS script (scripts/ is a sibling of
|
||||
# compiler/), so the script works regardless of the caller's CWD — the fixpoint
|
||||
# scripts run from the project root, the test runner from compiler/.
|
||||
SCRIPT_DIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)
|
||||
SRC="$SCRIPT_DIR/../compiler/src/main/pascal"
|
||||
if [ ! -f "$SRC/runtime.arc.pas" ]; then
|
||||
# Fall back to a CWD-relative path (e.g. an unusual invocation layout).
|
||||
SRC=compiler/src/main/pascal
|
||||
fi
|
||||
if [ ! -f "$SRC/runtime.arc.pas" ]; then
|
||||
echo "build-rtl-objects: RTL source not found under $SRC" >&2
|
||||
exit 2
|
||||
|
|
@ -81,23 +89,30 @@ fi
|
|||
OBJS=()
|
||||
for u in "${RTL_UNITS[@]}"; do
|
||||
obj="$OUTDIR/$u.o"
|
||||
"$BLAISE" --backend native --assembler internal \
|
||||
--source "$SRC/$u.pas" \
|
||||
--unit-path "$SRC" --unit-cache "$OUTDIR" \
|
||||
--output "$obj" >/dev/null 2>"$OUTDIR/$u.build.err" || {
|
||||
echo "build-rtl-objects: failed to build $u" >&2
|
||||
tail -5 "$OUTDIR/$u.build.err" >&2
|
||||
exit 1
|
||||
}
|
||||
src="$SRC/$u.pas"
|
||||
# Rebuild only when the cached object is missing or older than its source, so
|
||||
# a persistent $OUTDIR (e.g. reused across a test suite) builds the RTL once.
|
||||
if [ ! -f "$obj" ] || [ "$src" -nt "$obj" ]; then
|
||||
"$BLAISE" --backend native --assembler internal \
|
||||
--source "$src" \
|
||||
--unit-path "$SRC" --unit-cache "$OUTDIR" \
|
||||
--output "$obj" >/dev/null 2>"$OUTDIR/$u.build.err" || {
|
||||
echo "build-rtl-objects: failed to build $u" >&2
|
||||
tail -5 "$OUTDIR/$u.build.err" >&2
|
||||
exit 1
|
||||
}
|
||||
# Cache this object's defined symbols so repeated --exclude-defined-by calls
|
||||
# (e.g. one per test in a suite) don't re-run nm on the stable RTL objects.
|
||||
nm "$obj" 2>/dev/null | grep -E ' [TDBR] ' | awk '{print $3}' | sort -u \
|
||||
> "$obj.syms"
|
||||
fi
|
||||
if [ "$u" = "runtime.start" ] && [ "$WITH_STARTUP" -eq 0 ]; then
|
||||
continue
|
||||
fi
|
||||
# Drop this object if it re-defines any symbol the main program already owns
|
||||
# (it was inlined into the program) — otherwise the loose .o collides.
|
||||
if [ -n "$EXCL_DEFS" ]; then
|
||||
this_defs=$(nm "$obj" 2>/dev/null | grep -E ' [TDBR] ' | awk '{print $3}' | sort -u)
|
||||
if [ -n "$this_defs" ] && \
|
||||
[ -n "$(comm -12 <(printf '%s\n' "$this_defs") <(printf '%s\n' "$EXCL_DEFS"))" ]; then
|
||||
if [ -n "$EXCL_DEFS" ] && [ -f "$obj.syms" ] && [ -s "$obj.syms" ]; then
|
||||
if [ -n "$(comm -12 "$obj.syms" <(printf '%s\n' "$EXCL_DEFS"))" ]; then
|
||||
continue
|
||||
fi
|
||||
fi
|
||||
|
|
|
|||
Loading…
Reference in a new issue