Jelajahi Sumber

Update fork and align with head

Curtis Hamilton 3 minggu lalu
induk
melakukan
ead3c10421

+ 2 - 1
packages/fcl-base/examples/README.txt

@@ -81,4 +81,5 @@ csvbom.pp    Test/Demo for BOM detection in CSV document. (needs databom.txt)
 testappexit.pp Test/Demo for TApplication exit code handling. (ExitCode and ExceptionExitcode)
 testappexit.pp Test/Demo for TApplication exit code handling. (ExitCode and ExceptionExitcode)
 demoio.pp    Demo for AssignStream from streamio unit.
 demoio.pp    Demo for AssignStream from streamio unit.
 testthreadpool  Demo for fpthreadpool unit.
 testthreadpool  Demo for fpthreadpool unit.
-demolg       TLockGuard demo.
+demolg       TLockGuard demo.
+demo_spinlock TSpinLock demo.

+ 250 - 0
packages/fcl-base/examples/demo_spinlock.pp

@@ -0,0 +1,250 @@
+program demo_spinlock;
+
+{$mode objfpc}{$H+}
+
+uses
+  {$IFDEF UNIX}cthreads,{$ENDIF}
+  Classes, SysUtils, syncobjs, dateutils;
+
+const
+  THREAD_COUNT = 10;
+  INCREMENTS_PER_THREAD = 1000000; // We're supposed to go fast, so lots of increments :-)
+
+{
+  Results on my i9 machine, 24 cores: 
+  Linux: spinlock is ~4 times faster than criticalsection
+  Windows: spinlock is ~10 times faster than criticalsection
+}
+
+var
+  MySpinLock: TSpinLock;
+  CS: TCriticalSection;
+  Counter: Integer;
+  
+type
+  TSpinLockThread = class(TThread)
+    procedure Execute; override;
+  end;
+
+  TSpinLockRelaxedThread = class(TThread)
+    procedure Execute; override;
+  end;
+
+  TCriticalSectionThread = class(TThread)
+    procedure Execute; override;
+  end;
+
+procedure TSpinLockThread.Execute;
+var
+  I: Integer;
+begin
+  for I:=1 to INCREMENTS_PER_THREAD do
+    begin
+    MySpinLock.Enter;
+    try
+      Inc(Counter);
+    finally
+      MySpinLock.Exit;
+    end;
+    end;
+end;
+
+procedure TSpinLockRelaxedThread.Execute;
+var
+  I: Integer;
+begin
+  for I:=1 to INCREMENTS_PER_THREAD do
+    begin
+    MySpinLock.Enter;
+    try
+      Inc(Counter);
+    finally
+      MySpinLock.Exit(False);
+    end;
+    end;
+end;
+
+procedure TCriticalSectionThread.Execute;
+var
+  I: Integer;
+begin
+  for I:=1 to INCREMENTS_PER_THREAD do
+    begin
+    CS.Enter;
+    try
+      Inc(Counter);
+    finally
+      CS.Leave;
+    end;
+    end;
+end;
+
+function RunSpinLockBenchmark: Int64;
+var
+  Threads: array of TSpinLockThread;
+  I: Integer;
+  StartTime, EndTime: TDateTime;
+begin
+  WriteLn('--- Testing TSpinLock Performance (PublishNow=True) ---');
+  MySpinLock:=TSpinLock.Create(False);
+  Counter:=0;
+  SetLength(Threads, THREAD_COUNT);
+
+  WriteLn('Starting ', THREAD_COUNT, ' threads, ', INCREMENTS_PER_THREAD, ' ops each.');
+  StartTime:=Now;
+
+  for I:=0 to THREAD_COUNT - 1 do
+    begin
+    Threads[I]:=TSpinLockThread.Create(True);
+    Threads[I].FreeOnTerminate:=False;
+    Threads[I].Start;
+    end;
+
+  for I:=0 to THREAD_COUNT - 1 do
+    begin
+    Threads[I].WaitFor;
+    Threads[I].Free;
+    end;
+
+  EndTime:=Now;
+  Result:=MilliSecondsBetween(EndTime, StartTime);
+  WriteLn('TSpinLock (Strict) Time: ', Result, ' ms');
+  WriteLn('Counter: ', Counter);
+  WriteLn;
+end;
+
+function RunSpinLockRelaxedBenchmark: Int64;
+var
+  Threads: array of TSpinLockRelaxedThread;
+  I: Integer;
+  StartTime, EndTime: TDateTime;
+begin
+  WriteLn('--- Testing TSpinLock Performance (PublishNow=False) ---');
+  MySpinLock:=TSpinLock.Create(False);
+  Counter:=0;
+  SetLength(Threads, THREAD_COUNT);
+
+  WriteLn('Starting ', THREAD_COUNT, ' threads, ', INCREMENTS_PER_THREAD, ' ops each.');
+  StartTime:=Now;
+
+  for I:=0 to THREAD_COUNT - 1 do
+    begin
+    Threads[I]:=TSpinLockRelaxedThread.Create(True);
+    Threads[I].FreeOnTerminate:=False;
+    Threads[I].Start;
+    end;
+
+  for I:=0 to THREAD_COUNT - 1 do
+    begin
+    Threads[I].WaitFor;
+    Threads[I].Free;
+    end;
+
+  EndTime:=Now;
+  Result:=MilliSecondsBetween(EndTime, StartTime);
+  WriteLn('TSpinLock (Relaxed) Time: ', Result, ' ms');
+  WriteLn('Counter: ', Counter);
+  WriteLn;
+end;
+
+function RunCSBenchmark: Int64;
+var
+  Threads: array of TCriticalSectionThread;
+  I: Integer;
+  StartTime, EndTime: TDateTime;
+begin
+  WriteLn('--- Testing TCriticalSection Performance ---');
+  CS:=TCriticalSection.Create;
+  Counter:=0;
+  SetLength(Threads, THREAD_COUNT);
+
+  WriteLn('Starting ', THREAD_COUNT, ' threads, ', INCREMENTS_PER_THREAD, ' ops each.');
+  StartTime:=Now;
+
+  for I:=0 to THREAD_COUNT - 1 do
+    begin
+    Threads[I]:=TCriticalSectionThread.Create(True);
+    Threads[I].FreeOnTerminate:=False;
+    Threads[I].Start;
+    end;
+
+  for I:=0 to THREAD_COUNT - 1 do
+    begin
+    Threads[I].WaitFor;
+    Threads[I].Free;
+    end;
+
+  EndTime:=Now;
+  CS.Free;
+  Result:=MilliSecondsBetween(EndTime, StartTime);
+  WriteLn('TCriticalSection Time: ', Result, ' ms');
+  WriteLn('Counter: ', Counter);
+  WriteLn;
+end;
+
+procedure TestRecursion;
+begin
+  WriteLn('--- Testing Recursion (Thread Tracking) ---');
+  MySpinLock:=TSpinLock.Create(True);
+  WriteLn('Entering first time...');
+  MySpinLock.Enter;
+  try
+    WriteLn('Entering second time (recursive)...');
+    MySpinLock.Enter;
+    try
+      WriteLn('Inside recursive lock.');
+    finally
+      MySpinLock.Exit;
+    end;
+    WriteLn('Exited once.');
+  finally
+    MySpinLock.Exit;
+  end;
+  WriteLn('Exited twice. Success.');
+  WriteLn;
+end;
+
+procedure TestTryEnter;
+begin
+  WriteLn('--- Testing TryEnter ---');
+  MySpinLock:=TSpinLock.Create(False);
+  if MySpinLock.TryEnter then
+    begin
+    WriteLn('Acquired lock with TryEnter.');
+    if not MySpinLock.TryEnter then
+      WriteLn('Correctly failed to acquire already held lock with TryEnter.')
+    else
+      WriteLn('FAILURE: Acquired already held lock without tracking!');
+    MySpinLock.Exit;
+    end
+  else
+    WriteLn('FAILURE: Could not acquire free lock with TryEnter.');
+  WriteLn;
+end;
+
+var
+  SpinTime, SpinRelaxedTime, CSTime: Int64;
+begin
+  try
+    TestRecursion;
+    TestTryEnter;
+    
+    SpinTime:=RunSpinLockBenchmark;
+    SpinRelaxedTime:=RunSpinLockRelaxedBenchmark;
+    CSTime:=RunCSBenchmark;
+
+    WriteLn('--- Results ---');
+    WriteLn('TSpinLock (Strict):  ', SpinTime, ' ms');
+    WriteLn('TSpinLock (Relaxed): ', SpinRelaxedTime, ' ms');
+    WriteLn('TCriticalSection:    ', CSTime, ' ms');
+    
+    if SpinRelaxedTime < SpinTime then
+      WriteLn('Relaxed SpinLock was faster by ', SpinTime - SpinRelaxedTime, ' ms')
+    else
+      WriteLn('Relaxed SpinLock was slower/equal (Difference: ', SpinTime - SpinRelaxedTime, ' ms)');
+
+  except
+    on E: Exception do
+      WriteLn('Test failed with exception: ', E.ClassName, ': ', E.Message);
+  end;
+end.

+ 177 - 0
packages/fcl-base/src/syncobjs.pp

@@ -261,6 +261,35 @@ type
     property Count: Integer read FCount;
     property Count: Integer read FCount;
     property NextSpinCycleWillYield: Boolean read GetNextSpinCycleWillYield;
     property NextSpinCycleWillYield: Boolean read GetNextSpinCycleWillYield;
   end;
   end;
+  
+  // Fast lock, to be used only when lock is held for short times: uses the spinner.
+Type
+  { TSpinLock }
+
+  TSpinLock = record
+  private
+    FLock: LongInt;
+    FOwningThread: TThreadID;
+    FRecursionCount: Integer;
+    FThreadTracking: Boolean;
+    function GetIsLocked: Boolean; inline;
+    function GetIsLockedByCurrentThread: Boolean;
+    function GetIsThreadTrackingEnabled: Boolean; inline;
+    function GetIsOwnedByCurrentThread: Boolean; inline;
+  public
+    constructor Create(EnableThreadTracking: Boolean);
+    procedure Enter; inline;
+    procedure Exit(PublishNow: Boolean = True); inline;
+    function TryEnter: Boolean; overload; inline;
+    function TryEnter(Timeout: Cardinal): Boolean; overload; inline;
+    function TryEnter(const Timeout: TTimeSpan): Boolean; overload; inline;
+
+    property IsLocked: Boolean read GetIsLocked;
+    property IsOwnedByCurrentThread: Boolean read GetIsOwnedByCurrentThread;
+    property IsLockedByCurrentThread: Boolean read GetIsLockedByCurrentThread;
+    property IsThreadTrackingEnabled: Boolean read GetIsThreadTrackingEnabled;
+  end;
+
 
 
   // Guardian pattern. Use to see if an object was freed or not by adding a guardian instance to it.
   // Guardian pattern. Use to see if an object was freed or not by adding a guardian instance to it.
 
 
@@ -1222,6 +1251,154 @@ begin
   until (lElapsedTime >= lWaitTime) or lWait.NextSpinCycleWillYield;
   until (lElapsedTime >= lWaitTime) or lWait.NextSpinCycleWillYield;
 end;
 end;
 
 
+{ ---------------------------------------------------------------------
+  TSpinLock
+  ---------------------------------------------------------------------}
+
+
+function TSpinLock.GetIsLocked: Boolean;
+
+begin
+  Result:=FLock <> 0;
+end;
+
+
+function TSpinLock.GetIsThreadTrackingEnabled: Boolean; 
+
+begin
+  Result:=FThreadTracking;
+end;
+
+
+function TSpinLock.GetIsOwnedByCurrentThread: Boolean;
+
+begin
+  Result:=(FOwningThread = GetCurrentThreadId);
+end;
+
+
+function TSpinLock.GetIsLockedByCurrentThread: Boolean;
+
+begin
+  Result:=GetIsThreadTrackingEnabled and GetIsLocked and GetIsOwnedByCurrentThread
+end;
+
+
+constructor TSpinLock.Create(EnableThreadTracking: Boolean);
+ 
+begin
+  FLock:=0;
+  FOwningThread:=0;
+  FRecursionCount:=0;
+  FThreadTracking:=EnableThreadTracking;
+end;
+
+
+procedure TSpinLock.Enter;
+
+var
+  Spinner: TSpinWait;
+  CT: TThreadID;
+  
+begin
+  if FThreadTracking then
+    begin
+    CT:=GetCurrentThreadId;
+    if (FOwningThread=CT) then
+      begin
+      Inc(FRecursionCount);
+      System.Exit;
+      end;
+    end;
+
+  Spinner.Reset;
+  while TInterlocked.CompareExchange(FLock,1,0)<>0 do
+    Spinner.SpinCycle;
+
+  if FThreadTracking then
+    begin
+    FOwningThread:=GetCurrentThreadId;
+    FRecursionCount:=1;
+    end;
+end;
+
+
+procedure TSpinLock.Exit(PublishNow: Boolean);
+
+var
+  CT: TThreadID;
+
+begin
+  if FThreadTracking then
+    begin
+    CT:=GetCurrentThreadId;
+    if (FOwningThread<>CT) then
+      raise ELockException.Create('Thread does not own the lock');
+    
+    Dec(FRecursionCount);
+    if (FRecursionCount>0) then 
+      System.Exit;
+    FOwningThread:=TThreadID(0);
+    end;
+  
+  if PublishNow then
+    TInterlocked.Exchange(FLock, 0)
+  else
+    begin
+    // Not 100% sure about this one ?
+    ReadWriteBarrier;
+    FLock:=0;
+    end;
+end;
+
+
+function TSpinLock.TryEnter: Boolean;
+
+begin
+  if FThreadTracking and IsOwnedByCurrentThread then
+    begin
+    Inc(FRecursionCount);
+    System.Exit(True);
+    end;
+
+  Result:=TInterlocked.CompareExchange(FLock,1,0)=0;
+  
+  if Result and FThreadTracking then
+  begin
+    FOwningThread:=GetCurrentThreadId;
+    FRecursionCount:=1;
+  end;
+end;
+
+function TSpinLock.TryEnter(const Timeout: TTimeSpan): Boolean;
+var
+  LSpinner: TSpinWait;
+  LStart: QWord;
+  LTotalMs: QWord;
+begin
+  if TryEnter then 
+    System.Exit(True);
+
+  LTotalMs:=Round(Timeout.TotalMilliseconds);
+  LStart:=GetTickCount64;
+  LSpinner.Reset;
+
+  while (GetTickCount64-LStart)<LTotalMs do
+    begin
+    LSpinner.SpinCycle;
+    if TryEnter then 
+      System.Exit(True);
+    end;
+  Result:=False;
+end;
+
+
+function TSpinLock.TryEnter(Timeout: Cardinal): Boolean;
+
+begin
+  Result:=TryEnter(TTimeSpan.FromMilliseconds(Timeout));
+end;
+
 { ---------------------------------------------------------------------
 { ---------------------------------------------------------------------
   TGuardian
   TGuardian
   ---------------------------------------------------------------------}
   ---------------------------------------------------------------------}

+ 159 - 0
packages/libtar/tests/tlibtar1.pp

@@ -0,0 +1,159 @@
+{ Simple libtar test - creates a tar archive with a few entries }
+program libtar_simple_test;
+
+{$mode objfpc}{$H+}
+
+uses
+  SysUtils, libtar;
+
+const
+  TAR_FILENAME = 'simple_test.tar';
+  CONTENT_1 = 'Hello, World!';
+  CONTENT_2 = 'Some data in a subdirectory';
+  CONTENT_3 = 'hello.txt';
+  CONTENT_4 = 'hello.txt';
+
+  TotalEntries = 4;
+
+var
+  TW: TTarWriter;
+  TA: TTarArchive;
+  DirRec: TTarDirRec;
+  Content: RawByteString;
+  EntryCount: Integer;
+  Errors: Integer;
+begin
+  Errors := 0;
+
+  WriteLn('Creating tar archive: ', TAR_FILENAME);
+
+  try
+    TW := TTarWriter.Create(TAR_FILENAME);
+    try
+        { Set default permissions }
+        TW.Permissions := [tpReadByOwner, tpWriteByOwner, tpReadByGroup, tpReadByOther];
+
+        { Add a simple text file }
+        WriteLn('  Adding: hello.txt');
+        TW.AddString(CONTENT_1, 'hello.txt', Now);
+
+        { Add a file in a subdirectory }
+        WriteLn('  Adding: subdir/data.txt');
+        TW.AddString(CONTENT_2, 'subdir/data.txt', Now);
+
+        { Add a directory entry }
+        WriteLn('  Adding: emptydir/');
+        TW.AddDir(CONTENT_4, Now);
+
+        { Add a symbolic link }
+        WriteLn('  Adding: link.txt -> hello.txt');
+        TW.AddSymbolicLink('link.txt', CONTENT_3, Now);
+
+        TW.Finalize;
+    finally
+        TW.Free;
+    end;
+
+    WriteLn('Done. Archive created successfully.');
+    WriteLn;
+
+    WriteLn('=== Reading and verifying archive ===');
+    WriteLn;
+
+    TA := TTarArchive.Create(TAR_FILENAME);
+    try
+        EntryCount := 0;
+
+        while TA.FindNext(DirRec) do
+        begin
+        Inc(EntryCount);
+        WriteLn('Entry ', EntryCount, ':');
+        WriteLn('  Name: ', DirRec.Name);
+        WriteLn('  Size: ', DirRec.Size);
+        WriteLn('  Type: ', FILETYPE_NAME[DirRec.FileType]);
+
+        if DirRec.FileType = ftSymbolicLink then
+            begin
+            if DirRec.LinkName <> CONTENT_3 then
+                begin
+                Inc(Errors);
+                writeln('  Wrong link value');
+                end;
+            WriteLn('  Link: -> ', DirRec.LinkName);
+            end;
+
+        if DirRec.FileType = ftDirectory then
+            begin
+            if DirRec.Name <> CONTENT_4 then
+                begin
+                Inc(Errors);
+                writeln('  Wrong directory Name');
+                end;
+            end;
+
+        if DirRec.ChecksumOK then
+            WriteLn('  Checksum: OK')
+        else
+        begin
+            WriteLn('  Checksum: FAILED');
+            Inc(Errors);
+        end;
+
+        { Verify content for regular files }
+        if DirRec.FileType = ftNormal then
+        begin
+            Content := TA.ReadFile;
+
+            if DirRec.Name = 'hello.txt' then
+            begin
+            if Content = CONTENT_1 then
+                WriteLn('  Content: verified OK')
+            else
+            begin
+                WriteLn('  Content: MISMATCH');
+                Inc(Errors);
+            end;
+            end
+            else if DirRec.Name = 'subdir/data.txt' then
+            begin
+            if Content = CONTENT_2 then
+                WriteLn('  Content: verified OK')
+            else
+            begin
+                WriteLn('  Content: MISMATCH');
+                Inc(Errors);
+            end;
+            end;
+        end;
+
+        WriteLn;
+        end;
+    finally
+        TA.Free;
+    end;
+
+    { === PART 3: Summary === }
+    WriteLn('=== Summary ===');
+    WriteLn;
+    WriteLn('Total entries: ', EntryCount);
+
+    if EntryCount <> TotalEntries then
+    begin
+        WriteLn('ERROR: Expected ',TotalEntries,' entries!');
+        Inc(Errors);
+    end;
+
+    if Errors = 0 then
+        WriteLn('All tests PASSED.')
+    else
+        WriteLn('FAILED with ', Errors, ' error(s).');
+
+    WriteLn;
+
+  finally
+    { Cleanup }
+    DeleteFile(TAR_FILENAME);
+  end;
+
+  Halt(Errors);
+end.

File diff ditekan karena terlalu besar
+ 155 - 145
packages/vcl-compat/src/system.threading.pp


+ 8 - 2
tests/Makefile

@@ -2,7 +2,7 @@
 # Don't edit, this file is generated by FPCMake Version 2.0.0
 # Don't edit, this file is generated by FPCMake Version 2.0.0
 #
 #
 default: allexectests
 default: allexectests
-MAKEFILETARGETS=i386-linux i386-go32v2 i386-win32 i386-os2 i386-freebsd i386-beos i386-haiku i386-netbsd i386-solaris i386-netware i386-openbsd i386-wdosx i386-darwin i386-emx i386-watcom i386-netwlibc i386-wince i386-embedded i386-symbian i386-nativent i386-iphonesim i386-android i386-aros m68k-linux m68k-netbsd m68k-amiga m68k-atari m68k-palmos m68k-macosclassic m68k-embedded m68k-sinclairql m68k-human68k powerpc-linux powerpc-netbsd powerpc-amiga powerpc-macosclassic powerpc-darwin powerpc-morphos powerpc-embedded powerpc-wii powerpc-aix sparc-linux sparc-netbsd sparc-solaris sparc-embedded x86_64-linux x86_64-freebsd x86_64-haiku x86_64-netbsd x86_64-solaris x86_64-openbsd x86_64-darwin x86_64-win64 x86_64-embedded x86_64-iphonesim x86_64-android x86_64-aros x86_64-dragonfly arm-linux arm-netbsd arm-palmos arm-wince arm-gba arm-nds arm-embedded arm-symbian arm-android arm-aros arm-freertos arm-ios powerpc64-linux powerpc64-darwin powerpc64-embedded powerpc64-aix powerpc64-freebsd avr-embedded armeb-linux armeb-embedded mips-linux mipsel-linux mipsel-embedded mipsel-android mipsel-ps1 mips64-linux mips64el-linux jvm-java jvm-android i8086-embedded i8086-msdos i8086-win16 aarch64-linux aarch64-freebsd aarch64-darwin aarch64-win64 aarch64-embedded aarch64-iphonesim aarch64-android aarch64-ios wasm32-embedded wasm32-wasip1 wasm32-wasip1threads wasm32-wasip2 sparc64-linux riscv32-linux riscv32-embedded riscv32-freertos riscv64-linux riscv64-embedded xtensa-linux xtensa-embedded xtensa-freertos z80-embedded z80-zxspectrum z80-msxdos z80-amstradcpc loongarch64-linux
+MAKEFILETARGETS=i386-linux i386-go32v2 i386-win32 i386-os2 i386-freebsd i386-beos i386-haiku i386-netbsd i386-solaris i386-qnx i386-netware i386-openbsd i386-wdosx i386-darwin i386-emx i386-watcom i386-netwlibc i386-wince i386-embedded i386-symbian i386-nativent i386-iphonesim i386-android i386-aros m68k-linux m68k-netbsd m68k-amiga m68k-atari m68k-palmos m68k-macosclassic m68k-embedded m68k-sinclairql m68k-human68k powerpc-linux powerpc-netbsd powerpc-amiga powerpc-macosclassic powerpc-darwin powerpc-morphos powerpc-embedded powerpc-wii powerpc-aix sparc-linux sparc-netbsd sparc-solaris sparc-embedded x86_64-linux x86_64-freebsd x86_64-haiku x86_64-netbsd x86_64-solaris x86_64-openbsd x86_64-darwin x86_64-win64 x86_64-embedded x86_64-iphonesim x86_64-android x86_64-aros x86_64-dragonfly arm-linux arm-netbsd arm-palmos arm-wince arm-gba arm-nds arm-embedded arm-symbian arm-android arm-aros arm-freertos arm-ios powerpc64-linux powerpc64-darwin powerpc64-embedded powerpc64-aix powerpc64-freebsd avr-embedded armeb-linux armeb-embedded mips-linux mipsel-linux mipsel-embedded mipsel-android mipsel-ps1 mips64-linux mips64el-linux jvm-java jvm-android i8086-embedded i8086-msdos i8086-win16 aarch64-linux aarch64-freebsd aarch64-darwin aarch64-win64 aarch64-embedded aarch64-iphonesim aarch64-android aarch64-ios wasm32-embedded wasm32-wasip1 wasm32-wasip1threads wasm32-wasip2 sparc64-linux riscv32-linux riscv32-embedded riscv32-freertos riscv64-linux riscv64-embedded xtensa-linux xtensa-embedded xtensa-freertos z80-embedded z80-zxspectrum z80-msxdos z80-amstradcpc loongarch64-linux
 BSDs = freebsd netbsd openbsd darwin dragonfly
 BSDs = freebsd netbsd openbsd darwin dragonfly
 UNIXs = linux $(BSDs) solaris qnx haiku aix
 UNIXs = linux $(BSDs) solaris qnx haiku aix
 LIMIT83fs = go32v2 os2 emx watcom msdos win16 atari human68k
 LIMIT83fs = go32v2 os2 emx watcom msdos win16 atari human68k
@@ -393,6 +393,9 @@ endif
 ifeq ($(CPU_OS_TARGET),i386-solaris)
 ifeq ($(CPU_OS_TARGET),i386-solaris)
 override TARGET_PROGRAMS+=gparmake createlst
 override TARGET_PROGRAMS+=gparmake createlst
 endif
 endif
+ifeq ($(CPU_OS_TARGET),i386-qnx)
+override TARGET_PROGRAMS+=gparmake createlst
+endif
 ifeq ($(CPU_OS_TARGET),i386-netware)
 ifeq ($(CPU_OS_TARGET),i386-netware)
 override TARGET_PROGRAMS+=gparmake createlst
 override TARGET_PROGRAMS+=gparmake createlst
 endif
 endif
@@ -1543,6 +1546,9 @@ endif
 ifeq ($(CPU_OS_TARGET),i386-solaris)
 ifeq ($(CPU_OS_TARGET),i386-solaris)
 REQUIRE_PACKAGES_RTL=1
 REQUIRE_PACKAGES_RTL=1
 endif
 endif
+ifeq ($(CPU_OS_TARGET),i386-qnx)
+REQUIRE_PACKAGES_RTL=1
+endif
 ifeq ($(CPU_OS_TARGET),i386-netware)
 ifeq ($(CPU_OS_TARGET),i386-netware)
 REQUIRE_PACKAGES_RTL=1
 REQUIRE_PACKAGES_RTL=1
 endif
 endif
@@ -2603,7 +2609,7 @@ TESTDIRECTDIRS=
 TESTSUBDIRS=cg cg/variants cg/cdecl cpu8/avr cpu16 cpu16/i8086 library opt wasm $(addprefix units/,$(TESTUNITDIRS))
 TESTSUBDIRS=cg cg/variants cg/cdecl cpu8/avr cpu16 cpu16/i8086 library opt wasm $(addprefix units/,$(TESTUNITDIRS))
 TESTPACKAGESDIRS=bzip2 cocoaint fcl-base fcl-db fcl-image fcl-registry fcl-xml hash rtl-objpas univint webtbs win-base zlib
 TESTPACKAGESDIRS=bzip2 cocoaint fcl-base fcl-db fcl-image fcl-registry fcl-xml hash rtl-objpas univint webtbs win-base zlib
 TESTPACKAGESUBDIRS=$(addprefix packages/,$(TESTPACKAGESDIRS))
 TESTPACKAGESUBDIRS=$(addprefix packages/,$(TESTPACKAGESDIRS))
-TESTPACKAGESDIRECTDIRS=rtl-objpas rtl-generics hash regexpr fcl-registry fcl-passrc fcl-json fcl-image pastojs fcl-process
+TESTPACKAGESDIRECTDIRS=rtl-objpas rtl-generics hash regexpr fcl-registry fcl-passrc fcl-json fcl-image pastojs fcl-process libtar
 TESTPACKAGESDIRECTSUBDIRS=$(addprefix ../packages/,$(addsuffix /tests,$(TESTPACKAGESDIRECTDIRS)))
 TESTPACKAGESDIRECTSUBDIRS=$(addprefix ../packages/,$(addsuffix /tests,$(TESTPACKAGESDIRECTDIRS)))
 ifdef QUICKTEST
 ifdef QUICKTEST
 export QUICKTEST
 export QUICKTEST

+ 1 - 1
tests/Makefile.fpc

@@ -175,7 +175,7 @@ TESTSUBDIRS=cg cg/variants cg/cdecl cpu8/avr cpu16 cpu16/i8086 library opt wasm
 TESTPACKAGESDIRS=bzip2 cocoaint fcl-base fcl-db fcl-image fcl-registry fcl-xml hash rtl-objpas univint webtbs win-base zlib
 TESTPACKAGESDIRS=bzip2 cocoaint fcl-base fcl-db fcl-image fcl-registry fcl-xml hash rtl-objpas univint webtbs win-base zlib
 
 
 TESTPACKAGESUBDIRS=$(addprefix packages/,$(TESTPACKAGESDIRS))
 TESTPACKAGESUBDIRS=$(addprefix packages/,$(TESTPACKAGESDIRS))
-TESTPACKAGESDIRECTDIRS=rtl-objpas rtl-generics hash regexpr fcl-registry fcl-passrc fcl-json fcl-image pastojs fcl-process
+TESTPACKAGESDIRECTDIRS=rtl-objpas rtl-generics hash regexpr fcl-registry fcl-passrc fcl-json fcl-image pastojs fcl-process libtar
 TESTPACKAGESDIRECTSUBDIRS=$(addprefix ../packages/,$(addsuffix /tests,$(TESTPACKAGESDIRECTDIRS)))
 TESTPACKAGESDIRECTSUBDIRS=$(addprefix ../packages/,$(addsuffix /tests,$(TESTPACKAGESDIRECTDIRS)))
 
 
 ifdef QUICKTEST
 ifdef QUICKTEST

Beberapa file tidak ditampilkan karena terlalu banyak file yang berubah dalam diff ini