Browse Source

* synchronized with trunk

git-svn-id: branches/unicodekvm@49282 -
nickysn 4 years ago
parent
commit
23000ed570
100 changed files with 9305 additions and 295 deletions
  1. 37 0
      .gitattributes
  2. 0 0
      compiler/MPWMake
  3. 1 1
      compiler/Makefile
  4. 1 1
      compiler/Makefile.fpc
  5. 41 3
      compiler/aarch64/agcpugas.pas
  6. 15 7
      compiler/aarch64/aoptcpu.pas
  7. 2 1
      compiler/aarch64/hlcgcpu.pas
  8. 57 11
      compiler/aarch64/ncpuinl.pas
  9. 2 1
      compiler/aasmtai.pas
  10. 4 0
      compiler/arm/aasmcpu.pas
  11. 3 3
      compiler/arm/cpubase.pas
  12. 13 5
      compiler/arm/narmset.pas
  13. 6 0
      compiler/arm/raarmgas.pas
  14. 5 4
      compiler/arm/rgcpu.pas
  15. 9 1
      compiler/assemble.pas
  16. 1 0
      compiler/globals.pas
  17. 1 2
      compiler/m68k/ag68kvasm.pas
  18. 8 0
      compiler/m68k/cpubase.pas
  19. 8 8
      compiler/m68k/r68kgri.inc
  20. 18 18
      compiler/m68k/r68ksri.inc
  21. 6 1
      compiler/m68k/ra68kmot.pas
  22. 34 14
      compiler/ncnv.pas
  23. 4 0
      compiler/ogcoff.pas
  24. 4 1
      compiler/ogelf.pas
  25. 8 0
      compiler/options.pas
  26. 1 0
      compiler/pass_1.pas
  27. 2 1
      compiler/pdecsub.pas
  28. 3 0
      compiler/pgenutil.pas
  29. 8 0
      compiler/pmodules.pas
  30. 14 5
      compiler/scanner.pas
  31. 4 2
      compiler/systems/t_bsd.pas
  32. 17 2
      compiler/systems/t_freertos.pas
  33. 1 1
      compiler/systems/t_nds.pas
  34. 16 3
      compiler/systems/t_sinclairql.pas
  35. 2 2
      compiler/utils/mk68kreg.pp
  36. 35 27
      compiler/wasm32/agllvmmc.pas
  37. 5 2
      compiler/wasm32/agwat.pas
  38. 70 2
      compiler/wasm32/nwasmflw.pas
  39. 10 0
      compiler/x86/aasmcpu.pas
  40. 0 5
      compiler/x86/nx86cnv.pas
  41. 7 1
      packages/fcl-db/src/base/fields.inc
  42. 3265 0
      packages/fcl-mustache/Makefile
  43. 128 0
      packages/fcl-mustache/Makefile.fpc
  44. 27 0
      packages/fcl-mustache/examples/README.txt
  45. 57 0
      packages/fcl-mustache/examples/demo1.lpi
  46. 42 0
      packages/fcl-mustache/examples/demo1.lpr
  47. 57 0
      packages/fcl-mustache/examples/demo2.lpi
  48. 48 0
      packages/fcl-mustache/examples/demo2.lpr
  49. 7 0
      packages/fcl-mustache/examples/family.csv
  50. 10 0
      packages/fcl-mustache/examples/family.json
  51. 22 0
      packages/fcl-mustache/examples/family.tmpl
  52. 57 0
      packages/fcl-mustache/examples/mustache.lpi
  53. 207 0
      packages/fcl-mustache/examples/mustache.lpr
  54. 49 0
      packages/fcl-mustache/fpmake.pp
  55. 268 0
      packages/fcl-mustache/src/fpdbmustache.pp
  56. 399 0
      packages/fcl-mustache/src/fpexmustache.pp
  57. 1340 0
      packages/fcl-mustache/src/fpmustache.pp
  58. 0 0
      packages/fcl-mustache/tests/spec/comments.json
  59. 0 0
      packages/fcl-mustache/tests/spec/delimiters.json
  60. 0 0
      packages/fcl-mustache/tests/spec/interpolation.json
  61. 0 0
      packages/fcl-mustache/tests/spec/inverted.json
  62. 0 0
      packages/fcl-mustache/tests/spec/partials.json
  63. 0 0
      packages/fcl-mustache/tests/spec/sections.json
  64. 290 0
      packages/fcl-mustache/tests/tcbasemustache.pas
  65. 149 0
      packages/fcl-mustache/tests/tcdbmustache.pas
  66. 199 0
      packages/fcl-mustache/tests/tcexmustache.pas
  67. 728 0
      packages/fcl-mustache/tests/tcmustache.pas
  68. 188 0
      packages/fcl-mustache/tests/tcspecs.pas
  69. 88 0
      packages/fcl-mustache/tests/testmustache.lpi
  70. 29 0
      packages/fcl-mustache/tests/testmustache.lpr
  71. 50 9
      packages/fcl-passrc/src/pasresolver.pp
  72. 1 0
      packages/fcl-passrc/src/pasuseanalyzer.pas
  73. 5 2
      packages/fcl-passrc/src/pparser.pp
  74. 27 0
      packages/fcl-passrc/tests/tcresolvegenerics.pas
  75. 13 3
      packages/fcl-web/src/base/custhttpapp.pp
  76. 1 0
      packages/fpmake_add.inc
  77. 6 0
      packages/fpmake_proc.inc
  78. 85 30
      packages/fpmkunit/src/fpmkunit.pp
  79. 1 1
      packages/openssl/src/openssl.pas
  80. 76 39
      packages/pastojs/src/fppas2js.pp
  81. 140 10
      packages/pastojs/tests/tcgenerics.pas
  82. 38 37
      packages/pastojs/tests/tcmodules.pas
  83. 5 2
      packages/winunits-base/src/comserv.pp
  84. 13 2
      rtl/aarch64/math.inc
  85. 8 8
      rtl/aarch64/mathu.inc
  86. 1 1
      rtl/darwin/Makefile
  87. 1 1
      rtl/darwin/Makefile.fpc
  88. 7 5
      rtl/objpas/sysutils/dati.inc
  89. 1 1
      rtl/objpas/sysutils/filutilh.inc
  90. 1 1
      rtl/objpas/sysutils/osutil.inc
  91. 4 3
      rtl/sinclairql/buildrtl.pp
  92. 50 0
      rtl/sinclairql/classes.pp
  93. 1 1
      rtl/sinclairql/si_prc.pp
  94. 501 0
      rtl/sinclairql/sysutils.pp
  95. 9 4
      rtl/win/sysutils.pp
  96. 8 0
      tests/tbf/tb0274.pp
  97. 22 0
      tests/webtbf/tw38771.pp
  98. 42 0
      tests/webtbs/tw38353.pp
  99. 23 0
      tests/webtbs/tw38631.pp
  100. 28 0
      tests/webtbs/tw38766.pp

+ 37 - 0
.gitattributes

@@ -3837,6 +3837,35 @@ packages/fcl-json/tests/testjsondata.pp svneol=native#text/plain
 packages/fcl-json/tests/testjsonparser.pp svneol=native#text/plain
 packages/fcl-json/tests/testjsonreader.pp svneol=native#text/plain
 packages/fcl-json/tests/testjsonrtti.pp svneol=native#text/plain
+packages/fcl-mustache/Makefile svneol=native#text/plain
+packages/fcl-mustache/Makefile.fpc svneol=native#text/plain
+packages/fcl-mustache/examples/README.txt svneol=native#text/plain
+packages/fcl-mustache/examples/demo1.lpi svneol=native#text/plain
+packages/fcl-mustache/examples/demo1.lpr svneol=native#text/plain
+packages/fcl-mustache/examples/demo2.lpi svneol=native#text/plain
+packages/fcl-mustache/examples/demo2.lpr svneol=native#text/plain
+packages/fcl-mustache/examples/family.csv svneol=native#text/plain
+packages/fcl-mustache/examples/family.json svneol=native#text/plain
+packages/fcl-mustache/examples/family.tmpl svneol=native#text/plain
+packages/fcl-mustache/examples/mustache.lpi svneol=native#text/plain
+packages/fcl-mustache/examples/mustache.lpr svneol=native#text/plain
+packages/fcl-mustache/fpmake.pp svneol=native#text/plain
+packages/fcl-mustache/src/fpdbmustache.pp svneol=native#text/plain
+packages/fcl-mustache/src/fpexmustache.pp svneol=native#text/plain
+packages/fcl-mustache/src/fpmustache.pp svneol=native#text/plain
+packages/fcl-mustache/tests/spec/comments.json svneol=native#text/plain
+packages/fcl-mustache/tests/spec/delimiters.json svneol=native#text/plain
+packages/fcl-mustache/tests/spec/interpolation.json svneol=native#text/plain
+packages/fcl-mustache/tests/spec/inverted.json svneol=native#text/plain
+packages/fcl-mustache/tests/spec/partials.json svneol=native#text/plain
+packages/fcl-mustache/tests/spec/sections.json svneol=native#text/plain
+packages/fcl-mustache/tests/tcbasemustache.pas svneol=native#text/plain
+packages/fcl-mustache/tests/tcdbmustache.pas svneol=native#text/plain
+packages/fcl-mustache/tests/tcexmustache.pas svneol=native#text/plain
+packages/fcl-mustache/tests/tcmustache.pas svneol=native#text/plain
+packages/fcl-mustache/tests/tcspecs.pas svneol=native#text/plain
+packages/fcl-mustache/tests/testmustache.lpi svneol=native#text/plain
+packages/fcl-mustache/tests/testmustache.lpr svneol=native#text/plain
 packages/fcl-net/Makefile svneol=native#text/plain
 packages/fcl-net/Makefile.fpc svneol=native#text/plain
 packages/fcl-net/Makefile.fpc.fpcmake svneol=native#text/plain
@@ -12093,6 +12122,7 @@ rtl/riscv64/stringss.inc svneol=native#text/plain
 rtl/sinclairql/Makefile svneol=native#text/plain
 rtl/sinclairql/Makefile.fpc svneol=native#text/plain
 rtl/sinclairql/buildrtl.pp svneol=native#text/plain
+rtl/sinclairql/classes.pp svneol=native#text/plain
 rtl/sinclairql/qdos.inc svneol=native#text/plain
 rtl/sinclairql/qdosfuncs.inc svneol=native#text/plain
 rtl/sinclairql/qdosh.inc svneol=native#text/plain
@@ -12107,6 +12137,7 @@ rtl/sinclairql/sysheap.inc svneol=native#text/plain
 rtl/sinclairql/sysos.inc svneol=native#text/plain
 rtl/sinclairql/sysosh.inc svneol=native#text/plain
 rtl/sinclairql/system.pp svneol=native#text/plain
+rtl/sinclairql/sysutils.pp svneol=native#text/plain
 rtl/sinclairql/tthread.inc svneol=native#text/plain
 rtl/solaris/Makefile svneol=native#text/plain
 rtl/solaris/Makefile.fpc svneol=native#text/plain
@@ -12895,6 +12926,7 @@ tests/tbf/tb0270.pp svneol=native#text/pascal
 tests/tbf/tb0271.pp svneol=native#text/pascal
 tests/tbf/tb0272.pp svneol=native#text/plain
 tests/tbf/tb0273.pp svneol=native#text/pascal
+tests/tbf/tb0274.pp svneol=native#text/pascal
 tests/tbf/tb0588.pp svneol=native#text/pascal
 tests/tbf/ub0115.pp svneol=native#text/plain
 tests/tbf/ub0149.pp svneol=native#text/plain
@@ -16890,6 +16922,7 @@ tests/webtbf/tw38289b.pp svneol=native#text/pascal
 tests/webtbf/tw38439.pp svneol=native#text/pascal
 tests/webtbf/tw38504.pp svneol=native#text/pascal
 tests/webtbf/tw38504b.pp svneol=native#text/pascal
+tests/webtbf/tw38771.pp svneol=native#text/pascal
 tests/webtbf/tw3930a.pp svneol=native#text/plain
 tests/webtbf/tw3931b.pp svneol=native#text/plain
 tests/webtbf/tw3969.pp svneol=native#text/plain
@@ -18839,6 +18872,7 @@ tests/webtbs/tw3833.pp svneol=native#text/plain
 tests/webtbs/tw38337.pp svneol=native#text/plain
 tests/webtbs/tw38339.pp svneol=native#text/plain
 tests/webtbs/tw38351.pp -text svneol=native#text/pascal
+tests/webtbs/tw38353.pp svneol=native#text/pascal
 tests/webtbs/tw38385.pp svneol=native#text/pascal
 tests/webtbs/tw38390.pp svneol=native#text/pascal
 tests/webtbs/tw3840.pp svneol=native#text/plain
@@ -18856,6 +18890,7 @@ tests/webtbs/tw38549c.pp svneol=native#text/plain
 tests/webtbs/tw38549d.pp svneol=native#text/plain
 tests/webtbs/tw38557.pp svneol=native#text/pascal
 tests/webtbs/tw3863.pp svneol=native#text/plain
+tests/webtbs/tw38631.pp svneol=native#text/pascal
 tests/webtbs/tw38636.pp svneol=native#text/plain
 tests/webtbs/tw3864.pp svneol=native#text/plain
 tests/webtbs/tw38642.pp svneol=native#text/pascal
@@ -18865,6 +18900,8 @@ tests/webtbs/tw3870.pp svneol=native#text/plain
 tests/webtbs/tw38703.pp svneol=native#text/pascal
 tests/webtbs/tw38718.pp svneol=native#text/pascal
 tests/webtbs/tw38733.pp svneol=native#text/pascal
+tests/webtbs/tw38766.pp svneol=native#text/plain
+tests/webtbs/tw38802.pp svneol=native#text/pascal
 tests/webtbs/tw3893.pp svneol=native#text/plain
 tests/webtbs/tw3898.pp svneol=native#text/plain
 tests/webtbs/tw3899.pp svneol=native#text/plain

File diff suppressed because it is too large
+ 0 - 0
compiler/MPWMake


+ 1 - 1
compiler/Makefile

@@ -581,7 +581,7 @@ endif
 endif
 endif
 endif
-override LOCALOPT+=-d$(CPC_TARGET) -dGDB -dBROWSERLOG
+override LOCALOPT+=-d$(CPC_TARGET) -dGDB
 ifdef LLVM
 ifeq ($(findstring $(PPC_TARGET),x86_64 aarch64 arm),)
 $(error The $(PPC_TARGET) architecture is not (yet) supported by the FPC/LLVM code generator)

+ 1 - 1
compiler/Makefile.fpc

@@ -323,7 +323,7 @@ endif
 endif
 
 # set correct defines (-d$(CPU_TARGET) is automatically added in makefile.fpc)
-override LOCALOPT+=-d$(CPC_TARGET) -dGDB -dBROWSERLOG
+override LOCALOPT+=-d$(CPC_TARGET) -dGDB
 
 #include LLVM define/directory if requested
 ifdef LLVM

+ 41 - 3
compiler/aarch64/agcpugas.pas

@@ -41,10 +41,12 @@ unit agcpugas;
 
       TAArch64Assembler=class(TGNUassembler)
         constructor CreateWithWriter(info: pasminfo; wr: TExternalAssemblerOutputFile; freewriter, smart: boolean); override;
+        function MakeCmdLine: TCmdStr; override;
       end;
 
       TAArch64AppleAssembler=class(TAppleGNUassembler)
         constructor CreateWithWriter(info: pasminfo; wr: TExternalAssemblerOutputFile; freewriter, smart: boolean); override;
+        function MakeCmdLine: TCmdStr; override;
       end;
 
       TAArch64ClangGASAssembler=class(TAArch64Assembler)
@@ -53,6 +55,7 @@ unit agcpugas;
       protected
         function sectionflags(secflags:TSectionFlags):string;override;
       public
+        function MakeCmdLine: TCmdStr; override;
         procedure WriteAsmList; override;
       end;
 
@@ -64,6 +67,18 @@ unit agcpugas;
 
     const
       cputype_to_gas_march : array[tcputype] of string = (
+        '', // cpu_none
+        '', // armv8 is not accepted by GNU assembler
+        'armv8-a',
+        'armv8.1-a',
+        'armv8.2-a',
+        'armv8.3-a',
+        'armv8.4-a',
+        'armv8.5-a',
+        'armv8.6-a'
+      );
+
+      cputype_to_clang_march : array[tcputype] of string = (
         '', // cpu_none
         'armv8',
         'armv8-a',
@@ -94,6 +109,15 @@ unit agcpugas;
         InstrWriter := TAArch64InstrWriter.create(self);
       end;
 
+    function TAArch64Assembler.MakeCmdLine: TCmdStr;
+      begin
+        result:=inherited MakeCmdLine;
+        if cputype_to_gas_march[current_settings.cputype] <> '' then
+	  Replace(result,'$MARCHOPT','-march='+cputype_to_gas_march[current_settings.cputype])
+        else
+          Replace(result,'$MARCHOPT','');
+      end;
+
 {****************************************************************************}
 {                      Apple AArch64 Assembler writer                        }
 {****************************************************************************}
@@ -105,10 +129,24 @@ unit agcpugas;
       end;
 
 
+    function TAArch64AppleAssembler.MakeCmdLine: TCmdStr;
+      begin
+        result:=inherited MakeCmdLine;
+        if cputype_to_gas_march[current_settings.cputype] <> '' then
+	  Replace(result,'$MARCHOPT','-march='+cputype_to_gas_march[current_settings.cputype])
+        else
+          Replace(result,'$MARCHOPT','');
+      end;
+
 {****************************************************************************}
 {                      CLang AArch64 Assembler writer                        }
 {****************************************************************************}
 
+    function TAArch64ClangGASAssembler.MakeCmdLine: TCmdStr;
+      begin
+        result:=inherited MakeCmdLine;
+      end;
+
     procedure TAArch64ClangGASAssembler.TransformSEHDirectives(list:TAsmList);
 
       function convert_unwinddata(list:tasmlist):tdynamicarray;
@@ -773,7 +811,7 @@ unit agcpugas;
             id     : as_gas;
             idtxt  : 'AS';
             asmbin : 'as';
-            asmcmd : '-o $OBJ $EXTRAOPT $ASM';
+            asmcmd : '-o $OBJ $MARCHOPT $EXTRAOPT $ASM';
             supported_targets : [system_aarch64_freebsd,system_aarch64_linux,system_aarch64_android];
             flags : [af_needar,af_smartlink_sections];
             labelprefix : '.L';
@@ -787,7 +825,7 @@ unit agcpugas;
             id     : as_clang_asdarwin;
             idtxt  : 'CLANG';
             asmbin : 'clang';
-            asmcmd : '-x assembler -c -target $TRIPLET -o $OBJ $EXTRAOPT -x assembler $ASM';
+            asmcmd : '-x assembler -c -target $TRIPLET -o $OBJ $MARCHOPT $EXTRAOPT -x assembler $ASM';
             supported_targets : [system_aarch64_ios,system_aarch64_darwin];
             flags : [af_needar,af_smartlink_sections,af_supports_dwarf,af_llvm,af_supports_hlcfi];
             labelprefix : 'L';
@@ -801,7 +839,7 @@ unit agcpugas;
             id     : as_clang_gas;
             idtxt  : 'CLANG';
             asmbin : 'clang';
-            asmcmd : '-x assembler -c -target $TRIPLET -o $OBJ $EXTRAOPT -x assembler $ASM';
+            asmcmd : '-x assembler -c -target $TRIPLET -o $OBJ $MARCHOPT $EXTRAOPT -x assembler $ASM';
             supported_targets : [system_aarch64_win64];
             flags : [af_needar,af_smartlink_sections,af_supports_dwarf,af_llvm,af_supports_hlcfi];
             labelprefix : '.L';

+ 15 - 7
compiler/aarch64/aoptcpu.pas

@@ -379,15 +379,23 @@ Implementation
                          taicpu(hp1).oper[0]^.reg, taicpu(p).oper[1]^.reg,
                          shifterop);
 
+                { Make sure the register used in the shifting is tracked all
+                  the way through, otherwise it may become deallocated while
+                  it's still live and cause incorrect optimisations later }
+                if (taicpu(hp1).oper[0]^.reg <> taicpu(p).oper[1]^.reg) then
+                  begin
+                    TransferUsedRegs(TmpUsedRegs);
+                    UpdateUsedRegs(TmpUsedRegs, tai(p.Next));
+                    ALlocRegBetween(taicpu(p).oper[1]^.reg, p, hp1, TmpUsedRegs);
+                  end;
+
                 taicpu(hp2).fileinfo:=taicpu(hp1).fileinfo;
                 asml.insertbefore(hp2, hp1);
-                GetNextInstruction(p, hp2);
-                asml.remove(p);
-                asml.remove(hp1);
-                p.free;
-                hp1.free;
-                p:=hp2;
-                DebugMsg('Peephole FoldShiftProcess done', p);
+
+                RemoveInstruction(hp1);
+                RemoveCurrentp(p);
+
+                DebugMsg('Peephole FoldShiftProcess done', hp2);
                 Result:=true;
                 break;
               end;

+ 2 - 1
compiler/aarch64/hlcgcpu.pas

@@ -210,7 +210,8 @@ implementation
       if slopt in [SL_SETZERO,SL_SETMAX] then
         inherited
       else if not(sreg.bitlen in [32,64]) or
-              (sreg.startbit<>0) then
+              (sreg.startbit<>0) or
+              (getsubreg(fromreg)<getsubreg(sreg.subsetreg)) then
         begin
           makeregssamesize(list,def_cgsize(fromsize),sreg.subsetregsize,fromreg,sreg.subsetreg,fromreg,toreg);
           list.concat(taicpu.op_reg_reg_const_const(A_BFI,toreg,fromreg,sreg.startbit,sreg.bitlen))

+ 57 - 11
compiler/aarch64/ncpuinl.pas

@@ -1,7 +1,7 @@
 {
     Copyright (c) 1998-2002 by Florian Klaempfl
 
-    Generates ARM inline nodes
+    Generates AAarch64 inline nodes
 
     This program is free software; you can redistribute it and/or modify
     it under the terms of the GNU General Public License as published by
@@ -35,6 +35,8 @@ interface
         function first_sqrt_real: tnode; override;
         function first_round_real: tnode; override;
         function first_trunc_real: tnode; override;
+        function first_int_real: tnode; override;
+        function first_frac_real: tnode; override;
         function first_fma : tnode; override;
         procedure second_abs_real; override;
         procedure second_sqr_real; override;
@@ -42,6 +44,8 @@ interface
         procedure second_abs_long; override;
         procedure second_round_real; override;
         procedure second_trunc_real; override;
+        procedure second_int_real; override;
+        procedure second_frac_real; override;
         procedure second_get_frame; override;
         procedure second_fma; override;
         procedure second_prefetch; override;
@@ -108,16 +112,31 @@ implementation
       end;
 
 
-     function taarch64inlinenode.first_fma : tnode;
-       begin
-         if ((is_double(resultdef)) or (is_single(resultdef))) then
-           begin
-             expectloc:=LOC_MMREGISTER;
-             Result:=nil;
-           end
-         else
-           Result:=inherited first_fma;
-       end;
+    function taarch64inlinenode.first_int_real : tnode;
+      begin
+        expectloc:=LOC_MMREGISTER;
+        result:=nil;
+      end;
+
+
+    function taarch64inlinenode.first_frac_real : tnode;
+      begin
+        expectloc:=LOC_MMREGISTER;
+        result:=nil;
+      end;
+
+
+    function taarch64inlinenode.first_fma : tnode;
+      begin
+        if ((is_double(resultdef)) or (is_single(resultdef))) then
+          begin
+            expectloc:=LOC_MMREGISTER;
+            Result:=nil;
+          end
+        else
+          Result:=inherited first_fma;
+     end;
+
 
     procedure taarch64inlinenode.second_abs_real;
       begin
@@ -187,6 +206,33 @@ implementation
       end;
 
 
+    procedure taarch64inlinenode.second_int_real;
+      var
+        hreg: tregister;
+      begin
+        secondpass(left);
+        hlcg.location_force_mmregscalar(current_asmdata.CurrAsmList,left.location,left.resultdef,true);
+        location_reset(location,LOC_MMREGISTER,def_cgsize(resultdef));
+        location.register:=cg.getmmregister(current_asmdata.CurrAsmList,location.size);
+        current_asmdata.CurrAsmList.concat(taicpu.op_reg_reg(A_FRINTZ,location.register,left.location.register));
+        cg.maybe_check_for_fpu_exception(current_asmdata.CurrAsmList);
+      end;
+
+
+    procedure taarch64inlinenode.second_frac_real;
+      var
+        hreg: tregister;
+      begin
+        secondpass(left);
+        hlcg.location_force_mmregscalar(current_asmdata.CurrAsmList,left.location,left.resultdef,true);
+        location_reset(location,LOC_MMREGISTER,def_cgsize(resultdef));
+        location.register:=cg.getmmregister(current_asmdata.CurrAsmList,location.size);
+        current_asmdata.CurrAsmList.concat(taicpu.op_reg_reg(A_FRINTZ,location.register,left.location.register));
+        current_asmdata.CurrAsmList.concat(taicpu.op_reg_reg_reg(A_FSUB,location.register,left.location.register,location.register));
+        cg.maybe_check_for_fpu_exception(current_asmdata.CurrAsmList);
+      end;
+
+
     procedure taarch64inlinenode.second_get_frame;
       begin
         location_reset(location,LOC_CREGISTER,OS_ADDR);

+ 2 - 1
compiler/aasmtai.pas

@@ -402,7 +402,7 @@ interface
         { supported by recent clang-based assemblers for data-in-code  }
         asd_data_region, asd_end_data_region,
         { ARM }
-        asd_thumb_func,asd_code,
+        asd_thumb_func,asd_code,asd_force_thumb,
         { restricts the assembler only to those instructions, which are
           available on the specified CPU; this represents directives such as
           NASM's 'CPU 686' or MASM/TASM's '.686p'. Might not be supported by
@@ -452,6 +452,7 @@ interface
         { ARM }
         'thumb_func',
         'code',
+	'force_thumb',
         'cpu',
         { for the OMF object format }
         'omf_line',

+ 4 - 0
compiler/arm/aasmcpu.pas

@@ -1621,6 +1621,9 @@ implementation
           end;
       end;
 
+{$push}
+{ Disable range and overflow checking here }
+{$R-}{$Q-}        
     procedure fix_invalid_imms(list: TAsmList);
       var
         curtai: tai;
@@ -1669,6 +1672,7 @@ implementation
           end;
       end;
 
+{$pop}
 
     procedure gather_it_info(list: TAsmList);
       var

+ 3 - 3
compiler/arm/cpubase.pas

@@ -605,6 +605,9 @@ unit cpubase;
       end;
 
 
+{$push}
+{ Disable range and overflow checking here }
+{$R-}{$Q-}        
     function is_thumb32_imm(d: aint): boolean;
       var
         t : aint;
@@ -639,9 +642,6 @@ unit cpubase;
           end;
       end;
     
-{$push}
-{ Disable range and overflow checking here }
-{$R-}{$Q-}        
     function is_continuous_mask(d : aword;var lsb, width: byte) : boolean;
       var
         msb : byte;

+ 13 - 5
compiler/arm/narmset.pas

@@ -191,15 +191,23 @@ implementation
 
         procedure genitem_thumb2(list:TAsmList;t : pcaselabel);
           var
-            i : aint;
+            i : int64;
           begin
             if assigned(t^.less) then
               genitem_thumb2(list,t^.less);
             { fill possible hole }
-            for i:=last.svalue+1 to t^._low.svalue-1 do
-              list.concat(Tai_const.Create_rel_sym(aitconst_half16bit,tablelabel,elselabel));
-            for i:=t^._low.svalue to t^._high.svalue do
-              list.concat(Tai_const.Create_rel_sym(aitconst_half16bit,tablelabel,blocklabel(t^.blockid)));
+            i:=last.svalue+1;
+            while i<=t^._low.svalue-1 do
+              begin
+                list.concat(Tai_const.Create_rel_sym(aitconst_half16bit,tablelabel,elselabel));
+                i:=i+1;
+              end;
+            i:=t^._low.svalue;
+            while i<=t^._high.svalue do
+              begin
+                list.concat(Tai_const.Create_rel_sym(aitconst_half16bit,tablelabel,blocklabel(t^.blockid)));
+                i:=i+1;
+              end;
             last:=t^._high.svalue;
             if assigned(t^.greater) then
               genitem_thumb2(list,t^.greater);

+ 6 - 0
compiler/arm/raarmgas.pas

@@ -151,6 +151,7 @@ Unit raarmgas;
     function tarmattreader.is_targetdirective(const s: string): boolean;
       begin
         case s of
+          '.force_thumb',
           '.thumb_func',
           '.code',
           '.thumb_set':
@@ -1464,6 +1465,11 @@ Unit raarmgas;
             begin
               consume(AS_TARGET_DIRECTIVE);
               curList.concat(tai_directive.create(asd_thumb_func,''));
+            end;
+          '.force_thumb':
+            begin
+              consume(AS_TARGET_DIRECTIVE);
+              curList.concat(tai_directive.create(asd_force_thumb,''));
             end
           else
             inherited HandleTargetDirective;

+ 5 - 4
compiler/arm/rgcpu.pas

@@ -400,10 +400,11 @@ unit rgcpu;
         level := 0;
         while assigned(hp) do
           begin
-            if IsIT(taicpu(hp).opcode) then
-              break
-            else if hp.typ=ait_instruction then
-              inc(level);
+            if hp.typ=ait_instruction then
+              if IsIT(taicpu(hp).opcode) then
+                break
+              else
+                inc(level);
 
             hp:=tai(hp.Previous);
           end;

+ 9 - 1
compiler/assemble.pas

@@ -1403,8 +1403,8 @@ Implementation
                   len:=p-pstart;
                   if len>255 then
                     internalerror(200509187);
-                  move(pstart^,hs[1],len);
                   hs[0]:=chr(len);
+                  move(pstart^,hs[1],len);
                   sym:=objdata.symbolref(hs);
                   { Second symbol? }
                   if assigned(relocsym) then
@@ -1719,6 +1719,11 @@ Implementation
 {$ifdef ARM}
                    asd_thumb_func:
                      ObjData.ThumbFunc:=true;
+                   asd_force_thumb:
+                     begin
+                       ObjData.ThumbFunc:=true;
+                       Code16:=true;
+                     end;
                    asd_code:
                      begin
                        { ai_directive(hp).name can be only 16 or 32, this is checked by the reader }
@@ -1924,6 +1929,9 @@ Implementation
                    asd_thumb_func:
                      { ignore for now, but should be added}
                      ;
+                   asd_force_thumb:
+                     { ignore for now, but should be added}
+                     ;
                    asd_code:
                      { ignore for now, but should be added}
                      ;

+ 1 - 0
compiler/globals.pas

@@ -416,6 +416,7 @@ interface
 {$if defined(m68k)}
        { Sinclair QL specific }
        sinclairql_metadata_format: string[4] = 'QHDR';
+       sinclairql_vlink_experimental: boolean = false; { temporary }
 {$endif defined(m68k)}
 
        { default name of the C-style "main" procedure of the library/program }

+ 1 - 2
compiler/m68k/ag68kvasm.pas

@@ -96,8 +96,7 @@ unit ag68kvasm;
         result:=asminfo^.asmcmd;
 
         case target_info.system of
-          { a.out doesn't support named sections, a.out is limited 
-            (no named sections) lets use ELF for interoperability }
+          { a.out doesn't support named sections, lets use ELF for interoperability }
           system_m68k_amiga,
           system_m68k_atari,
           system_m68k_sinclairql: objtype:='-Felf';

+ 8 - 0
compiler/m68k/cpubase.pas

@@ -331,6 +331,10 @@ implementation
         {$i r68kstd.inc}
       );
 
+      std_regfullname_table : TRegNameTable = (
+        {$i r68kstdf.inc}
+      );
+
       regnumber_index : array[tregisterindex] of tregisterindex = (
         {$i r68krni.inc}
       );
@@ -484,6 +488,10 @@ implementation
     function std_regnum_search(const s:string):Tregister;
       begin
         result:=regnumber_table[findreg_by_name_table(s,std_regname_table,std_regname_index)];
+        if result=NR_NO then
+          begin
+            result:=regnumber_table[findreg_by_name_table(s,std_regfullname_table,std_regname_index)];
+          end;
       end;
 
 

+ 8 - 8
compiler/m68k/r68kgri.inc

@@ -16,30 +16,30 @@
 56,
 57,
 34,
+3,
 1,
 2,
-3,
+6,
 4,
 5,
-6,
+9,
 7,
 8,
-9,
 12,
-11,
 10,
+11,
+15,
 13,
 14,
-15,
+18,
 16,
 17,
-18,
+21,
 19,
 20,
-21,
 24,
-23,
 22,
+23,
 38,
 25,
 26,

+ 18 - 18
compiler/m68k/r68ksri.inc

@@ -1,46 +1,46 @@
 { don't edit, this file is generated from m68kreg.dat }
 0,
-43,
 42,
-45,
+43,
 44,
-47,
+45,
 46,
-49,
+47,
 48,
-51,
+49,
 50,
-53,
+51,
 52,
-55,
+53,
 54,
-57,
+55,
 56,
+57,
 34,
-1,
 3,
+1,
 2,
-5,
-4,
 6,
-7,
+4,
+5,
 9,
+7,
 8,
-11,
 12,
 10,
-13,
+11,
 15,
+13,
 14,
-17,
-16,
 18,
-19,
+16,
+17,
 21,
+19,
 20,
-23,
 24,
 22,
+23,
 38,
 25,
 26,

+ 6 - 1
compiler/m68k/ra68kmot.pas

@@ -216,6 +216,11 @@ const
         actasmregister:=std_regnum_search(lower(s));
         if actasmregister<>NR_NO then
           begin
+            { this is a hack. if the reg is valid, and its string doesn't
+              contain a dot, we make sure it's a full size reg (KB) }
+            if (getregtype(actasmregister) in [R_ADDRESSREGISTER,R_INTREGISTER]) and
+               (Pos('.',s) = 0) then
+              setsubreg(actasmregister,R_SUBWHOLE);
             result:=true;
             actasmtoken:=AS_REGISTER;
           end;
@@ -1196,7 +1201,7 @@ const
                      while actasmtoken <> AS_SEPARATOR do
                         Consume(actasmtoken);
                    end;
-                   exit;
+                 exit;
                end;
               { // (reg,reg .. // }
               Consume(AS_COMMA);

+ 34 - 14
compiler/ncnv.pas

@@ -1630,6 +1630,10 @@ implementation
                    include(flags,nf_is_currency);
                    typecheckpass(left);
                  end;
+             { comp is handled by the fpu but not a floating type point }
+             if is_fpucomp(resultdef) and not(is_fpucomp(left.resultdef)) and
+               not (nf_explicit in flags) then
+               Message(type_w_convert_real_2_comp);
            end
          else
            include(flags,nf_is_currency);
@@ -2948,6 +2952,11 @@ implementation
                 gotsint:=true;
                 result:=docheckremoveinttypeconvs(tunarynode(n).left);
               end;
+            shrn:
+              begin
+                result:=wasoriginallysmallerint(tbinarynode(n).left) and
+                  docheckremoveinttypeconvs(tbinarynode(n).right);
+              end;
             notn:
               result:=docheckremoveinttypeconvs(tunarynode(n).left);
             addn,muln,divn,modn,andn,shln:
@@ -2981,15 +2990,26 @@ implementation
 
 
     { remove int type conversions and set the result to the given type }
-    procedure doremoveinttypeconvs(var n: tnode; todef: tdef; forceunsigned: boolean; signedtype,unsignedtype : tdef);
+    procedure doremoveinttypeconvs(level : dword;var n: tnode; todef: tdef; forceunsigned: boolean; signedtype,unsignedtype : tdef);
       var
         newblock: tblocknode;
         newstatements: tstatementnode;
         originaldivtree: tnode;
         tempnode: ttempcreatenode;
       begin
+        { we may not recurse into shr nodes:
+
+          dword1:=dword1+((dword2+dword3) shr 2);
+
+          while we can remove an extension on the addition, we cannot remove it from the shr
+        }
+        if (n.nodetype=shrn) and (level<>0) then
+          begin
+            inserttypeconv_internal(n,todef);
+            exit;
+          end;
         case n.nodetype of
-          subn,addn,muln,divn,modn,xorn,andn,orn,shln:
+          subn,addn,muln,divn,modn,xorn,andn,orn,shln,shrn:
             begin
               exclude(n.flags,nf_internal);
               if not forceunsigned and
@@ -2998,8 +3018,8 @@ implementation
                   originaldivtree:=nil;
                   if n.nodetype in [divn,modn] then
                     originaldivtree:=n.getcopy;
-                  doremoveinttypeconvs(tbinarynode(n).left,signedtype,false,signedtype,unsignedtype);
-                  doremoveinttypeconvs(tbinarynode(n).right,signedtype,false,signedtype,unsignedtype);
+                  doremoveinttypeconvs(level+1,tbinarynode(n).left,signedtype,false,signedtype,unsignedtype);
+                  doremoveinttypeconvs(level+1,tbinarynode(n).right,signedtype,false,signedtype,unsignedtype);
                   n.resultdef:=signedtype;
                   if n.nodetype in [divn,modn] then
                     begin
@@ -3026,8 +3046,8 @@ implementation
                 end
               else
                 begin
-                  doremoveinttypeconvs(tbinarynode(n).left,unsignedtype,forceunsigned,signedtype,unsignedtype);
-                  doremoveinttypeconvs(tbinarynode(n).right,unsignedtype,forceunsigned,signedtype,unsignedtype);
+                  doremoveinttypeconvs(level+1,tbinarynode(n).left,unsignedtype,forceunsigned,signedtype,unsignedtype);
+                  doremoveinttypeconvs(level+1,tbinarynode(n).right,unsignedtype,forceunsigned,signedtype,unsignedtype);
                   n.resultdef:=unsignedtype;
                 end;
               //if ((n.nodetype=andn) and (tbinarynode(n).left.nodetype=ordconstn) and
@@ -3044,12 +3064,12 @@ implementation
               if not forceunsigned and
                  is_signed(n.resultdef) then
                 begin
-                  doremoveinttypeconvs(tunarynode(n).left,signedtype,false,signedtype,unsignedtype);
+                  doremoveinttypeconvs(level+1,tunarynode(n).left,signedtype,false,signedtype,unsignedtype);
                   n.resultdef:=signedtype;
                 end
               else
                 begin
-                  doremoveinttypeconvs(tunarynode(n).left,unsignedtype,forceunsigned,signedtype,unsignedtype);
+                  doremoveinttypeconvs(level+1,tunarynode(n).left,unsignedtype,forceunsigned,signedtype,unsignedtype);
                   n.resultdef:=unsignedtype;
                 end;
             end;
@@ -3344,22 +3364,22 @@ implementation
                     to 64 bit                                               }
                   if (resultdef.size <= 4) and
                     is_64bitint(left.resultdef) and
-                    (left.nodetype in [subn,addn,muln,divn,modn,xorn,andn,orn,notn,unaryminusn,shln]) and
+                    (left.nodetype in [subn,addn,muln,divn,modn,xorn,andn,orn,notn,unaryminusn,shln,shrn]) and
                     checkremovebiginttypeconvs(left,foundsint,[s8bit,u8bit,s16bit,u16bit,s32bit,u32bit],int64(low(longint)),high(cardinal)) then
-                    doremoveinttypeconvs(left,generrordef,not foundsint,s32inttype,u32inttype);
+                    doremoveinttypeconvs(0,left,generrordef,not foundsint,s32inttype,u32inttype);
 {$if defined(cpu16bitalu)}
                   if (resultdef.size <= 2) and
                     (is_32bitint(left.resultdef) or is_64bitint(left.resultdef)) and
-                    (left.nodetype in [subn,addn,muln,divn,modn,xorn,andn,orn,notn,unaryminusn,shln]) and
+                    (left.nodetype in [subn,addn,muln,divn,modn,xorn,andn,orn,notn,unaryminusn,shln,shrn]) and
                     checkremovebiginttypeconvs(left,foundsint,[s8bit,u8bit,s16bit,u16bit],int64(low(smallint)),high(word)) then
-                    doremoveinttypeconvs(left,generrordef,not foundsint,s16inttype,u16inttype);
+                    doremoveinttypeconvs(0,left,generrordef,not foundsint,s16inttype,u16inttype);
 {$endif defined(cpu16bitalu)}
 {$if defined(cpu8bitalu)}
                  if (resultdef.size<left.resultdef.size) and
                   is_integer(left.resultdef) and
-                  (left.nodetype in [subn,addn,muln,divn,modn,xorn,andn,orn,notn,unaryminusn,shln]) and
+                  (left.nodetype in [subn,addn,muln,divn,modn,xorn,andn,orn,notn,unaryminusn,shln,shrn]) and
                   checkremovebiginttypeconvs(left,foundsint,[s8bit,u8bit],int64(low(shortint)),high(byte)) then
-                    doremoveinttypeconvs(left,generrordef,not foundsint,s8inttype,u8inttype);
+                    doremoveinttypeconvs(0,left,generrordef,not foundsint,s8inttype,u8inttype);
 {$endif defined(cpu8bitalu)}
                   { the above simplification may have left a redundant equal
                     typeconv (e.g. int32 to int32). If that's the case, we remove it }

+ 4 - 0
compiler/ogcoff.pas

@@ -2063,6 +2063,8 @@ const pemagic : array[0..3] of byte = (
                   FCoffSyms.Read(bosym,sizeof(bosym));
                   if bosym.Name.Offset.Zeroes<>0 then
                     begin
+                      { Added for sake of global data analysis }
+                      strname[0]:=#0;
                       move(bosym.Name.ShortName,strname[1],8);
                       strname[9]:=#0;
                       strname[0]:=chr(strlen(@strname[1]));
@@ -2081,6 +2083,8 @@ const pemagic : array[0..3] of byte = (
                   FCoffSyms.Read(sym,sizeof(sym));
                   if plongint(@sym.name)^<>0 then
                     begin
+                      { Added for sake of global data analysis }
+                      strname[0]:=#0;
                       move(sym.name,strname[1],8);
                       strname[9]:=#0;
                       strname[0]:=chr(strlen(@strname[1]));

+ 4 - 1
compiler/ogelf.pas

@@ -672,7 +672,10 @@ implementation
         if assigned(objreloc) then
           begin
             objreloc.size:=len;
-            if reltype in [RELOC_RELATIVE{$ifdef x86},RELOC_PLT32{$endif}{$ifdef x86_64},RELOC_TLSGD,RELOC_GOTPCREL{$endif}] then
+            { RELOC_GOTPCREL, RELOC_REX_GOTPCRELX, RELOC_GOTPCRELX] need special handling
+              this is done in x86/aasmcpu unit }
+            if reltype in [RELOC_RELATIVE{$ifdef x86},RELOC_PLT32{$endif}
+               {$ifdef x86_64}, RELOC_GOTPCREL, RELOC_REX_GOTPCRELX, RELOC_GOTPCRELX,RELOC_TLSGD{$endif}] then
               dec(data,len);
             if ElfTarget.relocs_use_addend then
               begin

+ 8 - 0
compiler/options.pas

@@ -140,6 +140,7 @@ const
 
 
   suppported_targets_x_smallr = systems_linux + systems_solaris + systems_android
+                             + systems_openbsd
                              + [system_i386_haiku,system_x86_64_haiku]
                              + [system_i386_beos]
                              + [system_m68k_amiga];
@@ -2760,6 +2761,13 @@ begin
                           IllegalPara(opt);
                       end;
 {$if defined(m68k)}
+                    'L':
+                      begin
+                        if (target_info.system in [system_m68k_sinclairql]) then
+                          sinclairql_vlink_experimental:=true
+                        else
+                          IllegalPara(opt);
+                      end;
                     'Q':
                       begin
                         if (target_info.system in [system_m68k_sinclairql]) then

+ 1 - 0
compiler/pass_1.pas

@@ -189,6 +189,7 @@ implementation
                    { should the node be replaced? }
                    if assigned(hp) then
                      begin
+                       hp.flags := hp.flags + (p.flags * [nf_usercode_entry]);
                        p.free;
                        { switch to new node }
                        p:=hp;

+ 2 - 1
compiler/pdecsub.pas

@@ -1959,7 +1959,8 @@ var
   pt : tnode;
 {$endif WITHDMT}
 begin
-  if (not assigned(pd.owner.defowner) or
+  if assigned(pd.owner) and
+     (not assigned(pd.owner.defowner) or
       not is_java_class_or_interface(tdef(pd.owner.defowner))) and
      (po_external in pd.procoptions) then
     Message2(parser_e_proc_dir_conflict,'EXTERNAL','"VIRTUAL"');

+ 3 - 0
compiler/pgenutil.pas

@@ -813,6 +813,9 @@ uses
             st : TSymtable;
             i : longint;
           begin
+            { since commit 48986 deflist might have NIL entries }
+            if not assigned(def) then
+              exit;
             case def.typ of
               procdef:
                 tprocdef(def).forwarddef:=false;

+ 8 - 0
compiler/pmodules.pas

@@ -614,10 +614,14 @@ implementation
         i: longint;
         def: tdef;
         sym: tsym;
+        tmpidx: Integer;
       begin
         for i:=current_module.localsymtable.deflist.count-1 downto 0 do
           begin
             def:=tdef(current_module.localsymtable.deflist[i]);
+            { since commit 48986 deflist might have NIL entries }
+            if not assigned(def) then
+              continue;
             { this also frees def, as the defs are owned by the symtable }
             if not def.is_registered and
                not(df_not_registered_no_free in def.defoptions) then
@@ -630,6 +634,10 @@ implementation
                    tprocdef(def).procsym.is_registered then
                  tprocsym(tprocdef(def).procsym).ProcdefList.Remove(def);
                 current_module.localsymtable.deletedef(def);
+                { this prevents a dangling pointer and use after free }
+                tmpidx:=current_module.deflist.IndexOfItem(def,FromEnd);
+                if tmpidx<>-1 then
+                  current_module.deflist[tmpidx]:=nil;
               end;
           end;
         { from high to low so we hopefully have moves of less data }

+ 14 - 5
compiler/scanner.pas

@@ -4883,12 +4883,21 @@ type
                        inc(yylexcount);
                        substitutemacro(pattern,mac.buftext,mac.buflen,
                          mac.fileinfo.line,mac.fileinfo.fileindex);
-                     { handle empty macros }
+                       { handle empty macros }
                        if c=#0 then
-                         reload;
-                       readtoken(false);
-                       { that's all folks }
-                       dec(yylexcount);
+                         begin
+                           reload;
+                           { avoid macro nesting error in case of
+                             a sequence of empty macros, see #38802 }
+                           dec(yylexcount);
+                           readtoken(false);
+                         end
+                       else
+                         begin
+                           readtoken(false);
+                           { that's all folks }
+                           dec(yylexcount);
+                         end;
                        exit;
                      end
                     else

+ 4 - 2
compiler/systems/t_bsd.pas

@@ -482,12 +482,14 @@ begin
      (tf_smartlink_sections in target_info.flags) then
     GCSectionsStr:='--gc-sections';
 
-   if(cs_profile in current_settings.moduleswitches) or
+  if (cs_profile in current_settings.moduleswitches) or
      ((Info.DynamicLinker<>'') and
       ((not SharedLibFiles.Empty) or
        (target_info.system in systems_openbsd))) then
-   DynLinkStr:='-dynamic-linker='+Info.DynamicLinker;
+    DynLinkStr:='-dynamic-linker='+Info.DynamicLinker;
 
+  if rlinkpath<>'' then
+    DynLinkStr:=DynLinkStr+' --rpath-link '+rlinkpath;
   if CShared Then
    begin
       DynLinKStr:=DynLinkStr+' --shared'

+ 17 - 2
compiler/systems/t_freertos.pas

@@ -955,6 +955,8 @@ var
   t: Text;
   hp: TCmdStrListItem;
   filepath: TCmdStr;
+  i,j: integer;
+  lib: AnsiString;
 {$endif XTENSA}
 begin
 {$ifdef XTENSA}
@@ -1139,6 +1141,20 @@ begin
   if ioresult<>0 then
     exit;
 
+  { extract libraries from linker options and add to static libraries list }
+  Info.ExtraOptions:=trim(Info.ExtraOptions);
+  i := pos('-l', Info.ExtraOptions);
+  while i > 0 do
+   begin
+     j:=pos(' ',Info.ExtraOptions);
+     if j=0 then
+       j:=length(Info.ExtraOptions)+1;
+     lib:=copy(Info.ExtraOptions,i+2,j-i-2);
+     AddStaticCLibrary(lib);
+     delete(Info.ExtraOptions,i,j);
+     trim(Info.ExtraOptions);
+     i := pos('-l', Info.ExtraOptions);
+   end;
   hp:=TCmdStrListItem(StaticLibFiles.First);
   while assigned(hp) do
     begin
@@ -1256,8 +1272,7 @@ begin
     Replace(cmdstr,'$GCSECTIONS',GCSectionsStr);
     Replace(cmdstr,'$DYNLINK',DynLinkStr);
    end;
-  if success and not(cs_link_nolink in current_settings.globalswitches) then
-    success:=DoExec(FindUtil(utilsprefix+BinStr),cmdstr,true,false);
+   success:=DoExec(FindUtil(utilsprefix+BinStr),cmdstr,true,false);
 
 { Remove ReponseFile }
   if success and not(cs_link_nolink in current_settings.globalswitches) then

+ 1 - 1
compiler/systems/t_nds.pas

@@ -57,7 +57,7 @@ begin
   SharedLibFiles.doubles:=true;
   StaticLibFiles.doubles:=true;
   // set arm9 as default apptype
-  if (apptype <> app_arm9) or (apptype <> app_arm7) then
+  if (apptype <> app_arm9) and (apptype <> app_arm7) then
     apptype:=app_arm9;
 end;
 

+ 16 - 3
compiler/systems/t_sinclairql.pas

@@ -115,7 +115,7 @@ begin
      end
     else
      begin
-      ExeCmd[1]:='vlink -b rawseg -q $FLAGS $GCSECTIONS $OPT $STRIP $MAP -o $EXE -T $RES';
+      ExeCmd[1]:='vlink $QLFLAGS $FLAGS $GCSECTIONS $OPT $STRIP $MAP -o $EXE -T $RES';
      end;
    end;
 end;
@@ -245,6 +245,7 @@ var
   DynLinkStr : string;
   GCSectionsStr : string;
   FlagsStr : string;
+  QLFlagsStr: string;
   MapStr : string;
   ExeName: string;
   fd,fs: file;
@@ -258,6 +259,7 @@ var
   QLHeader: TQLHeader;
   XTccData: TXTccData;
   BinSize: longint;
+  RelocSize: longint;
   DataSpace: DWord;
 begin
   StripStr:='';
@@ -276,6 +278,10 @@ begin
     begin
       if create_smartlink_sections then
         GCSectionsStr:='-gc-all';
+      if sinclairql_vlink_experimental then
+        QLFlagsStr:='-b sinclairql -q -'+lower(sinclairql_metadata_format)+' -stack='+tostr(StackSize)
+      else
+        QLFlagsStr:='-b rawseg -q';
     end;
 
   ExeName:=current_module.exefilename;
@@ -292,18 +298,20 @@ begin
   Replace(cmdstr,'$STRIP',StripStr);
   Replace(cmdstr,'$GCSECTIONS',GCSectionsStr);
   Replace(cmdstr,'$DYNLINK',DynLinkStr);
+  Replace(cmdstr,'$QLFLAGS',QLFlagsStr);
 
   MakeSinclairQLExe:=DoExec(BinStr,CmdStr,true,false);
 
   { Kludge:
       With the above linker script, vlink will produce two files. The main binary 
       and the relocation info. Here we copy the two together. (KB) }
-  if MakeSinclairQLExe then
+  if MakeSinclairQLExe and not sinclairql_vlink_experimental then
     begin
       QLHeader:=DefaultQLHeader;
       XTccData:=DefaultXTccData;
 
       BinSize:=0;
+      RelocSize:=0;
       bufsize:=16384;
 {$push}
 {$i-}
@@ -321,13 +329,18 @@ begin
       assign(fd,ExeName);
       rewrite(fd,1);
 
+      assign(fs,ExeName+'.'+ProgramHeaderName+'.rel'+ProgramHeaderName);
+      reset(fs,1);
+      RelocSize := FileSize(fs);
+      close(fs);
+
       assign(fs,ExeName+'.'+ProgramHeaderName);
       reset(fs,1);
       BinSize := FileSize(fs);
 
       { We assume .bss size is total size indicated by linker minus emmited binary.
         DataSpace size is .bss + stack space }
-      DataSpace := NToBE(DWord(HeaderSize - BinSize + StackSize));
+      DataSpace := NToBE(DWord(max((HeaderSize - BinSize) - RelocSize + StackSize,0)));
 
       { Option: prepend QEmuLator and QPC2 v5 compatible header to EXE }
       if sinclairql_metadata_format='QHDR' then

+ 2 - 2
compiler/utils/mk68kreg.pp

@@ -133,7 +133,7 @@ begin
         i:=h;
         repeat
           j:=i+p;
-          if stdnames[std_regname_index[j]]>=stdnames[std_regname_index[i]] then
+          if stdfullnames[std_regname_index[j]]>=stdfullnames[std_regname_index[i]] then
             break;
           t:=std_regname_index[i];
           std_regname_index[i]:=std_regname_index[j];
@@ -164,7 +164,7 @@ begin
         i:=h;
         repeat
           j:=i+p;
-          if gasnames[gas_regname_index[j]]>=gasnames[gas_regname_index[i]] then
+          if gasfullnames[gas_regname_index[j]]>=gasfullnames[gas_regname_index[i]] then
             break;
           t:=gas_regname_index[i];
           gas_regname_index[i]:=gas_regname_index[j];

+ 35 - 27
compiler/wasm32/agllvmmc.pas

@@ -70,31 +70,36 @@ implementation
   procedure TLLVMMachineCodePlaygroundAssembler.WriteImports;
     var
       i    : integer;
+      def  : tdef;
       proc : tprocdef;
       list : TAsmList;
       cur_unit: tused_unit;
     begin
       for i:=0 to current_module.deflist.Count-1 do
-        if assigned(current_module.deflist[i]) and (tdef(current_module.deflist[i]).typ=procdef) then
-          begin
-            proc := tprocdef(current_module.deflist[i]);
-            if (po_external in proc.procoptions) and assigned(proc.import_dll) then
-              begin
-                //WriteProcDef(proc);
-                list:=TAsmList.Create;
-                thlcgwasm(hlcg).g_procdef(list,proc);
-                WriteTree(list);
-                list.free;
-                writer.AsmWrite(#9'.import_module'#9);
-                writer.AsmWrite(proc.mangledname);
-                writer.AsmWrite(', ');
-                writer.AsmWriteLn(proc.import_dll^);
-                writer.AsmWrite(#9'.import_name'#9);
-                writer.AsmWrite(proc.mangledname);
-                writer.AsmWrite(', ');
-                writer.AsmWriteLn(proc.import_name^);
-              end;
-          end;
+        begin
+          def:=tdef(current_module.deflist[i]);
+          { since commit 48986 deflist might have NIL entries }
+          if assigned(def) and (def.typ=procdef) then
+            begin
+              proc := tprocdef(def);
+              if (po_external in proc.procoptions) and assigned(proc.import_dll) then
+                begin
+                  //WriteProcDef(proc);
+                  list:=TAsmList.Create;
+                  thlcgwasm(hlcg).g_procdef(list,proc);
+                  WriteTree(list);
+                  list.free;
+                  writer.AsmWrite(#9'.import_module'#9);
+                  writer.AsmWrite(proc.mangledname);
+                  writer.AsmWrite(', ');
+                  writer.AsmWriteLn(proc.import_dll^);
+                  writer.AsmWrite(#9'.import_name'#9);
+                  writer.AsmWrite(proc.mangledname);
+                  writer.AsmWrite(', ');
+                  writer.AsmWriteLn(proc.import_name^);
+                end;
+            end;
+         end;
       list:=TAsmList.Create;
       cur_unit:=tused_unit(usedunits.First);
       while assigned(cur_unit) do
@@ -107,13 +112,16 @@ implementation
                 list.Concat(tai_functype.create(make_mangledname('FINALIZE$',cur_unit.u.globalsymtable,''),TWasmFuncType.Create([],[])));
             end;
           for i:=0 to cur_unit.u.deflist.Count-1 do
-            if assigned(cur_unit.u.deflist[i]) and (tdef(cur_unit.u.deflist[i]).typ = procdef) then
-              begin
-                proc := tprocdef(cur_unit.u.deflist[i]);
-                if (not proc.owner.iscurrentunit or (po_external in proc.procoptions)) and
-                   ((proc.paras.Count=0) or (proc.has_paraloc_info in [callerside,callbothsides])) then
-                  thlcgwasm(hlcg).g_procdef(list,proc);
-              end;
+            begin
+              def:=tdef(cur_unit.u.deflist[i]);
+              if assigned(def) and (tdef(def).typ = procdef) then
+                begin
+                  proc := tprocdef(def);
+                  if (not proc.owner.iscurrentunit or (po_external in proc.procoptions)) and
+                     ((proc.paras.Count=0) or (proc.has_paraloc_info in [callerside,callbothsides])) then
+                    thlcgwasm(hlcg).g_procdef(list,proc);
+                end;
+            end;
           cur_unit:=tused_unit(cur_unit.Next);
         end;
       WriteTree(list);

+ 5 - 2
compiler/wasm32/agwat.pas

@@ -957,14 +957,17 @@ implementation
     procedure TWabtTextAssembler.WriteImports;
       var
         i    : integer;
+        def  : tdef;
         proc : tprocdef;
         sym  : tsym;
         j    : integer;
         psym : tprocsym;
       begin
         for i:=0 to current_module.deflist.Count-1 do begin
-          if tdef(current_module.deflist[i]).typ = procdef then begin
-            proc := tprocdef(current_module.deflist[i]);
+          def:=tdef(current_module.deflist[i]);
+          { since commit 48986 deflist might have NIL entries }
+          if assigned(def) and (def.typ=procdef) then begin
+            proc := tprocdef(def);
             if (po_external in proc.procoptions) and assigned(proc.import_dll) then begin
               writer.AsmWrite(#9'(import "');
               writer.AsmWrite(proc.import_dll^);

+ 70 - 2
compiler/wasm32/nwasmflw.pas

@@ -49,6 +49,13 @@ interface
         procedure pass_generate_code;override;
       end;
 
+      { twasmraisenode }
+
+      twasmraisenode = class(tcgraisenode)
+      public
+        function pass_1 : tnode;override;
+      end;
+
       { twasmtryexceptnode }
 
       twasmtryexceptnode = class(tcgtryexceptnode)
@@ -68,9 +75,9 @@ implementation
     uses
       verbose,globals,systems,globtype,constexp,
       symconst,symdef,symsym,aasmtai,aasmdata,aasmcpu,defutil,defcmp,
-      procinfo,cgbase,pass_1,pass_2,parabase,
+      procinfo,cgbase,pass_1,pass_2,parabase,compinnr,
       cpubase,cpuinfo,
-      nbas,nld,ncon,ncnv,
+      nbas,nld,ncon,ncnv,ncal,ninl,nmem,nadd,
       tgobj,paramgr,
       cgutils,hlcgobj,hlcgcpu;
 
@@ -203,6 +210,66 @@ implementation
         flowcontrol := oldflowcontrol + (flowcontrol - [fc_inflowcontrol]);
       end;
 
+{*****************************************************************************
+                             twasmraisenode
+*****************************************************************************}
+
+    function twasmraisenode.pass_1 : tnode;
+      var
+        statements : tstatementnode;
+        //current_addr : tlabelnode;
+        raisenode : tcallnode;
+      begin
+        result:=internalstatements(statements);
+
+        if assigned(left) then
+          begin
+            { first para must be a class }
+            firstpass(left);
+            { insert needed typeconvs for addr,frame }
+            if assigned(right) then
+              begin
+                { addr }
+                firstpass(right);
+                { frame }
+                if assigned(third) then
+                  firstpass(third)
+                else
+                  third:=cpointerconstnode.Create(0,voidpointertype);
+              end
+            else
+              begin
+                third:=cinlinenode.create(in_get_frame,false,nil);
+                //current_addr:=clabelnode.create(cnothingnode.create,clabelsym.create('$raiseaddr'));
+                //addstatement(statements,current_addr);
+                //right:=caddrnode.create(cloadnode.create(current_addr.labsym,current_addr.labsym.owner));
+                right:=cnilnode.create;
+
+                { raise address off by one so we are for sure inside the action area for the raise }
+                if tf_use_psabieh in target_info.flags then
+                  right:=caddnode.create_internal(addn,right,cordconstnode.create(1,sizesinttype,false));
+              end;
+
+            raisenode:=ccallnode.createintern('fpc_raiseexception',
+              ccallparanode.create(third,
+              ccallparanode.create(right,
+              ccallparanode.create(left,nil)))
+              );
+            include(raisenode.callnodeflags,cnf_call_never_returns);
+            addstatement(statements,raisenode);
+          end
+        else
+          begin
+            addstatement(statements,ccallnode.createintern('fpc_popaddrstack',nil));
+            raisenode:=ccallnode.createintern('fpc_reraise',nil);
+            include(raisenode.callnodeflags,cnf_call_never_returns);
+            addstatement(statements,raisenode);
+          end;
+        left:=nil;
+        right:=nil;
+        third:=nil;
+      end;
+
 {*****************************************************************************
                              twasmtryexceptnode
 *****************************************************************************}
@@ -258,6 +325,7 @@ implementation
 initialization
   cifnode:=twasmifnode;
   cwhilerepeatnode:=twasmwhilerepeatnode;
+  craisenode:=twasmraisenode;
   ctryexceptnode:=twasmtryexceptnode;
   ctryfinallynode:=twasmtryfinallynode;
 end.

+ 10 - 0
compiler/x86/aasmcpu.pas

@@ -3682,6 +3682,16 @@ implementation
              end;
 {$endif i386}
            objdata.writereloc(data,len,p,Reloctype);
+{$ifdef x86_64}
+	   { Computed offset is not yet correct for GOTPC relocation }
+           { RELOC_GOTPCREL, RELOC_REX_GOTPCRELX, RELOC_GOTPCRELX need special handling }
+           if assigned(p) and (RelocType in [RELOC_GOTPCREL, RELOC_REX_GOTPCRELX, RELOC_GOTPCRELX]) and
+              { These relocations seem to be used only for ELF
+                which always has relocs_use_addend set to true 
+                so that it is the orgsize of the last relocation which needs to be fixed PM  }
+              (insend<>objdata.CurrObjSec.size) then
+             dec(TObjRelocation(objdata.CurrObjSec.ObjRelocations.Last).orgsize,insend-objdata.CurrObjSec.size);
+{$endif}
          end;
 
 

+ 0 - 5
compiler/x86/nx86cnv.pas

@@ -72,11 +72,6 @@ implementation
     function tx86typeconvnode.first_real_to_real : tnode;
       begin
          first_real_to_real:=nil;
-        { comp isn't a floating type }
-         if (tfloatdef(resultdef).floattype=s64comp) and
-            (tfloatdef(left.resultdef).floattype<>s64comp) and
-            not (nf_explicit in flags) then
-           CGMessage(type_w_convert_real_2_comp);
          if use_vectorfpu(resultdef) then
            expectloc:=LOC_MMREGISTER
          else

+ 7 - 1
packages/fcl-db/src/base/fields.inc

@@ -68,12 +68,13 @@ end;
 
 function TFieldDef.AddChild: TFieldDef;
 begin
+  // http://docwiki.embarcadero.com/Libraries/Sydney/en/Data.DB.TFieldDef.AddChild
   // Adds a new TFieldDef object to the ChildDefs array.
 end;
 
 function TFieldDef.GetChildDefs: TFieldDefs;
 begin
-
+  Result:=nil;
 end;
 
 procedure TFieldDef.SetChildDefs(AValue: TFieldDefs);
@@ -84,11 +85,13 @@ end;
 function TFieldDef.HasChildDefs: Boolean;
 begin
   // http://docwiki.embarcadero.com/Libraries/Sydney/en/Data.DB.TFieldDef.HasChildDefs
+  Result:=False;
 end;
 
 function TFieldDef.GetParentDef: TFieldDef;
 begin
   // http://docwiki.embarcadero.com/Libraries/Sydney/en/Data.DB.TFieldDef.ParentDef
+  Result:=nil;
 end;
 
 procedure TFieldDef.Assign(APersistent: TPersistent);
@@ -3705,6 +3708,7 @@ end;
 function TObjectField.GetFieldCount: Integer;
 begin
   // http://docwiki.embarcadero.com/Libraries/Sydney/en/Data.DB.TObjectField.GetFieldCount
+  Result:=0;
 end;
 
 function TObjectField.GetFields: TFields;
@@ -3716,6 +3720,7 @@ end;
 function TObjectField.GetFieldValue(AIndex: Integer): Variant;
 begin
   // http://docwiki.embarcadero.com/Libraries/Sydney/en/Data.DB.TObjectField.GetFieldValue
+  Result:=NULL;
 end;
 
 procedure TObjectField.SetFieldValue(AIndex: Integer; const AValue: Variant);
@@ -3732,6 +3737,7 @@ end;
 function TObjectField.GetAsVariant: Variant;
 begin
   // http://docwiki.embarcadero.com/Libraries/Sydney/en/Data.DB.TObjectField.GetAsVariant
+  Result:=NULL;
 end;
 
 procedure TObjectField.SetVarValue(const AValue: Variant);

+ 3265 - 0
packages/fcl-mustache/Makefile

@@ -0,0 +1,3265 @@
+#
+# Don't edit, this file is generated by FPCMake Version 2.0.0
+#
+default: all
+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 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 avr-embedded armeb-linux armeb-embedded mips-linux mipsel-linux mipsel-embedded mipsel-android mips64el-linux jvm-java jvm-android i8086-embedded i8086-msdos i8086-win16 aarch64-linux aarch64-freebsd aarch64-darwin aarch64-win64 aarch64-android aarch64-ios wasm32-embedded wasm32-wasi sparc64-linux riscv32-linux riscv32-embedded riscv64-linux riscv64-embedded xtensa-linux xtensa-embedded xtensa-freertos z80-embedded z80-zxspectrum z80-msxdos z80-amstradcpc
+BSDs = freebsd netbsd openbsd darwin dragonfly
+UNIXs = linux $(BSDs) solaris qnx haiku aix
+LIMIT83fs = go32v2 os2 emx watcom msdos win16 atari
+OSNeedsComspecToRunBatch = go32v2 watcom
+FORCE:
+.PHONY: FORCE
+override PATH:=$(patsubst %/,%,$(subst \,/,$(PATH)))
+ifneq ($(findstring darwin,$(OSTYPE)),)
+inUnix=1 #darwin
+SEARCHPATH:=$(filter-out .,$(subst :, ,$(PATH)))
+else
+ifeq ($(findstring ;,$(PATH)),)
+inUnix=1
+SEARCHPATH:=$(filter-out .,$(subst :, ,$(PATH)))
+else
+SEARCHPATH:=$(subst ;, ,$(PATH))
+endif
+endif
+SEARCHPATH+=$(patsubst %/,%,$(subst \,/,$(dir $(MAKE))))
+PWD:=$(strip $(wildcard $(addsuffix /pwd.exe,$(SEARCHPATH))))
+ifeq ($(PWD),)
+PWD:=$(strip $(wildcard $(addsuffix /pwd,$(SEARCHPATH))))
+ifeq ($(PWD),)
+$(error You need the GNU utils package to use this Makefile)
+else
+PWD:=$(firstword $(PWD))
+SRCEXEEXT=
+endif
+else
+PWD:=$(firstword $(PWD))
+SRCEXEEXT=.exe
+endif
+ifndef inUnix
+ifeq ($(OS),Windows_NT)
+inWinNT=1
+else
+ifdef OS2_SHELL
+inOS2=1
+endif
+endif
+else
+ifneq ($(findstring cygdrive,$(PATH)),)
+inCygWin=1
+endif
+endif
+ifdef inUnix
+SRCBATCHEXT=.sh
+else
+ifdef inOS2
+SRCBATCHEXT=.cmd
+else
+SRCBATCHEXT=.bat
+endif
+endif
+ifdef COMSPEC
+ifneq ($(findstring $(OS_SOURCE),$(OSNeedsComspecToRunBatch)),)
+ifndef RUNBATCH
+RUNBATCH=$(COMSPEC) /C
+endif
+endif
+endif
+ifdef inUnix
+PATHSEP=/
+else
+PATHSEP:=$(subst /,\,/)
+ifdef inCygWin
+PATHSEP=/
+endif
+endif
+ifdef PWD
+BASEDIR:=$(subst \,/,$(shell $(PWD)))
+ifdef inCygWin
+ifneq ($(findstring /cygdrive/,$(BASEDIR)),)
+BASENODIR:=$(patsubst /cygdrive%,%,$(BASEDIR))
+BASEDRIVE:=$(firstword $(subst /, ,$(BASENODIR)))
+BASEDIR:=$(subst /cygdrive/$(BASEDRIVE)/,$(BASEDRIVE):/,$(BASEDIR))
+endif
+endif
+else
+BASEDIR=.
+endif
+ifdef inOS2
+ifndef ECHO
+ECHO:=$(strip $(wildcard $(addsuffix /gecho$(SRCEXEEXT),$(SEARCHPATH))))
+ifeq ($(ECHO),)
+ECHO:=$(strip $(wildcard $(addsuffix /echo$(SRCEXEEXT),$(SEARCHPATH))))
+ifeq ($(ECHO),)
+ECHO=echo
+else
+ECHO:=$(firstword $(ECHO))
+endif
+else
+ECHO:=$(firstword $(ECHO))
+endif
+endif
+export ECHO
+endif
+override DEFAULT_FPCDIR=../..
+ifndef FPC
+ifdef PP
+FPC=$(PP)
+endif
+endif
+ifndef FPC
+FPCPROG:=$(strip $(wildcard $(addsuffix /fpc$(SRCEXEEXT),$(SEARCHPATH))))
+ifneq ($(FPCPROG),)
+FPCPROG:=$(firstword $(FPCPROG))
+ifneq ($(CPU_TARGET),)
+FPC:=$(shell $(FPCPROG) -P$(CPU_TARGET) -PB)
+else
+FPC:=$(shell $(FPCPROG) -PB)
+endif
+ifneq ($(findstring Error,$(FPC)),)
+override FPC=$(firstword $(strip $(wildcard $(addsuffix /ppc386$(SRCEXEEXT),$(SEARCHPATH)))))
+else
+ifeq ($(strip $(wildcard $(FPC))),)
+FPC:=$(firstword $(FPCPROG))
+endif
+endif
+else
+override FPC=$(firstword $(strip $(wildcard $(addsuffix /ppc386$(SRCEXEEXT),$(SEARCHPATH)))))
+endif
+endif
+override FPC:=$(subst $(SRCEXEEXT),,$(FPC))
+override FPC:=$(subst \,/,$(FPC))$(SRCEXEEXT)
+FOUNDFPC:=$(strip $(wildcard $(FPC)))
+ifeq ($(FOUNDFPC),)
+FOUNDFPC=$(strip $(wildcard $(addsuffix /$(FPC),$(SEARCHPATH))))
+ifeq ($(FOUNDFPC),)
+$(error Compiler $(FPC) not found)
+endif
+endif
+ifndef FPC_COMPILERINFO
+FPC_COMPILERINFO:=$(shell $(FPC) -iVSPTPSOTO)
+endif
+ifndef FPC_VERSION
+FPC_VERSION:=$(word 1,$(FPC_COMPILERINFO))
+endif
+export FPC FPC_VERSION FPC_COMPILERINFO
+unexport CHECKDEPEND ALLDEPENDENCIES
+ifndef CPU_TARGET
+ifdef CPU_TARGET_DEFAULT
+CPU_TARGET=$(CPU_TARGET_DEFAULT)
+endif
+endif
+ifndef OS_TARGET
+ifdef OS_TARGET_DEFAULT
+OS_TARGET=$(OS_TARGET_DEFAULT)
+endif
+endif
+ifndef CPU_SOURCE
+CPU_SOURCE:=$(word 2,$(FPC_COMPILERINFO))
+endif
+ifndef CPU_TARGET
+CPU_TARGET:=$(word 3,$(FPC_COMPILERINFO))
+endif
+ifndef OS_SOURCE
+OS_SOURCE:=$(word 4,$(FPC_COMPILERINFO))
+endif
+ifndef OS_TARGET
+OS_TARGET:=$(word 5,$(FPC_COMPILERINFO))
+endif
+FULL_TARGET=$(CPU_TARGET)-$(OS_TARGET)
+FULL_SOURCE=$(CPU_SOURCE)-$(OS_SOURCE)
+ifeq ($(CPU_TARGET),armeb)
+ARCH=arm
+override FPCOPT+=-Cb
+else
+ifeq ($(CPU_TARGET),armel)
+ARCH=arm
+override FPCOPT+=-CaEABI
+else
+ARCH=$(CPU_TARGET)
+endif
+endif
+ifeq ($(FULL_TARGET),arm-embedded)
+ifeq ($(SUBARCH),)
+$(error When compiling for arm-embedded, a sub-architecture (e.g. SUBARCH=armv4t or SUBARCH=armv7m) must be defined)
+endif
+override FPCOPT+=-Cp$(SUBARCH)
+endif
+ifeq ($(FULL_TARGET),avr-embedded)
+ifeq ($(SUBARCH),)
+$(error When compiling for avr-embedded, a sub-architecture (e.g. SUBARCH=avr25 or SUBARCH=avr35) must be defined)
+endif
+override FPCOPT+=-Cp$(SUBARCH)
+endif
+ifeq ($(FULL_TARGET),mipsel-embedded)
+ifeq ($(SUBARCH),)
+$(error When compiling for mipsel-embedded, a sub-architecture (e.g. SUBARCH=pic32mx) must be defined)
+endif
+override FPCOPT+=-Cp$(SUBARCH)
+endif
+ifeq ($(FULL_TARGET),xtensa-embedded)
+ifeq ($(SUBARCH),)
+$(error When compiling for xtensa-embedded, a sub-architecture (e.g. SUBARCH=lx106 or SUBARCH=lx6) must be defined)
+endif
+override FPCOPT+=-Cp$(SUBARCH)
+endif
+ifeq ($(FULL_TARGET),xtensa-freertos)
+ifeq ($(SUBARCH),)
+$(error When compiling for xtensa-freertos, a sub-architecture (e.g. SUBARCH=lx106 or SUBARCH=lx6) must be defined)
+endif
+override FPCOPT+=-Cp$(SUBARCH)
+endif
+ifeq ($(FULL_TARGET),arm-freertos)
+ifeq ($(SUBARCH),)
+$(error When compiling for arm-freertos, a sub-architecture (e.g. SUBARCH=armv6m or SUBARCH=armv7em) must be defined)
+endif
+override FPCOPT+=-Cp$(SUBARCH)
+endif
+ifneq ($(findstring $(OS_SOURCE),$(LIMIT83fs)),)
+TARGETSUFFIX=$(OS_TARGET)
+SOURCESUFFIX=$(OS_SOURCE)
+else
+ifneq ($(findstring $(OS_TARGET),$(LIMIT83fs)),)
+TARGETSUFFIX=$(OS_TARGET)
+else
+TARGETSUFFIX=$(FULL_TARGET)
+endif
+SOURCESUFFIX=$(FULL_SOURCE)
+endif
+ifneq ($(FULL_TARGET),$(FULL_SOURCE))
+CROSSCOMPILE=1
+endif
+ifeq ($(findstring makefile,$(MAKECMDGOALS)),)
+ifeq ($(findstring $(FULL_TARGET),$(MAKEFILETARGETS)),)
+$(error The Makefile doesn't support target $(FULL_TARGET), please run fpcmake first)
+endif
+endif
+ifneq ($(findstring $(OS_TARGET),$(BSDs)),)
+BSDhier=1
+endif
+ifeq ($(OS_TARGET),linux)
+linuxHier=1
+endif
+ifndef CROSSCOMPILE
+BUILDFULLNATIVE=1
+export BUILDFULLNATIVE
+endif
+ifdef BUILDFULLNATIVE
+BUILDNATIVE=1
+export BUILDNATIVE
+endif
+export OS_TARGET OS_SOURCE ARCH CPU_TARGET CPU_SOURCE FULL_TARGET FULL_SOURCE TARGETSUFFIX SOURCESUFFIX CROSSCOMPILE
+ifdef FPCDIR
+override FPCDIR:=$(subst \,/,$(FPCDIR))
+ifeq ($(wildcard $(addprefix $(FPCDIR)/,rtl)),)
+override FPCDIR=wrong
+endif
+else
+override FPCDIR=wrong
+endif
+ifdef DEFAULT_FPCDIR
+ifeq ($(FPCDIR),wrong)
+override FPCDIR:=$(subst \,/,$(DEFAULT_FPCDIR))
+ifeq ($(wildcard $(addprefix $(FPCDIR)/,rtl)),)
+override FPCDIR=wrong
+endif
+endif
+endif
+ifeq ($(FPCDIR),wrong)
+ifdef inUnix
+override FPCDIR=/usr/local/lib/fpc/$(FPC_VERSION)
+ifeq ($(wildcard $(FPCDIR)/units),)
+override FPCDIR=/usr/lib/fpc/$(FPC_VERSION)
+endif
+else
+override FPCDIR:=$(subst /$(FPC),,$(firstword $(strip $(wildcard $(addsuffix /$(FPC),$(SEARCHPATH))))))
+override FPCDIR:=$(FPCDIR)/..
+ifeq ($(wildcard $(addprefix $(FPCDIR)/,rtl)),)
+override FPCDIR:=$(FPCDIR)/..
+ifeq ($(wildcard $(addprefix $(FPCDIR)/,rtl)),)
+override FPCDIR:=$(BASEDIR)
+ifeq ($(wildcard $(addprefix $(FPCDIR)/,rtl)),)
+override FPCDIR=c:/pp
+endif
+endif
+endif
+endif
+endif
+ifndef CROSSBINDIR
+CROSSBINDIR:=$(wildcard $(FPCDIR)/bin/$(TARGETSUFFIX))
+endif
+ifneq ($(findstring $(OS_TARGET),darwin iphonesim ios),)
+ifneq ($(findstring $(OS_SOURCE),darwin ios),)
+DARWIN2DARWIN=1
+endif
+endif
+ifndef BINUTILSPREFIX
+ifndef CROSSBINDIR
+ifdef CROSSCOMPILE
+ifneq ($(OS_TARGET),msdos)
+ifndef DARWIN2DARWIN
+ifneq ($(CPU_TARGET),jvm)
+BINUTILSPREFIX=$(CPU_TARGET)-$(OS_TARGET)-
+ifeq ($(OS_TARGET),android)
+ifeq ($(CPU_TARGET),arm)
+BINUTILSPREFIX=arm-linux-androideabi-
+else
+ifeq ($(CPU_TARGET),i386)
+BINUTILSPREFIX=i686-linux-android-
+else
+BINUTILSPREFIX=$(CPU_TARGET)-linux-android-
+endif
+endif
+endif
+endif
+endif
+else
+BINUTILSPREFIX=$(OS_TARGET)-
+endif
+endif
+endif
+endif
+UNITSDIR:=$(wildcard $(FPCDIR)/units/$(TARGETSUFFIX))
+ifeq ($(UNITSDIR),)
+UNITSDIR:=$(wildcard $(FPCDIR)/units/$(OS_TARGET))
+endif
+PACKAGESDIR:=$(wildcard $(FPCDIR) $(FPCDIR)/packages)
+ifndef FPCFPMAKE
+ifdef CROSSCOMPILE
+ifeq ($(strip $(wildcard $(addsuffix /compiler/ppc$(SRCEXEEXT),$(FPCDIR)))),)
+FPCPROG:=$(strip $(wildcard $(addsuffix /fpc$(SRCEXEEXT),$(SEARCHPATH))))
+ifneq ($(FPCPROG),)
+FPCPROG:=$(firstword $(FPCPROG))
+FPCFPMAKE:=$(shell $(FPCPROG) -PB)
+ifeq ($(strip $(wildcard $(FPCFPMAKE))),)
+FPCFPMAKE:=$(firstword $(FPCPROG))
+endif
+else
+override FPCFPMAKE=$(firstword $(strip $(wildcard $(addsuffix /ppc386$(SRCEXEEXT),$(SEARCHPATH)))))
+endif
+else
+FPCFPMAKE=$(strip $(wildcard $(addsuffix /compiler/ppc$(SRCEXEEXT),$(FPCDIR))))
+FPMAKE_SKIP_CONFIG=-n
+export FPCFPMAKE
+export FPMAKE_SKIP_CONFIG
+endif
+else
+FPMAKE_SKIP_CONFIG=-n
+FPCFPMAKE=$(FPC)
+endif
+endif
+override PACKAGE_NAME=fcl-mustache
+override PACKAGE_VERSION=3.3.1
+FPMAKE_BIN_CLEAN=$(wildcard ./fpmake$(SRCEXEEXT))
+ifdef OS_TARGET
+FPC_TARGETOPT+=--os=$(OS_TARGET)
+endif
+ifdef CPU_TARGET
+FPC_TARGETOPT+=--cpu=$(CPU_TARGET)
+endif
+LOCALFPMAKE=./fpmake$(SRCEXEEXT)
+PACKAGEDIR_FPMKUNIT:=$(firstword $(subst /Makefile.fpc,,$(strip $(wildcard $(addsuffix /fpmkunit/Makefile.fpc,$(PACKAGESDIR))))))
+ifneq ($(PACKAGEDIR_FPMKUNIT),)
+ifneq ($(wildcard $(PACKAGEDIR_FPMKUNIT)/units_bs/$(SOURCESUFFIX)),)
+UNITDIR_FPMAKE_FPMKUNIT=$(PACKAGEDIR_FPMKUNIT)/units_bs/$(SOURCESUFFIX)
+else
+UNITDIR_FPMAKE_FPMKUNIT=$(PACKAGEDIR_FPMKUNIT)
+endif
+ifdef CHECKDEPEND
+$(PACKAGEDIR_FPMKUNIT)/$(FPCMADE):
+	$(MAKE) -C $(PACKAGEDIR_FPMKUNIT) $(FPCMADE)
+override ALLDEPENDENCIES+=$(PACKAGEDIR_FPMKUNIT)/$(FPCMADE)
+endif
+else
+PACKAGEDIR_FPMKUNIT=
+UNITDIR_FPMKUNIT:=$(subst /Package.fpc,,$(strip $(wildcard $(addsuffix /fpmkunit/Package.fpc,$(UNITSDIR)))))
+ifneq ($(UNITDIR_FPMKUNIT),)
+UNITDIR_FPMKUNIT:=$(firstword $(UNITDIR_FPMKUNIT))
+else
+UNITDIR_FPMKUNIT=
+endif
+endif
+ifdef UNITDIR_FPMAKE_FPMKUNIT
+override COMPILER_FPMAKE_UNITDIR+=$(UNITDIR_FPMAKE_FPMKUNIT)
+endif
+override INSTALL_FPCPACKAGE=y
+ifdef REQUIRE_UNITSDIR
+override UNITSDIR+=$(REQUIRE_UNITSDIR)
+endif
+ifdef REQUIRE_PACKAGESDIR
+override PACKAGESDIR+=$(REQUIRE_PACKAGESDIR)
+endif
+ifdef ZIPINSTALL
+ifneq ($(findstring $(OS_TARGET),$(UNIXs)),)
+UNIXHier=1
+endif
+else
+ifneq ($(findstring $(OS_SOURCE),$(UNIXs)),)
+UNIXHier=1
+endif
+endif
+ifndef INSTALL_PREFIX
+ifdef PREFIX
+INSTALL_PREFIX=$(PREFIX)
+endif
+endif
+ifndef INSTALL_PREFIX
+ifdef UNIXHier
+INSTALL_PREFIX=/usr/local
+else
+ifdef INSTALL_FPCPACKAGE
+INSTALL_BASEDIR:=/pp
+else
+INSTALL_BASEDIR:=/$(PACKAGE_NAME)
+endif
+endif
+endif
+export INSTALL_PREFIX
+ifdef INSTALL_FPCSUBDIR
+export INSTALL_FPCSUBDIR
+endif
+ifndef DIST_DESTDIR
+DIST_DESTDIR:=$(BASEDIR)
+endif
+export DIST_DESTDIR
+ifndef COMPILER_UNITTARGETDIR
+ifdef PACKAGEDIR_MAIN
+COMPILER_UNITTARGETDIR=$(PACKAGEDIR_MAIN)/units/$(TARGETSUFFIX)
+else
+COMPILER_UNITTARGETDIR=units/$(TARGETSUFFIX)
+endif
+endif
+ifndef COMPILER_TARGETDIR
+COMPILER_TARGETDIR=.
+endif
+ifndef INSTALL_BASEDIR
+ifdef UNIXHier
+ifdef INSTALL_FPCPACKAGE
+INSTALL_BASEDIR:=$(INSTALL_PREFIX)/lib/fpc/$(FPC_VERSION)
+else
+INSTALL_BASEDIR:=$(INSTALL_PREFIX)/lib/$(PACKAGE_NAME)
+endif
+else
+INSTALL_BASEDIR:=$(INSTALL_PREFIX)
+endif
+endif
+ifndef INSTALL_BINDIR
+ifdef UNIXHier
+INSTALL_BINDIR:=$(INSTALL_PREFIX)/bin
+else
+INSTALL_BINDIR:=$(INSTALL_BASEDIR)/bin
+ifdef INSTALL_FPCPACKAGE
+ifdef CROSSCOMPILE
+ifdef CROSSINSTALL
+INSTALL_BINDIR:=$(INSTALL_BINDIR)/$(SOURCESUFFIX)
+else
+INSTALL_BINDIR:=$(INSTALL_BINDIR)/$(TARGETSUFFIX)
+endif
+else
+INSTALL_BINDIR:=$(INSTALL_BINDIR)/$(TARGETSUFFIX)
+endif
+endif
+endif
+endif
+ifndef INSTALL_UNITDIR
+INSTALL_UNITDIR:=$(INSTALL_BASEDIR)/units/$(TARGETSUFFIX)
+ifdef INSTALL_FPCPACKAGE
+ifdef PACKAGE_NAME
+INSTALL_UNITDIR:=$(INSTALL_UNITDIR)/$(PACKAGE_NAME)
+endif
+endif
+endif
+ifndef INSTALL_LIBDIR
+ifdef UNIXHier
+INSTALL_LIBDIR:=$(INSTALL_PREFIX)/lib
+else
+INSTALL_LIBDIR:=$(INSTALL_UNITDIR)
+endif
+endif
+ifndef INSTALL_SOURCEDIR
+ifdef UNIXHier
+ifdef BSDhier
+SRCPREFIXDIR=share/src
+else
+ifdef linuxHier
+SRCPREFIXDIR=share/src
+else
+SRCPREFIXDIR=src
+endif
+endif
+ifdef INSTALL_FPCPACKAGE
+ifdef INSTALL_FPCSUBDIR
+INSTALL_SOURCEDIR:=$(INSTALL_PREFIX)/$(SRCPREFIXDIR)/fpc-$(FPC_VERSION)/$(INSTALL_FPCSUBDIR)/$(PACKAGE_NAME)
+else
+INSTALL_SOURCEDIR:=$(INSTALL_PREFIX)/$(SRCPREFIXDIR)/fpc-$(FPC_VERSION)/$(PACKAGE_NAME)
+endif
+else
+INSTALL_SOURCEDIR:=$(INSTALL_PREFIX)/$(SRCPREFIXDIR)/$(PACKAGE_NAME)-$(PACKAGE_VERSION)
+endif
+else
+ifdef INSTALL_FPCPACKAGE
+ifdef INSTALL_FPCSUBDIR
+INSTALL_SOURCEDIR:=$(INSTALL_BASEDIR)/source/$(INSTALL_FPCSUBDIR)/$(PACKAGE_NAME)
+else
+INSTALL_SOURCEDIR:=$(INSTALL_BASEDIR)/source/$(PACKAGE_NAME)
+endif
+else
+INSTALL_SOURCEDIR:=$(INSTALL_BASEDIR)/source
+endif
+endif
+endif
+ifndef INSTALL_DOCDIR
+ifdef UNIXHier
+ifdef BSDhier
+DOCPREFIXDIR=share/doc
+else
+ifdef linuxHier
+DOCPREFIXDIR=share/doc
+else
+DOCPREFIXDIR=doc
+endif
+endif
+ifdef INSTALL_FPCPACKAGE
+INSTALL_DOCDIR:=$(INSTALL_PREFIX)/$(DOCPREFIXDIR)/fpc-$(FPC_VERSION)/$(PACKAGE_NAME)
+else
+INSTALL_DOCDIR:=$(INSTALL_PREFIX)/$(DOCPREFIXDIR)/$(PACKAGE_NAME)-$(PACKAGE_VERSION)
+endif
+else
+ifdef INSTALL_FPCPACKAGE
+INSTALL_DOCDIR:=$(INSTALL_BASEDIR)/doc/$(PACKAGE_NAME)
+else
+INSTALL_DOCDIR:=$(INSTALL_BASEDIR)/doc
+endif
+endif
+endif
+ifndef INSTALL_EXAMPLEDIR
+ifdef UNIXHier
+ifdef INSTALL_FPCPACKAGE
+ifdef BSDhier
+INSTALL_EXAMPLEDIR:=$(INSTALL_PREFIX)/share/examples/fpc-$(FPC_VERSION)/$(PACKAGE_NAME)
+else
+ifdef linuxHier
+INSTALL_EXAMPLEDIR:=$(INSTALL_DOCDIR)/examples
+else
+INSTALL_EXAMPLEDIR:=$(INSTALL_PREFIX)/doc/fpc-$(FPC_VERSION)/examples/$(PACKAGE_NAME)
+endif
+endif
+else
+ifdef BSDhier
+INSTALL_EXAMPLEDIR:=$(INSTALL_PREFIX)/share/examples/$(PACKAGE_NAME)-$(PACKAGE_VERSION)
+else
+ifdef linuxHier
+INSTALL_EXAMPLEDIR:=$(INSTALL_DOCDIR)/examples/$(PACKAGE_NAME)-$(PACKAGE_VERSION)
+else
+INSTALL_EXAMPLEDIR:=$(INSTALL_PREFIX)/doc/$(PACKAGE_NAME)-$(PACKAGE_VERSION)
+endif
+endif
+endif
+else
+ifdef INSTALL_FPCPACKAGE
+INSTALL_EXAMPLEDIR:=$(INSTALL_BASEDIR)/examples/$(PACKAGE_NAME)
+else
+INSTALL_EXAMPLEDIR:=$(INSTALL_BASEDIR)/examples
+endif
+endif
+endif
+ifndef INSTALL_DATADIR
+INSTALL_DATADIR=$(INSTALL_BASEDIR)
+endif
+ifndef INSTALL_SHAREDDIR
+INSTALL_SHAREDDIR=$(INSTALL_PREFIX)/lib
+endif
+ifdef CROSSCOMPILE
+ifndef CROSSBINDIR
+CROSSBINDIR:=$(wildcard $(CROSSTARGETDIR)/bin/$(SOURCESUFFIX))
+ifeq ($(CROSSBINDIR),)
+CROSSBINDIR:=$(wildcard $(INSTALL_BASEDIR)/cross/$(TARGETSUFFIX)/bin/$(FULL_SOURCE))
+endif
+endif
+else
+CROSSBINDIR=
+endif
+ifeq ($(OS_SOURCE),linux)
+ifndef GCCLIBDIR
+ifeq ($(CPU_TARGET),i386)
+ifneq ($(findstring x86_64,$(shell uname -a)),)
+ifeq ($(BINUTILSPREFIX),)
+GCCLIBDIR:=$(shell dirname `gcc -m32 -print-libgcc-file-name`)
+else
+CROSSGCCOPT=-m32
+endif
+endif
+endif
+ifeq ($(CPU_TARGET),powerpc)
+ifeq ($(BINUTILSPREFIX),)
+GCCLIBDIR:=$(shell dirname `gcc -m32 -print-libgcc-file-name`)
+else
+CROSSGCCOPT=-m32
+endif
+endif
+ifeq ($(CPU_TARGET),powerpc64)
+ifeq ($(BINUTILSPREFIX),)
+GCCLIBDIR:=$(shell dirname `gcc -m64 -print-libgcc-file-name`)
+else
+CROSSGCCOPT=-m64
+endif
+endif
+ifeq ($(CPU_TARGET),sparc)
+ifneq ($(findstring sparc64,$(shell uname -a)),)
+ifeq ($(BINUTILSPREFIX),)
+GCCLIBDIR:=$(shell dirname `gcc -m32 -print-libgcc-file-name`)
+else
+ifneq ($(findstring $(FPCFPMAKE_CPU_OPT),mips mipsel),)
+CROSSGCCOPT=-mabi=32
+else
+CROSSGCCOPT=-m32
+endif
+endif
+endif
+endif
+endif
+ifdef FPCFPMAKE
+FPCFPMAKE_CPU_TARGET=$(shell $(FPCFPMAKE) -iTP)
+ifeq ($(CPU_TARGET),$(FPCFPMAKE_CPU_TARGET))
+FPCMAKEGCCLIBDIR:=$(GCCLIBDIR)
+else
+ifneq ($(findstring $(FPCFPMAKE_CPU_TARGET),aarch64 powerpc64 riscv64 sparc64 x86_64),)
+FPCMAKE_CROSSGCCOPT=-m64
+else
+ifneq ($(findstring $(FPCFPMAKE_CPU_OPT),mips64 mips64el),)
+FPCMAKE_CROSSGCCOPT=-mabi=64
+else
+ifneq ($(findstring $(FPCFPMAKE_CPU_OPT),mips mipsel),)
+FPCMAKE_CROSSGCCOPT=-mabi=32
+else
+ifneq ($(findstring $(FPCFPMAKE_CPU_OPT),riscv64),)
+FPCMAKE_CROSSGCCOPT=-mabi=lp64
+else
+ifneq ($(findstring $(FPCFPMAKE_CPU_OPT),riscv32),)
+FPCMAKE_CROSSGCCOPT=-mabi=ilp32
+else
+FPCMAKE_CROSSGCCOPT=-m32
+endif
+endif
+endif
+endif
+endif
+FPCMAKEGCCLIBDIR:=$(shell dirname `gcc $(FPCMAKE_CROSSGCCOPT) -print-libgcc-file-name`)
+endif
+endif
+ifndef FPCMAKEGCCLIBDIR
+FPCMAKEGCCLIBDIR:=$(shell dirname `gcc -print-libgcc-file-name`)
+endif
+ifndef GCCLIBDIR
+CROSSGCC=$(strip $(wildcard $(addsuffix /$(BINUTILSPREFIX)gcc$(SRCEXEEXT),$(SEARCHPATH))))
+ifneq ($(CROSSGCC),)
+GCCLIBDIR:=$(shell dirname `$(CROSSGCC) $(CROSSGCCOPT) -print-libgcc-file-name`)
+endif
+endif
+endif
+ifdef inUnix
+ifeq ($(OS_SOURCE),netbsd)
+OTHERLIBDIR:=/usr/pkg/lib
+endif
+export GCCLIBDIR FPCMAKEGCCLIBDIR OTHERLIBDIR
+endif
+BATCHEXT=.bat
+LOADEREXT=.as
+EXEEXT=.exe
+PPLEXT=.ppl
+PPUEXT=.ppu
+OEXT=.o
+LTOEXT=.bc
+ASMEXT=.s
+SMARTEXT=.sl
+STATICLIBEXT=.a
+SHAREDLIBEXT=.so
+SHAREDLIBPREFIX=libfp
+STATICLIBPREFIX=libp
+IMPORTLIBPREFIX=libimp
+RSTEXT=.rst
+EXEDBGEXT=.dbg
+ifeq ($(OS_TARGET),go32v1)
+STATICLIBPREFIX=
+SHORTSUFFIX=v1
+endif
+ifeq ($(OS_TARGET),go32v2)
+STATICLIBPREFIX=
+SHORTSUFFIX=dos
+IMPORTLIBPREFIX=
+endif
+ifeq ($(OS_TARGET),watcom)
+STATICLIBPREFIX=
+OEXT=.obj
+ASMEXT=.asm
+SHAREDLIBEXT=.dll
+SHORTSUFFIX=wat
+IMPORTLIBPREFIX=
+endif
+ifneq ($(CPU_TARGET),jvm)
+ifeq ($(OS_TARGET),android)
+BATCHEXT=.sh
+EXEEXT=
+HASSHAREDLIB=1
+SHORTSUFFIX=lnx
+endif
+endif
+ifeq ($(OS_TARGET),linux)
+BATCHEXT=.sh
+EXEEXT=
+HASSHAREDLIB=1
+SHORTSUFFIX=lnx
+endif
+ifeq ($(OS_TARGET),dragonfly)
+BATCHEXT=.sh
+EXEEXT=
+HASSHAREDLIB=1
+SHORTSUFFIX=df
+endif
+ifeq ($(OS_TARGET),freebsd)
+BATCHEXT=.sh
+EXEEXT=
+HASSHAREDLIB=1
+SHORTSUFFIX=fbs
+endif
+ifeq ($(OS_TARGET),netbsd)
+BATCHEXT=.sh
+EXEEXT=
+HASSHAREDLIB=1
+SHORTSUFFIX=nbs
+endif
+ifeq ($(OS_TARGET),openbsd)
+BATCHEXT=.sh
+EXEEXT=
+HASSHAREDLIB=1
+SHORTSUFFIX=obs
+endif
+ifeq ($(OS_TARGET),win32)
+SHAREDLIBEXT=.dll
+SHORTSUFFIX=w32
+endif
+ifeq ($(OS_TARGET),os2)
+BATCHEXT=.cmd
+AOUTEXT=.out
+STATICLIBPREFIX=
+SHAREDLIBEXT=.dll
+SHORTSUFFIX=os2
+ECHO=echo
+IMPORTLIBPREFIX=
+endif
+ifeq ($(OS_TARGET),emx)
+BATCHEXT=.cmd
+AOUTEXT=.out
+STATICLIBPREFIX=
+SHAREDLIBEXT=.dll
+SHORTSUFFIX=emx
+ECHO=echo
+IMPORTLIBPREFIX=
+endif
+ifeq ($(OS_TARGET),amiga)
+EXEEXT=
+SHAREDLIBEXT=.library
+SHORTSUFFIX=amg
+endif
+ifeq ($(OS_TARGET),aros)
+EXEEXT=
+SHAREDLIBEXT=.library
+SHORTSUFFIX=aros
+endif
+ifeq ($(OS_TARGET),morphos)
+EXEEXT=
+SHAREDLIBEXT=.library
+SHORTSUFFIX=mos
+endif
+ifeq ($(OS_TARGET),atari)
+EXEEXT=.ttp
+SHORTSUFFIX=ata
+endif
+ifeq ($(OS_TARGET),beos)
+BATCHEXT=.sh
+EXEEXT=
+SHORTSUFFIX=be
+endif
+ifeq ($(OS_TARGET),haiku)
+BATCHEXT=.sh
+EXEEXT=
+SHORTSUFFIX=hai
+endif
+ifeq ($(OS_TARGET),solaris)
+BATCHEXT=.sh
+EXEEXT=
+SHORTSUFFIX=sun
+endif
+ifeq ($(OS_TARGET),qnx)
+BATCHEXT=.sh
+EXEEXT=
+SHORTSUFFIX=qnx
+endif
+ifeq ($(OS_TARGET),netware)
+EXEEXT=.nlm
+STATICLIBPREFIX=
+SHORTSUFFIX=nw
+IMPORTLIBPREFIX=imp
+endif
+ifeq ($(OS_TARGET),netwlibc)
+EXEEXT=.nlm
+STATICLIBPREFIX=
+SHORTSUFFIX=nwl
+IMPORTLIBPREFIX=imp
+endif
+ifeq ($(OS_TARGET),macosclassic)
+BATCHEXT=
+EXEEXT=
+DEBUGSYMEXT=.xcoff
+SHORTSUFFIX=mac
+IMPORTLIBPREFIX=imp
+endif
+ifneq ($(findstring $(OS_TARGET),darwin iphonesim ios),)
+BATCHEXT=.sh
+EXEEXT=
+HASSHAREDLIB=1
+SHORTSUFFIX=dwn
+EXEDBGEXT=.dSYM
+endif
+ifeq ($(OS_TARGET),gba)
+EXEEXT=.gba
+SHAREDLIBEXT=.so
+SHORTSUFFIX=gba
+endif
+ifeq ($(OS_TARGET),symbian)
+SHAREDLIBEXT=.dll
+SHORTSUFFIX=symbian
+endif
+ifeq ($(OS_TARGET),NativeNT)
+SHAREDLIBEXT=.dll
+SHORTSUFFIX=nativent
+endif
+ifeq ($(OS_TARGET),wii)
+EXEEXT=.dol
+SHAREDLIBEXT=.so
+SHORTSUFFIX=wii
+endif
+ifeq ($(OS_TARGET),aix)
+BATCHEXT=.sh
+EXEEXT=
+SHAREDLIBEXT=.a
+SHORTSUFFIX=aix
+endif
+ifeq ($(OS_TARGET),java)
+OEXT=.class
+ASMEXT=.j
+SHAREDLIBEXT=.jar
+SHORTSUFFIX=java
+endif
+ifeq ($(CPU_TARGET),jvm)
+ifeq ($(OS_TARGET),android)
+OEXT=.class
+ASMEXT=.j
+SHAREDLIBEXT=.jar
+SHORTSUFFIX=android
+endif
+endif
+ifeq ($(OS_TARGET),msdos)
+STATICLIBPREFIX=
+STATICLIBEXT=.a
+SHORTSUFFIX=d16
+endif
+ifeq ($(OS_TARGET),msxdos)
+STATICLIBPREFIX=
+STATICLIBEXT=.a
+SHORTSUFFIX=msd
+endif
+ifeq ($(OS_TARGET),embedded)
+ifeq ($(CPU_TARGET),i8086)
+STATICLIBPREFIX=
+STATICLIBEXT=.a
+else
+EXEEXT=.bin
+endif
+ifeq ($(CPU_TARGET),z80)
+OEXT=.rel
+endif
+SHORTSUFFIX=emb
+endif
+ifeq ($(OS_TARGET),win16)
+STATICLIBPREFIX=
+STATICLIBEXT=.a
+SHAREDLIBEXT=.dll
+SHORTSUFFIX=w16
+endif
+ifeq ($(OS_TARGET),zxspectrum)
+OEXT=.rel
+endif
+ifneq ($(findstring $(OS_SOURCE),$(LIMIT83fs)),)
+FPCMADE=fpcmade.$(SHORTSUFFIX)
+ZIPSUFFIX=$(SHORTSUFFIX)
+ZIPCROSSPREFIX=
+ZIPSOURCESUFFIX=src
+ZIPEXAMPLESUFFIX=exm
+else
+FPCMADE=fpcmade.$(TARGETSUFFIX)
+ZIPSOURCESUFFIX=.source
+ZIPEXAMPLESUFFIX=.examples
+ifdef CROSSCOMPILE
+ZIPSUFFIX=.$(SOURCESUFFIX)
+ZIPCROSSPREFIX=$(TARGETSUFFIX)-
+else
+ZIPSUFFIX=.$(TARGETSUFFIX)
+ZIPCROSSPREFIX=
+endif
+endif
+ifndef ECHO
+ECHO:=$(strip $(wildcard $(addsuffix /gecho$(SRCEXEEXT),$(SEARCHPATH))))
+ifeq ($(ECHO),)
+ECHO:=$(strip $(wildcard $(addsuffix /echo$(SRCEXEEXT),$(SEARCHPATH))))
+ifeq ($(ECHO),)
+ECHO= __missing_command_ECHO
+else
+ECHO:=$(firstword $(ECHO))
+endif
+else
+ECHO:=$(firstword $(ECHO))
+endif
+endif
+export ECHO
+ifndef DATE
+DATE:=$(strip $(wildcard $(addsuffix /gdate$(SRCEXEEXT),$(SEARCHPATH))))
+ifeq ($(DATE),)
+DATE:=$(strip $(wildcard $(addsuffix /date$(SRCEXEEXT),$(SEARCHPATH))))
+ifeq ($(DATE),)
+DATE= __missing_command_DATE
+else
+DATE:=$(firstword $(DATE))
+endif
+else
+DATE:=$(firstword $(DATE))
+endif
+endif
+export DATE
+ifndef GINSTALL
+GINSTALL:=$(strip $(wildcard $(addsuffix /ginstall$(SRCEXEEXT),$(SEARCHPATH))))
+ifeq ($(GINSTALL),)
+GINSTALL:=$(strip $(wildcard $(addsuffix /install$(SRCEXEEXT),$(SEARCHPATH))))
+ifeq ($(GINSTALL),)
+GINSTALL= __missing_command_GINSTALL
+else
+GINSTALL:=$(firstword $(GINSTALL))
+endif
+else
+GINSTALL:=$(firstword $(GINSTALL))
+endif
+endif
+export GINSTALL
+ifndef CPPROG
+CPPROG:=$(strip $(wildcard $(addsuffix /cp$(SRCEXEEXT),$(SEARCHPATH))))
+ifeq ($(CPPROG),)
+CPPROG= __missing_command_CPPROG
+else
+CPPROG:=$(firstword $(CPPROG))
+endif
+endif
+export CPPROG
+ifndef RMPROG
+RMPROG:=$(strip $(wildcard $(addsuffix /rm$(SRCEXEEXT),$(SEARCHPATH))))
+ifeq ($(RMPROG),)
+RMPROG= __missing_command_RMPROG
+else
+RMPROG:=$(firstword $(RMPROG))
+endif
+endif
+export RMPROG
+ifndef MVPROG
+MVPROG:=$(strip $(wildcard $(addsuffix /mv$(SRCEXEEXT),$(SEARCHPATH))))
+ifeq ($(MVPROG),)
+MVPROG= __missing_command_MVPROG
+else
+MVPROG:=$(firstword $(MVPROG))
+endif
+endif
+export MVPROG
+ifndef MKDIRPROG
+MKDIRPROG:=$(strip $(wildcard $(addsuffix /gmkdir$(SRCEXEEXT),$(SEARCHPATH))))
+ifeq ($(MKDIRPROG),)
+MKDIRPROG:=$(strip $(wildcard $(addsuffix /mkdir$(SRCEXEEXT),$(SEARCHPATH))))
+ifeq ($(MKDIRPROG),)
+MKDIRPROG= __missing_command_MKDIRPROG
+else
+MKDIRPROG:=$(firstword $(MKDIRPROG))
+endif
+else
+MKDIRPROG:=$(firstword $(MKDIRPROG))
+endif
+endif
+export MKDIRPROG
+ifndef ECHOREDIR
+ifndef inUnix
+ECHOREDIR=echo
+else
+ECHOREDIR=$(ECHO)
+endif
+endif
+ifndef COPY
+COPY:=$(CPPROG) -fp
+endif
+ifndef COPYTREE
+COPYTREE:=$(CPPROG) -Rfp
+endif
+ifndef MKDIRTREE
+MKDIRTREE:=$(MKDIRPROG) -p
+endif
+ifndef MOVE
+MOVE:=$(MVPROG) -f
+endif
+ifndef DEL
+DEL:=$(RMPROG) -f
+endif
+ifndef DELTREE
+DELTREE:=$(RMPROG) -rf
+endif
+ifndef INSTALL
+ifdef inUnix
+INSTALL:=$(GINSTALL) -c -m 644
+else
+INSTALL:=$(COPY)
+endif
+endif
+ifndef INSTALLEXE
+ifdef inUnix
+INSTALLEXE:=$(GINSTALL) -c -m 755
+else
+INSTALLEXE:=$(COPY)
+endif
+endif
+ifndef MKDIR
+MKDIR:=$(GINSTALL) -m 755 -d
+endif
+export ECHOREDIR COPY COPYTREE MOVE DEL DELTREE INSTALL INSTALLEXE MKDIR
+ifndef PPUMOVE
+PPUMOVE:=$(strip $(wildcard $(addsuffix /ppumove$(SRCEXEEXT),$(SEARCHPATH))))
+ifeq ($(PPUMOVE),)
+PPUMOVE= __missing_command_PPUMOVE
+else
+PPUMOVE:=$(firstword $(PPUMOVE))
+endif
+endif
+export PPUMOVE
+ifndef FPCMAKE
+FPCMAKE:=$(strip $(wildcard $(addsuffix /fpcmake$(SRCEXEEXT),$(SEARCHPATH))))
+ifeq ($(FPCMAKE),)
+FPCMAKE= __missing_command_FPCMAKE
+else
+FPCMAKE:=$(firstword $(FPCMAKE))
+endif
+endif
+export FPCMAKE
+ifndef ZIPPROG
+ZIPPROG:=$(strip $(wildcard $(addsuffix /zip$(SRCEXEEXT),$(SEARCHPATH))))
+ifeq ($(ZIPPROG),)
+ZIPPROG= __missing_command_ZIPPROG
+else
+ZIPPROG:=$(firstword $(ZIPPROG))
+endif
+endif
+export ZIPPROG
+ifndef TARPROG
+TARPROG:=$(strip $(wildcard $(addsuffix /gtar$(SRCEXEEXT),$(SEARCHPATH))))
+ifeq ($(TARPROG),)
+TARPROG:=$(strip $(wildcard $(addsuffix /tar$(SRCEXEEXT),$(SEARCHPATH))))
+ifeq ($(TARPROG),)
+TARPROG= __missing_command_TARPROG
+else
+TARPROG:=$(firstword $(TARPROG))
+endif
+else
+TARPROG:=$(firstword $(TARPROG))
+endif
+endif
+export TARPROG
+ASNAME=$(BINUTILSPREFIX)as
+LDNAME=$(BINUTILSPREFIX)ld
+ARNAME=$(BINUTILSPREFIX)ar
+RCNAME=$(BINUTILSPREFIX)rc
+NASMNAME=$(BINUTILSPREFIX)nasm
+ifndef ASPROG
+ifdef CROSSBINDIR
+ASPROG=$(CROSSBINDIR)/$(ASNAME)$(SRCEXEEXT)
+else
+ASPROG=$(ASNAME)
+endif
+endif
+ifndef LDPROG
+ifdef CROSSBINDIR
+LDPROG=$(CROSSBINDIR)/$(LDNAME)$(SRCEXEEXT)
+else
+LDPROG=$(LDNAME)
+endif
+endif
+ifndef RCPROG
+ifdef CROSSBINDIR
+RCPROG=$(CROSSBINDIR)/$(RCNAME)$(SRCEXEEXT)
+else
+RCPROG=$(RCNAME)
+endif
+endif
+ifndef ARPROG
+ifdef CROSSBINDIR
+ARPROG=$(CROSSBINDIR)/$(ARNAME)$(SRCEXEEXT)
+else
+ARPROG=$(ARNAME)
+endif
+endif
+ifndef NASMPROG
+ifdef CROSSBINDIR
+NASMPROG=$(CROSSBINDIR)/$(NASMNAME)$(SRCEXEEXT)
+else
+NASMPROG=$(NASMNAME)
+endif
+endif
+AS=$(ASPROG)
+LD=$(LDPROG)
+RC=$(RCPROG)
+AR=$(ARPROG)
+NASM=$(NASMPROG)
+ifdef inUnix
+PPAS=./ppas$(SRCBATCHEXT)
+else
+PPAS=ppas$(SRCBATCHEXT)
+endif
+ifdef inUnix
+LDCONFIG=ldconfig
+else
+LDCONFIG=
+endif
+ifdef DATE
+DATESTR:=$(shell $(DATE) +%Y%m%d)
+else
+DATESTR=
+endif
+ZIPOPT=-9
+ZIPEXT=.zip
+ifeq ($(USETAR),bz2)
+TAROPT=vj
+TAREXT=.tar.bz2
+else
+TAROPT=vz
+TAREXT=.tar.gz
+endif
+override REQUIRE_PACKAGES=rtl fcl-base fcl-db fcl-json
+ifeq ($(FULL_TARGET),i386-linux)
+REQUIRE_PACKAGES_RTL=1
+REQUIRE_PACKAGES_PASZLIB=1
+REQUIRE_PACKAGES_FCL-PROCESS=1
+REQUIRE_PACKAGES_HASH=1
+REQUIRE_PACKAGES_LIBTAR=1
+REQUIRE_PACKAGES_FPMKUNIT=1
+REQUIRE_PACKAGES_FCL-BASE=1
+REQUIRE_PACKAGES_FCL-DB=1
+REQUIRE_PACKAGES_FCL-JSON=1
+endif
+ifeq ($(FULL_TARGET),i386-go32v2)
+REQUIRE_PACKAGES_RTL=1
+REQUIRE_PACKAGES_PASZLIB=1
+REQUIRE_PACKAGES_FCL-PROCESS=1
+REQUIRE_PACKAGES_HASH=1
+REQUIRE_PACKAGES_LIBTAR=1
+REQUIRE_PACKAGES_FPMKUNIT=1
+REQUIRE_PACKAGES_FCL-BASE=1
+REQUIRE_PACKAGES_FCL-DB=1
+REQUIRE_PACKAGES_FCL-JSON=1
+endif
+ifeq ($(FULL_TARGET),i386-win32)
+REQUIRE_PACKAGES_RTL=1
+REQUIRE_PACKAGES_PASZLIB=1
+REQUIRE_PACKAGES_FCL-PROCESS=1
+REQUIRE_PACKAGES_HASH=1
+REQUIRE_PACKAGES_LIBTAR=1
+REQUIRE_PACKAGES_FPMKUNIT=1
+REQUIRE_PACKAGES_FCL-BASE=1
+REQUIRE_PACKAGES_FCL-DB=1
+REQUIRE_PACKAGES_FCL-JSON=1
+endif
+ifeq ($(FULL_TARGET),i386-os2)
+REQUIRE_PACKAGES_RTL=1
+REQUIRE_PACKAGES_PASZLIB=1
+REQUIRE_PACKAGES_FCL-PROCESS=1
+REQUIRE_PACKAGES_HASH=1
+REQUIRE_PACKAGES_LIBTAR=1
+REQUIRE_PACKAGES_FPMKUNIT=1
+REQUIRE_PACKAGES_FCL-BASE=1
+REQUIRE_PACKAGES_FCL-DB=1
+REQUIRE_PACKAGES_FCL-JSON=1
+endif
+ifeq ($(FULL_TARGET),i386-freebsd)
+REQUIRE_PACKAGES_RTL=1
+REQUIRE_PACKAGES_PASZLIB=1
+REQUIRE_PACKAGES_FCL-PROCESS=1
+REQUIRE_PACKAGES_HASH=1
+REQUIRE_PACKAGES_LIBTAR=1
+REQUIRE_PACKAGES_FPMKUNIT=1
+REQUIRE_PACKAGES_FCL-BASE=1
+REQUIRE_PACKAGES_FCL-DB=1
+REQUIRE_PACKAGES_FCL-JSON=1
+endif
+ifeq ($(FULL_TARGET),i386-beos)
+REQUIRE_PACKAGES_RTL=1
+REQUIRE_PACKAGES_PASZLIB=1
+REQUIRE_PACKAGES_FCL-PROCESS=1
+REQUIRE_PACKAGES_HASH=1
+REQUIRE_PACKAGES_LIBTAR=1
+REQUIRE_PACKAGES_FPMKUNIT=1
+REQUIRE_PACKAGES_FCL-BASE=1
+REQUIRE_PACKAGES_FCL-DB=1
+REQUIRE_PACKAGES_FCL-JSON=1
+endif
+ifeq ($(FULL_TARGET),i386-haiku)
+REQUIRE_PACKAGES_RTL=1
+REQUIRE_PACKAGES_PASZLIB=1
+REQUIRE_PACKAGES_FCL-PROCESS=1
+REQUIRE_PACKAGES_HASH=1
+REQUIRE_PACKAGES_LIBTAR=1
+REQUIRE_PACKAGES_FPMKUNIT=1
+REQUIRE_PACKAGES_FCL-BASE=1
+REQUIRE_PACKAGES_FCL-DB=1
+REQUIRE_PACKAGES_FCL-JSON=1
+endif
+ifeq ($(FULL_TARGET),i386-netbsd)
+REQUIRE_PACKAGES_RTL=1
+REQUIRE_PACKAGES_PASZLIB=1
+REQUIRE_PACKAGES_FCL-PROCESS=1
+REQUIRE_PACKAGES_HASH=1
+REQUIRE_PACKAGES_LIBTAR=1
+REQUIRE_PACKAGES_FPMKUNIT=1
+REQUIRE_PACKAGES_FCL-BASE=1
+REQUIRE_PACKAGES_FCL-DB=1
+REQUIRE_PACKAGES_FCL-JSON=1
+endif
+ifeq ($(FULL_TARGET),i386-solaris)
+REQUIRE_PACKAGES_RTL=1
+REQUIRE_PACKAGES_PASZLIB=1
+REQUIRE_PACKAGES_FCL-PROCESS=1
+REQUIRE_PACKAGES_HASH=1
+REQUIRE_PACKAGES_LIBTAR=1
+REQUIRE_PACKAGES_FPMKUNIT=1
+REQUIRE_PACKAGES_FCL-BASE=1
+REQUIRE_PACKAGES_FCL-DB=1
+REQUIRE_PACKAGES_FCL-JSON=1
+endif
+ifeq ($(FULL_TARGET),i386-netware)
+REQUIRE_PACKAGES_RTL=1
+REQUIRE_PACKAGES_PASZLIB=1
+REQUIRE_PACKAGES_FCL-PROCESS=1
+REQUIRE_PACKAGES_HASH=1
+REQUIRE_PACKAGES_LIBTAR=1
+REQUIRE_PACKAGES_FPMKUNIT=1
+REQUIRE_PACKAGES_FCL-BASE=1
+REQUIRE_PACKAGES_FCL-DB=1
+REQUIRE_PACKAGES_FCL-JSON=1
+endif
+ifeq ($(FULL_TARGET),i386-openbsd)
+REQUIRE_PACKAGES_RTL=1
+REQUIRE_PACKAGES_PASZLIB=1
+REQUIRE_PACKAGES_FCL-PROCESS=1
+REQUIRE_PACKAGES_HASH=1
+REQUIRE_PACKAGES_LIBTAR=1
+REQUIRE_PACKAGES_FPMKUNIT=1
+REQUIRE_PACKAGES_FCL-BASE=1
+REQUIRE_PACKAGES_FCL-DB=1
+REQUIRE_PACKAGES_FCL-JSON=1
+endif
+ifeq ($(FULL_TARGET),i386-wdosx)
+REQUIRE_PACKAGES_RTL=1
+REQUIRE_PACKAGES_PASZLIB=1
+REQUIRE_PACKAGES_FCL-PROCESS=1
+REQUIRE_PACKAGES_HASH=1
+REQUIRE_PACKAGES_LIBTAR=1
+REQUIRE_PACKAGES_FPMKUNIT=1
+REQUIRE_PACKAGES_FCL-BASE=1
+REQUIRE_PACKAGES_FCL-DB=1
+REQUIRE_PACKAGES_FCL-JSON=1
+endif
+ifeq ($(FULL_TARGET),i386-darwin)
+REQUIRE_PACKAGES_RTL=1
+REQUIRE_PACKAGES_PASZLIB=1
+REQUIRE_PACKAGES_FCL-PROCESS=1
+REQUIRE_PACKAGES_HASH=1
+REQUIRE_PACKAGES_LIBTAR=1
+REQUIRE_PACKAGES_FPMKUNIT=1
+REQUIRE_PACKAGES_FCL-BASE=1
+REQUIRE_PACKAGES_FCL-DB=1
+REQUIRE_PACKAGES_FCL-JSON=1
+endif
+ifeq ($(FULL_TARGET),i386-emx)
+REQUIRE_PACKAGES_RTL=1
+REQUIRE_PACKAGES_PASZLIB=1
+REQUIRE_PACKAGES_FCL-PROCESS=1
+REQUIRE_PACKAGES_HASH=1
+REQUIRE_PACKAGES_LIBTAR=1
+REQUIRE_PACKAGES_FPMKUNIT=1
+REQUIRE_PACKAGES_FCL-BASE=1
+REQUIRE_PACKAGES_FCL-DB=1
+REQUIRE_PACKAGES_FCL-JSON=1
+endif
+ifeq ($(FULL_TARGET),i386-watcom)
+REQUIRE_PACKAGES_RTL=1
+REQUIRE_PACKAGES_PASZLIB=1
+REQUIRE_PACKAGES_FCL-PROCESS=1
+REQUIRE_PACKAGES_HASH=1
+REQUIRE_PACKAGES_LIBTAR=1
+REQUIRE_PACKAGES_FPMKUNIT=1
+REQUIRE_PACKAGES_FCL-BASE=1
+REQUIRE_PACKAGES_FCL-DB=1
+REQUIRE_PACKAGES_FCL-JSON=1
+endif
+ifeq ($(FULL_TARGET),i386-netwlibc)
+REQUIRE_PACKAGES_RTL=1
+REQUIRE_PACKAGES_PASZLIB=1
+REQUIRE_PACKAGES_FCL-PROCESS=1
+REQUIRE_PACKAGES_HASH=1
+REQUIRE_PACKAGES_LIBTAR=1
+REQUIRE_PACKAGES_FPMKUNIT=1
+REQUIRE_PACKAGES_FCL-BASE=1
+REQUIRE_PACKAGES_FCL-DB=1
+REQUIRE_PACKAGES_FCL-JSON=1
+endif
+ifeq ($(FULL_TARGET),i386-wince)
+REQUIRE_PACKAGES_RTL=1
+REQUIRE_PACKAGES_PASZLIB=1
+REQUIRE_PACKAGES_FCL-PROCESS=1
+REQUIRE_PACKAGES_HASH=1
+REQUIRE_PACKAGES_LIBTAR=1
+REQUIRE_PACKAGES_FPMKUNIT=1
+REQUIRE_PACKAGES_FCL-BASE=1
+REQUIRE_PACKAGES_FCL-DB=1
+REQUIRE_PACKAGES_FCL-JSON=1
+endif
+ifeq ($(FULL_TARGET),i386-embedded)
+REQUIRE_PACKAGES_RTL=1
+REQUIRE_PACKAGES_PASZLIB=1
+REQUIRE_PACKAGES_FCL-PROCESS=1
+REQUIRE_PACKAGES_HASH=1
+REQUIRE_PACKAGES_LIBTAR=1
+REQUIRE_PACKAGES_FPMKUNIT=1
+REQUIRE_PACKAGES_FCL-BASE=1
+REQUIRE_PACKAGES_FCL-DB=1
+REQUIRE_PACKAGES_FCL-JSON=1
+endif
+ifeq ($(FULL_TARGET),i386-symbian)
+REQUIRE_PACKAGES_RTL=1
+REQUIRE_PACKAGES_PASZLIB=1
+REQUIRE_PACKAGES_FCL-PROCESS=1
+REQUIRE_PACKAGES_HASH=1
+REQUIRE_PACKAGES_LIBTAR=1
+REQUIRE_PACKAGES_FPMKUNIT=1
+REQUIRE_PACKAGES_FCL-BASE=1
+REQUIRE_PACKAGES_FCL-DB=1
+REQUIRE_PACKAGES_FCL-JSON=1
+endif
+ifeq ($(FULL_TARGET),i386-nativent)
+REQUIRE_PACKAGES_RTL=1
+REQUIRE_PACKAGES_PASZLIB=1
+REQUIRE_PACKAGES_FCL-PROCESS=1
+REQUIRE_PACKAGES_HASH=1
+REQUIRE_PACKAGES_LIBTAR=1
+REQUIRE_PACKAGES_FPMKUNIT=1
+REQUIRE_PACKAGES_FCL-BASE=1
+REQUIRE_PACKAGES_FCL-DB=1
+REQUIRE_PACKAGES_FCL-JSON=1
+endif
+ifeq ($(FULL_TARGET),i386-iphonesim)
+REQUIRE_PACKAGES_RTL=1
+REQUIRE_PACKAGES_PASZLIB=1
+REQUIRE_PACKAGES_FCL-PROCESS=1
+REQUIRE_PACKAGES_HASH=1
+REQUIRE_PACKAGES_LIBTAR=1
+REQUIRE_PACKAGES_FPMKUNIT=1
+REQUIRE_PACKAGES_FCL-BASE=1
+REQUIRE_PACKAGES_FCL-DB=1
+REQUIRE_PACKAGES_FCL-JSON=1
+endif
+ifeq ($(FULL_TARGET),i386-android)
+REQUIRE_PACKAGES_RTL=1
+REQUIRE_PACKAGES_PASZLIB=1
+REQUIRE_PACKAGES_FCL-PROCESS=1
+REQUIRE_PACKAGES_HASH=1
+REQUIRE_PACKAGES_LIBTAR=1
+REQUIRE_PACKAGES_FPMKUNIT=1
+REQUIRE_PACKAGES_FCL-BASE=1
+REQUIRE_PACKAGES_FCL-DB=1
+REQUIRE_PACKAGES_FCL-JSON=1
+endif
+ifeq ($(FULL_TARGET),i386-aros)
+REQUIRE_PACKAGES_RTL=1
+REQUIRE_PACKAGES_PASZLIB=1
+REQUIRE_PACKAGES_FCL-PROCESS=1
+REQUIRE_PACKAGES_HASH=1
+REQUIRE_PACKAGES_LIBTAR=1
+REQUIRE_PACKAGES_FPMKUNIT=1
+REQUIRE_PACKAGES_FCL-BASE=1
+REQUIRE_PACKAGES_FCL-DB=1
+REQUIRE_PACKAGES_FCL-JSON=1
+endif
+ifeq ($(FULL_TARGET),m68k-linux)
+REQUIRE_PACKAGES_RTL=1
+REQUIRE_PACKAGES_PASZLIB=1
+REQUIRE_PACKAGES_FCL-PROCESS=1
+REQUIRE_PACKAGES_HASH=1
+REQUIRE_PACKAGES_LIBTAR=1
+REQUIRE_PACKAGES_FPMKUNIT=1
+REQUIRE_PACKAGES_FCL-BASE=1
+REQUIRE_PACKAGES_FCL-DB=1
+REQUIRE_PACKAGES_FCL-JSON=1
+endif
+ifeq ($(FULL_TARGET),m68k-netbsd)
+REQUIRE_PACKAGES_RTL=1
+REQUIRE_PACKAGES_PASZLIB=1
+REQUIRE_PACKAGES_FCL-PROCESS=1
+REQUIRE_PACKAGES_HASH=1
+REQUIRE_PACKAGES_LIBTAR=1
+REQUIRE_PACKAGES_FPMKUNIT=1
+REQUIRE_PACKAGES_FCL-BASE=1
+REQUIRE_PACKAGES_FCL-DB=1
+REQUIRE_PACKAGES_FCL-JSON=1
+endif
+ifeq ($(FULL_TARGET),m68k-amiga)
+REQUIRE_PACKAGES_RTL=1
+REQUIRE_PACKAGES_PASZLIB=1
+REQUIRE_PACKAGES_FCL-PROCESS=1
+REQUIRE_PACKAGES_HASH=1
+REQUIRE_PACKAGES_LIBTAR=1
+REQUIRE_PACKAGES_FPMKUNIT=1
+REQUIRE_PACKAGES_FCL-BASE=1
+REQUIRE_PACKAGES_FCL-DB=1
+REQUIRE_PACKAGES_FCL-JSON=1
+endif
+ifeq ($(FULL_TARGET),m68k-atari)
+REQUIRE_PACKAGES_RTL=1
+REQUIRE_PACKAGES_PASZLIB=1
+REQUIRE_PACKAGES_FCL-PROCESS=1
+REQUIRE_PACKAGES_HASH=1
+REQUIRE_PACKAGES_LIBTAR=1
+REQUIRE_PACKAGES_FPMKUNIT=1
+REQUIRE_PACKAGES_FCL-BASE=1
+REQUIRE_PACKAGES_FCL-DB=1
+REQUIRE_PACKAGES_FCL-JSON=1
+endif
+ifeq ($(FULL_TARGET),m68k-palmos)
+REQUIRE_PACKAGES_RTL=1
+REQUIRE_PACKAGES_PASZLIB=1
+REQUIRE_PACKAGES_FCL-PROCESS=1
+REQUIRE_PACKAGES_HASH=1
+REQUIRE_PACKAGES_LIBTAR=1
+REQUIRE_PACKAGES_FPMKUNIT=1
+REQUIRE_PACKAGES_FCL-BASE=1
+REQUIRE_PACKAGES_FCL-DB=1
+REQUIRE_PACKAGES_FCL-JSON=1
+endif
+ifeq ($(FULL_TARGET),m68k-macosclassic)
+REQUIRE_PACKAGES_RTL=1
+REQUIRE_PACKAGES_PASZLIB=1
+REQUIRE_PACKAGES_FCL-PROCESS=1
+REQUIRE_PACKAGES_HASH=1
+REQUIRE_PACKAGES_LIBTAR=1
+REQUIRE_PACKAGES_FPMKUNIT=1
+REQUIRE_PACKAGES_FCL-BASE=1
+REQUIRE_PACKAGES_FCL-DB=1
+REQUIRE_PACKAGES_FCL-JSON=1
+endif
+ifeq ($(FULL_TARGET),m68k-embedded)
+REQUIRE_PACKAGES_RTL=1
+REQUIRE_PACKAGES_PASZLIB=1
+REQUIRE_PACKAGES_FCL-PROCESS=1
+REQUIRE_PACKAGES_HASH=1
+REQUIRE_PACKAGES_LIBTAR=1
+REQUIRE_PACKAGES_FPMKUNIT=1
+REQUIRE_PACKAGES_FCL-BASE=1
+REQUIRE_PACKAGES_FCL-DB=1
+REQUIRE_PACKAGES_FCL-JSON=1
+endif
+ifeq ($(FULL_TARGET),m68k-sinclairql)
+REQUIRE_PACKAGES_RTL=1
+REQUIRE_PACKAGES_PASZLIB=1
+REQUIRE_PACKAGES_FCL-PROCESS=1
+REQUIRE_PACKAGES_HASH=1
+REQUIRE_PACKAGES_LIBTAR=1
+REQUIRE_PACKAGES_FPMKUNIT=1
+REQUIRE_PACKAGES_FCL-BASE=1
+REQUIRE_PACKAGES_FCL-DB=1
+REQUIRE_PACKAGES_FCL-JSON=1
+endif
+ifeq ($(FULL_TARGET),powerpc-linux)
+REQUIRE_PACKAGES_RTL=1
+REQUIRE_PACKAGES_PASZLIB=1
+REQUIRE_PACKAGES_FCL-PROCESS=1
+REQUIRE_PACKAGES_HASH=1
+REQUIRE_PACKAGES_LIBTAR=1
+REQUIRE_PACKAGES_FPMKUNIT=1
+REQUIRE_PACKAGES_FCL-BASE=1
+REQUIRE_PACKAGES_FCL-DB=1
+REQUIRE_PACKAGES_FCL-JSON=1
+endif
+ifeq ($(FULL_TARGET),powerpc-netbsd)
+REQUIRE_PACKAGES_RTL=1
+REQUIRE_PACKAGES_PASZLIB=1
+REQUIRE_PACKAGES_FCL-PROCESS=1
+REQUIRE_PACKAGES_HASH=1
+REQUIRE_PACKAGES_LIBTAR=1
+REQUIRE_PACKAGES_FPMKUNIT=1
+REQUIRE_PACKAGES_FCL-BASE=1
+REQUIRE_PACKAGES_FCL-DB=1
+REQUIRE_PACKAGES_FCL-JSON=1
+endif
+ifeq ($(FULL_TARGET),powerpc-amiga)
+REQUIRE_PACKAGES_RTL=1
+REQUIRE_PACKAGES_PASZLIB=1
+REQUIRE_PACKAGES_FCL-PROCESS=1
+REQUIRE_PACKAGES_HASH=1
+REQUIRE_PACKAGES_LIBTAR=1
+REQUIRE_PACKAGES_FPMKUNIT=1
+REQUIRE_PACKAGES_FCL-BASE=1
+REQUIRE_PACKAGES_FCL-DB=1
+REQUIRE_PACKAGES_FCL-JSON=1
+endif
+ifeq ($(FULL_TARGET),powerpc-macosclassic)
+REQUIRE_PACKAGES_RTL=1
+REQUIRE_PACKAGES_PASZLIB=1
+REQUIRE_PACKAGES_FCL-PROCESS=1
+REQUIRE_PACKAGES_HASH=1
+REQUIRE_PACKAGES_LIBTAR=1
+REQUIRE_PACKAGES_FPMKUNIT=1
+REQUIRE_PACKAGES_FCL-BASE=1
+REQUIRE_PACKAGES_FCL-DB=1
+REQUIRE_PACKAGES_FCL-JSON=1
+endif
+ifeq ($(FULL_TARGET),powerpc-darwin)
+REQUIRE_PACKAGES_RTL=1
+REQUIRE_PACKAGES_PASZLIB=1
+REQUIRE_PACKAGES_FCL-PROCESS=1
+REQUIRE_PACKAGES_HASH=1
+REQUIRE_PACKAGES_LIBTAR=1
+REQUIRE_PACKAGES_FPMKUNIT=1
+REQUIRE_PACKAGES_FCL-BASE=1
+REQUIRE_PACKAGES_FCL-DB=1
+REQUIRE_PACKAGES_FCL-JSON=1
+endif
+ifeq ($(FULL_TARGET),powerpc-morphos)
+REQUIRE_PACKAGES_RTL=1
+REQUIRE_PACKAGES_PASZLIB=1
+REQUIRE_PACKAGES_FCL-PROCESS=1
+REQUIRE_PACKAGES_HASH=1
+REQUIRE_PACKAGES_LIBTAR=1
+REQUIRE_PACKAGES_FPMKUNIT=1
+REQUIRE_PACKAGES_FCL-BASE=1
+REQUIRE_PACKAGES_FCL-DB=1
+REQUIRE_PACKAGES_FCL-JSON=1
+endif
+ifeq ($(FULL_TARGET),powerpc-embedded)
+REQUIRE_PACKAGES_RTL=1
+REQUIRE_PACKAGES_PASZLIB=1
+REQUIRE_PACKAGES_FCL-PROCESS=1
+REQUIRE_PACKAGES_HASH=1
+REQUIRE_PACKAGES_LIBTAR=1
+REQUIRE_PACKAGES_FPMKUNIT=1
+REQUIRE_PACKAGES_FCL-BASE=1
+REQUIRE_PACKAGES_FCL-DB=1
+REQUIRE_PACKAGES_FCL-JSON=1
+endif
+ifeq ($(FULL_TARGET),powerpc-wii)
+REQUIRE_PACKAGES_RTL=1
+REQUIRE_PACKAGES_PASZLIB=1
+REQUIRE_PACKAGES_FCL-PROCESS=1
+REQUIRE_PACKAGES_HASH=1
+REQUIRE_PACKAGES_LIBTAR=1
+REQUIRE_PACKAGES_FPMKUNIT=1
+REQUIRE_PACKAGES_FCL-BASE=1
+REQUIRE_PACKAGES_FCL-DB=1
+REQUIRE_PACKAGES_FCL-JSON=1
+endif
+ifeq ($(FULL_TARGET),powerpc-aix)
+REQUIRE_PACKAGES_RTL=1
+REQUIRE_PACKAGES_PASZLIB=1
+REQUIRE_PACKAGES_FCL-PROCESS=1
+REQUIRE_PACKAGES_HASH=1
+REQUIRE_PACKAGES_LIBTAR=1
+REQUIRE_PACKAGES_FPMKUNIT=1
+REQUIRE_PACKAGES_FCL-BASE=1
+REQUIRE_PACKAGES_FCL-DB=1
+REQUIRE_PACKAGES_FCL-JSON=1
+endif
+ifeq ($(FULL_TARGET),sparc-linux)
+REQUIRE_PACKAGES_RTL=1
+REQUIRE_PACKAGES_PASZLIB=1
+REQUIRE_PACKAGES_FCL-PROCESS=1
+REQUIRE_PACKAGES_HASH=1
+REQUIRE_PACKAGES_LIBTAR=1
+REQUIRE_PACKAGES_FPMKUNIT=1
+REQUIRE_PACKAGES_FCL-BASE=1
+REQUIRE_PACKAGES_FCL-DB=1
+REQUIRE_PACKAGES_FCL-JSON=1
+endif
+ifeq ($(FULL_TARGET),sparc-netbsd)
+REQUIRE_PACKAGES_RTL=1
+REQUIRE_PACKAGES_PASZLIB=1
+REQUIRE_PACKAGES_FCL-PROCESS=1
+REQUIRE_PACKAGES_HASH=1
+REQUIRE_PACKAGES_LIBTAR=1
+REQUIRE_PACKAGES_FPMKUNIT=1
+REQUIRE_PACKAGES_FCL-BASE=1
+REQUIRE_PACKAGES_FCL-DB=1
+REQUIRE_PACKAGES_FCL-JSON=1
+endif
+ifeq ($(FULL_TARGET),sparc-solaris)
+REQUIRE_PACKAGES_RTL=1
+REQUIRE_PACKAGES_PASZLIB=1
+REQUIRE_PACKAGES_FCL-PROCESS=1
+REQUIRE_PACKAGES_HASH=1
+REQUIRE_PACKAGES_LIBTAR=1
+REQUIRE_PACKAGES_FPMKUNIT=1
+REQUIRE_PACKAGES_FCL-BASE=1
+REQUIRE_PACKAGES_FCL-DB=1
+REQUIRE_PACKAGES_FCL-JSON=1
+endif
+ifeq ($(FULL_TARGET),sparc-embedded)
+REQUIRE_PACKAGES_RTL=1
+REQUIRE_PACKAGES_PASZLIB=1
+REQUIRE_PACKAGES_FCL-PROCESS=1
+REQUIRE_PACKAGES_HASH=1
+REQUIRE_PACKAGES_LIBTAR=1
+REQUIRE_PACKAGES_FPMKUNIT=1
+REQUIRE_PACKAGES_FCL-BASE=1
+REQUIRE_PACKAGES_FCL-DB=1
+REQUIRE_PACKAGES_FCL-JSON=1
+endif
+ifeq ($(FULL_TARGET),x86_64-linux)
+REQUIRE_PACKAGES_RTL=1
+REQUIRE_PACKAGES_PASZLIB=1
+REQUIRE_PACKAGES_FCL-PROCESS=1
+REQUIRE_PACKAGES_HASH=1
+REQUIRE_PACKAGES_LIBTAR=1
+REQUIRE_PACKAGES_FPMKUNIT=1
+REQUIRE_PACKAGES_FCL-BASE=1
+REQUIRE_PACKAGES_FCL-DB=1
+REQUIRE_PACKAGES_FCL-JSON=1
+endif
+ifeq ($(FULL_TARGET),x86_64-freebsd)
+REQUIRE_PACKAGES_RTL=1
+REQUIRE_PACKAGES_PASZLIB=1
+REQUIRE_PACKAGES_FCL-PROCESS=1
+REQUIRE_PACKAGES_HASH=1
+REQUIRE_PACKAGES_LIBTAR=1
+REQUIRE_PACKAGES_FPMKUNIT=1
+REQUIRE_PACKAGES_FCL-BASE=1
+REQUIRE_PACKAGES_FCL-DB=1
+REQUIRE_PACKAGES_FCL-JSON=1
+endif
+ifeq ($(FULL_TARGET),x86_64-haiku)
+REQUIRE_PACKAGES_RTL=1
+REQUIRE_PACKAGES_PASZLIB=1
+REQUIRE_PACKAGES_FCL-PROCESS=1
+REQUIRE_PACKAGES_HASH=1
+REQUIRE_PACKAGES_LIBTAR=1
+REQUIRE_PACKAGES_FPMKUNIT=1
+REQUIRE_PACKAGES_FCL-BASE=1
+REQUIRE_PACKAGES_FCL-DB=1
+REQUIRE_PACKAGES_FCL-JSON=1
+endif
+ifeq ($(FULL_TARGET),x86_64-netbsd)
+REQUIRE_PACKAGES_RTL=1
+REQUIRE_PACKAGES_PASZLIB=1
+REQUIRE_PACKAGES_FCL-PROCESS=1
+REQUIRE_PACKAGES_HASH=1
+REQUIRE_PACKAGES_LIBTAR=1
+REQUIRE_PACKAGES_FPMKUNIT=1
+REQUIRE_PACKAGES_FCL-BASE=1
+REQUIRE_PACKAGES_FCL-DB=1
+REQUIRE_PACKAGES_FCL-JSON=1
+endif
+ifeq ($(FULL_TARGET),x86_64-solaris)
+REQUIRE_PACKAGES_RTL=1
+REQUIRE_PACKAGES_PASZLIB=1
+REQUIRE_PACKAGES_FCL-PROCESS=1
+REQUIRE_PACKAGES_HASH=1
+REQUIRE_PACKAGES_LIBTAR=1
+REQUIRE_PACKAGES_FPMKUNIT=1
+REQUIRE_PACKAGES_FCL-BASE=1
+REQUIRE_PACKAGES_FCL-DB=1
+REQUIRE_PACKAGES_FCL-JSON=1
+endif
+ifeq ($(FULL_TARGET),x86_64-openbsd)
+REQUIRE_PACKAGES_RTL=1
+REQUIRE_PACKAGES_PASZLIB=1
+REQUIRE_PACKAGES_FCL-PROCESS=1
+REQUIRE_PACKAGES_HASH=1
+REQUIRE_PACKAGES_LIBTAR=1
+REQUIRE_PACKAGES_FPMKUNIT=1
+REQUIRE_PACKAGES_FCL-BASE=1
+REQUIRE_PACKAGES_FCL-DB=1
+REQUIRE_PACKAGES_FCL-JSON=1
+endif
+ifeq ($(FULL_TARGET),x86_64-darwin)
+REQUIRE_PACKAGES_RTL=1
+REQUIRE_PACKAGES_PASZLIB=1
+REQUIRE_PACKAGES_FCL-PROCESS=1
+REQUIRE_PACKAGES_HASH=1
+REQUIRE_PACKAGES_LIBTAR=1
+REQUIRE_PACKAGES_FPMKUNIT=1
+REQUIRE_PACKAGES_FCL-BASE=1
+REQUIRE_PACKAGES_FCL-DB=1
+REQUIRE_PACKAGES_FCL-JSON=1
+endif
+ifeq ($(FULL_TARGET),x86_64-win64)
+REQUIRE_PACKAGES_RTL=1
+REQUIRE_PACKAGES_PASZLIB=1
+REQUIRE_PACKAGES_FCL-PROCESS=1
+REQUIRE_PACKAGES_HASH=1
+REQUIRE_PACKAGES_LIBTAR=1
+REQUIRE_PACKAGES_FPMKUNIT=1
+REQUIRE_PACKAGES_FCL-BASE=1
+REQUIRE_PACKAGES_FCL-DB=1
+REQUIRE_PACKAGES_FCL-JSON=1
+endif
+ifeq ($(FULL_TARGET),x86_64-embedded)
+REQUIRE_PACKAGES_RTL=1
+REQUIRE_PACKAGES_PASZLIB=1
+REQUIRE_PACKAGES_FCL-PROCESS=1
+REQUIRE_PACKAGES_HASH=1
+REQUIRE_PACKAGES_LIBTAR=1
+REQUIRE_PACKAGES_FPMKUNIT=1
+REQUIRE_PACKAGES_FCL-BASE=1
+REQUIRE_PACKAGES_FCL-DB=1
+REQUIRE_PACKAGES_FCL-JSON=1
+endif
+ifeq ($(FULL_TARGET),x86_64-iphonesim)
+REQUIRE_PACKAGES_RTL=1
+REQUIRE_PACKAGES_PASZLIB=1
+REQUIRE_PACKAGES_FCL-PROCESS=1
+REQUIRE_PACKAGES_HASH=1
+REQUIRE_PACKAGES_LIBTAR=1
+REQUIRE_PACKAGES_FPMKUNIT=1
+REQUIRE_PACKAGES_FCL-BASE=1
+REQUIRE_PACKAGES_FCL-DB=1
+REQUIRE_PACKAGES_FCL-JSON=1
+endif
+ifeq ($(FULL_TARGET),x86_64-android)
+REQUIRE_PACKAGES_RTL=1
+REQUIRE_PACKAGES_PASZLIB=1
+REQUIRE_PACKAGES_FCL-PROCESS=1
+REQUIRE_PACKAGES_HASH=1
+REQUIRE_PACKAGES_LIBTAR=1
+REQUIRE_PACKAGES_FPMKUNIT=1
+REQUIRE_PACKAGES_FCL-BASE=1
+REQUIRE_PACKAGES_FCL-DB=1
+REQUIRE_PACKAGES_FCL-JSON=1
+endif
+ifeq ($(FULL_TARGET),x86_64-aros)
+REQUIRE_PACKAGES_RTL=1
+REQUIRE_PACKAGES_PASZLIB=1
+REQUIRE_PACKAGES_FCL-PROCESS=1
+REQUIRE_PACKAGES_HASH=1
+REQUIRE_PACKAGES_LIBTAR=1
+REQUIRE_PACKAGES_FPMKUNIT=1
+REQUIRE_PACKAGES_FCL-BASE=1
+REQUIRE_PACKAGES_FCL-DB=1
+REQUIRE_PACKAGES_FCL-JSON=1
+endif
+ifeq ($(FULL_TARGET),x86_64-dragonfly)
+REQUIRE_PACKAGES_RTL=1
+REQUIRE_PACKAGES_PASZLIB=1
+REQUIRE_PACKAGES_FCL-PROCESS=1
+REQUIRE_PACKAGES_HASH=1
+REQUIRE_PACKAGES_LIBTAR=1
+REQUIRE_PACKAGES_FPMKUNIT=1
+REQUIRE_PACKAGES_FCL-BASE=1
+REQUIRE_PACKAGES_FCL-DB=1
+REQUIRE_PACKAGES_FCL-JSON=1
+endif
+ifeq ($(FULL_TARGET),arm-linux)
+REQUIRE_PACKAGES_RTL=1
+REQUIRE_PACKAGES_PASZLIB=1
+REQUIRE_PACKAGES_FCL-PROCESS=1
+REQUIRE_PACKAGES_HASH=1
+REQUIRE_PACKAGES_LIBTAR=1
+REQUIRE_PACKAGES_FPMKUNIT=1
+REQUIRE_PACKAGES_FCL-BASE=1
+REQUIRE_PACKAGES_FCL-DB=1
+REQUIRE_PACKAGES_FCL-JSON=1
+endif
+ifeq ($(FULL_TARGET),arm-netbsd)
+REQUIRE_PACKAGES_RTL=1
+REQUIRE_PACKAGES_PASZLIB=1
+REQUIRE_PACKAGES_FCL-PROCESS=1
+REQUIRE_PACKAGES_HASH=1
+REQUIRE_PACKAGES_LIBTAR=1
+REQUIRE_PACKAGES_FPMKUNIT=1
+REQUIRE_PACKAGES_FCL-BASE=1
+REQUIRE_PACKAGES_FCL-DB=1
+REQUIRE_PACKAGES_FCL-JSON=1
+endif
+ifeq ($(FULL_TARGET),arm-palmos)
+REQUIRE_PACKAGES_RTL=1
+REQUIRE_PACKAGES_PASZLIB=1
+REQUIRE_PACKAGES_FCL-PROCESS=1
+REQUIRE_PACKAGES_HASH=1
+REQUIRE_PACKAGES_LIBTAR=1
+REQUIRE_PACKAGES_FPMKUNIT=1
+REQUIRE_PACKAGES_FCL-BASE=1
+REQUIRE_PACKAGES_FCL-DB=1
+REQUIRE_PACKAGES_FCL-JSON=1
+endif
+ifeq ($(FULL_TARGET),arm-wince)
+REQUIRE_PACKAGES_RTL=1
+REQUIRE_PACKAGES_PASZLIB=1
+REQUIRE_PACKAGES_FCL-PROCESS=1
+REQUIRE_PACKAGES_HASH=1
+REQUIRE_PACKAGES_LIBTAR=1
+REQUIRE_PACKAGES_FPMKUNIT=1
+REQUIRE_PACKAGES_FCL-BASE=1
+REQUIRE_PACKAGES_FCL-DB=1
+REQUIRE_PACKAGES_FCL-JSON=1
+endif
+ifeq ($(FULL_TARGET),arm-gba)
+REQUIRE_PACKAGES_RTL=1
+REQUIRE_PACKAGES_PASZLIB=1
+REQUIRE_PACKAGES_FCL-PROCESS=1
+REQUIRE_PACKAGES_HASH=1
+REQUIRE_PACKAGES_LIBTAR=1
+REQUIRE_PACKAGES_FPMKUNIT=1
+REQUIRE_PACKAGES_FCL-BASE=1
+REQUIRE_PACKAGES_FCL-DB=1
+REQUIRE_PACKAGES_FCL-JSON=1
+endif
+ifeq ($(FULL_TARGET),arm-nds)
+REQUIRE_PACKAGES_RTL=1
+REQUIRE_PACKAGES_PASZLIB=1
+REQUIRE_PACKAGES_FCL-PROCESS=1
+REQUIRE_PACKAGES_HASH=1
+REQUIRE_PACKAGES_LIBTAR=1
+REQUIRE_PACKAGES_FPMKUNIT=1
+REQUIRE_PACKAGES_FCL-BASE=1
+REQUIRE_PACKAGES_FCL-DB=1
+REQUIRE_PACKAGES_FCL-JSON=1
+endif
+ifeq ($(FULL_TARGET),arm-embedded)
+REQUIRE_PACKAGES_RTL=1
+REQUIRE_PACKAGES_PASZLIB=1
+REQUIRE_PACKAGES_FCL-PROCESS=1
+REQUIRE_PACKAGES_HASH=1
+REQUIRE_PACKAGES_LIBTAR=1
+REQUIRE_PACKAGES_FPMKUNIT=1
+REQUIRE_PACKAGES_FCL-BASE=1
+REQUIRE_PACKAGES_FCL-DB=1
+REQUIRE_PACKAGES_FCL-JSON=1
+endif
+ifeq ($(FULL_TARGET),arm-symbian)
+REQUIRE_PACKAGES_RTL=1
+REQUIRE_PACKAGES_PASZLIB=1
+REQUIRE_PACKAGES_FCL-PROCESS=1
+REQUIRE_PACKAGES_HASH=1
+REQUIRE_PACKAGES_LIBTAR=1
+REQUIRE_PACKAGES_FPMKUNIT=1
+REQUIRE_PACKAGES_FCL-BASE=1
+REQUIRE_PACKAGES_FCL-DB=1
+REQUIRE_PACKAGES_FCL-JSON=1
+endif
+ifeq ($(FULL_TARGET),arm-android)
+REQUIRE_PACKAGES_RTL=1
+REQUIRE_PACKAGES_PASZLIB=1
+REQUIRE_PACKAGES_FCL-PROCESS=1
+REQUIRE_PACKAGES_HASH=1
+REQUIRE_PACKAGES_LIBTAR=1
+REQUIRE_PACKAGES_FPMKUNIT=1
+REQUIRE_PACKAGES_FCL-BASE=1
+REQUIRE_PACKAGES_FCL-DB=1
+REQUIRE_PACKAGES_FCL-JSON=1
+endif
+ifeq ($(FULL_TARGET),arm-aros)
+REQUIRE_PACKAGES_RTL=1
+REQUIRE_PACKAGES_PASZLIB=1
+REQUIRE_PACKAGES_FCL-PROCESS=1
+REQUIRE_PACKAGES_HASH=1
+REQUIRE_PACKAGES_LIBTAR=1
+REQUIRE_PACKAGES_FPMKUNIT=1
+REQUIRE_PACKAGES_FCL-BASE=1
+REQUIRE_PACKAGES_FCL-DB=1
+REQUIRE_PACKAGES_FCL-JSON=1
+endif
+ifeq ($(FULL_TARGET),arm-freertos)
+REQUIRE_PACKAGES_RTL=1
+REQUIRE_PACKAGES_PASZLIB=1
+REQUIRE_PACKAGES_FCL-PROCESS=1
+REQUIRE_PACKAGES_HASH=1
+REQUIRE_PACKAGES_LIBTAR=1
+REQUIRE_PACKAGES_FPMKUNIT=1
+REQUIRE_PACKAGES_FCL-BASE=1
+REQUIRE_PACKAGES_FCL-DB=1
+REQUIRE_PACKAGES_FCL-JSON=1
+endif
+ifeq ($(FULL_TARGET),arm-ios)
+REQUIRE_PACKAGES_RTL=1
+REQUIRE_PACKAGES_PASZLIB=1
+REQUIRE_PACKAGES_FCL-PROCESS=1
+REQUIRE_PACKAGES_HASH=1
+REQUIRE_PACKAGES_LIBTAR=1
+REQUIRE_PACKAGES_FPMKUNIT=1
+REQUIRE_PACKAGES_FCL-BASE=1
+REQUIRE_PACKAGES_FCL-DB=1
+REQUIRE_PACKAGES_FCL-JSON=1
+endif
+ifeq ($(FULL_TARGET),powerpc64-linux)
+REQUIRE_PACKAGES_RTL=1
+REQUIRE_PACKAGES_PASZLIB=1
+REQUIRE_PACKAGES_FCL-PROCESS=1
+REQUIRE_PACKAGES_HASH=1
+REQUIRE_PACKAGES_LIBTAR=1
+REQUIRE_PACKAGES_FPMKUNIT=1
+REQUIRE_PACKAGES_FCL-BASE=1
+REQUIRE_PACKAGES_FCL-DB=1
+REQUIRE_PACKAGES_FCL-JSON=1
+endif
+ifeq ($(FULL_TARGET),powerpc64-darwin)
+REQUIRE_PACKAGES_RTL=1
+REQUIRE_PACKAGES_PASZLIB=1
+REQUIRE_PACKAGES_FCL-PROCESS=1
+REQUIRE_PACKAGES_HASH=1
+REQUIRE_PACKAGES_LIBTAR=1
+REQUIRE_PACKAGES_FPMKUNIT=1
+REQUIRE_PACKAGES_FCL-BASE=1
+REQUIRE_PACKAGES_FCL-DB=1
+REQUIRE_PACKAGES_FCL-JSON=1
+endif
+ifeq ($(FULL_TARGET),powerpc64-embedded)
+REQUIRE_PACKAGES_RTL=1
+REQUIRE_PACKAGES_PASZLIB=1
+REQUIRE_PACKAGES_FCL-PROCESS=1
+REQUIRE_PACKAGES_HASH=1
+REQUIRE_PACKAGES_LIBTAR=1
+REQUIRE_PACKAGES_FPMKUNIT=1
+REQUIRE_PACKAGES_FCL-BASE=1
+REQUIRE_PACKAGES_FCL-DB=1
+REQUIRE_PACKAGES_FCL-JSON=1
+endif
+ifeq ($(FULL_TARGET),powerpc64-aix)
+REQUIRE_PACKAGES_RTL=1
+REQUIRE_PACKAGES_PASZLIB=1
+REQUIRE_PACKAGES_FCL-PROCESS=1
+REQUIRE_PACKAGES_HASH=1
+REQUIRE_PACKAGES_LIBTAR=1
+REQUIRE_PACKAGES_FPMKUNIT=1
+REQUIRE_PACKAGES_FCL-BASE=1
+REQUIRE_PACKAGES_FCL-DB=1
+REQUIRE_PACKAGES_FCL-JSON=1
+endif
+ifeq ($(FULL_TARGET),avr-embedded)
+REQUIRE_PACKAGES_RTL=1
+REQUIRE_PACKAGES_PASZLIB=1
+REQUIRE_PACKAGES_FCL-PROCESS=1
+REQUIRE_PACKAGES_HASH=1
+REQUIRE_PACKAGES_LIBTAR=1
+REQUIRE_PACKAGES_FPMKUNIT=1
+REQUIRE_PACKAGES_FCL-BASE=1
+REQUIRE_PACKAGES_FCL-DB=1
+REQUIRE_PACKAGES_FCL-JSON=1
+endif
+ifeq ($(FULL_TARGET),armeb-linux)
+REQUIRE_PACKAGES_RTL=1
+REQUIRE_PACKAGES_PASZLIB=1
+REQUIRE_PACKAGES_FCL-PROCESS=1
+REQUIRE_PACKAGES_HASH=1
+REQUIRE_PACKAGES_LIBTAR=1
+REQUIRE_PACKAGES_FPMKUNIT=1
+REQUIRE_PACKAGES_FCL-BASE=1
+REQUIRE_PACKAGES_FCL-DB=1
+REQUIRE_PACKAGES_FCL-JSON=1
+endif
+ifeq ($(FULL_TARGET),armeb-embedded)
+REQUIRE_PACKAGES_RTL=1
+REQUIRE_PACKAGES_PASZLIB=1
+REQUIRE_PACKAGES_FCL-PROCESS=1
+REQUIRE_PACKAGES_HASH=1
+REQUIRE_PACKAGES_LIBTAR=1
+REQUIRE_PACKAGES_FPMKUNIT=1
+REQUIRE_PACKAGES_FCL-BASE=1
+REQUIRE_PACKAGES_FCL-DB=1
+REQUIRE_PACKAGES_FCL-JSON=1
+endif
+ifeq ($(FULL_TARGET),mips-linux)
+REQUIRE_PACKAGES_RTL=1
+REQUIRE_PACKAGES_PASZLIB=1
+REQUIRE_PACKAGES_FCL-PROCESS=1
+REQUIRE_PACKAGES_HASH=1
+REQUIRE_PACKAGES_LIBTAR=1
+REQUIRE_PACKAGES_FPMKUNIT=1
+REQUIRE_PACKAGES_FCL-BASE=1
+REQUIRE_PACKAGES_FCL-DB=1
+REQUIRE_PACKAGES_FCL-JSON=1
+endif
+ifeq ($(FULL_TARGET),mipsel-linux)
+REQUIRE_PACKAGES_RTL=1
+REQUIRE_PACKAGES_PASZLIB=1
+REQUIRE_PACKAGES_FCL-PROCESS=1
+REQUIRE_PACKAGES_HASH=1
+REQUIRE_PACKAGES_LIBTAR=1
+REQUIRE_PACKAGES_FPMKUNIT=1
+REQUIRE_PACKAGES_FCL-BASE=1
+REQUIRE_PACKAGES_FCL-DB=1
+REQUIRE_PACKAGES_FCL-JSON=1
+endif
+ifeq ($(FULL_TARGET),mipsel-embedded)
+REQUIRE_PACKAGES_RTL=1
+REQUIRE_PACKAGES_PASZLIB=1
+REQUIRE_PACKAGES_FCL-PROCESS=1
+REQUIRE_PACKAGES_HASH=1
+REQUIRE_PACKAGES_LIBTAR=1
+REQUIRE_PACKAGES_FPMKUNIT=1
+REQUIRE_PACKAGES_FCL-BASE=1
+REQUIRE_PACKAGES_FCL-DB=1
+REQUIRE_PACKAGES_FCL-JSON=1
+endif
+ifeq ($(FULL_TARGET),mipsel-android)
+REQUIRE_PACKAGES_RTL=1
+REQUIRE_PACKAGES_PASZLIB=1
+REQUIRE_PACKAGES_FCL-PROCESS=1
+REQUIRE_PACKAGES_HASH=1
+REQUIRE_PACKAGES_LIBTAR=1
+REQUIRE_PACKAGES_FPMKUNIT=1
+REQUIRE_PACKAGES_FCL-BASE=1
+REQUIRE_PACKAGES_FCL-DB=1
+REQUIRE_PACKAGES_FCL-JSON=1
+endif
+ifeq ($(FULL_TARGET),mips64el-linux)
+REQUIRE_PACKAGES_RTL=1
+REQUIRE_PACKAGES_PASZLIB=1
+REQUIRE_PACKAGES_FCL-PROCESS=1
+REQUIRE_PACKAGES_HASH=1
+REQUIRE_PACKAGES_LIBTAR=1
+REQUIRE_PACKAGES_FPMKUNIT=1
+REQUIRE_PACKAGES_FCL-BASE=1
+REQUIRE_PACKAGES_FCL-DB=1
+REQUIRE_PACKAGES_FCL-JSON=1
+endif
+ifeq ($(FULL_TARGET),jvm-java)
+REQUIRE_PACKAGES_RTL=1
+REQUIRE_PACKAGES_PASZLIB=1
+REQUIRE_PACKAGES_FCL-PROCESS=1
+REQUIRE_PACKAGES_HASH=1
+REQUIRE_PACKAGES_LIBTAR=1
+REQUIRE_PACKAGES_FPMKUNIT=1
+REQUIRE_PACKAGES_FCL-BASE=1
+REQUIRE_PACKAGES_FCL-DB=1
+REQUIRE_PACKAGES_FCL-JSON=1
+endif
+ifeq ($(FULL_TARGET),jvm-android)
+REQUIRE_PACKAGES_RTL=1
+REQUIRE_PACKAGES_PASZLIB=1
+REQUIRE_PACKAGES_FCL-PROCESS=1
+REQUIRE_PACKAGES_HASH=1
+REQUIRE_PACKAGES_LIBTAR=1
+REQUIRE_PACKAGES_FPMKUNIT=1
+REQUIRE_PACKAGES_FCL-BASE=1
+REQUIRE_PACKAGES_FCL-DB=1
+REQUIRE_PACKAGES_FCL-JSON=1
+endif
+ifeq ($(FULL_TARGET),i8086-embedded)
+REQUIRE_PACKAGES_RTL=1
+REQUIRE_PACKAGES_PASZLIB=1
+REQUIRE_PACKAGES_FCL-PROCESS=1
+REQUIRE_PACKAGES_HASH=1
+REQUIRE_PACKAGES_LIBTAR=1
+REQUIRE_PACKAGES_FPMKUNIT=1
+REQUIRE_PACKAGES_FCL-BASE=1
+REQUIRE_PACKAGES_FCL-DB=1
+REQUIRE_PACKAGES_FCL-JSON=1
+endif
+ifeq ($(FULL_TARGET),i8086-msdos)
+REQUIRE_PACKAGES_RTL=1
+REQUIRE_PACKAGES_PASZLIB=1
+REQUIRE_PACKAGES_FCL-PROCESS=1
+REQUIRE_PACKAGES_HASH=1
+REQUIRE_PACKAGES_LIBTAR=1
+REQUIRE_PACKAGES_FPMKUNIT=1
+REQUIRE_PACKAGES_FCL-BASE=1
+REQUIRE_PACKAGES_FCL-DB=1
+REQUIRE_PACKAGES_FCL-JSON=1
+endif
+ifeq ($(FULL_TARGET),i8086-win16)
+REQUIRE_PACKAGES_RTL=1
+REQUIRE_PACKAGES_PASZLIB=1
+REQUIRE_PACKAGES_FCL-PROCESS=1
+REQUIRE_PACKAGES_HASH=1
+REQUIRE_PACKAGES_LIBTAR=1
+REQUIRE_PACKAGES_FPMKUNIT=1
+REQUIRE_PACKAGES_FCL-BASE=1
+REQUIRE_PACKAGES_FCL-DB=1
+REQUIRE_PACKAGES_FCL-JSON=1
+endif
+ifeq ($(FULL_TARGET),aarch64-linux)
+REQUIRE_PACKAGES_RTL=1
+REQUIRE_PACKAGES_PASZLIB=1
+REQUIRE_PACKAGES_FCL-PROCESS=1
+REQUIRE_PACKAGES_HASH=1
+REQUIRE_PACKAGES_LIBTAR=1
+REQUIRE_PACKAGES_FPMKUNIT=1
+REQUIRE_PACKAGES_FCL-BASE=1
+REQUIRE_PACKAGES_FCL-DB=1
+REQUIRE_PACKAGES_FCL-JSON=1
+endif
+ifeq ($(FULL_TARGET),aarch64-freebsd)
+REQUIRE_PACKAGES_RTL=1
+REQUIRE_PACKAGES_PASZLIB=1
+REQUIRE_PACKAGES_FCL-PROCESS=1
+REQUIRE_PACKAGES_HASH=1
+REQUIRE_PACKAGES_LIBTAR=1
+REQUIRE_PACKAGES_FPMKUNIT=1
+REQUIRE_PACKAGES_FCL-BASE=1
+REQUIRE_PACKAGES_FCL-DB=1
+REQUIRE_PACKAGES_FCL-JSON=1
+endif
+ifeq ($(FULL_TARGET),aarch64-darwin)
+REQUIRE_PACKAGES_RTL=1
+REQUIRE_PACKAGES_PASZLIB=1
+REQUIRE_PACKAGES_FCL-PROCESS=1
+REQUIRE_PACKAGES_HASH=1
+REQUIRE_PACKAGES_LIBTAR=1
+REQUIRE_PACKAGES_FPMKUNIT=1
+REQUIRE_PACKAGES_FCL-BASE=1
+REQUIRE_PACKAGES_FCL-DB=1
+REQUIRE_PACKAGES_FCL-JSON=1
+endif
+ifeq ($(FULL_TARGET),aarch64-win64)
+REQUIRE_PACKAGES_RTL=1
+REQUIRE_PACKAGES_PASZLIB=1
+REQUIRE_PACKAGES_FCL-PROCESS=1
+REQUIRE_PACKAGES_HASH=1
+REQUIRE_PACKAGES_LIBTAR=1
+REQUIRE_PACKAGES_FPMKUNIT=1
+REQUIRE_PACKAGES_FCL-BASE=1
+REQUIRE_PACKAGES_FCL-DB=1
+REQUIRE_PACKAGES_FCL-JSON=1
+endif
+ifeq ($(FULL_TARGET),aarch64-android)
+REQUIRE_PACKAGES_RTL=1
+REQUIRE_PACKAGES_PASZLIB=1
+REQUIRE_PACKAGES_FCL-PROCESS=1
+REQUIRE_PACKAGES_HASH=1
+REQUIRE_PACKAGES_LIBTAR=1
+REQUIRE_PACKAGES_FPMKUNIT=1
+REQUIRE_PACKAGES_FCL-BASE=1
+REQUIRE_PACKAGES_FCL-DB=1
+REQUIRE_PACKAGES_FCL-JSON=1
+endif
+ifeq ($(FULL_TARGET),aarch64-ios)
+REQUIRE_PACKAGES_RTL=1
+REQUIRE_PACKAGES_PASZLIB=1
+REQUIRE_PACKAGES_FCL-PROCESS=1
+REQUIRE_PACKAGES_HASH=1
+REQUIRE_PACKAGES_LIBTAR=1
+REQUIRE_PACKAGES_FPMKUNIT=1
+REQUIRE_PACKAGES_FCL-BASE=1
+REQUIRE_PACKAGES_FCL-DB=1
+REQUIRE_PACKAGES_FCL-JSON=1
+endif
+ifeq ($(FULL_TARGET),wasm32-embedded)
+REQUIRE_PACKAGES_RTL=1
+REQUIRE_PACKAGES_PASZLIB=1
+REQUIRE_PACKAGES_FCL-PROCESS=1
+REQUIRE_PACKAGES_HASH=1
+REQUIRE_PACKAGES_LIBTAR=1
+REQUIRE_PACKAGES_FPMKUNIT=1
+REQUIRE_PACKAGES_FCL-BASE=1
+REQUIRE_PACKAGES_FCL-DB=1
+REQUIRE_PACKAGES_FCL-JSON=1
+endif
+ifeq ($(FULL_TARGET),wasm32-wasi)
+REQUIRE_PACKAGES_RTL=1
+REQUIRE_PACKAGES_PASZLIB=1
+REQUIRE_PACKAGES_FCL-PROCESS=1
+REQUIRE_PACKAGES_HASH=1
+REQUIRE_PACKAGES_LIBTAR=1
+REQUIRE_PACKAGES_FPMKUNIT=1
+REQUIRE_PACKAGES_FCL-BASE=1
+REQUIRE_PACKAGES_FCL-DB=1
+REQUIRE_PACKAGES_FCL-JSON=1
+endif
+ifeq ($(FULL_TARGET),sparc64-linux)
+REQUIRE_PACKAGES_RTL=1
+REQUIRE_PACKAGES_PASZLIB=1
+REQUIRE_PACKAGES_FCL-PROCESS=1
+REQUIRE_PACKAGES_HASH=1
+REQUIRE_PACKAGES_LIBTAR=1
+REQUIRE_PACKAGES_FPMKUNIT=1
+REQUIRE_PACKAGES_FCL-BASE=1
+REQUIRE_PACKAGES_FCL-DB=1
+REQUIRE_PACKAGES_FCL-JSON=1
+endif
+ifeq ($(FULL_TARGET),riscv32-linux)
+REQUIRE_PACKAGES_RTL=1
+REQUIRE_PACKAGES_PASZLIB=1
+REQUIRE_PACKAGES_FCL-PROCESS=1
+REQUIRE_PACKAGES_HASH=1
+REQUIRE_PACKAGES_LIBTAR=1
+REQUIRE_PACKAGES_FPMKUNIT=1
+REQUIRE_PACKAGES_FCL-BASE=1
+REQUIRE_PACKAGES_FCL-DB=1
+REQUIRE_PACKAGES_FCL-JSON=1
+endif
+ifeq ($(FULL_TARGET),riscv32-embedded)
+REQUIRE_PACKAGES_RTL=1
+REQUIRE_PACKAGES_PASZLIB=1
+REQUIRE_PACKAGES_FCL-PROCESS=1
+REQUIRE_PACKAGES_HASH=1
+REQUIRE_PACKAGES_LIBTAR=1
+REQUIRE_PACKAGES_FPMKUNIT=1
+REQUIRE_PACKAGES_FCL-BASE=1
+REQUIRE_PACKAGES_FCL-DB=1
+REQUIRE_PACKAGES_FCL-JSON=1
+endif
+ifeq ($(FULL_TARGET),riscv64-linux)
+REQUIRE_PACKAGES_RTL=1
+REQUIRE_PACKAGES_PASZLIB=1
+REQUIRE_PACKAGES_FCL-PROCESS=1
+REQUIRE_PACKAGES_HASH=1
+REQUIRE_PACKAGES_LIBTAR=1
+REQUIRE_PACKAGES_FPMKUNIT=1
+REQUIRE_PACKAGES_FCL-BASE=1
+REQUIRE_PACKAGES_FCL-DB=1
+REQUIRE_PACKAGES_FCL-JSON=1
+endif
+ifeq ($(FULL_TARGET),riscv64-embedded)
+REQUIRE_PACKAGES_RTL=1
+REQUIRE_PACKAGES_PASZLIB=1
+REQUIRE_PACKAGES_FCL-PROCESS=1
+REQUIRE_PACKAGES_HASH=1
+REQUIRE_PACKAGES_LIBTAR=1
+REQUIRE_PACKAGES_FPMKUNIT=1
+REQUIRE_PACKAGES_FCL-BASE=1
+REQUIRE_PACKAGES_FCL-DB=1
+REQUIRE_PACKAGES_FCL-JSON=1
+endif
+ifeq ($(FULL_TARGET),xtensa-linux)
+REQUIRE_PACKAGES_RTL=1
+REQUIRE_PACKAGES_PASZLIB=1
+REQUIRE_PACKAGES_FCL-PROCESS=1
+REQUIRE_PACKAGES_HASH=1
+REQUIRE_PACKAGES_LIBTAR=1
+REQUIRE_PACKAGES_FPMKUNIT=1
+REQUIRE_PACKAGES_FCL-BASE=1
+REQUIRE_PACKAGES_FCL-DB=1
+REQUIRE_PACKAGES_FCL-JSON=1
+endif
+ifeq ($(FULL_TARGET),xtensa-embedded)
+REQUIRE_PACKAGES_RTL=1
+REQUIRE_PACKAGES_PASZLIB=1
+REQUIRE_PACKAGES_FCL-PROCESS=1
+REQUIRE_PACKAGES_HASH=1
+REQUIRE_PACKAGES_LIBTAR=1
+REQUIRE_PACKAGES_FPMKUNIT=1
+REQUIRE_PACKAGES_FCL-BASE=1
+REQUIRE_PACKAGES_FCL-DB=1
+REQUIRE_PACKAGES_FCL-JSON=1
+endif
+ifeq ($(FULL_TARGET),xtensa-freertos)
+REQUIRE_PACKAGES_RTL=1
+REQUIRE_PACKAGES_PASZLIB=1
+REQUIRE_PACKAGES_FCL-PROCESS=1
+REQUIRE_PACKAGES_HASH=1
+REQUIRE_PACKAGES_LIBTAR=1
+REQUIRE_PACKAGES_FPMKUNIT=1
+REQUIRE_PACKAGES_FCL-BASE=1
+REQUIRE_PACKAGES_FCL-DB=1
+REQUIRE_PACKAGES_FCL-JSON=1
+endif
+ifeq ($(FULL_TARGET),z80-embedded)
+REQUIRE_PACKAGES_RTL=1
+REQUIRE_PACKAGES_PASZLIB=1
+REQUIRE_PACKAGES_FCL-PROCESS=1
+REQUIRE_PACKAGES_HASH=1
+REQUIRE_PACKAGES_LIBTAR=1
+REQUIRE_PACKAGES_FPMKUNIT=1
+REQUIRE_PACKAGES_FCL-BASE=1
+REQUIRE_PACKAGES_FCL-DB=1
+REQUIRE_PACKAGES_FCL-JSON=1
+endif
+ifeq ($(FULL_TARGET),z80-zxspectrum)
+REQUIRE_PACKAGES_RTL=1
+REQUIRE_PACKAGES_PASZLIB=1
+REQUIRE_PACKAGES_FCL-PROCESS=1
+REQUIRE_PACKAGES_HASH=1
+REQUIRE_PACKAGES_LIBTAR=1
+REQUIRE_PACKAGES_FPMKUNIT=1
+REQUIRE_PACKAGES_FCL-BASE=1
+REQUIRE_PACKAGES_FCL-DB=1
+REQUIRE_PACKAGES_FCL-JSON=1
+endif
+ifeq ($(FULL_TARGET),z80-msxdos)
+REQUIRE_PACKAGES_RTL=1
+REQUIRE_PACKAGES_PASZLIB=1
+REQUIRE_PACKAGES_FCL-PROCESS=1
+REQUIRE_PACKAGES_HASH=1
+REQUIRE_PACKAGES_LIBTAR=1
+REQUIRE_PACKAGES_FPMKUNIT=1
+REQUIRE_PACKAGES_FCL-BASE=1
+REQUIRE_PACKAGES_FCL-DB=1
+REQUIRE_PACKAGES_FCL-JSON=1
+endif
+ifeq ($(FULL_TARGET),z80-amstradcpc)
+REQUIRE_PACKAGES_RTL=1
+REQUIRE_PACKAGES_PASZLIB=1
+REQUIRE_PACKAGES_FCL-PROCESS=1
+REQUIRE_PACKAGES_HASH=1
+REQUIRE_PACKAGES_LIBTAR=1
+REQUIRE_PACKAGES_FPMKUNIT=1
+REQUIRE_PACKAGES_FCL-BASE=1
+REQUIRE_PACKAGES_FCL-DB=1
+REQUIRE_PACKAGES_FCL-JSON=1
+endif
+ifdef REQUIRE_PACKAGES_RTL
+PACKAGEDIR_RTL:=$(firstword $(subst /Makefile.fpc,,$(strip $(wildcard $(addsuffix /rtl/Makefile.fpc,$(PACKAGESDIR))))))
+ifneq ($(PACKAGEDIR_RTL),)
+ifneq ($(wildcard $(PACKAGEDIR_RTL)/units/$(TARGETSUFFIX)),)
+UNITDIR_RTL=$(PACKAGEDIR_RTL)/units/$(TARGETSUFFIX)
+else
+UNITDIR_RTL=$(PACKAGEDIR_RTL)
+endif
+ifneq ($(wildcard $(PACKAGEDIR_RTL)/units/$(SOURCESUFFIX)),)
+UNITDIR_FPMAKE_RTL=$(PACKAGEDIR_RTL)/units/$(SOURCESUFFIX)
+else
+ifneq ($(wildcard $(PACKAGEDIR_RTL)/units_bs/$(SOURCESUFFIX)),)
+UNITDIR_FPMAKE_RTL=$(PACKAGEDIR_RTL)/units_bs/$(SOURCESUFFIX)
+else
+UNITDIR_FPMAKE_RTL=$(PACKAGEDIR_RTL)
+endif
+endif
+ifdef CHECKDEPEND
+$(PACKAGEDIR_RTL)/$(OS_TARGET)/$(FPCMADE):
+	$(MAKE) -C $(PACKAGEDIR_RTL)/$(OS_TARGET) $(FPCMADE)
+override ALLDEPENDENCIES+=$(PACKAGEDIR_RTL)/$(OS_TARGET)/$(FPCMADE)
+endif
+else
+PACKAGEDIR_RTL=
+UNITDIR_RTL:=$(subst /Package.fpc,,$(strip $(wildcard $(addsuffix /rtl/Package.fpc,$(UNITSDIR)))))
+ifneq ($(UNITDIR_RTL),)
+UNITDIR_RTL:=$(firstword $(UNITDIR_RTL))
+else
+UNITDIR_RTL=
+endif
+endif
+ifdef UNITDIR_RTL
+override COMPILER_UNITDIR+=$(UNITDIR_RTL)
+endif
+ifdef UNITDIR_FPMAKE_RTL
+override COMPILER_FPMAKE_UNITDIR+=$(UNITDIR_FPMAKE_RTL)
+endif
+endif
+ifdef REQUIRE_PACKAGES_PASZLIB
+PACKAGEDIR_PASZLIB:=$(firstword $(subst /Makefile.fpc,,$(strip $(wildcard $(addsuffix /paszlib/Makefile.fpc,$(PACKAGESDIR))))))
+ifneq ($(PACKAGEDIR_PASZLIB),)
+ifneq ($(wildcard $(PACKAGEDIR_PASZLIB)/units/$(TARGETSUFFIX)),)
+UNITDIR_PASZLIB=$(PACKAGEDIR_PASZLIB)/units/$(TARGETSUFFIX)
+else
+UNITDIR_PASZLIB=$(PACKAGEDIR_PASZLIB)
+endif
+ifneq ($(wildcard $(PACKAGEDIR_PASZLIB)/units/$(SOURCESUFFIX)),)
+UNITDIR_FPMAKE_PASZLIB=$(PACKAGEDIR_PASZLIB)/units/$(SOURCESUFFIX)
+else
+ifneq ($(wildcard $(PACKAGEDIR_PASZLIB)/units_bs/$(SOURCESUFFIX)),)
+UNITDIR_FPMAKE_PASZLIB=$(PACKAGEDIR_PASZLIB)/units_bs/$(SOURCESUFFIX)
+else
+UNITDIR_FPMAKE_PASZLIB=$(PACKAGEDIR_PASZLIB)
+endif
+endif
+ifdef CHECKDEPEND
+$(PACKAGEDIR_PASZLIB)/$(FPCMADE):
+	$(MAKE) -C $(PACKAGEDIR_PASZLIB) $(FPCMADE)
+override ALLDEPENDENCIES+=$(PACKAGEDIR_PASZLIB)/$(FPCMADE)
+endif
+else
+PACKAGEDIR_PASZLIB=
+UNITDIR_PASZLIB:=$(subst /Package.fpc,,$(strip $(wildcard $(addsuffix /paszlib/Package.fpc,$(UNITSDIR)))))
+ifneq ($(UNITDIR_PASZLIB),)
+UNITDIR_PASZLIB:=$(firstword $(UNITDIR_PASZLIB))
+else
+UNITDIR_PASZLIB=
+endif
+endif
+ifdef UNITDIR_PASZLIB
+override COMPILER_UNITDIR+=$(UNITDIR_PASZLIB)
+endif
+ifdef UNITDIR_FPMAKE_PASZLIB
+override COMPILER_FPMAKE_UNITDIR+=$(UNITDIR_FPMAKE_PASZLIB)
+endif
+endif
+ifdef REQUIRE_PACKAGES_FCL-PROCESS
+PACKAGEDIR_FCL-PROCESS:=$(firstword $(subst /Makefile.fpc,,$(strip $(wildcard $(addsuffix /fcl-process/Makefile.fpc,$(PACKAGESDIR))))))
+ifneq ($(PACKAGEDIR_FCL-PROCESS),)
+ifneq ($(wildcard $(PACKAGEDIR_FCL-PROCESS)/units/$(TARGETSUFFIX)),)
+UNITDIR_FCL-PROCESS=$(PACKAGEDIR_FCL-PROCESS)/units/$(TARGETSUFFIX)
+else
+UNITDIR_FCL-PROCESS=$(PACKAGEDIR_FCL-PROCESS)
+endif
+ifneq ($(wildcard $(PACKAGEDIR_FCL-PROCESS)/units/$(SOURCESUFFIX)),)
+UNITDIR_FPMAKE_FCL-PROCESS=$(PACKAGEDIR_FCL-PROCESS)/units/$(SOURCESUFFIX)
+else
+ifneq ($(wildcard $(PACKAGEDIR_FCL-PROCESS)/units_bs/$(SOURCESUFFIX)),)
+UNITDIR_FPMAKE_FCL-PROCESS=$(PACKAGEDIR_FCL-PROCESS)/units_bs/$(SOURCESUFFIX)
+else
+UNITDIR_FPMAKE_FCL-PROCESS=$(PACKAGEDIR_FCL-PROCESS)
+endif
+endif
+ifdef CHECKDEPEND
+$(PACKAGEDIR_FCL-PROCESS)/$(FPCMADE):
+	$(MAKE) -C $(PACKAGEDIR_FCL-PROCESS) $(FPCMADE)
+override ALLDEPENDENCIES+=$(PACKAGEDIR_FCL-PROCESS)/$(FPCMADE)
+endif
+else
+PACKAGEDIR_FCL-PROCESS=
+UNITDIR_FCL-PROCESS:=$(subst /Package.fpc,,$(strip $(wildcard $(addsuffix /fcl-process/Package.fpc,$(UNITSDIR)))))
+ifneq ($(UNITDIR_FCL-PROCESS),)
+UNITDIR_FCL-PROCESS:=$(firstword $(UNITDIR_FCL-PROCESS))
+else
+UNITDIR_FCL-PROCESS=
+endif
+endif
+ifdef UNITDIR_FCL-PROCESS
+override COMPILER_UNITDIR+=$(UNITDIR_FCL-PROCESS)
+endif
+ifdef UNITDIR_FPMAKE_FCL-PROCESS
+override COMPILER_FPMAKE_UNITDIR+=$(UNITDIR_FPMAKE_FCL-PROCESS)
+endif
+endif
+ifdef REQUIRE_PACKAGES_HASH
+PACKAGEDIR_HASH:=$(firstword $(subst /Makefile.fpc,,$(strip $(wildcard $(addsuffix /hash/Makefile.fpc,$(PACKAGESDIR))))))
+ifneq ($(PACKAGEDIR_HASH),)
+ifneq ($(wildcard $(PACKAGEDIR_HASH)/units/$(TARGETSUFFIX)),)
+UNITDIR_HASH=$(PACKAGEDIR_HASH)/units/$(TARGETSUFFIX)
+else
+UNITDIR_HASH=$(PACKAGEDIR_HASH)
+endif
+ifneq ($(wildcard $(PACKAGEDIR_HASH)/units/$(SOURCESUFFIX)),)
+UNITDIR_FPMAKE_HASH=$(PACKAGEDIR_HASH)/units/$(SOURCESUFFIX)
+else
+ifneq ($(wildcard $(PACKAGEDIR_HASH)/units_bs/$(SOURCESUFFIX)),)
+UNITDIR_FPMAKE_HASH=$(PACKAGEDIR_HASH)/units_bs/$(SOURCESUFFIX)
+else
+UNITDIR_FPMAKE_HASH=$(PACKAGEDIR_HASH)
+endif
+endif
+ifdef CHECKDEPEND
+$(PACKAGEDIR_HASH)/$(FPCMADE):
+	$(MAKE) -C $(PACKAGEDIR_HASH) $(FPCMADE)
+override ALLDEPENDENCIES+=$(PACKAGEDIR_HASH)/$(FPCMADE)
+endif
+else
+PACKAGEDIR_HASH=
+UNITDIR_HASH:=$(subst /Package.fpc,,$(strip $(wildcard $(addsuffix /hash/Package.fpc,$(UNITSDIR)))))
+ifneq ($(UNITDIR_HASH),)
+UNITDIR_HASH:=$(firstword $(UNITDIR_HASH))
+else
+UNITDIR_HASH=
+endif
+endif
+ifdef UNITDIR_HASH
+override COMPILER_UNITDIR+=$(UNITDIR_HASH)
+endif
+ifdef UNITDIR_FPMAKE_HASH
+override COMPILER_FPMAKE_UNITDIR+=$(UNITDIR_FPMAKE_HASH)
+endif
+endif
+ifdef REQUIRE_PACKAGES_LIBTAR
+PACKAGEDIR_LIBTAR:=$(firstword $(subst /Makefile.fpc,,$(strip $(wildcard $(addsuffix /libtar/Makefile.fpc,$(PACKAGESDIR))))))
+ifneq ($(PACKAGEDIR_LIBTAR),)
+ifneq ($(wildcard $(PACKAGEDIR_LIBTAR)/units/$(TARGETSUFFIX)),)
+UNITDIR_LIBTAR=$(PACKAGEDIR_LIBTAR)/units/$(TARGETSUFFIX)
+else
+UNITDIR_LIBTAR=$(PACKAGEDIR_LIBTAR)
+endif
+ifneq ($(wildcard $(PACKAGEDIR_LIBTAR)/units/$(SOURCESUFFIX)),)
+UNITDIR_FPMAKE_LIBTAR=$(PACKAGEDIR_LIBTAR)/units/$(SOURCESUFFIX)
+else
+ifneq ($(wildcard $(PACKAGEDIR_LIBTAR)/units_bs/$(SOURCESUFFIX)),)
+UNITDIR_FPMAKE_LIBTAR=$(PACKAGEDIR_LIBTAR)/units_bs/$(SOURCESUFFIX)
+else
+UNITDIR_FPMAKE_LIBTAR=$(PACKAGEDIR_LIBTAR)
+endif
+endif
+ifdef CHECKDEPEND
+$(PACKAGEDIR_LIBTAR)/$(FPCMADE):
+	$(MAKE) -C $(PACKAGEDIR_LIBTAR) $(FPCMADE)
+override ALLDEPENDENCIES+=$(PACKAGEDIR_LIBTAR)/$(FPCMADE)
+endif
+else
+PACKAGEDIR_LIBTAR=
+UNITDIR_LIBTAR:=$(subst /Package.fpc,,$(strip $(wildcard $(addsuffix /libtar/Package.fpc,$(UNITSDIR)))))
+ifneq ($(UNITDIR_LIBTAR),)
+UNITDIR_LIBTAR:=$(firstword $(UNITDIR_LIBTAR))
+else
+UNITDIR_LIBTAR=
+endif
+endif
+ifdef UNITDIR_LIBTAR
+override COMPILER_UNITDIR+=$(UNITDIR_LIBTAR)
+endif
+ifdef UNITDIR_FPMAKE_LIBTAR
+override COMPILER_FPMAKE_UNITDIR+=$(UNITDIR_FPMAKE_LIBTAR)
+endif
+endif
+ifdef REQUIRE_PACKAGES_FPMKUNIT
+PACKAGEDIR_FPMKUNIT:=$(firstword $(subst /Makefile.fpc,,$(strip $(wildcard $(addsuffix /fpmkunit/Makefile.fpc,$(PACKAGESDIR))))))
+ifneq ($(PACKAGEDIR_FPMKUNIT),)
+ifneq ($(wildcard $(PACKAGEDIR_FPMKUNIT)/units/$(TARGETSUFFIX)),)
+UNITDIR_FPMKUNIT=$(PACKAGEDIR_FPMKUNIT)/units/$(TARGETSUFFIX)
+else
+UNITDIR_FPMKUNIT=$(PACKAGEDIR_FPMKUNIT)
+endif
+ifneq ($(wildcard $(PACKAGEDIR_FPMKUNIT)/units/$(SOURCESUFFIX)),)
+UNITDIR_FPMAKE_FPMKUNIT=$(PACKAGEDIR_FPMKUNIT)/units/$(SOURCESUFFIX)
+else
+ifneq ($(wildcard $(PACKAGEDIR_FPMKUNIT)/units_bs/$(SOURCESUFFIX)),)
+UNITDIR_FPMAKE_FPMKUNIT=$(PACKAGEDIR_FPMKUNIT)/units_bs/$(SOURCESUFFIX)
+else
+UNITDIR_FPMAKE_FPMKUNIT=$(PACKAGEDIR_FPMKUNIT)
+endif
+endif
+ifdef CHECKDEPEND
+$(PACKAGEDIR_FPMKUNIT)/$(FPCMADE):
+	$(MAKE) -C $(PACKAGEDIR_FPMKUNIT) $(FPCMADE)
+override ALLDEPENDENCIES+=$(PACKAGEDIR_FPMKUNIT)/$(FPCMADE)
+endif
+else
+PACKAGEDIR_FPMKUNIT=
+UNITDIR_FPMKUNIT:=$(subst /Package.fpc,,$(strip $(wildcard $(addsuffix /fpmkunit/Package.fpc,$(UNITSDIR)))))
+ifneq ($(UNITDIR_FPMKUNIT),)
+UNITDIR_FPMKUNIT:=$(firstword $(UNITDIR_FPMKUNIT))
+else
+UNITDIR_FPMKUNIT=
+endif
+endif
+ifdef UNITDIR_FPMKUNIT
+override COMPILER_UNITDIR+=$(UNITDIR_FPMKUNIT)
+endif
+ifdef UNITDIR_FPMAKE_FPMKUNIT
+override COMPILER_FPMAKE_UNITDIR+=$(UNITDIR_FPMAKE_FPMKUNIT)
+endif
+endif
+ifdef REQUIRE_PACKAGES_FCL-BASE
+PACKAGEDIR_FCL-BASE:=$(firstword $(subst /Makefile.fpc,,$(strip $(wildcard $(addsuffix /fcl-base/Makefile.fpc,$(PACKAGESDIR))))))
+ifneq ($(PACKAGEDIR_FCL-BASE),)
+ifneq ($(wildcard $(PACKAGEDIR_FCL-BASE)/units/$(TARGETSUFFIX)),)
+UNITDIR_FCL-BASE=$(PACKAGEDIR_FCL-BASE)/units/$(TARGETSUFFIX)
+else
+UNITDIR_FCL-BASE=$(PACKAGEDIR_FCL-BASE)
+endif
+ifneq ($(wildcard $(PACKAGEDIR_FCL-BASE)/units/$(SOURCESUFFIX)),)
+UNITDIR_FPMAKE_FCL-BASE=$(PACKAGEDIR_FCL-BASE)/units/$(SOURCESUFFIX)
+else
+ifneq ($(wildcard $(PACKAGEDIR_FCL-BASE)/units_bs/$(SOURCESUFFIX)),)
+UNITDIR_FPMAKE_FCL-BASE=$(PACKAGEDIR_FCL-BASE)/units_bs/$(SOURCESUFFIX)
+else
+UNITDIR_FPMAKE_FCL-BASE=$(PACKAGEDIR_FCL-BASE)
+endif
+endif
+ifdef CHECKDEPEND
+$(PACKAGEDIR_FCL-BASE)/$(FPCMADE):
+	$(MAKE) -C $(PACKAGEDIR_FCL-BASE) $(FPCMADE)
+override ALLDEPENDENCIES+=$(PACKAGEDIR_FCL-BASE)/$(FPCMADE)
+endif
+else
+PACKAGEDIR_FCL-BASE=
+UNITDIR_FCL-BASE:=$(subst /Package.fpc,,$(strip $(wildcard $(addsuffix /fcl-base/Package.fpc,$(UNITSDIR)))))
+ifneq ($(UNITDIR_FCL-BASE),)
+UNITDIR_FCL-BASE:=$(firstword $(UNITDIR_FCL-BASE))
+else
+UNITDIR_FCL-BASE=
+endif
+endif
+ifdef UNITDIR_FCL-BASE
+override COMPILER_UNITDIR+=$(UNITDIR_FCL-BASE)
+endif
+ifdef UNITDIR_FPMAKE_FCL-BASE
+override COMPILER_FPMAKE_UNITDIR+=$(UNITDIR_FPMAKE_FCL-BASE)
+endif
+endif
+ifdef REQUIRE_PACKAGES_FCL-DB
+PACKAGEDIR_FCL-DB:=$(firstword $(subst /Makefile.fpc,,$(strip $(wildcard $(addsuffix /fcl-db/Makefile.fpc,$(PACKAGESDIR))))))
+ifneq ($(PACKAGEDIR_FCL-DB),)
+ifneq ($(wildcard $(PACKAGEDIR_FCL-DB)/units/$(TARGETSUFFIX)),)
+UNITDIR_FCL-DB=$(PACKAGEDIR_FCL-DB)/units/$(TARGETSUFFIX)
+else
+UNITDIR_FCL-DB=$(PACKAGEDIR_FCL-DB)
+endif
+ifneq ($(wildcard $(PACKAGEDIR_FCL-DB)/units/$(SOURCESUFFIX)),)
+UNITDIR_FPMAKE_FCL-DB=$(PACKAGEDIR_FCL-DB)/units/$(SOURCESUFFIX)
+else
+ifneq ($(wildcard $(PACKAGEDIR_FCL-DB)/units_bs/$(SOURCESUFFIX)),)
+UNITDIR_FPMAKE_FCL-DB=$(PACKAGEDIR_FCL-DB)/units_bs/$(SOURCESUFFIX)
+else
+UNITDIR_FPMAKE_FCL-DB=$(PACKAGEDIR_FCL-DB)
+endif
+endif
+ifdef CHECKDEPEND
+$(PACKAGEDIR_FCL-DB)/$(FPCMADE):
+	$(MAKE) -C $(PACKAGEDIR_FCL-DB) $(FPCMADE)
+override ALLDEPENDENCIES+=$(PACKAGEDIR_FCL-DB)/$(FPCMADE)
+endif
+else
+PACKAGEDIR_FCL-DB=
+UNITDIR_FCL-DB:=$(subst /Package.fpc,,$(strip $(wildcard $(addsuffix /fcl-db/Package.fpc,$(UNITSDIR)))))
+ifneq ($(UNITDIR_FCL-DB),)
+UNITDIR_FCL-DB:=$(firstword $(UNITDIR_FCL-DB))
+else
+UNITDIR_FCL-DB=
+endif
+endif
+ifdef UNITDIR_FCL-DB
+override COMPILER_UNITDIR+=$(UNITDIR_FCL-DB)
+endif
+ifdef UNITDIR_FPMAKE_FCL-DB
+override COMPILER_FPMAKE_UNITDIR+=$(UNITDIR_FPMAKE_FCL-DB)
+endif
+endif
+ifdef REQUIRE_PACKAGES_FCL-JSON
+PACKAGEDIR_FCL-JSON:=$(firstword $(subst /Makefile.fpc,,$(strip $(wildcard $(addsuffix /fcl-json/Makefile.fpc,$(PACKAGESDIR))))))
+ifneq ($(PACKAGEDIR_FCL-JSON),)
+ifneq ($(wildcard $(PACKAGEDIR_FCL-JSON)/units/$(TARGETSUFFIX)),)
+UNITDIR_FCL-JSON=$(PACKAGEDIR_FCL-JSON)/units/$(TARGETSUFFIX)
+else
+UNITDIR_FCL-JSON=$(PACKAGEDIR_FCL-JSON)
+endif
+ifneq ($(wildcard $(PACKAGEDIR_FCL-JSON)/units/$(SOURCESUFFIX)),)
+UNITDIR_FPMAKE_FCL-JSON=$(PACKAGEDIR_FCL-JSON)/units/$(SOURCESUFFIX)
+else
+ifneq ($(wildcard $(PACKAGEDIR_FCL-JSON)/units_bs/$(SOURCESUFFIX)),)
+UNITDIR_FPMAKE_FCL-JSON=$(PACKAGEDIR_FCL-JSON)/units_bs/$(SOURCESUFFIX)
+else
+UNITDIR_FPMAKE_FCL-JSON=$(PACKAGEDIR_FCL-JSON)
+endif
+endif
+ifdef CHECKDEPEND
+$(PACKAGEDIR_FCL-JSON)/$(FPCMADE):
+	$(MAKE) -C $(PACKAGEDIR_FCL-JSON) $(FPCMADE)
+override ALLDEPENDENCIES+=$(PACKAGEDIR_FCL-JSON)/$(FPCMADE)
+endif
+else
+PACKAGEDIR_FCL-JSON=
+UNITDIR_FCL-JSON:=$(subst /Package.fpc,,$(strip $(wildcard $(addsuffix /fcl-json/Package.fpc,$(UNITSDIR)))))
+ifneq ($(UNITDIR_FCL-JSON),)
+UNITDIR_FCL-JSON:=$(firstword $(UNITDIR_FCL-JSON))
+else
+UNITDIR_FCL-JSON=
+endif
+endif
+ifdef UNITDIR_FCL-JSON
+override COMPILER_UNITDIR+=$(UNITDIR_FCL-JSON)
+endif
+ifdef UNITDIR_FPMAKE_FCL-JSON
+override COMPILER_FPMAKE_UNITDIR+=$(UNITDIR_FPMAKE_FCL-JSON)
+endif
+endif
+ifndef NOCPUDEF
+override FPCOPTDEF=$(ARCH)
+endif
+ifneq ($(OS_TARGET),$(OS_SOURCE))
+override FPCOPT+=-T$(OS_TARGET)
+endif
+ifneq ($(CPU_TARGET),$(CPU_SOURCE))
+override FPCOPT+=-P$(ARCH)
+endif
+ifeq ($(OS_SOURCE),openbsd)
+override FPCOPT+=-FD$(NEW_BINUTILS_PATH)
+override FPCMAKEOPT+=-FD$(NEW_BINUTILS_PATH)
+override FPMAKE_BUILD_OPT+=-FD$(NEW_BINUTILS_PATH)
+endif
+ifndef CROSSBOOTSTRAP
+ifneq ($(BINUTILSPREFIX),)
+override FPCOPT+=-XP$(BINUTILSPREFIX)
+ifneq ($(RLINKPATH),)
+override FPCOPT+=-Xr$(RLINKPATH)
+endif
+endif
+endif
+ifndef CROSSCOMPILE
+ifneq ($(BINUTILSPREFIX),)
+override FPCMAKEOPT+=-XP$(BINUTILSPREFIX)
+override FPMAKE_BUILD_OPT+=-XP$(BINUTILSPREFIX)
+endif
+endif
+ifdef UNITDIR
+override FPCOPT+=$(addprefix -Fu,$(UNITDIR))
+endif
+ifdef LIBDIR
+override FPCOPT+=$(addprefix -Fl,$(LIBDIR))
+endif
+ifdef OBJDIR
+override FPCOPT+=$(addprefix -Fo,$(OBJDIR))
+endif
+ifdef INCDIR
+override FPCOPT+=$(addprefix -Fi,$(INCDIR))
+endif
+ifdef LINKSMART
+override FPCOPT+=-XX
+endif
+ifdef CREATESMART
+override FPCOPT+=-CX
+endif
+ifdef DEBUG
+override FPCOPT+=-gl
+override FPCOPTDEF+=DEBUG
+endif
+ifdef RELEASE
+FPCCPUOPT:=-O2
+override FPCOPT+=-Ur -Xs $(FPCCPUOPT) -n
+override FPCOPTDEF+=RELEASE
+endif
+ifdef STRIP
+override FPCOPT+=-Xs
+endif
+ifdef OPTIMIZE
+override FPCOPT+=-O2
+endif
+ifdef VERBOSE
+override FPCOPT+=-vwni
+endif
+ifdef COMPILER_OPTIONS
+override FPCOPT+=$(COMPILER_OPTIONS)
+endif
+ifdef COMPILER_UNITDIR
+override FPCOPT+=$(addprefix -Fu,$(COMPILER_UNITDIR))
+endif
+ifdef COMPILER_LIBRARYDIR
+override FPCOPT+=$(addprefix -Fl,$(COMPILER_LIBRARYDIR))
+endif
+ifdef COMPILER_OBJECTDIR
+override FPCOPT+=$(addprefix -Fo,$(COMPILER_OBJECTDIR))
+endif
+ifdef COMPILER_INCLUDEDIR
+override FPCOPT+=$(addprefix -Fi,$(COMPILER_INCLUDEDIR))
+endif
+ifdef CROSSBINDIR
+override FPCOPT+=-FD$(CROSSBINDIR)
+endif
+ifdef COMPILER_TARGETDIR
+override FPCOPT+=-FE$(COMPILER_TARGETDIR)
+ifeq ($(COMPILER_TARGETDIR),.)
+override TARGETDIRPREFIX=
+else
+override TARGETDIRPREFIX=$(COMPILER_TARGETDIR)/
+endif
+endif
+ifdef COMPILER_UNITTARGETDIR
+override FPCOPT+=-FU$(COMPILER_UNITTARGETDIR)
+ifeq ($(COMPILER_UNITTARGETDIR),.)
+override UNITTARGETDIRPREFIX=
+else
+override UNITTARGETDIRPREFIX=$(COMPILER_UNITTARGETDIR)/
+endif
+else
+ifdef COMPILER_TARGETDIR
+override COMPILER_UNITTARGETDIR=$(COMPILER_TARGETDIR)
+override UNITTARGETDIRPREFIX=$(TARGETDIRPREFIX)
+endif
+endif
+ifdef SYSROOTPATH
+override FPCOPT+=-XR$(SYSROOTPATH)
+else
+ifeq ($(OS_TARGET),$(OS_SOURCE))
+ifneq ($(findstring $(OS_TARGET),darwin),)
+ifneq ($(findstring $(CPU_TARGET),aarch64),)
+ifneq ($(wildcard /Library/Developer/CommandLineTools/SDKs/MacOSX.sdk),)
+override FPCOPT+=-XR/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk
+endif
+endif
+endif
+endif
+endif
+ifdef CREATESHARED
+override FPCOPT+=-Cg
+endif
+ifneq ($(findstring $(OS_TARGET),dragonfly freebsd openbsd netbsd linux solaris),)
+ifneq ($(findstring $(CPU_TARGET),x86_64 mips mipsel riscv64),)
+override FPCOPT+=-Cg
+endif
+endif
+ifdef LINKSHARED
+endif
+ifdef GCCLIBDIR
+override FPCOPT+=-Fl$(GCCLIBDIR)
+ifdef FPCMAKEGCCLIBDIR
+override FPCMAKEOPT+=-Fl$(FPCMAKEGCCLIBDIR)
+else
+override FPCMAKEOPT+=-Fl$(GCCLIBDIR)
+endif
+endif
+ifdef OTHERLIBDIR
+override FPCOPT+=$(addprefix -Fl,$(OTHERLIBDIR))
+endif
+ifdef OPT
+override FPCOPT+=$(OPT)
+endif
+ifdef FPMAKEBUILDOPT
+override FPMAKE_BUILD_OPT+=$(FPMAKEBUILDOPT)
+endif
+ifdef FPCOPTDEF
+override FPCOPT+=$(addprefix -d,$(FPCOPTDEF))
+endif
+ifdef CFGFILE
+override FPCOPT+=@$(CFGFILE)
+endif
+ifdef USEENV
+override FPCEXTCMD:=$(FPCOPT)
+override FPCOPT:=!FPCEXTCMD
+export FPCEXTCMD
+endif
+override AFULL_TARGET=$(CPU_TARGET)-$(OS_TARGET)
+override AFULL_SOURCE=$(CPU_SOURCE)-$(OS_SOURCE)
+ifneq ($(AFULL_TARGET),$(AFULL_SOURCE))
+override ACROSSCOMPILE=1
+endif
+ifdef ACROSSCOMPILE
+override FPCOPT+=$(CROSSOPT)
+endif
+override COMPILER:=$(strip $(FPC) $(FPCOPT))
+ifneq (,$(findstring -sh ,$(COMPILER)))
+UseEXECPPAS=1
+endif
+ifneq (,$(findstring -s ,$(COMPILER)))
+ifeq ($(FULL_SOURCE),$(FULL_TARGET))
+UseEXECPPAS=1
+endif
+endif
+ifneq ($(UseEXECPPAS),1)
+EXECPPAS=
+else
+ifdef RUNBATCH
+EXECPPAS:=@$(RUNBATCH) $(PPAS)
+else
+EXECPPAS:=@$(PPAS)
+endif
+endif
+ifdef TARGET_RSTS
+override RSTFILES=$(addsuffix $(RSTEXT),$(TARGET_RSTS))
+override CLEANRSTFILES+=$(RSTFILES)
+endif
+.PHONY: fpc_install fpc_sourceinstall fpc_exampleinstall
+ifdef INSTALL_UNITS
+override INSTALLPPUFILES+=$(addsuffix $(PPUEXT),$(INSTALL_UNITS))
+endif
+ifdef INSTALL_BUILDUNIT
+override INSTALLPPUFILES:=$(filter-out $(INSTALL_BUILDUNIT)$(PPUEXT),$(INSTALLPPUFILES))
+endif
+ifdef INSTALLPPUFILES
+ifneq ($(IMPORTLIBPREFIX)-$(STATICLIBEXT),$(STATICLIBPREFIX)-$(STATICLIBEXT))
+override INSTALLPPULINKFILES:=$(subst $(PPUEXT),$(OEXT),$(INSTALLPPUFILES)) $(subst $(PPUEXT),$(LTOEXT),$(INSTALLPPUFILES)) $(addprefix $(STATICLIBPREFIX),$(subst $(PPUEXT),$(STATICLIBEXT),$(INSTALLPPUFILES))) $(addprefix $(IMPORTLIBPREFIX),$(subst $(PPUEXT),$(STATICLIBEXT),$(INSTALLPPUFILES)))
+else
+override INSTALLPPULINKFILES:=$(subst $(PPUEXT),$(OEXT),$(INSTALLPPUFILES)) $(subst $(PPUEXT),$(LTOEXT),$(INSTALLPPUFILES)) $(addprefix $(STATICLIBPREFIX),$(subst $(PPUEXT),$(STATICLIBEXT),$(INSTALLPPUFILES)))
+endif
+ifneq ($(UNITTARGETDIRPREFIX),)
+override INSTALLPPUFILENAMES:=$(notdir $(INSTALLPPUFILES))
+override INSTALLPPULINKFILENAMES:=$(notdir $(INSTALLPPULINKFILES))
+override INSTALLPPUFILES=$(addprefix $(UNITTARGETDIRPREFIX),$(INSTALLPPUFILENAMES))
+override INSTALLPPULINKFILES=$(wildcard $(addprefix $(UNITTARGETDIRPREFIX),$(INSTALLPPULINKFILENAMES)))
+endif
+override INSTALL_CREATEPACKAGEFPC=1
+endif
+ifdef INSTALLEXEFILES
+ifneq ($(TARGETDIRPREFIX),)
+override INSTALLEXEFILES:=$(addprefix $(TARGETDIRPREFIX),$(notdir $(INSTALLEXEFILES)))
+endif
+endif
+fpc_install: all $(INSTALLTARGET)
+ifdef INSTALLEXEFILES
+	$(MKDIR) $(INSTALL_BINDIR)
+	$(INSTALLEXE) $(INSTALLEXEFILES) $(INSTALL_BINDIR)
+endif
+ifdef INSTALL_CREATEPACKAGEFPC
+ifdef FPCMAKE
+ifdef PACKAGE_VERSION
+ifneq ($(wildcard Makefile.fpc),)
+	$(FPCMAKE) -p -T$(CPU_TARGET)-$(OS_TARGET) Makefile.fpc
+	$(MKDIR) $(INSTALL_UNITDIR)
+	$(INSTALL) Package.fpc $(INSTALL_UNITDIR)
+endif
+endif
+endif
+endif
+ifdef INSTALLPPUFILES
+	$(MKDIR) $(INSTALL_UNITDIR)
+	$(INSTALL) $(INSTALLPPUFILES) $(INSTALL_UNITDIR)
+ifneq ($(INSTALLPPULINKFILES),)
+	$(INSTALL) $(INSTALLPPULINKFILES) $(INSTALL_UNITDIR)
+endif
+ifneq ($(wildcard $(LIB_FULLNAME)),)
+	$(MKDIR) $(INSTALL_LIBDIR)
+	$(INSTALL) $(LIB_FULLNAME) $(INSTALL_LIBDIR)
+ifdef inUnix
+	ln -sf $(LIB_FULLNAME) $(INSTALL_LIBDIR)/$(LIB_NAME)
+endif
+endif
+endif
+ifdef INSTALL_FILES
+	$(MKDIR) $(INSTALL_DATADIR)
+	$(INSTALL) $(INSTALL_FILES) $(INSTALL_DATADIR)
+endif
+fpc_sourceinstall: distclean
+	$(MKDIR) $(INSTALL_SOURCEDIR)
+	$(COPYTREE) $(BASEDIR)/* $(INSTALL_SOURCEDIR)
+fpc_exampleinstall: $(EXAMPLEINSTALLTARGET) $(addsuffix _distclean,$(TARGET_EXAMPLEDIRS))
+ifdef HASEXAMPLES
+	$(MKDIR) $(INSTALL_EXAMPLEDIR)
+endif
+ifdef EXAMPLESOURCEFILES
+	$(COPY) $(EXAMPLESOURCEFILES) $(INSTALL_EXAMPLEDIR)
+endif
+ifdef TARGET_EXAMPLEDIRS
+	$(COPYTREE) $(addsuffix /*,$(TARGET_EXAMPLEDIRS)) $(INSTALL_EXAMPLEDIR)
+endif
+.PHONY: fpc_distinstall
+fpc_distinstall: install exampleinstall
+.PHONY: fpc_zipinstall fpc_zipsourceinstall fpc_zipexampleinstall
+ifndef PACKDIR
+ifndef inUnix
+PACKDIR=$(BASEDIR)/../fpc-pack
+else
+PACKDIR=/tmp/fpc-pack
+endif
+endif
+ifndef ZIPNAME
+ifdef DIST_ZIPNAME
+ZIPNAME=$(DIST_ZIPNAME)
+else
+ZIPNAME=$(PACKAGE_NAME)
+endif
+endif
+ifndef FULLZIPNAME
+FULLZIPNAME=$(ZIPCROSSPREFIX)$(ZIPPREFIX)$(ZIPNAME)$(ZIPSUFFIX)
+endif
+ifndef ZIPTARGET
+ifdef DIST_ZIPTARGET
+ZIPTARGET=DIST_ZIPTARGET
+else
+ZIPTARGET=install
+endif
+endif
+ifndef USEZIP
+ifdef inUnix
+USETAR=1
+endif
+endif
+ifndef inUnix
+USEZIPWRAPPER=1
+endif
+ifdef USEZIPWRAPPER
+ZIPPATHSEP=$(PATHSEP)
+ZIPWRAPPER=$(subst /,$(PATHSEP),$(DIST_DESTDIR)/fpczip$(SRCBATCHEXT))
+else
+ZIPPATHSEP=/
+endif
+ZIPCMD_CDPACK:=cd $(subst /,$(ZIPPATHSEP),$(PACKDIR))
+ZIPCMD_CDBASE:=cd $(subst /,$(ZIPPATHSEP),$(BASEDIR))
+ifdef USETAR
+ZIPDESTFILE:=$(DIST_DESTDIR)/$(FULLZIPNAME)$(TAREXT)
+ZIPCMD_ZIP:=$(TARPROG) c$(TAROPT)f $(ZIPDESTFILE) *
+else
+ZIPDESTFILE:=$(DIST_DESTDIR)/$(FULLZIPNAME)$(ZIPEXT)
+ZIPCMD_ZIP:=$(subst /,$(ZIPPATHSEP),$(ZIPPROG)) -Dr $(ZIPOPT) $(ZIPDESTFILE) *
+endif
+fpc_zipinstall:
+	$(MAKE) $(ZIPTARGET) INSTALL_PREFIX=$(PACKDIR) ZIPINSTALL=1
+	$(MKDIR) $(DIST_DESTDIR)
+	$(DEL) $(ZIPDESTFILE)
+ifdef USEZIPWRAPPER
+ifneq ($(ECHOREDIR),echo)
+	$(ECHOREDIR) -e "$(subst \,\\,$(ZIPCMD_CDPACK))" > $(ZIPWRAPPER)
+	$(ECHOREDIR) -e "$(subst \,\\,$(ZIPCMD_ZIP))" >> $(ZIPWRAPPER)
+	$(ECHOREDIR) -e "$(subst \,\\,$(ZIPCMD_CDBASE))" >> $(ZIPWRAPPER)
+else
+	echo $(ZIPCMD_CDPACK) > $(ZIPWRAPPER)
+	echo $(ZIPCMD_ZIP) >> $(ZIPWRAPPER)
+	echo $(ZIPCMD_CDBASE) >> $(ZIPWRAPPER)
+endif
+ifdef inUnix
+	/bin/sh $(ZIPWRAPPER)
+else
+ifdef RUNBATCH
+	$(RUNBATCH) $(ZIPWRAPPER)
+else
+	$(ZIPWRAPPER)
+endif
+endif
+	$(DEL) $(ZIPWRAPPER)
+else
+	$(ZIPCMD_CDPACK) ; $(ZIPCMD_ZIP) ; $(ZIPCMD_CDBASE)
+endif
+	$(DELTREE) $(PACKDIR)
+fpc_zipsourceinstall:
+	$(MAKE) fpc_zipinstall ZIPTARGET=sourceinstall ZIPSUFFIX=$(ZIPSOURCESUFFIX)
+fpc_zipexampleinstall:
+ifdef HASEXAMPLES
+	$(MAKE) fpc_zipinstall ZIPTARGET=exampleinstall ZIPSUFFIX=$(ZIPEXAMPLESUFFIX)
+endif
+fpc_zipdistinstall:
+	$(MAKE) fpc_zipinstall ZIPTARGET=distinstall
+.PHONY: fpc_clean fpc_cleanall fpc_distclean
+ifdef EXEFILES
+override CLEANEXEFILES:=$(addprefix $(TARGETDIRPREFIX),$(CLEANEXEFILES))
+override CLEANEXEDBGFILES:=$(addprefix $(TARGETDIRPREFIX),$(CLEANEXEDBGFILES))
+endif
+ifdef CLEAN_PROGRAMS
+override CLEANEXEFILES+=$(addprefix $(TARGETDIRPREFIX),$(addsuffix $(EXEEXT), $(CLEAN_PROGRAMS)))
+override CLEANEXEDBGFILES+=$(addprefix $(TARGETDIRPREFIX),$(addsuffix $(EXEDBGEXT), $(CLEAN_PROGRAMS)))
+endif
+ifdef CLEAN_UNITS
+override CLEANPPUFILES+=$(addsuffix $(PPUEXT),$(CLEAN_UNITS))
+endif
+ifdef CLEANPPUFILES
+override CLEANPPULINKFILES:=$(subst $(PPUEXT),$(OEXT),$(CLEANPPUFILES)) $(subst $(PPUEXT),$(LTOEXT),$(CLEANPPUFILES)) $(addprefix $(STATICLIBPREFIX),$(subst $(PPUEXT),$(STATICLIBEXT),$(CLEANPPUFILES))) $(addprefix $(IMPORTLIBPREFIX),$(subst $(PPUEXT),$(STATICLIBEXT),$(CLEANPPUFILES)))
+ifdef DEBUGSYMEXT
+override CLEANPPULINKFILES+=$(subst $(PPUEXT),$(DEBUGSYMEXT),$(CLEANPPUFILES))
+endif
+override CLEANPPUFILENAMES:=$(CLEANPPUFILES)
+override CLEANPPUFILES=$(addprefix $(UNITTARGETDIRPREFIX),$(CLEANPPUFILENAMES))
+override CLEANPPULINKFILENAMES:=$(CLEANPPULINKFILES)
+override CLEANPPULINKFILES=$(wildcard $(addprefix $(UNITTARGETDIRPREFIX),$(CLEANPPULINKFILENAMES)))
+endif
+fpc_clean: $(CLEANTARGET)
+ifdef CLEANEXEFILES
+	-$(DEL) $(CLEANEXEFILES)
+endif
+ifdef CLEANEXEDBGFILES
+	-$(DELTREE) $(CLEANEXEDBGFILES)
+endif
+ifdef CLEANPPUFILES
+	-$(DEL) $(CLEANPPUFILES)
+endif
+ifneq ($(CLEANPPULINKFILES),)
+	-$(DEL) $(CLEANPPULINKFILES)
+endif
+ifdef CLEANRSTFILES
+	-$(DEL) $(addprefix $(UNITTARGETDIRPREFIX),$(CLEANRSTFILES))
+endif
+ifdef CLEAN_FILES
+	-$(DEL) $(CLEAN_FILES)
+endif
+ifdef LIB_NAME
+	-$(DEL) $(LIB_NAME) $(LIB_FULLNAME)
+endif
+	-$(DEL) $(FPCMADE) *$(FULL_TARGET).fpm Package.fpc *$(ASMEXT)
+	-$(DEL) $(FPCEXTFILE) $(REDIRFILE) script*.res link*.res *_script.res *_link.res
+	-$(DEL) $(PPAS) *_ppas$(BATCHEXT) ppas$(BATCHEXT) ppaslink$(BATCHEXT)
+fpc_cleanall: $(CLEANTARGET)
+ifdef CLEANEXEFILES
+	-$(DEL) $(CLEANEXEFILES)
+endif
+ifdef COMPILER_UNITTARGETDIR
+ifdef CLEANPPUFILES
+	-$(DEL) $(CLEANPPUFILES)
+endif
+ifneq ($(CLEANPPULINKFILES),)
+	-$(DEL) $(CLEANPPULINKFILES)
+endif
+ifdef CLEANRSTFILES
+	-$(DEL) $(addprefix $(UNITTARGETDIRPREFIX),$(CLEANRSTFILES))
+endif
+endif
+ifdef CLEAN_FILES
+	-$(DEL) $(CLEAN_FILES)
+endif
+	-$(DELTREE) units
+	-$(DELTREE) bin
+	-$(DEL) *$(OEXT) *$(LTOEXT) *$(PPUEXT) *$(RSTEXT) *$(ASMEXT) *$(STATICLIBEXT) *$(SHAREDLIBEXT) *$(PPLEXT)
+ifneq ($(PPUEXT),.ppu)
+	-$(DEL) *.o *.ppu *.a
+endif
+	-$(DELTREE) *$(SMARTEXT)
+	-$(DEL) fpcmade.* Package.fpc *.fpm
+	-$(DEL) $(FPCEXTFILE) $(REDIRFILE) script*.res link*.res *_script.res *_link.res
+	-$(DEL) $(PPAS) *_ppas$(BATCHEXT) ppas$(BATCHEXT) ppaslink$(BATCHEXT)
+ifdef AOUTEXT
+	-$(DEL) *$(AOUTEXT)
+endif
+ifdef DEBUGSYMEXT
+	-$(DEL) *$(DEBUGSYMEXT)
+endif
+ifdef LOCALFPMAKEBIN
+	-$(DEL) $(LOCALFPMAKEBIN)
+	-$(DEL) $(FPMAKEBINOBJ)
+endif
+fpc_distclean: cleanall
+.PHONY: fpc_baseinfo
+override INFORULES+=fpc_baseinfo
+fpc_baseinfo:
+	@$(ECHO)
+	@$(ECHO)  == Package info ==
+	@$(ECHO)  Package Name..... $(PACKAGE_NAME)
+	@$(ECHO)  Package Version.. $(PACKAGE_VERSION)
+	@$(ECHO)
+	@$(ECHO)  == Configuration info ==
+	@$(ECHO)
+	@$(ECHO)  FPC.......... $(FPC)
+	@$(ECHO)  FPC Version.. $(FPC_VERSION)
+	@$(ECHO)  Source CPU... $(CPU_SOURCE)
+	@$(ECHO)  Target CPU... $(CPU_TARGET)
+	@$(ECHO)  Source OS.... $(OS_SOURCE)
+	@$(ECHO)  Target OS.... $(OS_TARGET)
+	@$(ECHO)  Full Source.. $(FULL_SOURCE)
+	@$(ECHO)  Full Target.. $(FULL_TARGET)
+	@$(ECHO)  SourceSuffix. $(SOURCESUFFIX)
+	@$(ECHO)  TargetSuffix. $(TARGETSUFFIX)
+	@$(ECHO)  FPC fpmake... $(FPCFPMAKE)
+	@$(ECHO)
+	@$(ECHO)  == Directory info ==
+	@$(ECHO)
+	@$(ECHO)  Required pkgs... $(REQUIRE_PACKAGES)
+	@$(ECHO)
+	@$(ECHO)  Basedir......... $(BASEDIR)
+	@$(ECHO)  FPCDir.......... $(FPCDIR)
+	@$(ECHO)  CrossBinDir..... $(CROSSBINDIR)
+	@$(ECHO)  UnitsDir........ $(UNITSDIR)
+	@$(ECHO)  PackagesDir..... $(PACKAGESDIR)
+	@$(ECHO)
+	@$(ECHO)  GCC library..... $(GCCLIBDIR)
+	@$(ECHO)  Other library... $(OTHERLIBDIR)
+	@$(ECHO)
+	@$(ECHO)  == Tools info ==
+	@$(ECHO)
+	@$(ECHO)  As........ $(AS)
+	@$(ECHO)  Ld........ $(LD)
+	@$(ECHO)  Ar........ $(AR)
+	@$(ECHO)  Rc........ $(RC)
+	@$(ECHO)
+	@$(ECHO)  Mv........ $(MVPROG)
+	@$(ECHO)  Cp........ $(CPPROG)
+	@$(ECHO)  Rm........ $(RMPROG)
+	@$(ECHO)  GInstall.. $(GINSTALL)
+	@$(ECHO)  Echo...... $(ECHO)
+	@$(ECHO)  Shell..... $(SHELL)
+	@$(ECHO)  Date...... $(DATE)
+	@$(ECHO)  FPCMake... $(FPCMAKE)
+	@$(ECHO)  PPUMove... $(PPUMOVE)
+	@$(ECHO)  Zip....... $(ZIPPROG)
+	@$(ECHO)
+	@$(ECHO)  == Object info ==
+	@$(ECHO)
+	@$(ECHO)  Target Loaders........ $(TARGET_LOADERS)
+	@$(ECHO)  Target Units.......... $(TARGET_UNITS)
+	@$(ECHO)  Target Implicit Units. $(TARGET_IMPLICITUNITS)
+	@$(ECHO)  Target Programs....... $(TARGET_PROGRAMS)
+	@$(ECHO)  Target Dirs........... $(TARGET_DIRS)
+	@$(ECHO)  Target Examples....... $(TARGET_EXAMPLES)
+	@$(ECHO)  Target ExampleDirs.... $(TARGET_EXAMPLEDIRS)
+	@$(ECHO)
+	@$(ECHO)  Clean Units......... $(CLEAN_UNITS)
+	@$(ECHO)  Clean Files......... $(CLEAN_FILES)
+	@$(ECHO)
+	@$(ECHO)  Install Units....... $(INSTALL_UNITS)
+	@$(ECHO)  Install Files....... $(INSTALL_FILES)
+	@$(ECHO)
+	@$(ECHO)  == Install info ==
+	@$(ECHO)
+	@$(ECHO)  DateStr.............. $(DATESTR)
+	@$(ECHO)  ZipName.............. $(ZIPNAME)
+	@$(ECHO)  ZipPrefix............ $(ZIPPREFIX)
+	@$(ECHO)  ZipCrossPrefix....... $(ZIPCROSSPREFIX)
+	@$(ECHO)  ZipSuffix............ $(ZIPSUFFIX)
+	@$(ECHO)  FullZipName.......... $(FULLZIPNAME)
+	@$(ECHO)  Install FPC Package.. $(INSTALL_FPCPACKAGE)
+	@$(ECHO)
+	@$(ECHO)  Install base dir..... $(INSTALL_BASEDIR)
+	@$(ECHO)  Install binary dir... $(INSTALL_BINDIR)
+	@$(ECHO)  Install library dir.. $(INSTALL_LIBDIR)
+	@$(ECHO)  Install units dir.... $(INSTALL_UNITDIR)
+	@$(ECHO)  Install source dir... $(INSTALL_SOURCEDIR)
+	@$(ECHO)  Install doc dir...... $(INSTALL_DOCDIR)
+	@$(ECHO)  Install example dir.. $(INSTALL_EXAMPLEDIR)
+	@$(ECHO)  Install data dir..... $(INSTALL_DATADIR)
+	@$(ECHO)
+	@$(ECHO)  Dist destination dir. $(DIST_DESTDIR)
+	@$(ECHO)  Dist zip name........ $(DIST_ZIPNAME)
+	@$(ECHO)
+.PHONY: fpc_info
+fpc_info: $(INFORULES)
+.PHONY: fpc_makefile fpc_makefiles fpc_makefile_sub1 fpc_makefile_sub2 \
+	fpc_makefile_dirs
+fpc_makefile:
+	$(FPCMAKE) -w -T$(OS_TARGET) Makefile.fpc
+fpc_makefile_sub1:
+ifdef TARGET_DIRS
+	$(FPCMAKE) -w -T$(OS_TARGET) $(addsuffix /Makefile.fpc,$(TARGET_DIRS))
+endif
+ifdef TARGET_EXAMPLEDIRS
+	$(FPCMAKE) -w -T$(OS_TARGET) $(addsuffix /Makefile.fpc,$(TARGET_EXAMPLEDIRS))
+endif
+fpc_makefile_sub2: $(addsuffix _makefile_dirs,$(TARGET_DIRS) $(TARGET_EXAMPLEDIRS))
+fpc_makefile_dirs: fpc_makefile_sub1 fpc_makefile_sub2
+fpc_makefiles: fpc_makefile fpc_makefile_dirs
+units:
+examples:
+shared:
+sourceinstall: fpc_sourceinstall
+exampleinstall: fpc_exampleinstall
+zipexampleinstall: fpc_zipexampleinstall
+info: fpc_info
+makefiles: fpc_makefiles
+.PHONY: units examples shared sourceinstall exampleinstall zipexampleinstall info makefiles
+ifneq ($(wildcard fpcmake.loc),)
+include fpcmake.loc
+endif
+override FPCOPT:=$(filter-out -FU%,$(FPCOPT))
+override FPCOPT:=$(filter-out -FE%,$(FPCOPT))
+override FPCOPT:=$(filter-out $(addprefix -Fu,$(COMPILER_UNITDIR)),$(FPCOPT))# Compose general fpmake-parameters
+ifdef FPMAKEOPT
+FPMAKE_OPT+=$(FPMAKEOPT)
+endif
+FPMAKE_OPT+=--localunitdir=../..
+FPMAKE_OPT+=--globalunitdir=..
+FPMAKE_OPT+=$(FPC_TARGETOPT)
+FPMAKE_OPT+=$(addprefix -o ,$(FPCOPT))
+FPMAKE_OPT+=--compiler=$(FPC)
+FPMAKE_OPT+=-bu
+.NOTPARALLEL:
+fpmake$(SRCEXEEXT): fpmake.pp
+	$(FPCFPMAKE) fpmake.pp $(FPMAKE_SKIP_CONFIG) $(addprefix -Fu,$(COMPILER_FPMAKE_UNITDIR)) $(FPCMAKEOPT) $(OPT)
+all:	fpmake$(SRCEXEEXT)
+	$(LOCALFPMAKE) compile $(FPMAKE_OPT)
+smart:	fpmake$(SRCEXEEXT)
+	$(LOCALFPMAKE) compile $(FPMAKE_OPT) -o -XX -o -CX
+release:	fpmake$(SRCEXEEXT)
+	$(LOCALFPMAKE) compile $(FPMAKE_OPT) -o -dRELEASE
+debug:	fpmake$(SRCEXEEXT)
+	$(LOCALFPMAKE) compile $(FPMAKE_OPT) -o -dDEBUG
+ifeq ($(FPMAKE_BIN_CLEAN),)
+clean:
+else
+clean:
+	$(FPMAKE_BIN_CLEAN) clean $(FPMAKE_OPT)
+endif
+ifeq ($(FPMAKE_BIN_CLEAN),)
+distclean:	$(addsuffix _distclean,$(TARGET_DIRS)) fpc_cleanall
+else
+distclean:
+ifdef inUnix
+	{ $(FPMAKE_BIN_CLEAN) distclean $(FPMAKE_OPT); if [ $$? != "0" ]; then { echo Something wrong with fpmake exectable. Remove the executable and call make recursively to recover.; $(DEL) $(FPMAKE_BIN_CLEAN); $(MAKE) fpc_cleanall; }; fi;  }
+else
+	$(FPMAKE_BIN_CLEAN) distclean $(FPMAKE_OPT)
+endif
+	-$(DEL) $(LOCALFPMAKE)
+endif
+cleanall: distclean
+install:	fpmake$(SRCEXEEXT)
+ifdef UNIXHier
+	$(LOCALFPMAKE) install $(FPMAKE_OPT) --prefix=$(INSTALL_PREFIX) --baseinstalldir=$(INSTALL_LIBDIR)/fpc/$(FPC_VERSION) --unitinstalldir=$(INSTALL_UNITDIR)
+else
+	$(LOCALFPMAKE) install $(FPMAKE_OPT) --prefix=$(INSTALL_BASEDIR) --baseinstalldir=$(INSTALL_BASEDIR) --unitinstalldir=$(INSTALL_UNITDIR)
+endif
+distinstall:	fpmake$(SRCEXEEXT)
+ifdef UNIXHier
+	$(LOCALFPMAKE) install $(FPMAKE_OPT) --prefix=$(INSTALL_PREFIX) --baseinstalldir=$(INSTALL_LIBDIR)/fpc/$(FPC_VERSION) --unitinstalldir=$(INSTALL_UNITDIR) -ie -fsp 0
+else
+	$(LOCALFPMAKE) install $(FPMAKE_OPT) --prefix=$(INSTALL_BASEDIR) --baseinstalldir=$(INSTALL_BASEDIR) --unitinstalldir=$(INSTALL_UNITDIR) -ie -fsp 0
+endif
+zipinstall:	fpmake$(SRCEXEEXT)
+	$(LOCALFPMAKE) zipinstall $(FPMAKE_OPT) --zipprefix=$(DIST_DESTDIR)/$(ZIPPREFIX)
+zipdistinstall:	fpmake$(SRCEXEEXT)
+	$(LOCALFPMAKE) zipinstall $(FPMAKE_OPT) --zipprefix=$(DIST_DESTDIR)/$(ZIPPREFIX) -ie -fsp 0
+zipsourceinstall:	fpmake$(SRCEXEEXT)
+ifdef UNIXHier
+	$(LOCALFPMAKE) archive $(FPMAKE_OPT) --zipprefix=$(DIST_DESTDIR)/$(ZIPPREFIX) --prefix=share/src/fpc-\$$\(PACKAGEVERSION\)/$(INSTALL_FPCSUBDIR)/\$$\(PACKAGEDIRECTORY\)
+else
+	$(LOCALFPMAKE) archive $(FPMAKE_OPT) --zipprefix=$(DIST_DESTDIR)/$(ZIPPREFIX) --prefix=source\\$(INSTALL_FPCSUBDIR)\\\$$\(PACKAGEDIRECTORY\)
+endif

+ 128 - 0
packages/fcl-mustache/Makefile.fpc

@@ -0,0 +1,128 @@
+#
+#   Makefile.fpc for running fpmake
+#
+
+[package]
+name=fcl-mustache
+version=3.3.1
+
+[require]
+packages=rtl fcl-base fcl-db fcl-json
+
+[install]
+fpcpackage=y
+
+[default]
+fpcdir=../..
+
+[prerules]
+FPMAKE_BIN_CLEAN=$(wildcard ./fpmake$(SRCEXEEXT))
+ifdef OS_TARGET
+FPC_TARGETOPT+=--os=$(OS_TARGET)
+endif
+ifdef CPU_TARGET
+FPC_TARGETOPT+=--cpu=$(CPU_TARGET)
+endif
+LOCALFPMAKE=./fpmake$(SRCEXEEXT)
+# Adding a dependency on fpmkunit is not possbile due to an infinite loop. So
+# the fpmkunit-searchpath is added here:
+PACKAGEDIR_FPMKUNIT:=$(firstword $(subst /Makefile.fpc,,$(strip $(wildcard $(addsuffix /fpmkunit/Makefile.fpc,$(PACKAGESDIR))))))
+ifneq ($(PACKAGEDIR_FPMKUNIT),)
+ifneq ($(wildcard $(PACKAGEDIR_FPMKUNIT)/units_bs/$(SOURCESUFFIX)),)
+UNITDIR_FPMAKE_FPMKUNIT=$(PACKAGEDIR_FPMKUNIT)/units_bs/$(SOURCESUFFIX)
+else
+UNITDIR_FPMAKE_FPMKUNIT=$(PACKAGEDIR_FPMKUNIT)
+endif
+ifdef CHECKDEPEND
+$(PACKAGEDIR_FPMKUNIT)/$(FPCMADE):
+        $(MAKE) -C $(PACKAGEDIR_FPMKUNIT) $(FPCMADE)
+override ALLDEPENDENCIES+=$(PACKAGEDIR_FPMKUNIT)/$(FPCMADE)
+endif
+else
+PACKAGEDIR_FPMKUNIT=
+UNITDIR_FPMKUNIT:=$(subst /Package.fpc,,$(strip $(wildcard $(addsuffix /fpmkunit/Package.fpc,$(UNITSDIR)))))
+ifneq ($(UNITDIR_FPMKUNIT),)
+UNITDIR_FPMKUNIT:=$(firstword $(UNITDIR_FPMKUNIT))
+else
+UNITDIR_FPMKUNIT=
+endif
+endif
+ifdef UNITDIR_FPMAKE_FPMKUNIT
+override COMPILER_FPMAKE_UNITDIR+=$(UNITDIR_FPMAKE_FPMKUNIT)
+endif
+
+[rules]
+# Do not pass the Makefile's unit and binary target locations. Fpmake uses it's own.
+override FPCOPT:=$(filter-out -FU%,$(FPCOPT))
+override FPCOPT:=$(filter-out -FE%,$(FPCOPT))
+# Do not pass the package-unitdirectories. Fpmake adds those and this way they don't apear in the .fpm
+override FPCOPT:=$(filter-out $(addprefix -Fu,$(COMPILER_UNITDIR)),$(FPCOPT))# Compose general fpmake-parameters
+# Compose general fpmake-parameters
+ifdef FPMAKEOPT
+FPMAKE_OPT+=$(FPMAKEOPT)
+endif
+FPMAKE_OPT+=--localunitdir=../..
+FPMAKE_OPT+=--globalunitdir=..
+FPMAKE_OPT+=$(FPC_TARGETOPT)
+FPMAKE_OPT+=$(addprefix -o ,$(FPCOPT))
+FPMAKE_OPT+=--compiler=$(FPC)
+FPMAKE_OPT+=-bu
+.NOTPARALLEL:
+
+fpmake$(SRCEXEEXT): fpmake.pp
+	$(FPCFPMAKE) fpmake.pp $(FPMAKE_SKIP_CONFIG) $(addprefix -Fu,$(COMPILER_FPMAKE_UNITDIR)) $(FPCMAKEOPT) $(OPT)
+all:	fpmake$(SRCEXEEXT)
+	$(LOCALFPMAKE) compile $(FPMAKE_OPT)
+smart:	fpmake$(SRCEXEEXT)
+	$(LOCALFPMAKE) compile $(FPMAKE_OPT) -o -XX -o -CX
+release:	fpmake$(SRCEXEEXT)
+	$(LOCALFPMAKE) compile $(FPMAKE_OPT) -o -dRELEASE
+debug:	fpmake$(SRCEXEEXT)
+	$(LOCALFPMAKE) compile $(FPMAKE_OPT) -o -dDEBUG
+# If no fpmake exists and (dist)clean is called, do not try to build fpmake, it will
+# most often fail because the dependencies are cleared.
+# In case of a clean, simply do nothing
+ifeq ($(FPMAKE_BIN_CLEAN),)
+clean:
+else
+clean:
+	$(FPMAKE_BIN_CLEAN) clean $(FPMAKE_OPT)
+endif
+# In case of a distclean, perform an 'old'-style distclean. This to avoid problems
+# when the package is compiled using fpcmake prior to running this clean using fpmake
+ifeq ($(FPMAKE_BIN_CLEAN),)
+distclean:	$(addsuffix _distclean,$(TARGET_DIRS)) fpc_cleanall
+else
+distclean:
+ifdef inUnix
+        { $(FPMAKE_BIN_CLEAN) distclean $(FPMAKE_OPT); if [ $$? != "0" ]; then { echo Something wrong with fpmake exectable. Remove the executable and call make recursively to recover.; $(DEL) $(FPMAKE_BIN_CLEAN); $(MAKE) fpc_cleanall; }; fi;  }
+else
+        $(FPMAKE_BIN_CLEAN) distclean $(FPMAKE_OPT)
+endif
+	-$(DEL) $(LOCALFPMAKE)
+endif
+cleanall: distclean
+install:	fpmake$(SRCEXEEXT)
+ifdef UNIXHier
+	$(LOCALFPMAKE) install $(FPMAKE_OPT) --prefix=$(INSTALL_PREFIX) --baseinstalldir=$(INSTALL_LIBDIR)/fpc/$(FPC_VERSION) --unitinstalldir=$(INSTALL_UNITDIR)
+else
+	$(LOCALFPMAKE) install $(FPMAKE_OPT) --prefix=$(INSTALL_BASEDIR) --baseinstalldir=$(INSTALL_BASEDIR) --unitinstalldir=$(INSTALL_UNITDIR)
+endif
+# distinstall also installs the example-sources and omits the location of the source-
+# files from the fpunits.cfg files.
+distinstall:	fpmake$(SRCEXEEXT)
+ifdef UNIXHier
+	$(LOCALFPMAKE) install $(FPMAKE_OPT) --prefix=$(INSTALL_PREFIX) --baseinstalldir=$(INSTALL_LIBDIR)/fpc/$(FPC_VERSION) --unitinstalldir=$(INSTALL_UNITDIR) -ie -fsp 0
+else
+	$(LOCALFPMAKE) install $(FPMAKE_OPT) --prefix=$(INSTALL_BASEDIR) --baseinstalldir=$(INSTALL_BASEDIR) --unitinstalldir=$(INSTALL_UNITDIR) -ie -fsp 0
+endif
+zipinstall:	fpmake$(SRCEXEEXT)
+	$(LOCALFPMAKE) zipinstall $(FPMAKE_OPT) --zipprefix=$(DIST_DESTDIR)/$(ZIPPREFIX)
+zipdistinstall:	fpmake$(SRCEXEEXT)
+	$(LOCALFPMAKE) zipinstall $(FPMAKE_OPT) --zipprefix=$(DIST_DESTDIR)/$(ZIPPREFIX) -ie -fsp 0
+zipsourceinstall:	fpmake$(SRCEXEEXT)
+ifdef UNIXHier
+	$(LOCALFPMAKE) archive $(FPMAKE_OPT) --zipprefix=$(DIST_DESTDIR)/$(ZIPPREFIX) --prefix=share/src/fpc-\$$\(PACKAGEVERSION\)/$(INSTALL_FPCSUBDIR)/\$$\(PACKAGEDIRECTORY\)
+else
+	$(LOCALFPMAKE) archive $(FPMAKE_OPT) --zipprefix=$(DIST_DESTDIR)/$(ZIPPREFIX) --prefix=source\\$(INSTALL_FPCSUBDIR)\\\$$\(PACKAGEDIRECTORY\)
+endif

+ 27 - 0
packages/fcl-mustache/examples/README.txt

@@ -0,0 +1,27 @@
+demo1 sample program:
+
+Demonstrates the most basic use of the mustache parser
+
+demo2 sample program:
+
+Demonstrates the use of the mustache parser with a CSV dataset
+
+
+mustache example program:
+
+Can be used to load a template and data, and process the result.
+Output to standard output or file.
+The template and JSON value can be loaded from file (using @filename), 
+or their value can be specified directly on the command-line.
+
+Example usage:
+
+Load template from family.tmpl file, data from family.json file:
+
+./mustache -d title="my family" -t @family.tmpl -j @family.json
+
+Load template from family.tmpl file, data from family.csv file:
+
+./mustache -d title="my family" -t @family.tmpl -c family.csv
+
+Use of expressions can be enabled with the -e switch.

+ 57 - 0
packages/fcl-mustache/examples/demo1.lpi

@@ -0,0 +1,57 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<CONFIG>
+  <ProjectOptions>
+    <Version Value="12"/>
+    <General>
+      <Flags>
+        <MainUnitHasCreateFormStatements Value="False"/>
+        <MainUnitHasTitleStatement Value="False"/>
+        <MainUnitHasScaledStatement Value="False"/>
+      </Flags>
+      <SessionStorage Value="InProjectDir"/>
+      <Title Value="demo1"/>
+      <UseAppBundle Value="False"/>
+      <ResourceType Value="res"/>
+    </General>
+    <BuildModes>
+      <Item Name="Default" Default="True"/>
+    </BuildModes>
+    <PublishOptions>
+      <Version Value="2"/>
+      <UseFileFilters Value="True"/>
+    </PublishOptions>
+    <RunParams>
+      <FormatVersion Value="2"/>
+    </RunParams>
+    <Units>
+      <Unit>
+        <Filename Value="demo1.lpr"/>
+        <IsPartOfProject Value="True"/>
+      </Unit>
+    </Units>
+  </ProjectOptions>
+  <CompilerOptions>
+    <Version Value="11"/>
+    <Target>
+      <Filename Value="demo1"/>
+    </Target>
+    <SearchPaths>
+      <IncludeFiles Value="$(ProjOutDir)"/>
+      <OtherUnitFiles Value="../src"/>
+      <UnitOutputDirectory Value="lib/$(TargetCPU)-$(TargetOS)"/>
+    </SearchPaths>
+  </CompilerOptions>
+  <Debugging>
+    <Exceptions>
+      <Item>
+        <Name Value="EAbort"/>
+      </Item>
+      <Item>
+        <Name Value="ECodetoolError"/>
+      </Item>
+      <Item>
+        <Name Value="EFOpenError"/>
+      </Item>
+    </Exceptions>
+  </Debugging>
+</CONFIG>

+ 42 - 0
packages/fcl-mustache/examples/demo1.lpr

@@ -0,0 +1,42 @@
+{ Demo for mustache engine with JSON context
+
+  Copyright (C) 2021 michael Van Canneyt [email protected]
+
+  This source is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as
+  published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version.
+
+  This code is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
+
+  A copy of the GNU General Public License is available on the World Wide Web at <http://www.gnu.org/copyleft/gpl.html>. You can
+  also obtain it by writing to the Free Software Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1335, USA.
+}
+program demo1;
+
+// jsonparser includes the json parser.
+
+uses jsonparser, fpmustache;
+
+Const
+  JSON = '{ "products" : [ {"name" : "BMW" }, {"name" : "Mercedes"}, { "name" : "Audi" }] }';
+
+  // Mock markdown table
+  Template =
+     '| name |'+sLineBreak+
+     '|------|'+sLineBreak+
+     '{{#products}}| {{name}} |'+sLineBreak+
+     '{{/products}}';
+
+Var
+  M : TMustache;
+
+begin
+  M:=TMustache.Create(Nil);
+  try
+   // Json support enabled by default
+   Writeln(M.Render(Template,JSON));
+  finally
+    M.Free;
+  end;
+end.
+

+ 57 - 0
packages/fcl-mustache/examples/demo2.lpi

@@ -0,0 +1,57 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<CONFIG>
+  <ProjectOptions>
+    <Version Value="12"/>
+    <General>
+      <Flags>
+        <MainUnitHasCreateFormStatements Value="False"/>
+        <MainUnitHasTitleStatement Value="False"/>
+        <MainUnitHasScaledStatement Value="False"/>
+      </Flags>
+      <SessionStorage Value="InProjectDir"/>
+      <Title Value="demo2"/>
+      <UseAppBundle Value="False"/>
+      <ResourceType Value="res"/>
+    </General>
+    <BuildModes>
+      <Item Name="Default" Default="True"/>
+    </BuildModes>
+    <PublishOptions>
+      <Version Value="2"/>
+      <UseFileFilters Value="True"/>
+    </PublishOptions>
+    <RunParams>
+      <FormatVersion Value="2"/>
+    </RunParams>
+    <Units>
+      <Unit>
+        <Filename Value="demo2.lpr"/>
+        <IsPartOfProject Value="True"/>
+      </Unit>
+    </Units>
+  </ProjectOptions>
+  <CompilerOptions>
+    <Version Value="11"/>
+    <Target>
+      <Filename Value="demo2"/>
+    </Target>
+    <SearchPaths>
+      <IncludeFiles Value="$(ProjOutDir)"/>
+      <OtherUnitFiles Value="../src"/>
+      <UnitOutputDirectory Value="lib/$(TargetCPU)-$(TargetOS)"/>
+    </SearchPaths>
+  </CompilerOptions>
+  <Debugging>
+    <Exceptions>
+      <Item>
+        <Name Value="EAbort"/>
+      </Item>
+      <Item>
+        <Name Value="ECodetoolError"/>
+      </Item>
+      <Item>
+        <Name Value="EFOpenError"/>
+      </Item>
+    </Exceptions>
+  </Debugging>
+</CONFIG>

+ 48 - 0
packages/fcl-mustache/examples/demo2.lpr

@@ -0,0 +1,48 @@
+{ Demo for mustache engine with database context
+
+  Copyright (C) 2021 michael Van Canneyt [email protected]
+
+  This source is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as
+  published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version.
+
+  This code is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
+
+  A copy of the GNU General Public License is available on the World Wide Web at <http://www.gnu.org/copyleft/gpl.html>. You can
+  also obtain it by writing to the Free Software Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1335, USA.
+}
+
+program demo2;
+
+uses csvdataset, fpmustache, fpdbmustache;
+
+Const
+  // Mock markdown table
+  Template =
+     '| name | age | '+sLineBreak+
+     '|------|------|'+sLineBreak+
+     '{{#family}}| {{name}} | {{age}} |'+sLineBreak+
+     '{{/family}}';
+
+Var
+  M : TMustache;
+  C : TMustacheDBContext;
+  D : TCSVDataset;
+
+begin
+  M:=TMustache.Create(Nil);
+  try
+    D:=TCSVDataset.Create(Nil);
+    D.CSVOptions.FirstLineAsFieldNames:=True;
+    D.LoadFromFile('family.csv');
+    C:=TMustacheDBContext.Create(Nil);
+    C.AddDataset(D,'family');
+    M.Template:=Template;
+    Writeln(M.Render(C));
+  finally
+    M.Free;
+    D.Free;
+    C.Free;
+  end;
+end.
+

+ 7 - 0
packages/fcl-mustache/examples/family.csv

@@ -0,0 +1,7 @@
+name,age
+Father,30
+Mother,29
+Grandfather,62
+GrandMother,61
+Child 1,2
+Child 2,4

+ 10 - 0
packages/fcl-mustache/examples/family.json

@@ -0,0 +1,10 @@
+{
+  "data" : [
+   { "name" : "Father", "age": 30 },
+   { "name" : "Mother", "age": 29 },
+   { "name" : "Grandfather", "age": 62 },
+   { "name" : "GrandMother", "age": 61 },
+   { "name" : "Child 1", "age": 2 },
+   { "name" : "Child 2", "age": 4 }
+  ]
+}

+ 22 - 0
packages/fcl-mustache/examples/family.tmpl

@@ -0,0 +1,22 @@
+<html>
+  <head>
+    <title>{{title}}</title>
+  </head>
+    <body>
+      <h1>Family members</h1>
+      <table>
+        <thead>
+          <tr>
+            <th>Name</th><th>Age</th>
+          </tr>
+        </thead>
+        <tbody>
+{{#data}}
+        <tr>  
+          <td>{{name}}</td><td>{{age}}</td>
+        </tr> 
+{{/data}}
+        </tbody>
+      </table>
+    </body>
+</html>

+ 57 - 0
packages/fcl-mustache/examples/mustache.lpi

@@ -0,0 +1,57 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<CONFIG>
+  <ProjectOptions>
+    <Version Value="12"/>
+    <General>
+      <Flags>
+        <MainUnitHasCreateFormStatements Value="False"/>
+        <MainUnitHasTitleStatement Value="False"/>
+        <MainUnitHasScaledStatement Value="False"/>
+      </Flags>
+      <SessionStorage Value="InProjectDir"/>
+      <Title Value="Mustache Templater"/>
+      <UseAppBundle Value="False"/>
+      <ResourceType Value="res"/>
+    </General>
+    <BuildModes>
+      <Item Name="Default" Default="True"/>
+    </BuildModes>
+    <PublishOptions>
+      <Version Value="2"/>
+      <UseFileFilters Value="True"/>
+    </PublishOptions>
+    <RunParams>
+      <FormatVersion Value="2"/>
+    </RunParams>
+    <Units>
+      <Unit>
+        <Filename Value="mustache.lpr"/>
+        <IsPartOfProject Value="True"/>
+      </Unit>
+    </Units>
+  </ProjectOptions>
+  <CompilerOptions>
+    <Version Value="11"/>
+    <Target>
+      <Filename Value="mustache"/>
+    </Target>
+    <SearchPaths>
+      <IncludeFiles Value="$(ProjOutDir)"/>
+      <OtherUnitFiles Value="../src"/>
+      <UnitOutputDirectory Value="lib/$(TargetCPU)-$(TargetOS)"/>
+    </SearchPaths>
+  </CompilerOptions>
+  <Debugging>
+    <Exceptions>
+      <Item>
+        <Name Value="EAbort"/>
+      </Item>
+      <Item>
+        <Name Value="ECodetoolError"/>
+      </Item>
+      <Item>
+        <Name Value="EFOpenError"/>
+      </Item>
+    </Exceptions>
+  </Debugging>
+</CONFIG>

+ 207 - 0
packages/fcl-mustache/examples/mustache.lpr

@@ -0,0 +1,207 @@
+program mustache;
+
+{$mode objfpc}{$H+}
+
+uses
+  {$IFDEF UNIX}
+  cthreads,
+  {$ENDIF}
+  Classes, SysUtils, CustApp, strutils, fpjson, jsonparser, csvdataset, fpMustache, fpexmustache, fpdbmustache, iostream;
+
+type
+
+  { TMustacheApplication }
+
+  TMustacheApplication = class(TCustomApplication)
+  private
+    FTemplate : TMustacheString;
+    FJSON : TJSONStringType;
+    FCSV: TCSVDataset;
+    FPartials,
+    FDefines : TStrings;
+    FAllowExpressions : Boolean;
+    Foutput,
+    FSection,
+    FRootPath : String;
+    procedure DoGetDefine(const aName: TMustacheString; var aHandled: Boolean;
+      var aValue: TMustacheString);
+    procedure ProcessOptions;
+    Procedure Createoutput;
+    procedure Usage(ErrorMsg: String);
+  protected
+    procedure DoRun; override;
+
+  public
+    constructor Create(TheOwner: TComponent); override;
+    destructor Destroy; override;
+  end;
+
+{ TMustacheApplication }
+
+procedure TMustacheApplication.Usage(ErrorMsg : String);
+
+begin
+  If ErrorMsg<>'' then
+    Writeln('Error : ',ErrorMsg);
+  Writeln('Usage : mustache [options]');
+  Writeln('Where options is one or more of:');
+  writeln('-c --csv=FILE             Use a CSV file as data source. First line must contain column names.');
+  writeln('-d --define=name=value    Define fixed value.');
+  writeln('-e --expressions          Allow expressions.');
+  writeln('-h --help                 This message.');
+  writeln('-j --json=JSON            Use JSON as data source. @FILENAME will read JSON from file (UTF8).');
+  writeln('-o --output=FILE          output file to write output to. If empty, stdout is assumed.');
+  writeln('-p --partial=name=PARTIAL Register partial. @FILENAME reads partial from file.');
+  writeln('-r --root=PATH            Register variables at root path PATH for expression engine');
+  writeln('-s --section=SECTIOn    Section name for CSV data');
+  writeln('-t --template=TEMPLATE    Use TEMPLATE as data source. @FILENAME will read template from file (UTF8). Required.');
+  Halt(Ord(ErrorMsg<>''));
+end;
+
+procedure TMustacheApplication.ProcessOptions;
+
+  Function StringOrFile(S : String) : UTF8String;
+
+  begin
+    if Copy(S,1,1)<>'@' then
+      Result:=S
+    else
+      With TFileStream.Create(Copy(S,2,Length(S)-1),fmOpenRead or fmShareDenyNone) do
+        try
+          SetLength(Result,Size);
+          ReadBuffer(Result[1],Size);
+        finally
+          Free;
+        end;
+  end;
+
+Var
+  S : String;
+
+begin
+  if Not HasOption('t','template') then
+    Raise Exception.Create('Need a template');
+  if HasOption('c','csv') and HasOption('j','json')  then
+    Raise Exception.Create('Cannot specify both JSON or CSV');
+  FTemplate:=StringOrFile(GetOptionValue('t','template'));
+  if HasOption('j','json') then
+    FJSON:=StringOrFile(GetOptionValue('j','json'))
+  else if HasOption('c','csv') then
+    begin
+    FCSV:=TCSVDataset.Create(Self);
+    FCSV.FileName:=GetOptionValue('c','csv');
+    FCSV.CSVOptions.FirstLineAsFieldNames:=True;
+    FCSV.Open;
+    end;
+  for S in GetOptionValues('d','define') do
+    FDefines.Add(S);
+  for S in GetOptionValues('p','partial') do
+    FPartials.Add(ExtractWord(1,S,['='])+'='+StringOrFile(ExtractWord(2,S,['='])));
+  FAllowExpressions:=HasOption('e','expressions');
+  FRootPath:=GetOptionValue('r','root');
+  FSection:=GetOptionValue('s','section');
+  if FSection='' then
+    FSection:='data';
+  Foutput:=GetOptionValue('o','output');
+end;
+
+procedure TMustacheApplication.DoGetDefine(const aName: TMustacheString;
+  var aHandled: Boolean; var aValue: TMustacheString);
+
+Var
+  Idx : Integer;
+
+begin
+  Writeln('Getting define ',aName);
+  Idx:=FDefines.IndexOfName(aName);
+  aHandled:=Idx<>-1;
+  if aHandled then
+    aValue:=FDefines.ValueFromIndex[Idx]
+  else
+    aValue:='';
+end;
+
+procedure TMustacheApplication.DoRun;
+var
+  ErrorMsg: String;
+begin
+  Terminate;
+  // quick check parameters
+  ErrorMsg:=CheckOptions('het:j:c:d:o:r:', ['help','template','json','csv','define','output','expressions','root']);
+  if (ErrorMsg<>'') or HasOption('h','help') then
+    Usage(ErrorMsg);
+  ProcessOptions;
+  CreateOutput;
+end;
+
+procedure TMustacheApplication.CreateOutput;
+
+Var
+  M : TMustache;
+  C : TMustacheContext;
+  O : TStream;
+  S : TMustacheString;
+
+begin
+  O:=Nil;
+  M:=Nil;
+  C:=Nil;
+  try
+    if FAllowExpressions then
+      M:=TMustache.Create(Self)
+    else
+      begin
+      M:=TMustacheExpr.Create(Self);
+      if (FRootPath<>'') and (FJSON<>'') then
+        TMustacheExpr(M).RegisterVariables(FJSON,FRootPath,True);
+      end;
+    M.Partials:=FPartials;
+    if Assigned(FCSV) then
+      begin
+      C:=TMustacheDBContext.Create(@DoGetDefine);
+      TMustacheDBContext(C).AddDataset(FCSV,FSection);
+      end
+    else if (FJSON<>'') then
+      C:=TMustacheJSONContext.Create(GetJSON(FJSON),@DoGetDefine)
+    else
+      C:=TMustacheContext.Create(@DoGetDefine);
+    if Foutput<>'' then
+      O:=TFileStream.Create(Foutput,fmCreate)
+    else
+      O:=TIOStream.Create(iosOutput);
+    M.Template:=FTemplate;
+    S:=M.Render(C);
+    O.WriteBuffer(S[1],Length(S));
+  finally
+    O.Free;
+    C.Free;
+    M.Free;
+  end;
+end;
+
+constructor TMustacheApplication.Create(TheOwner: TComponent);
+begin
+  inherited Create(TheOwner);
+  FPartials:=TStringList.Create;
+  FDefines:=TStringList.Create;
+  StopOnException:=True;
+end;
+
+destructor TMustacheApplication.Destroy;
+begin
+  FreeAndNil(FPartials);
+  FreeAndNil(FDefines);
+  FreeAndNil(FCSV);
+  inherited Destroy;
+end;
+
+
+var
+  Application: TMustacheApplication;
+begin
+  Application:=TMustacheApplication.Create(nil);
+  Application.Title:='Mustache Templater';
+  Application.Run;
+  Application.Free;
+end.
+

+ 49 - 0
packages/fcl-mustache/fpmake.pp

@@ -0,0 +1,49 @@
+{$ifndef ALLPACKAGES}
+{$mode objfpc}{$H+}
+program fpmake;
+
+uses fpmkunit;
+
+Var
+  P : TPackage;
+  T : TTarget;
+begin
+  With Installer do
+    begin
+{$endif ALLPACKAGES}
+    P:=AddPackage('fcl-mustache');
+    P.ShortName:='mustache';
+
+{$ifdef ALLPACKAGES}
+    P.Directory:=ADirectory;
+{$endif ALLPACKAGES}
+
+    P.Author := 'Michael Van Canneyt';
+    P.License := 'LGPL with modification, ';
+    P.HomepageURL := 'www.freepascal.org';
+    P.Email := '';
+    P.Description := 'Mustache templates for FPC';
+    P.NeedLibC:= false;
+    P.SourcePath.Add('src');
+    P.OSes:=P.OSes-[embedded,win16,msdos,nativent,macosclassic,palmos,zxspectrum,msxdos,amstradcpc,sinclairql,wasi];
+    if Defaults.CPU=jvm then
+      P.OSes := P.OSes - [java,android];
+
+    P.Dependencies.Add('rtl-objpas');
+    P.Dependencies.Add('fcl-base');
+    P.Dependencies.Add('fcl-json');
+    P.Dependencies.Add('fcl-db');
+    P.Version:='3.3.1';
+    T:=P.Targets.AddUnit('src/fpmustache.pp');
+      T.ResourceStrings:=true;
+    T:=P.Targets.AddUnit('src/fpdbmustache.pp');
+      T.ResourceStrings:=true;
+      T.Dependencies.AddUnit('fpmustache');
+    T:=P.Targets.AddUnit('src/fpexmustache.pp');
+      T.ResourceStrings:=true;
+      T.Dependencies.AddUnit('fpmustache');
+{$ifndef ALLPACKAGES}
+    Run;
+    end;
+end.
+{$endif ALLPACKAGES}

+ 268 - 0
packages/fcl-mustache/src/fpdbmustache.pp

@@ -0,0 +1,268 @@
+{
+    This file is part of the Free Pascal Run time library.
+    Copyright (c) 2021 by Michael Van Canneyt ([email protected])
+
+    This file contains a Mustache DB context, getting data from a dataset
+
+    See the File COPYING.FPC, included in this distribution,
+    for details about the copyright.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+
+ **********************************************************************}
+
+unit fpdbmustache;
+
+{$mode ObjFPC}{$H+}
+
+interface
+
+uses
+  Classes, SysUtils, db, fpMustache;
+
+Type
+
+  { TDatasetCollectionItem }
+
+  TDatasetCollectionItem = Class(TCollectionItem)
+  private
+    FDataset: TDataSet;
+    FSection: String;
+  Public
+    Property Dataset : TDataSet Read FDataset Write FDataset;
+    Property SectionName : String Read FSection Write FSection;
+  end;
+
+  TDatasetCollection = Class(TCollection)
+  private
+    function GetDS(aIndex : Integer): TDatasetCollectionItem;
+  Public
+    Function IndexOfDataset(aDataset : TDataset) : Integer;
+    Function IndexOfSection(aSection : String) : Integer;
+    Property Datasets[aIndex : Integer] : TDatasetCollectionItem Read GetDS; default;
+  end;
+
+  { TMustacheDBContext }
+
+  TMustacheDBContext = Class(TMustacheContext)
+  Private
+    Type
+      TPair = Record
+        atStart : Boolean;
+        Value : TDataset;
+      end;
+  Private
+    FStack : Array of TPair;
+    FCount : Integer;
+    FStaticValues: TStrings;
+    FDatasets : TDatasetCollection;
+    Function FindField(Const aName : TMustacheString) : TField;
+    function GetDataset(aIndex : Integer): TDatasetCollectionItem;
+    function GetDatasetCount: INteger;
+    procedure SetStaticValues(AValue: TStrings);
+  Public
+    Constructor Create(aCallback : TGetTextValueEvent); override;
+    Destructor destroy; override;
+    Procedure Clear;
+    Function MoveNextSectionItem(Const aName : TMustacheString) : Boolean; override;
+    Function PushSection(Const aName : TMustacheString) : TMustacheSectionType; override;
+    Procedure PopSection(Const aName : TMustacheString); override;
+    Function GetTextValue(Const aName : TMustacheString) : TMustacheString; override;
+    Procedure AddDataset(aDataset : TDataset; aSectionName : String = '');
+    Procedure RemoveDataset(aDataset : TDataset);
+    Property StaticValues : TStrings Read FStaticValues Write SetStaticValues;
+    Property Datasets[aIndex : Integer] : TDatasetCollectionItem Read GetDataset;
+    Property DatasetCount : INteger Read GetDatasetCount;
+  end;
+
+implementation
+
+uses StrUtils;
+
+Resourcestring
+  SErrPopSectionNoPush = 'PopSection %s without push';
+  SErrDatasetNameEmpty = 'Dataset name and section cannot both be empty';
+  SErrDatasetEmpty = 'Dataset is Nil';
+  SErrDuplicateDataSetName = 'Duplicate dataset name: %s';
+
+{ TMustacheDBContext }
+
+function TMustacheDBContext.FindField(const aName: TMustacheString): TField;
+
+Var
+  aCount : Integer;
+
+begin
+  Result:=Nil;
+  aCount:=FCount-1;
+  While (Result=Nil) and (aCount>=0) do
+    begin
+    Result:=FStack[aCount].Value.FieldByName(aName);
+    Dec(aCount);
+    end;
+end;
+
+function TMustacheDBContext.GetDataset(aIndex : Integer
+  ): TDatasetCollectionItem;
+begin
+  Result:=FDatasets[aIndex];
+end;
+
+function TMustacheDBContext.GetDatasetCount: INteger;
+begin
+  Result:=FDatasets.Count;
+end;
+
+procedure TMustacheDBContext.SetStaticValues(AValue: TStrings);
+begin
+  if FStaticValues=AValue then Exit;
+  FStaticValues.Assign(AValue);
+end;
+
+constructor TMustacheDBContext.Create(aCallback: TGetTextValueEvent);
+begin
+  inherited Create(aCallback);
+  FDatasets:=TDatasetCollection.Create(TDatasetCollectionItem);
+  FStaticValues:=TStringList.Create;
+  SetLength(FStack,JSONListGrowCount);
+  FCount:=0;
+end;
+
+destructor TMustacheDBContext.destroy;
+begin
+  FreeAndNil(FStaticValues);
+  FreeAndNil(FDatasets);
+  inherited destroy;
+end;
+
+procedure TMustacheDBContext.Clear;
+begin
+  FStaticValues.Clear;
+  FDatasets.Clear;
+end;
+
+function TMustacheDBContext.MoveNextSectionItem(const aName: TMustacheString
+  ): Boolean;
+begin
+  if FStack[FCount-1].atStart then
+    FStack[FCount-1].atStart:=False
+  else
+    FStack[FCount-1].Value.Next;
+  Result:=Not FStack[FCount-1].Value.EOF;
+end;
+
+function TMustacheDBContext.PushSection(const aName: TMustacheString
+  ): TMustacheSectionType;
+
+Var
+  aDS : TDataset;
+  Idx : Integer;
+begin
+  Result:=mstNone;
+  Idx:=FDatasets.IndexOfSection(aName);
+  if Idx=-1 then
+    Exit;
+  aDS:=FDatasets[Idx].Dataset;
+  if aDS.IsEmpty then
+    exit;
+  if FCount=Length(FStack) then
+    SetLength(FStack,FCount+JSONListGrowCount);
+  FStack[FCount].Value:=aDS;
+  FStack[FCount].atStart:=True;
+  Inc(FCount,1);
+  Result:=mstList;
+end;
+
+procedure TMustacheDBContext.PopSection(const aName: TMustacheString);
+begin
+  if FCount<1 then
+    Raise EMustache.CreateFmt(SErrPopSectionNoPush,[aName]);
+  Dec(FCount,1);
+end;
+
+function TMustacheDBContext.GetTextValue(const aName: TMustacheString
+  ): TMustacheString;
+
+Var
+  F : TField;
+  idx : Integer;
+
+begin
+  F:=Nil;
+  if Pos('.',aName)=0 then
+    F:=FindField(aName)
+  else if WordCount(aName,['.'])=2 then
+    begin
+    Idx:=FDatasets.IndexOfSection(ExtractWord(1,aName,['.']));
+    if (Idx<>-1) then
+      F:=FDatasets[Idx].Dataset.FindField(ExtractWord(2,aName,['.']));
+    end;
+  If Assigned(F) then
+    Result:=F.AsString
+  else
+    begin
+    Idx:=FStaticValues.IndexOfName(aName);
+    if Idx<>-1 then
+      Result:=FStaticValues.ValueFromIndex[Idx]
+    else
+      Result:=Inherited GetTextValue(aName);
+    end;
+end;
+
+procedure TMustacheDBContext.AddDataset(aDataset: TDataset; aSectionName: String);
+
+Var
+  DCI : TDatasetCollectionItem;
+  aName : String;
+
+begin
+  aName:=aSectionName;
+  if aName='' then
+    aName:=aDataset.Name;
+  if aName='' then
+    raise EMustache.Create(SErrDatasetNameEmpty);
+  if aDataset=Nil then
+    raise EMustache.Create(SErrDatasetEmpty);
+  if FDatasets.IndexOfSection(aName)<>-1 then
+    raise EMustache.CreateFmt(SErrDuplicateDataSetName, [aName]);
+  DCI:=FDatasets.Add as TDatasetCollectionItem;
+  DCI.Dataset:=aDataset;
+  DCI.SectionName:=aName;
+end;
+
+procedure TMustacheDBContext.RemoveDataset(aDataset: TDataset);
+
+Var
+  Idx : Integer;
+
+begin
+  Idx:=FDatasets.IndexOfDataset(aDataset);
+  if Idx<>-1 then
+    FDatasets.Delete(Idx);
+end;
+
+{ TDatasetCollection }
+
+function TDatasetCollection.GetDS(aIndex : Integer): TDatasetCollectionItem;
+begin
+  Result:=Items[aIndex] as TDatasetCollectionItem;
+end;
+
+function TDatasetCollection.IndexOfDataset(aDataset: TDataset): Integer;
+begin
+  Result:=Count-1;
+  While (Result>=0) and (GetDS(Result).Dataset<>ADataset) do
+    Dec(Result);
+end;
+
+function TDatasetCollection.IndexOfSection(aSection: String): Integer;
+begin
+  Result:=Count-1;
+  While (Result>=0) and not SameText(GetDS(Result).SectionName,ASection) do
+    Dec(Result);
+end;
+
+end.
+

+ 399 - 0
packages/fcl-mustache/src/fpexmustache.pp

@@ -0,0 +1,399 @@
+{
+    This file is part of the Free Pascal Run time library.
+    Copyright (c) 2021 by Michael Van Canneyt ([email protected])
+
+    This file contains a Mustache descendent with FPExpr parser expression support
+
+    See the File COPYING.FPC, included in this distribution,
+    for details about the copyright.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+
+ **********************************************************************}
+
+unit fpexmustache;
+
+{$mode ObjFPC}{$H+}
+
+interface
+
+uses
+  Classes, fpexprpars, fpmustache, fpjson;
+
+Type
+
+  { TMustacheExprElement }
+
+  TMustacheExprElement = Class(TMustacheElement)
+  private
+    FNode: TFPExprNode;
+    FExpr : TMustacheString;
+  Protected
+    Procedure SetNode(aNode : TFPExprNode); virtual;
+    Function GetData : TMustacheString;override;
+    Procedure SetData(const aValue : TMustacheString) ; override;
+  Public
+    Destructor Destroy; override;
+    Procedure Render(aContext : TMustacheContext; aOutput : TMustacheOutput; const aPrefix : String = ''; aLast : Boolean = False); override;
+    Property Node : TFPExprNode Read FNode;
+  end;
+
+  { TMustacheExprParser }
+
+  TMustacheExprParser = class(TMustacheParser)
+  private
+    FExprEnd: Char;
+    FExprParser: TFPExpressionParser;
+    FExprStart: Char;
+  Protected
+    function CreateDefault(aParent: TMustacheElement; aPosition: Integer; const aName: String): TMustacheElement; override;
+  Public
+    Constructor Create(aTemplate : TMustacheString = '';aStart: TMustacheString='';aStop: TMustacheString = ''); override;
+    // Default [
+    Property ExprStart : Char Read FExprStart Write FExprStart;
+    // Default ]
+    Property ExprEnd : Char Read FExprEnd Write FExprEnd;
+    // Our instance
+    Property ExprParser : TFPExpressionParser Read FExprParser Write FExprParser;
+  end;
+
+  { TMustacheExpr }
+
+  TMustacheExpr = Class(TMustache)
+  private
+    FExprEndChar: String;
+    FExpressionParser: TFPExpressionParser;
+    FExprStartChar: String;
+    FCurrentContext : TMustacheContext;
+    function GetResultType(aValue: TJSONData): TResultType;
+    procedure SetExprEndChar(AValue: String);
+    procedure SetExpressionParser(AValue: TFPExpressionParser);
+    procedure SetExprStartChar(AValue: String);
+    function DoGetExpressionParser : TFPExpressionParser;
+  Protected
+    procedure DoGetVariable(var Result: TFPExpressionResult; ConstRef  AName: ShortString); virtual;
+    Procedure Notification(AComponent: TComponent; Operation: TOperation); override;
+    Function CreateParser(aTemplate: TMustacheString): TMustacheParser; override;
+    function GetExpressionParser(aOwner : TComponent): TFPExpressionParser; virtual;
+  Public
+    Constructor Create(aOwner : TComponent); override;
+    Procedure Render(aContext : TMustacheContext; aOutput : TMustacheOutput); override; overload;
+    // Register variables from JSON in the expression engine.
+    // If UseEvent is true, the variables will be retrieved while parsing with an event.
+    // If UseEvent is false, the variables will be registered as static values.
+    Procedure RegisterVariables (aContext : TMustacheJSONContext; aPath : TJSONStringType = ''; UseEvent : Boolean = True);
+    Procedure RegisterVariables (aJSON : String; aPath : TJSONStringType = ''; UseEvent : Boolean = True);
+    Procedure RegisterVariables (aJSON : TJSONObject; aPath : TJSONStringType = ''; UseEvent : Boolean = True);
+  Published
+    // Default [
+    Property ExprStartChar : String Read FExprStartChar Write SetExprStartChar;
+    // Default ]
+    Property ExprEndChar : String Read FExprEndChar Write SetExprEndChar;
+    // An expression parser instance. If none is specified, then a default is created.
+    Property ExpressionParser : TFPExpressionParser Read DoGetExpressionParser Write SetExpressionParser;
+  end;
+
+  { TMustacheExpressionParser }
+
+  TMustacheExpressionParser = class(TFPExpressionParser)
+  end;
+
+implementation
+
+uses sysutils;
+
+Resourcestring
+  SErrLengthStartMustBe1 = 'Length expression start delimiter must be 1';
+  SErrLengthEndMustBe1 = 'Length expression end delimiter must be 1';
+
+{ TMustacheExprElement }
+
+procedure TMustacheExprElement.SetNode(aNode: TFPExprNode);
+begin
+  FNode:=aNode;
+end;
+
+function TMustacheExprElement.GetData: TMustacheString;
+begin
+  Result:=FExpr;
+end;
+
+procedure TMustacheExprElement.SetData(const aValue: TMustacheString);
+begin
+  FExpr:=aValue;
+end;
+
+procedure TMustacheExprElement.Render(aContext: TMustacheContext;
+  aOutput: TMustacheOutput; const aPrefix: String; aLast: Boolean);
+
+Var
+  Res : TFPExpressionResult;
+  S : TMustacheString;
+
+begin
+  Res:=Node.NodeValue;
+  case Res.ResultType of
+    rtString   : S:=Res.ResString;
+    rtBoolean  : S:=BoolToStr(Res.ResBoolean,True);
+    rtInteger  : S:=IntToStr(Res.ResInteger);
+    rtFloat    : S:=FormatFloat('0.0#######',Res.ResFloat);
+    rtCurrency : S:=CurrToStr(Res.ResCurrency);
+    rtDateTime : S:=DateTimeToStr(Res.ResDateTime);
+  end;
+  aOutput.Output(aPrefix+S);
+end;
+
+destructor TMustacheExprElement.Destroy;
+begin
+  FreeAndNil(FNode);
+  inherited Destroy;
+end;
+
+{ TMustacheExprParser }
+
+function TMustacheExprParser.CreateDefault(aParent: TMustacheElement;
+  aPosition: Integer; const aName: String): TMustacheElement;
+
+Var
+  L : Integer;
+  N : TFPExprNode;
+
+begin
+  N:=Nil;
+  L:=Length(aName);
+  If (aName[1]=FExprStart) and (aName[L]=FExprEnd) then
+    begin
+    Result:=TMustacheExprElement.Create(metVariable,aParent,aPosition);
+    Result.Data:=Copy(aName,2,L-2);
+    ExprParser.Expression:=Result.Data;
+    ExprParser.ExtractNode(N);
+    TMustacheExprElement(Result).SetNode(N);
+    aParent.AddChild(Result);
+    end
+  else
+    Result:=Inherited CreateDefault(aParent,aPosition,aName);
+end;
+
+constructor TMustacheExprParser.Create(aTemplate: TMustacheString;
+  aStart: TMustacheString; aStop: TMustacheString);
+begin
+  inherited Create(aTemplate, aStart, aStop);
+  FExprStart:='[';
+  FExprEnd:=']';
+end;
+
+{ TMustacheExpr }
+
+procedure TMustacheExpr.SetExprEndChar(AValue: String);
+begin
+  if FExprEndChar=AValue then Exit;
+  if Length(aValue)<>1 then
+    EMustache.Create(SErrLengthStartMustBe1);
+  FExprEndChar:=AValue;
+end;
+
+function TMustacheExpr.GetExpressionParser(aOwner : TComponent): TFPExpressionParser;
+begin
+  Result:=TMustacheExpressionParser.Create(AOwner);
+end;
+
+procedure TMustacheExpr.SetExpressionParser(AValue: TFPExpressionParser);
+
+begin
+  if FExpressionParser=AValue then Exit;
+  If assigned(FExpressionParser) then
+    FExpressionParser.RemoveFreeNotification(Self);
+  FExpressionParser:=AValue;
+  If assigned(FExpressionParser) then
+    FExpressionParser.FreeNotification(Self);
+end;
+
+procedure TMustacheExpr.SetExprStartChar(AValue: String);
+begin
+  if FExprStartChar=AValue then Exit;
+  if Length(aValue)<>1 then
+    EMustache.Create(SErrLengthEndMustBe1);
+  FExprStartChar:=AValue;
+end;
+
+function TMustacheExpr.DoGetExpressionParser: TFPExpressionParser;
+begin
+  if FExpressionParser=Nil then
+    begin
+    FExpressionParser:=GetExpressionParser(Self);
+    FExpressionParser.SetSubComponent(True);
+    FExpressionParser.FreeNotification(Self);
+    end;
+  Result:=FExpressionParser;
+end;
+
+procedure TMustacheExpr.Notification(AComponent: TComponent;
+  Operation: TOperation);
+begin
+  inherited Notification(AComponent, Operation);
+  if (Operation=opRemove) and (aComponent=FExpressionParser) then
+    FExpressionParser:=Nil;
+end;
+
+function TMustacheExpr.CreateParser(aTemplate: TMustacheString ): TMustacheParser;
+
+Var
+  Exp : TMustacheExprParser;
+
+begin
+  Exp:=TMustacheExprParser.Create(aTemplate);
+  Exp.ExprParser:=Self.ExpressionParser;
+  Result:=Exp;
+end;
+
+constructor TMustacheExpr.Create(aOwner: TComponent);
+begin
+  inherited Create(aOwner);
+  DoGetExpressionParser;
+end;
+
+procedure TMustacheExpr.Render(aContext: TMustacheContext; aOutput: TMustacheOutput);
+
+begin
+  FCurrentContext:=aContext;
+  try
+    inherited Render(aContext, aOutput);
+  finally
+    FCurrentContext:=nil;
+  end;
+end;
+
+procedure TMustacheExpr.DoGetVariable(var Result: TFPExpressionResult; ConstRef
+  AName: ShortString);
+
+Var
+  S : TMustacheString;
+  V : Double;
+  C : Integer;
+
+begin
+  If not Assigned(FCurrentContext) then
+    case result.ResultType of
+      rtInteger : Result.ResInteger:=0;
+      rtDateTime : Result.ResDateTime:=0.0;
+      rtString : Result.ResString:='';
+      rtFloat: Result.ResFloat:=0.0;
+      rtCurrency: Result.ResCurrency:=0.0;
+      rtBoolean: Result.ResBoolean:=False;
+    end
+  else
+    begin
+    S:=FCurrentContext.GetTextValue(aName);
+    case result.ResultType of
+      rtInteger : Result.ResInteger:=StrToInt64Def(S,0);
+      rtDateTime : if Not TryStrToDateTime(S,Result.ResDateTime) then
+                     Result.ResDateTime:=0.0;
+      rtString : Result.ResString:=S;
+      rtFloat: begin
+               Val(S,V,C);
+               if C<>0 then
+                 Result.ResFloat:=0.0
+               else
+                 Result.ResFloat:=V;
+               end;
+      rtCurrency:
+               begin
+               Val(S,V,C);
+               if (C<>0) then
+                 Result.ResCurrency:=0.0
+               else
+                 Result.ResCurrency:=V;
+               end;
+      rtBoolean: Result.ResBoolean:=StrToBoolDef(S,False);
+    end;
+    end;
+end;
+
+function TMustacheExpr.GetResultType(aValue: TJSONData): TResultType;
+
+begin
+  Case aValue.JSONType of
+    jtBoolean : Result:=rtBoolean;
+    jtString,
+    jtArray,
+    jtObject,
+    jtNull : Result:=rtString;
+    jtNumber :
+       begin
+       Case TJSONNumber(aValue).NumberType of
+         ntFloat : Result:=rtFloat;
+         ntInteger,
+         ntInt64 : Result:=rtInteger;
+         ntQWord : Raise EMustache.Create('Unsupported JSON type');
+       end;
+       end;
+  end;
+end;
+
+procedure TMustacheExpr.RegisterVariables(aContext: TMustacheJSONContext;
+  aPath: TJSONStringType; UseEvent: Boolean);
+
+begin
+  RegisterVariables(aContext.RootData as TJSONObject,aPath,UseEvent);
+end;
+
+procedure TMustacheExpr.RegisterVariables(aJSON: String;
+  aPath: TJSONStringType; UseEvent: Boolean);
+
+Var
+  aData : TJSONData;
+  aObj : TJSONObject absolute aData;
+
+
+begin
+  aData:=getJSON(aJSON,True);
+  try
+    if aData is TJSONObject then
+      RegisterVariables(aObj,aPath,useEvent)
+    else
+      Raise EMustache.Create('Invalid JSON data to register variables');
+  finally
+    aData.Free;
+  end;
+end;
+
+procedure TMustacheExpr.RegisterVariables(aJSON: TJSONObject; aPath: TJSONStringType; UseEvent: Boolean);
+
+Var
+  aData,aValue : TJSONData;
+  aEnum : TJSONEnum;
+  aKey : TJSONStringType;
+  rt : TResultType;
+  aParser : TFPExpressionParser;
+
+begin
+  aParser:=ExpressionParser;
+  aData:=aJSON.FindPath(aPath);
+  if aData is TJSONObject then
+    for aEnum in aData do
+      begin
+      aKey:=aEnum.Key;
+      aValue:=aEnum.Value;
+      rt:=GetResultType(aValue);
+      if UseEvent then
+        aParser.Identifiers.AddVariable(aKey,rt,@DoGetVariable)
+      else
+        case rt of
+          rtBoolean: aParser.Identifiers.AddBooleanVariable(aKey,aValue.AsBoolean);
+          rtFloat: aParser.Identifiers.AddFloatVariable(aKey,aValue.AsFloat);
+          rtInteger: aParser.Identifiers.AddIntegerVariable(aKey,aValue.AsInteger);
+          rtString: Case aValue.JSONType of
+                      jtNull: aParser.Identifiers.AddStringVariable(aKey,'');
+                      jtArray,
+                      jtObject: aParser.Identifiers.AddStringVariable(aKey, aValue.AsJSON);
+                    else
+                      aParser.Identifiers.AddStringVariable(aKey,aValue.AsString);
+                    end;
+          end;
+      end;
+end;
+
+end.
+

+ 1340 - 0
packages/fcl-mustache/src/fpmustache.pp

@@ -0,0 +1,1340 @@
+{
+    This file is part of the Free Pascal Run time library.
+    Copyright (c) 2021 by Michael Van Canneyt ([email protected])
+
+    This file contains a Mustache parser and renderer.
+
+    See the File COPYING.FPC, included in this distribution,
+    for details about the copyright.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+
+ **********************************************************************}
+
+unit fpmustache;
+
+{$mode objfpc}{$H+}
+{$WARN 5024 off : Parameter "$1" not used}
+interface
+
+uses
+  Classes, SysUtils, fpjson;
+
+Type
+  EMustache = Class(Exception);
+
+  TMustacheString = UTF8String;
+  TMustacheChar = AnsiChar;
+  TMustacheContext = class;
+
+  TMustacheOutput = Class(TObject)
+  Public
+    // add atext to output
+    Procedure Output(Const aText : TMustacheString); virtual; abstract;
+    Procedure Reset; virtual; abstract;
+  end;
+
+  { TMustacheStringOutput }
+
+  TMustacheStringOutput = Class(TMustacheOutput)
+  private
+    FData: TMustacheString;
+  Public
+    // Override
+    Procedure Output(Const aText : TMustacheString); override;
+    Procedure Reset; override;
+    // The rendered TMustacheString
+    Property Data : TMustacheString Read FData;
+  end;
+
+  { TMustacheElement }
+
+  TMustacheElementType = (metRoot,metComment,metText,metVariable,metSection,metInvertedSection,metPartial);
+
+  TMustacheElement = Class(TObject)
+  private
+    FPosition: Integer;
+    FType : TMustacheElementType;
+    FParent : TMustacheElement;
+  Protected
+    function GetCount: Integer; virtual;
+    function GetElement(aIndex : Integer): TMustacheElement; virtual;
+    Function GetData : TMustacheString ; virtual; abstract;
+    Procedure SetData(Const aData : TMustacheString) ; virtual; abstract;
+    Function GetPrefix : TMustacheString; virtual;
+    Procedure SetPrefix (aValue : TMustacheString); virtual;
+    Procedure Dump(aList : Tstrings; aIndent : TMustacheString; aDumpChildren : Boolean = true); virtual;
+  Public
+    Constructor Create(aType : TMustacheElementType; aParent : TMustacheElement;aPosition : Integer); virtual;
+    // Add a child. Parent always owns child
+    Procedure AddChild(aChild : TMustacheElement); virtual;
+    // Render the text for this element
+    Procedure Render(aContext : TMustacheContext; aOutput : TMustacheOutput; const aPrefix : String = ''; aLast : Boolean = False); virtual; abstract;
+    // Position in template
+    Property Position : Integer Read FPosition;
+    // Parent element
+    Property Parent : TMustacheElement Read FParent;
+    // Access to children
+    Property Children[aIndex : Integer] : TMustacheElement Read GetElement;
+    // Child count
+    Property ChildCount : Integer Read GetCount;
+    // Element type
+    Property ElementType : TMustacheElementType Read FType;
+    // The data for this element. What this is, depends on the kind.
+    // etText : the text;
+    // etValue : the variable name
+    // etSection : the section name.
+    // etInvertedSection : the section name.
+    Property Data : TMustacheString Read GetData Write SetData;
+    // Whitespace prefix. Normally only used for partials
+    Property Prefix : TMustacheString Read GetPrefix Write SetPrefix;
+  end;
+  TMustacheElementClass = Class of TMustacheElement;
+  TMustacheElementArray = Array of TMustacheElement;
+
+  { TMustacheNamedElement }
+
+  TMustacheNamedElement = Class(TMustacheElement)
+  private
+    FName: TMustacheString;
+  Protected
+    Procedure SetData(Const aData : TMustacheString); override;
+    Function GetData : TMustacheString; override;
+  Public
+    Property Name : TMustacheString Read FName;
+  end;
+
+  { TMustacheParentElement }
+
+  TMustacheParentElement = Class(TMustacheNamedElement)
+  Private
+    FChildren : TMustacheElementArray;
+    FCount : Integer;
+  Protected
+    function GetElement(aIndex : Integer): TMustacheElement; override;
+    function GetCount : Integer; override;
+  Public
+    Destructor Destroy; override;
+    Procedure AddChild(aChild : TMustacheElement); override;
+    Procedure Render(aContext : TMustacheContext; aOutput : TMustacheOutput; const aPrefix : String = ''; aLast : Boolean = False); override;
+  end;
+
+
+  { TMustacheTextElement }
+
+  TMustacheTextElement = Class(TMustacheElement)
+  Private
+    FData : TMustacheString;
+  Protected
+    Procedure SetData(Const aData : TMustacheString) ; override;
+    Function GetData : TMustacheString; override;
+  Public
+    Procedure Render(aContext : TMustacheContext; aOutput : TMustacheOutput; const aPrefix : String = ''; aLast : Boolean = False); override;
+  end;
+
+  { TMustacheVariableElement }
+
+  TMustacheVariableElement = Class(TMustacheNamedElement)
+  private
+    FNoUnescape: Boolean;
+  Protected
+    Procedure SetData(Const aData : TMustacheString); override;
+  Public
+    Procedure Render(aContext : TMustacheContext; aOutput : TMustacheOutput; const aPrefix : String = ''; aLast : Boolean = False); override;
+    Property NoUnescape : Boolean Read FNoUnescape;
+  end;
+
+  { TMustacheSectionElement }
+
+  TMustacheSectionElement = Class(TMustacheParentElement)
+  Public
+    Procedure Render(aContext : TMustacheContext; aOutput : TMustacheOutput; const aPrefix : String = ''; aLast : Boolean = False); override;
+  end;
+
+  { TMustachePartialElement }
+
+  TMustachePartialElement = Class(TMustacheElement)
+  Private
+    FPrefix : TMustacheString;
+    FPartialName : TMustacheString;
+    FPartial : TMustacheElement;
+  Protected
+    Function GetData : TMustacheString ; override;
+    Procedure SetData(Const aData : TMustacheString) ; override;
+    Procedure Dump(aList : Tstrings; aIndent : TMustacheString; aDumpChildren : Boolean = true); override;
+    Function GetPrefix : TMustacheString; override;
+    Procedure SetPrefix (aValue : TMustacheString); override;
+  Public
+    Destructor Destroy; override;
+    Procedure AddChild(aChild: TMustacheElement); override;
+    Procedure Render(aContext : TMustacheContext; aOutput : TMustacheOutput; const aPrefix : String = ''; aLast : Boolean = False); override;
+    Property Partial : TMustacheElement Read FPartial;
+  end;
+
+  { TMustachePartialList }
+
+  TMustachePartialList = Class(TMustacheParentElement)
+  Public
+    Function FindPartial(aName : TMustacheString) : TMustacheElement;
+  end;
+
+  { TMustacheParser }
+  TGetTextValueEvent = Procedure (Const aName : TMustacheString; var aHandled : Boolean; var aValue : TMustacheString) of Object;
+
+  TMustacheParser = Class(TObject)
+  private
+    FStopTag: TMustacheString;
+    FStartTag: TMustacheString;
+    FTemplate: TMustacheString;
+    FOnGetPartial: TGetTextValueEvent;
+    FPartials: TMustachePartialList;
+    Class var DefaultTypes : Array[TMustacheElementType] of TMustacheElementClass;
+  Protected
+    // Called to create a default element for a {{ }} tag. By default creates a variable.
+    // Override this if you want to create additional elements.
+    function CreateDefault(aParent: TMustacheElement; aPosition: Integer; const aName: String): TMustacheElement; virtual;
+    // Create element for indicated type, must add it to parent.
+    // You can override this to provide customized behaviour.
+    function CreateElement(aType: TMustacheElementType; aParent: TMustacheElement; aPosition: Integer): TMustacheElement; virtual;
+    // Parse
+    Procedure DoParse(aParent : TMustacheElement; Const aTemplate, aStart, aStop : TMustacheString); virtual;
+    // Called to get the template of a partial. The template is parsed, and the result added to the partials list.
+    Function GetPartial(const aName : TMustacheString) : TMustacheString; virtual;
+    // Auxuliary functions for the peculiar whitespace handling of Mustache specs...
+    function EndsOnWhiteSpace(aElement: TMustacheElement): Boolean; virtual;
+    function GetEndingWhiteSpace(aElement: TMustacheElement): TMustacheString; virtual;
+    procedure ExtractStartStop(const aName: TMustacheString; out aStart, aStop: TMustacheString); virtual;
+    procedure TrimEndingWhiteSpace(aElement: TMustacheElement); virtual;
+  Public
+    // Create a new parser.
+    Constructor Create(aTemplate : TMustacheString = '';aStart: TMustacheString='';aStop: TMustacheString = ''); virtual;
+    // Set the default TMustacheElements for the descendents
+    Class procedure SetDefaultTypeClass(aType : TMustacheElementType; aClass: TMustacheElementClass);
+    // Parse the template and
+    Procedure Parse(aParent : TMustacheElement);
+    Function Parse : TMustacheElement;
+    // Will be used to hold partials. You must set this before calling Parse.
+    Property Partials : TMustachePartialList Read FPartials Write FPartials;
+    // The template created on startup
+    Property Template : TMustacheString Read FTemplate write FTemplate;
+    // The initial start tag marker, by default {{
+    Property StartTag : TMustacheString Read FStartTag Write FStartTag;
+    // The initial end tag marker, by default }}
+    Property StopTag : TMustacheString Read FStopTag Write FSTopTag;
+    // Event called to get the source of a partial.
+    Property OnGetPartial : TGetTextValueEvent Read FOnGetPartial Write FOnGetPartial;
+  end;
+
+  { TMustacheContext }
+  TMustacheSectionType = (mstNone,mstSingle,mstList);
+
+  TMustacheContext = Class(TObject)
+  Private
+    FCallback : TGetTextValueEvent;
+  Public
+    Constructor Create(aCallback : TGetTextValueEvent); virtual;
+    // Helper function to quote HTML
+    Class Function QuoteHTML(aString : TMustacheString) :TMustacheString; virtual;
+    // Move to next section item. aName is section name. Returns True if move successful
+    Function MoveNextSectionItem(Const aName : TMustacheString) : Boolean; virtual;
+    // Push a new section context with name aName.
+    Function PushSection(Const aName : TMustacheString) : TMustacheSectionType; virtual;
+    // Pop current section. aName is for verification.
+    Procedure PopSection(Const aName : TMustacheString); virtual;
+    // Return the value of a variable with name aName.
+    Function GetTextValue(Const aName : TMustacheString) : TMustacheString; virtual;
+  end;
+
+  { TMustacheJSONContext }
+
+  TMustacheJSONContext = Class(TMustacheContext)
+  Private
+    Type
+      TPair = Record
+        Index : Integer; // if array, index of current element.
+        Value : TJSONData;
+      end;
+  Private
+    FCurrentData: TJSONData;
+    FStack : Array of TPair;
+    FCount : Integer;
+    Function FindValue(Const aName : TMustacheString) : TJSONData;
+    function GetRootData: TJSONData;
+  Public
+    Constructor Create(aJSON : TJSONData; aCallback : TGetTextValueEvent); reintroduce;
+    Function MoveNextSectionItem(Const aName : TMustacheString) : Boolean; override;
+    Function PushSection(Const aName : TMustacheString) : TMustacheSectionType; override;
+    Procedure PopSection(Const aName : TMustacheString); override;
+    Function GetTextValue(Const aName : TMustacheString) : TMustacheString; override;
+    Property RootData : TJSONData read GetRootData;
+  end;
+
+  TMustache = Class(TComponent)
+  private
+    FCompiled: TMustacheElement;
+    FCompiledPartials: TMustachePartialList;
+    FOnGetValue: TGetTextValueEvent;
+    FPartials: TStrings;
+    FStartTag: TMustacheString;
+    FStopTag: TMustacheString;
+    FTemplate: TMustacheString;
+    procedure SetPartials(AValue: TStrings);
+    procedure SetStartTag(AValue: TMustacheString);
+    procedure SetStopTag(AValue: TMustacheString);
+    procedure SetTemplate(AValue: TMustacheString);
+  Protected
+    Procedure DoGetPartial(Const aName : TMustacheString; var aHandled : Boolean; var aValue : TMustacheString); virtual;
+    Procedure Reset; virtual;
+    Function CreatePartials : TMustachePartialList;
+    function CreateParser(aTemplate: TMustacheString): TMustacheParser; virtual;
+    Property Compiled : TMustacheElement Read FCompiled;
+    Property CompiledPartials : TMustachePartialList Read FCompiledPartials;
+  Public
+    Constructor Create(aOwner : TComponent); override;
+    Destructor Destroy; override;
+    Procedure Compile;
+    Procedure Dump(aList : Tstrings; aindent : TMustacheString); overload; virtual;
+    Function Dump: TMustacheString;overload;
+    Procedure Render(aContext : TMustacheContext; aOutput : TMustacheOutput); virtual; overload;
+    Function Render(aContext : TMustacheContext) : TMustacheString; overload;
+    Function Render(const aJSON : TJSONData) : TMustacheString; overload;
+    Function Render(const aJSON : TJSONStringType) : TMustacheString; overload;
+    Class function CreateMustache(aOwner: TComponent; aTemplate: TMustacheString): TMustache; virtual;
+    Class Function Render(aTemplate : TMustacheString; const aJSON : TJSONStringType) : TMustacheString;
+  Published
+    Property Template : TMustacheString Read FTemplate Write SetTemplate;
+    Property OnGetValue : TGetTextValueEvent Read FOnGetValue Write FOnGetValue;
+    Property StartTag : TMustacheString Read FStartTag Write SetStartTag;
+    Property StopTag : TMustacheString Read FStopTag Write SetStopTag;
+    Property Partials : TStrings Read FPartials Write SetPartials;
+  end;
+
+
+Const
+  ListGrowCount = 10;
+  JSONListGrowCount = 10;
+
+implementation
+
+uses TypInfo;
+
+
+
+Resourcestring
+  SErrNoChildForElement = 'Class %s does not support child elements.';
+  SErrInvalidIndex= '%s: Index %d is not in valid range [0..%d].';
+  SErrUnterminatedTag = 'Tag %s opened on position %d but not closed.';
+  SErrEmptyTag = 'Tag %s on position %d is empty.';
+  SErrSectionClose = 'Structural error: Section "%s" on position %d is closed by tag "%s" on position %d.';
+  SErrNotClosedSection = 'Structural error: Section "%s" on position %d is not closed.';
+  SErrNoSectionToClose = 'Structural error: Section "%s" on position %d was never opened.';
+  SErrInvalidDelimiter = 'Invalid set delimiter: %s';
+  SErrInvalidDelimiterValue = 'Invalid set delimiter %s value: %s in "%s"';
+  SErrNoPartials = 'No partials list';
+
+  // SErrPartialNotFound = 'Partial "%s" not found.';
+  SStartTag = 'Start';
+  SStopTag = 'Stop';
+
+{ TMustachePartialList }
+
+function TMustachePartialList.FindPartial(aName: TMustacheString ): TMustacheElement;
+
+Var
+  I : Integer;
+
+begin
+  Result:=Nil;
+  I:=ChildCount-1;
+  While (Result=Nil) and (I>=0) do
+    begin
+    Result:=Children[I];
+    If (Result.Data<>aName) then
+      Result:=Nil;
+    Dec(I);
+    end;
+end;
+
+{ TMustachePartialElement }
+
+function TMustachePartialElement.GetData: TMustacheString;
+begin
+  Result:=FPartialName;
+end;
+
+procedure TMustachePartialElement.SetData(const aData: TMustacheString);
+begin
+  FPartialName:=aData;
+end;
+
+procedure TMustachePartialElement.AddChild(aChild: TMustacheElement);
+begin
+  If (FPartial<>Nil) and (aChild<>Nil) then
+    Raise EMustache.Create('Cannot set partial twice');
+  FPartial:=aChild;
+end;
+
+procedure TMustachePartialElement.Dump(aList: Tstrings;
+  aIndent: TMustacheString; aDumpChildren: Boolean);
+begin
+  inherited Dump(aList, aIndent, aDumpChildren);
+  if Prefix<>'' then
+    aList[aList.Count-1]:=aList[aList.Count-1]+' Prefix: "'+Prefix+'"';
+end;
+
+function TMustachePartialElement.GetPrefix: TMustacheString;
+begin
+  Result:=FPrefix;
+end;
+
+procedure TMustachePartialElement.SetPrefix(aValue: TMustacheString);
+begin
+  FPrefix:=aValue;
+end;
+
+procedure TMustachePartialElement.Render(aContext: TMustacheContext;
+  aOutput: TMustacheOutput; const aPrefix : String = ''; aLast : Boolean = False);
+
+begin
+  FPartial.Render(aContext,aOutput,Prefix);
+end;
+
+destructor TMustachePartialElement.Destroy;
+begin
+  inherited Destroy;
+end;
+
+
+{ TMustache }
+
+function TMustache.CreateParser(aTemplate : TMustacheString): TMustacheParser;
+begin
+  Result:=TMustacheParser.Create(aTemplate);
+end;
+
+constructor TMustache.Create(aOwner: TComponent);
+begin
+  Inherited;
+  FPartials:=TStringList.Create;
+  FCompiledPartials:=CreatePartials;
+end;
+
+destructor TMustache.Destroy;
+begin
+  Reset;
+  FreeAndNil(FPartials);
+  FreeAndNil(FCompiledPartials);
+  inherited Destroy;
+end;
+
+procedure TMustache.Compile;
+
+Var
+  Parser : TMustacheParser;
+
+begin
+  Parser:=CreateParser(Self.Template);
+  try
+    Parser.OnGetPartial:=@DoGetPartial;
+    //Parser.Template:=Self.Template;
+    Parser.Partials:=Self.FCompiledPartials;
+    if Self.StartTag<>'' then
+      Parser.StartTag:=Self.StartTag;
+    if Self.StopTag<>'' then
+      Parser.StopTag:=Self.StopTag;
+    FCompiled:=Parser.Parse;
+  finally
+    Parser.Free;
+  end;
+end;
+
+procedure TMustache.Dump(aList: Tstrings; aindent: TMustacheString);
+begin
+  if Assigned(Compiled) then
+    Compiled.Dump(aList,aIndent);
+end;
+
+function TMustache.Dump: TMustacheString;
+
+Var
+  I : integer;
+  L : TStrings;
+
+begin
+  L:=TStringList.Create;
+  try
+    Dump(L,'');
+    if Partials.Count>0 then
+      begin
+      L.Add('Partials:');
+      for I:=0 to Partials.Count-1 do
+        L.Add('Partial '+IntToStr(I)+': '+Partials[i]);
+      L.Add('End of partials');
+      end;
+    Result:=L.Text;
+  finally
+    L.Free;
+  end;
+end;
+
+procedure TMustache.Render(aContext: TMustacheContext; aOutput: TMustacheOutput);
+
+begin
+  if not Assigned(Compiled) then
+    Compile;
+  Compiled.Render(aContext,aOutput);
+end;
+
+function TMustache.Render(aContext: TMustacheContext): TMustacheString;
+
+Var
+  S : TMustacheStringOutput;
+
+begin
+  S:=TMustacheStringOutput.Create;
+  try
+    Render(aContext,S);
+    Result:=S.Data;
+  finally
+    S.Free;
+  end;
+end;
+
+function TMustache.Render(const aJSON: TJSONData): TMustacheString;
+
+Var
+  C : TMustacheJSONContext;
+
+begin
+  C:=TMustacheJSONContext.Create(aJSON,FOnGetValue);
+  try
+    Result:=Render(C);
+  finally
+    C.Free;
+  end;
+end;
+
+function TMustache.Render(const aJSON: TJSONStringType): TMustacheString;
+
+Var
+  JSONData : TJSONData;
+
+begin
+  JSONData:=GetJSON(aJSON);
+  try
+    Result:=Render(JSONData);
+  finally
+    JSONData.Free;
+  end;
+end;
+
+class function TMustache.CreateMustache(aOwner : TComponent; aTemplate : TMustacheString) : TMustache;
+
+begin
+  Result:=TMustache.Create(aOwner);
+  Result.Template:=aTemplate;
+end;
+
+procedure TMustache.SetStartTag(AValue: TMustacheString);
+begin
+  if FStartTag=AValue then Exit;
+  FStartTag:=AValue;
+  Reset;
+end;
+
+procedure TMustache.SetPartials(AValue: TStrings);
+begin
+  if FPartials=AValue then Exit;
+  FPartials.Assign(AValue);
+end;
+
+procedure TMustache.SetStopTag(AValue: TMustacheString);
+begin
+  if FStopTag=AValue then Exit;
+  FStopTag:=AValue;
+  Reset;
+end;
+
+procedure TMustache.SetTemplate(AValue: TMustacheString);
+begin
+  if FTemplate=AValue then Exit;
+  FTemplate:=AValue;
+  Reset;
+end;
+
+procedure TMustache.DoGetPartial(const aName: TMustacheString;
+  var aHandled: Boolean; var aValue: TMustacheString);
+begin
+  aValue:=FPartials.Values[aName];
+  aHandled:=aValue<>'';
+  if Not aHandled then
+    aHandled:=FPartials.IndexOfName(aName)<>-1;
+end;
+
+procedure TMustache.Reset;
+begin
+  FreeAndNil(FCompiled);
+  FreeAndNil(FCompiledPartials);
+  FCompiledPartials:=CreatePartials;
+end;
+
+function TMustache.CreatePartials: TMustachePartialList;
+begin
+  Result:=TMustachePartialList.Create(metRoot,Nil,0);
+end;
+
+class function TMustache.Render(aTemplate: TMustacheString;
+  const aJSON: TJSONStringType): TMustacheString;
+
+begin
+  With CreateMustache(Nil,aTemplate) do
+    try
+      Result:=Render(aJSON);
+    finally
+      Free;
+    end;
+end;
+
+{ TMustacheJSONContext }
+
+function TMustacheJSONContext.FindValue(const aName: TMustacheString
+  ): TJSONData;
+Var
+  aCount : Integer;
+  N : TMustacheString;
+
+begin
+  Result:=Nil;
+  aCount:=FCount-1;
+  While (Result=Nil) and (aCount>=0) do
+    begin
+    N:=aName;
+    if N='.' then
+      N:='';
+    With FStack[aCount] do
+      if (Index>=0) and (Index<Value.Count) then
+        Result:=Value.Items[Index].FindPath(N)
+      else
+        Result:=Value.FindPath(N);
+    Dec(aCount);
+    end;
+end;
+
+function TMustacheJSONContext.GetRootData: TJSONData;
+begin
+  Result:=FStack[0].Value;
+end;
+
+
+constructor TMustacheJSONContext.Create(aJSON: TJSONData;
+  aCallback: TGetTextValueEvent);
+begin
+  Inherited Create(aCallBack);
+  SetLength(FStack,JSONListGrowCount);
+  FStack[0].Value:=aJSON;
+  FStack[0].Index:=-1;
+  FCount:=1;
+end;
+
+function TMustacheJSONContext.MoveNextSectionItem(const aName: TMustacheString
+  ): Boolean;
+
+begin
+  With FStack[FCount-1] do
+    begin
+    Inc(Index);
+    Result:=Index<Value.Count;
+    end;
+end;
+
+function TMustacheJSONContext.PushSection(const aName: TMustacheString
+  ): TMustacheSectionType;
+
+Var
+  S : TJSONData;
+
+begin
+  Result:=mstNone;
+  S:=FindValue(aName);
+  if S=Nil then
+    Exit;
+  if (S.JSONType=jtArray) then
+    begin
+    if (S.Count>0) then
+      Result:=mstList
+    end
+  else if Not ((S.JSONType=jtNull) or ((S.JSONType=jtBoolean) and Not S.AsBoolean)) then
+     Result:=mstSingle;
+  if Result<>mstNone then
+    begin
+    if FCount=Length(FStack) then
+      SetLength(FStack,FCount+JSONListGrowCount);
+    FStack[FCount].Value:=S;
+    FStack[FCount].Index:=-1;
+    Inc(FCount,1);
+    end;
+end;
+
+procedure TMustacheJSONContext.PopSection(const aName: TMustacheString);
+begin
+  if FCount<1 then
+    Raise EMustache.CreateFmt('PopSection %s without push',[aName]);
+  Dec(FCount,1);
+end;
+
+function TMustacheJSONContext.GetTextValue(const aName: TMustacheString): TMustacheString;
+
+Var
+  aJSON : TJSONData;
+
+begin
+  Result:='';
+  aJSON:=FindValue(aName);
+  if not Assigned(aJSON) then
+    Result:=Inherited GetTextValue(aName)
+  else
+    if (AJSON.JSONType=jtNumber) and (TJSONNumber(aJSON).NumberType=ntFloat) then
+      Result:=FormatFloat('0.0###########',aJSON.AsFloat)
+    else
+      Result:=aJSON.AsString;
+end;
+
+{ TMustacheSectionElement }
+
+procedure TMustacheSectionElement.Render(aContext: TMustacheContext;
+  aOutput: TMustacheOutput; const aPrefix: String; aLast : Boolean = False);
+
+Var
+  L : TMustacheSectionType;
+
+begin
+   L:=aContext.PushSection(Name);
+   if ElementType=metInvertedSection then
+     begin
+     if L=mstNone then
+       inherited Render(aContext, aOutput,aPrefix);
+     end
+   else
+     Case L of
+     mstSingle :
+        inherited Render(aContext, aOutput);
+     mstList :
+        while aContext.MoveNextSectionItem(Name) do
+          inherited Render(aContext, aOutput,aPrefix);
+     end;
+  if L<>mstNone then
+    aContext.PopSection(Name);
+end;
+
+{ TMustacheContext }
+
+constructor TMustacheContext.Create(aCallback: TGetTextValueEvent);
+begin
+  FCallback:=aCallback;
+end;
+
+class function TMustacheContext.QuoteHTML(aString: TMustacheString
+  ): TMustacheString;
+
+Const
+  QuoteChars = ['<','>','&','"'];
+
+Var
+  I,Last,Len : Integer;
+  Res : TMustacheString;
+
+  Procedure AddToResult; overload;
+
+  begin
+    Res:=Res+Copy(aString,Last,I-Last);
+    Last:=I;
+  end;
+
+  Procedure AddToResult(aTerm : TMustacheString); overload;
+
+  begin
+    Res:=Res+aTerm;
+    Last:=Last+1;
+  end;
+
+begin
+  Res:='';
+  Last:=1;
+  Len:=Length(Astring);
+  I:=1;
+  While (I<=Len) do
+    begin
+    While (I<=Len) and not (aString[i] in QuoteChars) do
+      Inc(I);
+    AddToResult;
+    if I<=Len then
+      Case aString[i] of
+        '<' : AddToResult('&lt;');
+        '>' : AddToResult('&gt;');
+        '&' : AddToResult('&amp;');
+        '"' : AddToResult('&quot;');
+      end;
+    Inc(i);
+    end;
+  AddToResult;
+  Result:=Res;
+end;
+
+function TMustacheContext.MoveNextSectionItem(const aName: TMustacheString): Boolean;
+begin
+  Result:=False
+end;
+
+function TMustacheContext.PushSection(const aName: TMustacheString): TMustacheSectionType;
+begin
+  Result:=mstNone;
+end;
+
+procedure TMustacheContext.PopSection(const aName: TMustacheString);
+begin
+  //
+end;
+
+function TMustacheContext.GetTextValue(const aName: TMustacheString): TMustacheString;
+
+var
+  aHandled : Boolean;
+
+begin
+  aHandled:=False;
+  Result:='';
+  if Assigned(FCallBack) then
+    FCallBack(aName,aHandled,Result);
+end;
+
+{ TMustacheTextElement }
+
+procedure TMustacheTextElement.SetData(const aData: TMustacheString);
+begin
+  FData:=aData;
+end;
+
+function TMustacheTextElement.GetData: TMustacheString;
+begin
+  Result:=FData;
+end;
+
+procedure TMustacheTextElement.Render(aContext: TMustacheContext;
+  aOutput: TMustacheOutput; const aPrefix: String; aLast : Boolean = False);
+
+Var
+  S : String;
+  L : Integer;
+
+begin
+  if (ElementType=metText) then
+    begin
+    S:=FData;
+    L:=Length(S);
+    if (aPrefix<>'')  then
+      begin
+      if (S[L]=#10) and aLast then
+        S:=StringReplace(Copy(S,1,L-1),#10,#10+aPrefix,[rfReplaceAll])+#10
+      else
+        S:=StringReplace(S,#10,#10+aPrefix,[rfReplaceAll]);
+{$IFDEF DEBUGMUSTACHE}
+      Writeln('Adding prefix =]',aPrefix,'[= to =]',FData, '[=  --->  =]',S,'["');
+{$ENDIF}
+      end;
+    aOutput.Output(S);
+    end;
+end;
+
+{ TMustacheVariableElement }
+
+procedure TMustacheVariableElement.SetData(const aData: TMustacheString);
+
+Var
+  L : Integer;
+  N : TMustacheString;
+begin
+  N:=aData;
+  L:=Length(N);
+  FNoUnescape:=(L>1) and (N[1]='{') and (N[L]='}');
+  if NoUnescape then
+    N:=Copy(N,2,L-2)
+  else
+    begin
+    FNoUnescape:=(L>0) and (N[1]='&');
+    if NoUnescape then
+      N:=Copy(N,2,L-1);
+    end;
+  inherited SetData(N);
+end;
+
+procedure TMustacheVariableElement.Render(aContext: TMustacheContext;
+  aOutput: TMustacheOutput; const aPrefix: String; aLast : Boolean = False);
+
+Var
+  aValue : TMustacheString;
+
+begin
+  aValue:='';
+  if Assigned(aContext) then
+    begin
+    aValue:=aContext.GetTextValue(Name);
+    if Not NoUnescape then
+      aValue:=aContext.QuoteHTML(aValue);
+    end;
+  aOutput.Output(aValue);
+end;
+
+{ TMustacheParser }
+
+function TMustacheParser.CreateElement(aType: TMustacheElementType; aParent : TMustacheElement; aPosition : Integer): TMustacheElement;
+
+begin
+  Result:=DefaultTypes[aType].Create(aType,aParent,aPosition);
+  if Assigned(aParent) then
+    aParent.AddChild(Result);
+end;
+
+constructor TMustacheParser.Create(aTemplate: TMustacheString; aStart: TMustacheString;
+  aStop: TMustacheString);
+begin
+  FStartTag:=aStart;
+  FStopTag:=aStop;
+  FTemplate:=aTemplate;
+  if FStartTag='' then
+    FStartTag:='{{';
+  if FStopTag='' then
+    FStopTag:='}}';
+end;
+
+class procedure TMustacheParser.SetDefaultTypeClass(aType: TMustacheElementType;
+  aClass: TMustacheElementClass);
+
+begin
+  DefaultTypes[aType]:=aClass;
+end;
+
+function TMustacheParser.GetPartial(const aName: TMustacheString): TMustacheString;
+
+Var
+  Handled : Boolean;
+begin
+  Result:='';
+  Handled:=False;
+  if Assigned(FOnGetPartial) then
+    FOnGetPartial(aName,Handled,Result);
+//  If not Handled then
+//    Raise EMustache.CreateFmt(SErrPartialNotFound,[aName]);
+end;
+
+procedure TMustacheParser.ExtractStartStop(const aName: TMustacheString; out aStart,
+  aStop: TMustacheString);
+
+  Function Invalid(S : TMustacheString) : Boolean;
+  begin
+    Invalid:=(Length(S)=0) or (Pos('=',S)<>0);
+  end;
+
+Var
+  DLen,NLen : Integer;
+  N : TMustacheString;
+
+begin
+  NLen:=Length(aName);
+  if aName[NLen]<>'=' then
+    Raise EMustache.CreateFmt(SErrInvalidDelimiter,[aName]);
+  N:=Copy(aName,1,NLen-1);
+  DLen:=(NLen-1) div 2;
+  aStart:=Trim(Copy(N,1,DLen));
+  aStop:=Trim(Copy(N,NLen-DLen,DLen));
+  // Writeln('New: "',aStart,'" - "',aStop,'" - ',DLEn);
+  if Invalid(aStop) then
+    Raise EMustache.CreateFmt(SErrInvalidDelimiterValue,[SStopTag,aStop,N]);
+  if Invalid(aStart) then
+    Raise EMustache.CreateFmt(SErrInvalidDelimiterValue,[SStartTag,aStart,N]);
+end;
+
+procedure TMustacheParser.Parse(aParent: TMustacheElement);
+
+begin
+  DoParse(aParent,FTemplate,StartTag, StopTag);
+end;
+
+function TMustacheParser.EndsOnWhiteSpace(aElement: TMustacheElement): Boolean;
+
+Var
+  I : Integer;
+  S : TMustacheString;
+
+begin
+  // if on standalone line, the entire line must be removed, see specs comments.standalone
+  Result:=(aElement.ElementType=metText);
+  s:=aElement.Data;
+  I:=Length(S);
+  While Result and (I>0) do
+     begin
+     if S[i] in [#13,#10] then
+       Break;
+     Result:=(S[I]=' ');
+     Dec(i);
+     end;
+  Result:=Result and ((I>0) or (aElement.Position=1));
+end;
+
+function TMustacheParser.GetEndingWhiteSpace(aElement: TMustacheElement): TMustacheString;
+
+Var
+  S : TMustacheString;
+  I : Integer;
+
+begin
+  s:=aElement.Data;
+  I:=Length(S);
+  While (I>0) and (S[I]=' ') do
+     Dec(i);
+  Result:=Copy(S,I+1);
+end;
+
+procedure TMustacheParser.TrimEndingWhiteSpace(aElement: TMustacheElement);
+
+Var
+  I : Integer;
+  S : TMustacheString;
+
+begin
+  s:=aElement.Data;
+  I:=Length(S);
+  While (I>0) and (S[I]=' ') do
+     Dec(i);
+  aElement.Data:=Copy(S,1,I);
+end;
+
+Function TMustacheParser.CreateDefault(aParent : TMustacheElement; aPosition : Integer;Const aName : String) : TMustacheElement;
+
+begin
+  Result:=CreateElement(metVariable,aParent,aPosition);
+  Result.SetData(aName);
+end;
+
+procedure TMustacheParser.DoParse(aParent: TMustacheElement; const aTemplate,
+  aStart, aStop: TMustacheString);
+
+Var
+  currParent : TMustacheElement;
+  aLen,clStop, lStart,lStop, NewPos, Current, Total : Integer;
+  aName,cStart,cStop,R : TMustacheString;
+  C: TMustacheChar;
+  IsWhiteSpace : Boolean;
+  Partial,WhiteSpaceEl : TMustacheELement;
+
+  Function CheckWhiteSpace : Boolean;
+
+  begin
+    WhiteSpaceEl:=Nil;
+    With CurrParent do
+      begin
+      Result:=(ChildCount=0) or EndsOnWhiteSpace(Children[ChildCount-1]);
+      if Result and (ChildCount>0) then
+         WhiteSpaceEl:=Children[ChildCount-1];
+      end;
+  end;
+
+  Procedure FinishWhiteSpace(Full : Boolean = true);
+  Var
+    I : Integer;
+  begin
+    I:=NewPos;
+    While IsWhiteSpace and (I+clStop<=Total) do
+      begin
+      C:=aTemplate[I+clStop];
+      if (C in [#13,#10]) then
+        Break;
+      isWhiteSpace:=aTemplate[I+clStop]=' ';
+      I:=I+1;
+      end;
+    if isWhiteSpace then
+      begin
+      While (I<=Total) and (aTemplate[I+clStop] in [#13,#10]) do
+        Inc(I);
+      NewPos:=I;
+      if Assigned(WhiteSpaceEl) and full then
+        TrimEndingWhiteSpace(WhiteSpaceEl);
+      end;
+  end;
+
+begin
+  currParent:=aParent;
+  cStart:=aStart;
+  cStop:=aStop;
+  lStart:=Length(cStart);
+  lStop:=Length(cStop);
+  Current:=1;
+  Total:=Length(aTemplate);
+  While (Current<=Total) do
+    begin
+    C:=Template[Current];
+    NewPos:=Pos(cStart,aTemplate,Current);
+    if NewPos=0 then
+      NewPos:=Total+1;
+    // Stash what we have till now.
+    if NewPos>Current then
+      begin
+      R:=Copy(aTemplate,Current,NewPos-Current);
+      CreateElement(metText,currParent,Current).SetData(R);
+      Current:=NewPos;
+      end;
+    if Current<Total then
+      begin
+      NewPos:=Pos(cStop,aTemplate,Current+lStart);
+      if (NewPos=0) then
+        Raise EMustache.CreateFmt(SErrUnterminatedTag,[cStart,Current]);
+      aLen:=NewPos-Current-LStart;
+      aName:=Copy(aTemplate,Current+LStart,ALen);
+      if (aName='') then
+        Raise EMustache.CreateFmt(SErrEmptyTag,[cStart,Current]);
+      C:=aName[1];
+      if C in ['=','#','^','/','!','>'] then
+        aName:=Copy(aName,2,Length(aName)-1);
+      clStop:=Lstop; // Can change.
+      case C of
+        '=' :
+          begin
+          IsWhiteSpace:=CheckWhiteSpace;
+          if IsWhiteSpace then
+            FinishWhiteSpace;
+          ExtractStartStop(aName,cStart,cStop);
+          lStart:=Length(cStart);
+          lStop:=Length(cStop);
+          //R:=Copy(aTemplate,newPos+clStop);
+          //Writeln(R);
+          end;
+        '{' :
+          begin
+          if (cStop='}}') then
+            begin
+            if (FTemplate[NewPos+lStop]<>'}') then
+              Raise EMustache.CreateFmt(SErrUnterminatedTag,[cStart,Current]);
+            inc(NewPos);
+            aName:=aName+'}';
+            end;
+          CreateElement(metVariable,currParent,Current).SetData(aName);
+          end;
+        '#' :
+          begin
+          IsWhiteSpace:=CheckWhiteSpace;
+          CurrParent:=CreateElement(metSection,currParent,Current);
+          CurrParent.SetData(aName);
+          if IsWhiteSpace then
+            FinishWhiteSpace;
+          end;
+        '!' :
+          begin
+          IsWhiteSpace:=CheckWhiteSpace;
+          CreateElement(metComment,currParent,Current).SetData(aName);
+          if IsWhiteSpace then
+            FinishWhiteSpace;
+          end;
+        '^' :
+          begin
+          IsWhiteSpace:=CheckWhiteSpace;
+          CurrParent:=CreateElement(metInvertedSection,currParent,Current);
+          CurrParent.SetData(aName);
+          if IsWhiteSpace then
+            FinishWhiteSpace;
+          end;
+        '>' :
+          begin
+          // Find or create compiled partial;
+          IsWhiteSpace:=CheckWhiteSpace;
+          aName:=Trim(aName);
+          if not Assigned(Partials) then
+            Raise EMustache.Create(SErrNoPartials);
+          Partial:=Partials.FindPartial(aName);
+          if Partial=Nil then
+            begin
+            Partial:=CreateElement(metRoot,Partials,Current);
+            Partial.Data:=aName;
+            DoParse(Partial,GetPartial(aName),FStartTag,FStopTag);
+            end;
+          // Create reference and insert into current tree
+          With CreateElement(metPartial,currParent,Current) do
+            begin
+            AddChild(Partial);
+            Data:=aName;
+            if isWhitespace and assigned(WhiteSpaceEl) then
+              Prefix:=GetEndingWhiteSpace(WhiteSpaceEl);
+            end;
+          if IsWhiteSpace then
+            FinishWhiteSpace(False);
+          end;
+        '/' :
+          begin
+          IsWhiteSpace:=CheckWhiteSpace;
+          if Not (CurrParent.ElementType in [metSection,metInvertedSection]) then
+            Raise EMustache.CreateFmt(SErrNoSectionToClose,[aName,Current])
+          else if (CurrParent.Data<>Trim(aName)) then
+            Raise EMustache.CreateFmt(SErrSectionClose,[currParent.Data,CurrParent.Position,aName,Current])
+          else
+            currParent:=currParent.Parent;
+          if IsWhiteSpace then
+            FinishWhiteSpace;
+          end
+      else
+        CreateDefault(CurrParent,Current,aName);
+      end;
+      Current:=NewPos+clStop;
+      end;
+    end;
+  if CurrParent<>aParent then
+    Raise EMustache.CreateFmt(SErrNotClosedSection,[currParent.Data,CurrParent.Position])
+
+end;
+
+function TMustacheParser.Parse: TMustacheElement;
+
+begin
+  Result:=TMustacheParentElement.Create(metRoot,Nil,1);
+  try
+    Parse(Result);
+  except
+    Result.Free;
+    Raise;
+  end;
+end;
+
+{ TMustacheNamedElement }
+
+procedure TMustacheNamedElement.SetData(Const aData: TMustacheString);
+
+begin
+  FName:=Trim(aData);
+end;
+
+function TMustacheNamedElement.GetData: TMustacheString;
+begin
+  Result:=FName;
+end;
+
+{ TMustacheParentElement }
+
+function TMustacheParentElement.GetElement(aIndex : Integer): TMustacheElement;
+begin
+  If (aIndex<0) or (aIndex>=FCount) then
+    Raise EMustache.CreateFmt(SErrInvalidIndex,[ClassName,aIndex,FCount-1]);
+  Result:=FChildren[aIndex];
+end;
+
+function TMustacheParentElement.GetCount: Integer;
+begin
+  Result:=FCount;
+end;
+
+destructor TMustacheParentElement.Destroy;
+begin
+  While FCount>0 do
+    begin
+    Dec(FCount);
+    FreeAndNil(FChildren[FCount]);
+    end;
+  inherited Destroy;
+end;
+
+
+procedure TMustacheParentElement.AddChild(aChild: TMustacheElement);
+
+Var
+  Len : Integer;
+
+begin
+  Len:=Length(FChildren);
+  if (FCount>=Len) then
+    SetLength(FChildren,Len+ListGrowCount);
+  FChildren[FCount]:=aChild;
+  Inc(FCount);
+end;
+
+procedure TMustacheParentElement.Render(aContext: TMustacheContext;
+  aOutput: TMustacheOutput; const aPrefix: String; aLast : Boolean = False);
+
+Var
+  I : integer;
+
+begin
+  For I:=0 to ChildCount-1 do
+    Children[I].Render(aContext,aOutPut,aPrefix,I=ChildCount-1);
+end;
+
+{ TMustacheElement }
+
+function TMustacheElement.GetCount: Integer;
+begin
+  Result:=0;
+end;
+
+function TMustacheElement.GetElement(aIndex : Integer): TMustacheElement;
+begin
+  Result:=Nil;
+end;
+
+function TMustacheElement.GetPrefix: TMustacheString;
+begin
+  Result:='';
+end;
+
+procedure TMustacheElement.SetPrefix(aValue: TMustacheString);
+begin
+  //
+end;
+
+procedure TMustacheElement.Dump(aList: Tstrings; aIndent: TMustacheString; aDumpChildren : Boolean = true);
+
+Var
+  I : Integer;
+
+begin
+  aList.Add(aIndent+Format('%s (%s, %d) : "%s"',[ClassName,GetEnumName(TypeInfo(TMustacheElementType),Ord(ElementType)),Position,Data]));
+  if aDumpChildren then
+    For I:=0 to ChildCount-1 do
+      Children[I].Dump(aList,'  '+aIndent);
+end;
+
+constructor TMustacheElement.Create(aType : TMustacheElementType; aParent : TMustacheElement;aPosition: Integer);
+begin
+  FType:=aType;
+  FParent:=aParent;
+  FPosition:=aPosition;
+end;
+
+procedure TMustacheElement.AddChild(aChild: TMustacheElement);
+begin
+  Raise EMustache.CreateFmt(SErrNoChildForElement,[ClassName])
+end;
+
+{ TMustacheStringOutput }
+
+procedure TMustacheStringOutput.Output(const aText: TMustacheString);
+begin
+  FData:=FData+aText;
+{$IFDEF DEBUGMUSTACHE}
+  Writeln('--');
+  Writeln('Output -]',aText,'[-');
+  Writeln('--');
+{$ENDIF}
+end;
+
+procedure TMustacheStringOutput.Reset;
+begin
+  FData:='';
+end;
+
+begin
+  TMustacheParser.SetDefaultTypeClass(metRoot,TMustacheParentElement);
+  TMustacheParser.SetDefaultTypeClass(metComment,TMustacheTextElement);
+  TMustacheParser.SetDefaultTypeClass(metText,TMustacheTextElement);
+  TMustacheParser.SetDefaultTypeClass(metVariable,TMustacheVariableElement);
+  TMustacheParser.SetDefaultTypeClass(metSection,TMustacheSectionElement);
+  TMustacheParser.SetDefaultTypeClass(metInvertedSection,TMustacheSectionElement);
+  TMustacheParser.SetDefaultTypeClass(metPartial,TMustachePartialElement);
+end.
+

File diff suppressed because it is too large
+ 0 - 0
packages/fcl-mustache/tests/spec/comments.json


File diff suppressed because it is too large
+ 0 - 0
packages/fcl-mustache/tests/spec/delimiters.json


File diff suppressed because it is too large
+ 0 - 0
packages/fcl-mustache/tests/spec/interpolation.json


File diff suppressed because it is too large
+ 0 - 0
packages/fcl-mustache/tests/spec/inverted.json


File diff suppressed because it is too large
+ 0 - 0
packages/fcl-mustache/tests/spec/partials.json


File diff suppressed because it is too large
+ 0 - 0
packages/fcl-mustache/tests/spec/sections.json


+ 290 - 0
packages/fcl-mustache/tests/tcbasemustache.pas

@@ -0,0 +1,290 @@
+{
+    This file is part of the Free Pascal Run time library.
+    Copyright (c) 2021 by Michael Van Canneyt ([email protected])
+
+    Helper classes for Mustache test cases
+
+    See the File COPYING.FPC, included in this distribution,
+    for details about the copyright.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+
+ **********************************************************************}
+unit tcbasemustache;
+
+{$mode ObjFPC}{$H+}
+
+interface
+
+uses
+  Classes, SysUtils, fpcunit, fpmustache;
+
+type
+
+  { TTestContext }
+
+  (* StringList with following encoding
+    // Null value
+    aName=<null>
+    // false value
+    aName=<null>
+    // plain value
+    aName=AValue
+    // Object value & member. Object value must be present
+    SubObj={}
+    SubObj.aName=aValue
+    // Array and members. Array value must be present
+    SubObj.SubArr=[]
+    SubObj.SubArr[0]={}
+    SubObj.SubArr[0].aName=aValue
+    SubObj.SubArr[1]={}
+    Subobj.SubArr[1].aName=aValue
+  *)
+
+  TTestContext = class (TMustacheContext)
+  Private
+    FValues : TStringList;
+    FPath : String;
+  public
+    Constructor Create(aCallback: TGetTextValueEvent); override;
+    Destructor destroy; override;
+    Function GetTextValue(Const aName : TMustacheString) : TMustacheString; override;
+    Function MoveNextSectionItem(Const aName : TMustacheString) : Boolean; override;
+    Function PushSection(Const aName : TMustacheString) : TMustacheSectionType; override;
+    Procedure PopSection(Const aName : TMustacheString); override;
+    Procedure SetValue(const aPath,aValue : string);
+    Property Values : TStringList read FValues;
+  end;
+
+  TBaseMustacheTest = class(TTestCase)
+  Private
+    FPartials: TStrings;
+    FTemplate: String;
+    FResult: TMustacheElement;
+    FParser: TMustacheParser;
+  Protected
+    Function CreateParser : TMustacheParser; virtual; abstract;
+    Procedure DoGetPartial(const aName: TMustacheString; var aHandled: Boolean;  var aValue: TMustacheString);
+  Public
+    Class Procedure AssertEquals(Msg : String; aExpected,aActual : TMustacheElementType); overload;
+    Class Function AssertElement(aParent : TMustacheElement; aIndex: Integer; aType: TMustacheElementType; aData: String; aClass : TMustacheElementClass = Nil) : TMustacheElement; overload;
+    Function AssertElement(aIndex: Integer; aType: TMustacheElementType; aData: String; aClass : TMustacheElementClass = Nil) : TMustacheElement; overload;
+    Procedure AssertResultCount(aCount : Integer);
+    procedure SetUp; override;
+    procedure TearDown; override;
+    Procedure CallParser;
+    Procedure AddPartial(Const aName,aText: TMustacheString);
+    Property Partials : TStrings Read FPartials;
+    Property Template : String Read FTemplate Write FTemplate;
+    property ParseResult : TMustacheElement Read FResult;
+    property Parser : TMustacheParser Read FParser;
+  end;
+
+
+implementation
+
+uses strutils, typinfo;
+
+{ TTestContext }
+
+constructor TTestContext.Create(aCallback: TGetTextValueEvent);
+begin
+  inherited Create(aCallback);
+  FValues:=TStringList.Create;
+  FValues.OwnsObjects:=True;
+end;
+
+destructor TTestContext.destroy;
+begin
+  FreeAndNil(FValues);
+  inherited destroy;
+end;
+
+function TTestContext.GetTextValue(const aName: TMustacheString
+  ): TMustacheString;
+
+Var
+  aPath,N : String;
+  Done : Boolean;
+begin
+  Result:='';
+  aPath:=FPath;
+  Done:=False;
+  Repeat
+    if aPath<>'' then
+      N:=aPath+'.'+aName
+    else
+      begin
+      N:=aName;
+      Done:=True;
+      end;
+    Result:=FValues.Values[N];
+    if not Done then
+      aPath:=Copy(aPath,1,RPos('.',aPath)-1);
+  until (Result<>'') or Done;
+end;
+
+function TTestContext.MoveNextSectionItem(const aName: TMustacheString
+  ): Boolean;
+
+Var
+  L,P,Idx : Integer;
+  N : String;
+
+begin
+  L:=Length(FPath);
+  if (L>0) and (FPath[L]=']') then
+    begin
+    P:=RPos('[',FPath)+1;
+    Idx:=StrToIntDef(Copy(FPath,P,L-P),-1);
+    N:=Copy(FPath,1,P-1)+IntToStr(Idx+1)+']';
+    Result:=FValues.Values[N]<>''; // We could check for {}
+    if Result then
+      FPath:=N;
+    end;
+
+end;
+
+function TTestContext.PushSection(const aName: TMustacheString): TMustacheSectionType;
+
+Var
+  aPath,S : String;
+
+begin
+  if FPath<>'' then
+    FPath:=FPath+'.';
+  aPath:=FPath+aName;
+  S:=Values.Values[aPath];
+  if S='{}' then
+    begin
+    FPath:=aPath;
+    result:=mstSingle;
+    end;
+  if S='[]' then
+    begin
+    if Values.Values[aPath+'[0]']='' then
+      Result:=mstNone
+    else
+      begin
+      FPath:=aPath+'[-1]';
+      result:=mstList;
+      end;
+    end
+  else if (s='<null>') or (s='<false>') or (s='') then
+    begin
+    Result:=mstNone;
+    end
+  else
+    begin
+    FPath:=aPath;
+    result:=mstSingle;
+    end;
+
+end;
+
+procedure TTestContext.PopSection(const aName: TMustacheString);
+begin
+  FPath:=Copy(FPath,1,RPos('.',FPath)-1);
+end;
+
+procedure TTestContext.SetValue(const aPath, aValue: string);
+begin
+  Values.Values[aPath]:=aValue;
+end;
+
+
+{ TBaseMustacheTest }
+
+procedure TBaseMustacheTest.SetUp;
+
+begin
+  Inherited;
+  FParser:=CreateParser;
+  FParser.Partials:=TMustachePartialList.Create(metRoot,Nil,0);
+  FParser.OnGetPartial:=@DoGetPartial;
+  FPartials:=TStringList.Create;
+  TStringList(FPartials).OwnsObjects:=True;
+end;
+
+procedure TBaseMustacheTest.TearDown;
+
+begin
+  FreeAndNil(FPartials);
+  FreeAndNil(FResult);
+  FParser.Partials.Free;
+  FreeAndNil(FParser);
+  Inherited;
+end;
+
+procedure TBaseMustacheTest.DoGetPartial(const aName: TMustacheString;
+  var aHandled: Boolean; var aValue: TMustacheString);
+begin
+  aValue:=FPartials.Values[aName];
+  aHandled:=FPartials.IndexOfName(aName)<>-1;
+end;
+
+class function TBaseMustacheTest.AssertElement(aParent: TMustacheElement;
+  aIndex: Integer; aType: TMustacheElementType; aData: String;
+  aClass: TMustacheElementClass): TMustacheElement;
+Var
+  El : TMustacheElement;
+  aChild : String;
+begin
+  AssertNotNull('Have parent',aParent);
+  AssertTrue(Format('Index %d in range 0..%d',[aIndex,aParent.ChildCount-1]),(aIndex>=0) and (aIndex<aParent.ChildCount));
+  EL:=aParent.Children[aIndex];
+  aChild:=Format('Child %d',[aIndex]);
+  AssertNotNull('Have result '+aChild,El);
+  AssertEquals(aChild+' has correct type',aType,El.ElementType);
+  AssertEquals(aChild+' has correct data',aData,El.Data);
+  if (aClass<>Nil) then
+    AssertEquals(aChild+' has correct class',aClass,el.Classtype);
+  Result:=El;
+end;
+
+function TBaseMustacheTest.AssertElement(aIndex: Integer;
+  aType: TMustacheElementType; aData: String; aClass : TMustacheElementClass = Nil): TMustacheElement;
+
+begin
+  AssertNotNull('Have result',FResult);
+  Result:=AssertElement(FResult,aIndex,aType,aData,aClass);
+end;
+
+procedure TBaseMustacheTest.AssertResultCount(aCount: Integer);
+begin
+  AssertNotNull('Have result',FResult);
+  AssertEquals('Result count',aCount,FResult.ChildCount);
+end;
+
+
+procedure TBaseMustacheTest.CallParser;
+
+begin
+  Parser.Template:=Template;
+  FResult:=Parser.Parse;
+end;
+
+procedure TBaseMustacheTest.AddPartial(const aName, aText: TMustacheString);
+
+//Var
+//  T : TMustacheTextElement;
+
+begin
+//  T:=TMustacheTextElement.Create(metText,Nil,0);
+//  T.Data:=aText;
+  FPartials.Add(aName+'='+atext);
+end;
+
+class procedure TBaseMustacheTest.AssertEquals(Msg: String; aExpected,
+  aActual: TMustacheElementType);
+
+begin
+  AssertEquals(Msg,GetEnumName(typeInfo(TMustacheElementType),Ord(aExpected)),
+                       GetEnumName(typeInfo(TMustacheElementType),Ord(aActual)));
+end;
+
+
+end.
+

+ 149 - 0
packages/fcl-mustache/tests/tcdbmustache.pas

@@ -0,0 +1,149 @@
+{
+    This file is part of the Free Pascal Run time library.
+    Copyright (c) 2021 by Michael Van Canneyt ([email protected])
+
+    Test cases for DB Context for Mustache
+
+    See the File COPYING.FPC, included in this distribution,
+    for details about the copyright.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+
+ **********************************************************************}
+unit tcdbmustache;
+
+{$mode ObjFPC}{$H+}
+
+interface
+
+uses
+  Classes, SysUtils, fpcunit, testregistry, fpmustache, db, bufdataset, fpdbmustache;
+
+Type
+
+  { TTestMustacheDBContext }
+
+  TTestMustacheDBContext = Class(TTestCase)
+  private
+    FContext: TMustacheDBContext;
+    FDataset1: TBufDataset;
+    FDataset2: TBufDataset;
+    FMustache: TMustache;
+  Public
+    Procedure Setup; override;
+    Procedure TearDown; override;
+    Procedure CreateDataset1;
+    Procedure CreateDataset2;
+    Property Dataset1 : TBufDataset Read FDataset1;
+    Property Dataset2 : TBufDataset Read FDataset2;
+    Property Context : TMustacheDBContext Read FContext;
+    Property Mustache : TMustache Read FMustache;
+  Published
+    Procedure TestEmpty;
+    Procedure TestSingleSection;
+    Procedure TestTwoSections;
+  end;
+
+implementation
+
+Const
+  Template1 = '{{title}}! {{#Parents}}{{name}} {{age}} - {{/Parents}}';
+  Template2 = '{{title}}! {{#Parents}}{{name}}({{age}}) : {{#Children}}{{name}} {{age}},{{/Children}} - {{/Parents}}';
+
+{ TTestMustacheDBContext }
+
+procedure TTestMustacheDBContext.Setup;
+begin
+  Inherited;
+  FDataset1:=TBufDataset.Create(Nil);
+  FDataset1.Name:='Parents';
+  FDataset2:=TBufDataset.Create(Nil);
+  FDataset2.Name:='Children';
+  FContext:=TMustacheDBContext.Create(Nil);
+  FContext.StaticValues.Values['title']:='Family';
+  FMustache:=TMustache.Create(Nil);
+end;
+
+procedure TTestMustacheDBContext.TearDown;
+begin
+  FreeAndNil(FDataset1);
+  FreeAndNil(FDataset2);
+  FreeAndNil(FContext);
+  FreeAndNil(FMustache);
+end;
+
+procedure TTestMustacheDBContext.CreateDataset1;
+begin
+  FDataset1.FieldDefs.Add('name',ftString,20);
+  FDataset1.FieldDefs.Add('age',ftInteger);
+  FDataset1.CreateDataset;
+  FDataset1.Append;
+  FDataset1.FieldByName('name').AsString:='Father';
+  FDataset1.FieldByName('age').AsInteger:=40;
+  FDataset1.Post;
+  FDataset1.Append;
+  FDataset1.FieldByName('name').AsString:='Mother';
+  FDataset1.FieldByName('age').AsInteger:=39;
+  FDataset1.Post;
+  FDataset1.First;
+end;
+
+procedure TTestMustacheDBContext.CreateDataset2;
+begin
+  FDataset2.FieldDefs.Add('name',ftString,20);
+  FDataset2.FieldDefs.Add('age',ftInteger);
+  FDataset2.CreateDataset;
+  FDataset2.Append;
+  FDataset2.FieldByName('name').AsString:='Child1';
+  FDataset2.FieldByName('age').AsInteger:=4;
+  FDataset2.Post;
+  FDataset2.Append;
+  FDataset2.FieldByName('name').AsString:='Child2';
+  FDataset2.FieldByName('age').AsInteger:=2;
+  FDataset2.Post;
+  FDataset2.First;
+end;
+
+procedure TTestMustacheDBContext.TestEmpty;
+begin
+  AssertNotNull('Mustache',Mustache);
+  AssertNotNull('Dataset1',Dataset1);
+  AssertNotNull('Dataset2',Dataset2);
+  AssertNotNull('Context',Context);
+  AssertEquals('Context static','Family',Context.StaticValues.Values['title']);
+end;
+
+procedure TTestMustacheDBContext.TestSingleSection;
+
+Var
+  S : String;
+
+begin
+  Mustache.Template:=Template1;
+  CreateDataset1;
+  Context.AddDataset(FDataset1);
+  S:=Mustache.Render(Context);
+  AssertEquals('Correct result','Family! Father 40 - Mother 39 - ',S);
+end;
+
+procedure TTestMustacheDBContext.TestTwoSections;
+
+Var
+  S : String;
+
+begin
+  Mustache.Template:=Template2;
+  CreateDataset1;
+  CreateDataset2;
+  Context.AddDataset(FDataset1);
+  Context.AddDataset(FDataset2);
+  S:=Mustache.Render(Context);
+  AssertEquals('Correct result','Family! Father(40) : Child1 4,Child2 2, - Mother(39) :  - ',S);
+end;
+
+initialization
+  RegisterTest(TTestMustacheDBContext);
+end.
+

+ 199 - 0
packages/fcl-mustache/tests/tcexmustache.pas

@@ -0,0 +1,199 @@
+{
+    This file is part of the Free Pascal Run time library.
+    Copyright (c) 2021 by Michael Van Canneyt ([email protected])
+
+    Test cases for expression parser support
+
+    See the File COPYING.FPC, included in this distribution,
+    for details about the copyright.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+
+ **********************************************************************}
+
+unit tcexmustache;
+
+{$mode ObjFPC}{$H+}
+
+interface
+
+uses
+  Classes, SysUtils, fpcunit, fpjson, testregistry, fpmustache, tcbasemustache, fpexmustache, fpexprpars;
+
+Type
+
+  { TTestExMustacheParser }
+
+  TTestExMustacheParser = Class(TBaseMustacheTest)
+  private
+    FExpr: TFPExpressionParser;
+    FOutput: TMustacheStringOutput;
+    FContext : TMustacheJSONContext;
+    FData : TJSONData;
+    procedure GetVar(var Result: TFPExpressionResult; ConstRef
+      AName: ShortString);
+  Public
+    Procedure SetUp; override;
+    Procedure TearDown; override;
+    Function CreateParser: TMustacheParser; override;
+    Property Expr : TFPExpressionParser Read FExpr;
+    Property Output : TMustacheStringOutput Read FOutput;
+  Published
+    Procedure TestSimple;
+    Procedure TestRenderSimple;
+    Procedure TestRenderSection;
+  end;
+
+  { TTestMustacheExpr }
+
+  TTestMustacheExpr = Class(TTestCase)
+  private
+    FJSON: TJSONObject;
+    FMustache: TMustacheExpr;
+  public
+    Procedure SetUp; override;
+    Procedure TearDown; override;
+    Property Mustache : TMustacheExpr Read FMustache;
+    Property JSON : TJSONObject Read FJSON;
+  Published
+    Procedure TestEmpty;
+    Procedure TestRegisterVariables;
+    Procedure TestRenderSection;
+    procedure TestRenderSectionStaticVariables;
+  end;
+
+
+implementation
+
+Const
+  STestJSON = '{ "data" : [ { "name": "me", "age" : 10}, { "name": "you",  "age" : 12  }, { "name": "he", "age" : 13 } ] }';
+
+{ TTestMustacheExpr }
+
+procedure TTestMustacheExpr.SetUp;
+begin
+  inherited SetUp;
+  FMustache:=TMustacheExpr.Create(Nil);
+  FJSON:=GetJSON(STestJSON) as TJSONObject;
+end;
+
+procedure TTestMustacheExpr.TearDown;
+begin
+  FreeAndNil(FJSON);
+  FreeAndNil(FMustache);
+  inherited TearDown;
+end;
+
+procedure TTestMustacheExpr.TestEmpty;
+begin
+  AssertNotNull('Have mustache instance',Mustache);
+  AssertNotNull('Have mustache expression engine instance',Mustache.ExpressionParser);
+end;
+
+procedure TTestMustacheExpr.TestRegisterVariables;
+begin
+  Mustache.RegisterVariables(JSON,'data[0]',True);
+  AssertEquals('Variable count',2,Mustache.ExpressionParser.Identifiers.Count);
+  AssertEquals('Variable 0','name',Mustache.ExpressionParser.Identifiers[0].Name);
+  AssertEquals('Variable 1','age',Mustache.ExpressionParser.Identifiers[1].Name);
+  AssertTrue('Variable 0 type',rtString=Mustache.ExpressionParser.Identifiers[0].ResultType);
+  AssertTrue('Variable 1 type',rtInteger=Mustache.ExpressionParser.Identifiers[1].ResultType);
+end;
+
+procedure TTestMustacheExpr.TestRenderSection;
+
+Var
+  S : String;
+
+Const
+  Template = '{{#data}}{{[name]}}:{{[age>11]}} {{/data}}';
+
+begin
+  Mustache.Template:=Template;
+  Mustache.RegisterVariables(JSON,'data[0]',True);
+  S:=Mustache.Render(JSON);
+  AssertEquals('Correct result','me:False you:True he:True ',S);
+end;
+
+procedure TTestMustacheExpr.TestRenderSectionStaticVariables;
+Var
+  S : String;
+
+Const
+  Template = '{{#data}}{{[name]}}:{{[age>11]}} {{/data}}';
+
+begin
+  Mustache.Template:=Template;
+  Mustache.RegisterVariables(JSON,'data[0]',False);
+  S:=Mustache.Render(JSON);
+  AssertEquals('Correct result','me:False me:False me:False ',S);
+end;
+
+
+{ TTestExMustacheParser }
+
+procedure TTestExMustacheParser.SetUp;
+begin
+  FExpr:=TFPExpressionParser.Create(Nil);
+  Foutput:=TMustacheStringOutput.Create;
+  inherited SetUp;
+end;
+
+procedure TTestExMustacheParser.TearDown;
+begin
+  inherited TearDown;
+  FreeAndNil(FExpr);
+  FreeAndNil(Foutput);
+  FreeAndNil(FContext);
+  FreeAndNil(FData);
+end;
+
+function TTestExMustacheParser.CreateParser: TMustacheParser;
+
+Var
+  P : TMustacheExprParser;
+
+begin
+  P:=TMustacheExprParser.Create;
+  P.ExprParser:=FExpr;
+  Result:=P;
+end;
+
+procedure TTestExMustacheParser.TestSimple;
+begin
+  Template:='{{[1+2]}}';
+  CallParser;
+  AssertElement(0,metVariable,'1+2',TMustacheExprElement);
+end;
+
+procedure TTestExMustacheParser.TestRenderSimple;
+begin
+  TestSimple;
+  ParseResult.Children[0].Render(Nil,Output,'',False);
+  AssertEquals('Correct result','3',Output.Data);
+end;
+
+procedure TTestExMustacheParser.GetVar(Var Result : TFPExpressionResult; ConstRef AName : ShortString);
+
+begin
+  Result.ResultType:=rtInteger;
+  Result.ResInteger:=StrToINt(FContext.GetTextValue('age'));
+end;
+
+procedure TTestExMustacheParser.TestRenderSection;
+begin
+  FData:=GetJSON(STestJSON);
+  FContext:=TMustacheJSONContext.Create(FData,Nil);
+  FExpr.Identifiers.AddVariable('age',rtInteger,@GetVar);
+  Template:='{{#data}}{{{name}}}:{{[age>11]}} {{/data}}';
+  CallParser;
+  ParseResult.Render(FContext,Output,'',False);
+  AssertEquals('Correct result','me:False you:True he:True ',Output.Data);
+end;
+
+initialization
+  RegisterTests([TTestExMustacheParser,TTestMustacheExpr]);
+end.
+

+ 728 - 0
packages/fcl-mustache/tests/tcmustache.pas

@@ -0,0 +1,728 @@
+{
+    This file is part of the Free Pascal Run time library.
+    Copyright (c) 2021 by Michael Van Canneyt ([email protected])
+
+    Test cases for basic mustache parser support
+
+    See the File COPYING.FPC, included in this distribution,
+    for details about the copyright.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+
+ **********************************************************************}
+
+unit tcmustache;
+
+{$mode objfpc}{$H+}
+
+interface
+
+uses
+  Classes, SysUtils, fpcunit, testregistry, fpmustache, tcbasemustache;
+
+type
+
+  { TTestMustacheParser }
+
+
+  TTestMustacheParser= class(TBaseMustacheTest)
+  private
+  protected
+    Function CreateParser : TMustacheParser; override;
+  Public
+    procedure SetUp; override;
+    procedure TearDown; override;
+  published
+    procedure TestEmpty;
+    procedure TestText;
+    procedure TestVariable;
+    procedure TestVariableErrNonClosed;
+    procedure TestVariableAlternateStartStop;
+    procedure TestDottedVariable;
+    procedure TestVariableNoUnescape;
+    procedure TestVariableNoUnescapeErrNonClosed;
+    procedure TestVariableNoUnescapeAlternateStartStop;
+    procedure TestComment;
+    procedure TestCommentSurround;
+    procedure TestCommentStandalone;
+    procedure TestCommentStandaloneSpaced;
+    procedure TestSetDelimiter;
+    procedure TestSetDelimiterErrInvalid;
+    procedure TestSection;
+    procedure TestSectionNested;
+    procedure TestSectionErrNotClosed;
+    procedure TestSectionErrWrongClosed;
+    procedure TestSectionErrNotStarted;
+    procedure TestTextSection;
+    procedure TestPartial;
+  end;
+
+  { TTestMustacheOutput }
+
+  TTestMustacheOutput = class(TTestCase)
+  Published
+    Procedure TestStringOutput;
+  end;
+
+  { TTestMustacheElement }
+
+  TTestMustacheElement = class(TTestCase)
+  private
+    FContext: TTestContext;
+    FEl: TMustacheElement;
+    Foutput: TMustacheStringOutput;
+    procedure DoCallBack(const aName: TMustacheString; var aHandled: Boolean;
+      var aValue: TMustacheString);
+  Public
+    Procedure SetUp; override;
+    Procedure TearDown; override;
+    Property Context : TTestContext Read FContext;
+    Property Output : TMustacheStringOutput Read Foutput;
+    Property El : TMustacheElement Read FEl;
+  Published
+    Procedure TestEmpty;
+    Procedure TestTextElement;
+    Procedure TestTextElementNoEscape;
+    Procedure TestTextElementComment;
+    Procedure TestTextElementPrefix;
+    procedure TestTextElementPrefixNotLast;
+    procedure TestTextElementPrefixLast;
+    Procedure TestVariableElement;
+    Procedure TestVariableElementNoEscape;
+    Procedure TestVariableElementEscape;
+    Procedure TestSectionEmpty;
+    Procedure TestSectionValue;
+    Procedure TestSectionValueFalse;
+    Procedure TestSectionValueNull;
+    Procedure TestSectionValueEmptyArray;
+    Procedure TestSectionValueArray1El;
+    Procedure TestSectionValueArray2El;
+    Procedure TestSectionValueArray2ElValue;
+    Procedure TestSectionValueArray1ElValueSuper;
+    Procedure TestSectionValueArray2ElValueSuper;
+    Procedure TestParentElement;
+    Procedure TestParentElementRender;
+    Procedure TestParentElementRenderPrefix;
+  end;
+
+implementation
+
+uses Typinfo;
+
+Const
+  SNeedsQuoting = '< > & "';
+  SQuotedResult = '&lt; &gt; &amp; &quot;';
+
+
+{ TTestMustacheElement }
+
+procedure TTestMustacheElement.DoCallBack(const aName: TMustacheString;
+  var aHandled: Boolean; var aValue: TMustacheString);
+begin
+  aValue:='';
+end;
+
+procedure TTestMustacheElement.SetUp;
+begin
+  inherited SetUp;
+  FOutput:=TMustacheStringOutput.Create;
+  FContext:=TTestContext.Create(@DoCallBack);
+end;
+
+procedure TTestMustacheElement.TearDown;
+begin
+  FreeAndNil(FContext);
+  FreeAndNil(FOutput);
+  FreeAndNil(FEl);
+  inherited TearDown;
+end;
+
+procedure TTestMustacheElement.TestEmpty;
+begin
+  AssertNotNull('Have output',Output);
+end;
+
+procedure TTestMustacheElement.TestTextElement;
+
+begin
+  Fel:=TMustacheTextElement.Create(metText,Nil,0);
+  El.Render(Nil,Output);
+  AssertEquals('No output','',Output.Data);
+  El.Data:='me';
+  El.Render(Nil,Output);
+  AssertEquals('Correct output','me',Output.Data);
+end;
+
+procedure TTestMustacheElement.TestTextElementNoEscape;
+begin
+  Fel:=TMustacheTextElement.Create(metText,Nil,0);
+  El.Data:=SNeedsQuoting;
+  El.Render(Nil,Output);
+  AssertEquals('Correct output',SNeedsQuoting,Output.Data);
+end;
+
+procedure TTestMustacheElement.TestTextElementComment;
+begin
+  Fel:=TMustacheTextElement.Create(metComment,Nil,0);
+  El.Data:='Something';
+  El.Render(Nil,Output);
+  AssertEquals('Correct output','',Output.Data);
+end;
+
+procedure TTestMustacheElement.TestTextElementPrefix;
+begin
+  Fel:=TMustacheTextElement.Create(metText,Nil,0);
+  El.Data:='me'#10'you';
+  El.Render(Nil,Output,'  ');
+  AssertEquals('Correct output 1','me'#10'  you',Output.Data);
+end;
+
+procedure TTestMustacheElement.TestTextElementPrefixNotLast;
+begin
+  Fel:=TMustacheTextElement.Create(metText,Nil,0);
+  El.Data:='me'#10'you'#10;
+  El.Render(Nil,Output,'  ');
+  AssertEquals('Correct output 2','me'#10'  you'#10'  ',Output.Data);
+end;
+
+procedure TTestMustacheElement.TestTextElementPrefixLast;
+
+begin
+  Fel:=TMustacheTextElement.Create(metText,Nil,0);
+  El.Data:='me'#10'you'#10;
+  El.Render(Nil,Output,'  ',True);
+  AssertEquals('Correct output 2','me'#10'  you'#10,Output.Data);
+end;
+
+
+procedure TTestMustacheElement.TestVariableElement;
+begin
+  Fel:=TMustacheVariableElement.Create(metText,Nil,0);
+  Context.Values.Values['name']:='abc';
+  El.Data:='name';
+  El.Render(Context,Output);
+  AssertEquals('Correct output','abc',Output.Data);
+end;
+
+
+procedure TTestMustacheElement.TestVariableElementNoEscape;
+begin
+  Fel:=TMustacheVariableElement.Create(metText,Nil,0);
+  Context.Values.Values['name']:=SNeedsQuoting;
+  El.Data:='{name}';
+  El.Render(Context,Output);
+  AssertEquals('Correct output',SNeedsQuoting,Output.Data);
+end;
+
+procedure TTestMustacheElement.TestVariableElementEscape;
+begin
+  Fel:=TMustacheVariableElement.Create(metText,Nil,0);
+  Context.Values.Values['name']:=SNeedsQuoting;
+  El.Data:='name';
+  El.Render(Context,Output);
+  AssertEquals('Correct output',SQuotedResult,Output.Data);
+end;
+
+procedure TTestMustacheElement.TestSectionEmpty;
+
+Var
+  T : TMustacheTextElement;
+
+begin
+  Fel:=TMustacheSectionElement.Create(metSection,Nil,0);
+  Fel.Data:='s';
+  T:=TMustacheTextElement.Create(metText,Nil,0);
+  Fel.AddChild(T);
+  T.Data:='a';
+  Fel.Render(Context,Output);
+  AssertEquals('No output','',Output.Data);
+end;
+
+procedure TTestMustacheElement.TestSectionValue;
+Var
+  T : TMustacheTextElement;
+
+begin
+  Context.SetValue('s','b');
+  Fel:=TMustacheSectionElement.Create(metSection,Nil,0);
+  Fel.Data:='s';
+  T:=TMustacheTextElement.Create(metText,Nil,0);
+  Fel.AddChild(T);
+  T.Data:='a';
+  Fel.Render(Context,Output);
+  AssertEquals('Single pass','a',Output.Data);
+end;
+
+procedure TTestMustacheElement.TestSectionValueFalse;
+Var
+  T : TMustacheTextElement;
+
+begin
+  Context.SetValue('s','<false>');
+  Fel:=TMustacheSectionElement.Create(metSection,Nil,0);
+  Fel.Data:='s';
+  T:=TMustacheTextElement.Create(metText,Nil,0);
+  Fel.AddChild(T);
+  T.Data:='a';
+  Fel.Render(Context,Output);
+  AssertEquals('no pass','',Output.Data);
+end;
+
+procedure TTestMustacheElement.TestSectionValueNull;
+
+Var
+  T : TMustacheTextElement;
+
+begin
+  Context.SetValue('s','<null>');
+  Fel:=TMustacheSectionElement.Create(metSection,Nil,0);
+  Fel.Data:='s';
+  T:=TMustacheTextElement.Create(metText,Nil,0);
+  Fel.AddChild(T);
+  T.Data:='a';
+  Fel.Render(Context,Output);
+  AssertEquals('no pass','',Output.Data);
+end;
+
+procedure TTestMustacheElement.TestSectionValueEmptyArray;
+Var
+  T : TMustacheTextElement;
+
+begin
+  Context.SetValue('s','[]');
+  Fel:=TMustacheSectionElement.Create(metSection,Nil,0);
+  Fel.Data:='s';
+  T:=TMustacheTextElement.Create(metText,Nil,0);
+  Fel.AddChild(T);
+  T.Data:='a';
+  Fel.Render(Context,Output);
+  AssertEquals('no pass','',Output.Data);
+end;
+
+procedure TTestMustacheElement.TestSectionValueArray1El;
+Var
+  T : TMustacheTextElement;
+
+begin
+  Context.SetValue('s','[]');
+  Context.SetValue('s[0]','toto');
+  Fel:=TMustacheSectionElement.Create(metSection,Nil,0);
+  Fel.Data:='s';
+  T:=TMustacheTextElement.Create(metText,Nil,0);
+  Fel.AddChild(T);
+  T.Data:='a';
+  Fel.Render(Context,Output);
+  AssertEquals('Single pass','a',Output.Data);
+end;
+
+procedure TTestMustacheElement.TestSectionValueArray2El;
+
+Var
+  T : TMustacheTextElement;
+
+begin
+  Context.SetValue('s','[]');
+  Context.SetValue('s[0]','toto');
+  Context.SetValue('s[1]','tata');
+  Fel:=TMustacheSectionElement.Create(metSection,Nil,0);
+  Fel.Data:='s';
+  T:=TMustacheTextElement.Create(metText,Nil,0);
+  Fel.AddChild(T);
+  T.Data:='a';
+  Fel.Render(Context,Output);
+  AssertEquals('Double pass','aa',Output.Data);
+end;
+
+procedure TTestMustacheElement.TestSectionValueArray2ElValue;
+
+Var
+  T : TMustacheElement;
+
+begin
+  Context.SetValue('s','[]');
+  Context.SetValue('s[0]','{}');
+  Context.SetValue('s[0].a','1');
+  Context.SetValue('s[1]','{}');
+  Context.SetValue('s[1].a','2');
+  Fel:=TMustacheSectionElement.Create(metSection,Nil,0);
+  Fel.Data:='s';
+  T:=TMustacheVariableElement.Create(metVariable,Nil,0);
+  Fel.AddChild(T);
+  T.Data:='a';
+  Fel.Render(Context,Output);
+  AssertEquals('Double pass','12',Output.Data);
+end;
+
+procedure TTestMustacheElement.TestSectionValueArray1ElValueSuper;
+
+Var
+  T : TMustacheElement;
+
+begin
+  Context.SetValue('s','[]');
+  Context.SetValue('s[0]','{}');
+  Context.SetValue('s[0].b','1');
+  Context.SetValue('a','2');
+  Fel:=TMustacheSectionElement.Create(metSection,Nil,0);
+  Fel.Data:='s';
+  T:=TMustacheVariableElement.Create(metVariable,Nil,0);
+  Fel.AddChild(T);
+  T.Data:='a';
+  Fel.Render(Context,Output);
+  AssertEquals('Single pass','2',Output.Data);
+end;
+
+procedure TTestMustacheElement.TestSectionValueArray2ElValueSuper;
+Var
+  T : TMustacheElement;
+
+begin
+  Context.SetValue('s','[]');
+  Context.SetValue('s[0]','{}');
+  Context.SetValue('s[0].b','1');
+  Context.SetValue('s[1]','{}');
+  Context.SetValue('s[1].b','2');
+  Context.SetValue('a','.a.');
+  Fel:=TMustacheSectionElement.Create(metSection,Nil,0);
+  Fel.Data:='s';
+  T:=TMustacheVariableElement.Create(metVariable,Nil,0);
+  Fel.AddChild(T);
+  T.Data:='a';
+  T:=TMustacheVariableElement.Create(metVariable,Nil,0);
+  Fel.AddChild(T);
+  T.Data:='b';
+  Fel.Render(Context,Output);
+  AssertEquals('Single pass','.a.1.a.2',Output.Data);
+end;
+
+procedure TTestMustacheElement.TestParentElement;
+
+Var
+  SEl : TMustacheElement;
+
+begin
+  Fel:=TMustacheParentElement.Create(metSection,Nil,0);
+  Sel:=TMustacheTextElement.Create(metText,Fel,0);
+  AssertSame('Parent stored',Fel,Sel.Parent);
+  AssertEquals('Not added to parent',0,FEl.ChildCount);
+  Fel.AddChild(Sel);
+  AssertEquals('added to parent - count',1,FEl.ChildCount);
+  AssertSame('added to parent - stored',Sel,FEl.Children[0]);
+end;
+
+procedure TTestMustacheElement.TestParentElementRender;
+
+Var
+  SEl : TMustacheElement;
+
+begin
+  Fel:=TMustacheParentElement.Create(metSection,Nil,0);
+  Sel:=TMustacheTextElement.Create(metText,Fel,0);
+  Sel.Data:='a';
+  Fel.AddChild(Sel);
+  Sel:=TMustacheTextElement.Create(metText,Fel,0);
+  Sel.Data:='b';
+  Fel.AddChild(Sel);
+  Sel:=TMustacheTextElement.Create(metText,Fel,0);
+  Sel.Data:='c';
+  Fel.AddChild(Sel);
+  Fel.Render(Context,Output);
+  AssertEquals('Correct output','abc',Output.Data);
+end;
+
+procedure TTestMustacheElement.TestParentElementRenderPrefix;
+Var
+  SEl : TMustacheElement;
+
+begin
+  Fel:=TMustacheParentElement.Create(metSection,Nil,0);
+  Sel:=TMustacheTextElement.Create(metText,Fel,0);
+  Sel.Data:='a'#10'b';
+  Fel.AddChild(Sel);
+  Sel:=TMustacheTextElement.Create(metText,Fel,0);
+  Sel.Data:='d'#10'e';
+  Fel.AddChild(Sel);
+  Sel:=TMustacheTextElement.Create(metText,Fel,0);
+  Sel.Data:='f'#10;
+  Fel.AddChild(Sel);
+  Fel.Render(Context,Output,'  ');
+  AssertEquals('Correct output','a'#10'  bd'#10'  ef'#10,Output.Data);
+end;
+
+{ TTestMustacheOutput }
+
+procedure TTestMustacheOutput.TestStringOutput;
+
+Var
+  SO : TMustacheStringOutput;
+
+begin
+  SO:=TMustacheStringOutput.Create;
+  try
+     AssertEquals('Empty start','',SO.Data);
+     SO.Output('abc');
+     AssertEquals('Output 1','abc',SO.Data);
+     SO.Output('def');
+     AssertEquals('Output 2','abcdef',SO.Data);
+  finally
+    SO.Free;
+  end;
+
+end;
+
+function TTestMustacheParser.CreateParser: TMustacheParser;
+begin
+  Result:=TMustacheParser.Create;
+end;
+
+procedure TTestMustacheParser.SetUp;
+begin
+  inherited SetUp;
+end;
+
+procedure TTestMustacheParser.TearDown;
+begin
+  inherited TearDown;
+end;
+
+procedure TTestMustacheParser.TestEmpty;
+
+begin
+  AssertNotNull('Have parser',Parser);
+  AssertNull('Have no result',ParseResult);
+  AssertEquals('Have no template','',Template);
+end;
+
+
+procedure TTestMustacheParser.TestText;
+
+begin
+  Template:='a simple text';
+  CallParser;
+  AssertResultCount(1);
+  AssertElement(0,metText,'a simple text');
+end;
+
+procedure TTestMustacheParser.TestVariable;
+
+Var
+  el : TMustacheVariableElement;
+
+begin
+  Template:='{{a}}';
+  CallParser;
+  AssertResultCount(1);
+  el:=AssertElement(0,metVariable,'a',TMustacheVariableElement) as TMustacheVariableElement;
+  AssertFalse('unescape',El.NoUnescape);
+end;
+
+procedure TTestMustacheParser.TestVariableErrNonClosed;
+
+begin
+  Template:='{{a';
+  AssertException('Have error',EMustache,@CallParser,'Tag {{ opened on position 1 but not closed.');
+  Template:='{{a}';
+  AssertException('Have error',EMustache,@CallParser,'Tag {{ opened on position 1 but not closed.');
+end;
+
+procedure TTestMustacheParser.TestVariableAlternateStartStop;
+Var
+  el : TMustacheVariableElement;
+
+begin
+  Parser.StartTag:='<<';
+  Parser.StopTag:='>>';
+  Template:='<<a>>';
+  CallParser;
+  AssertResultCount(1);
+  el:=AssertElement(0,metVariable,'a',TMustacheVariableElement) as TMustacheVariableElement;
+  AssertFalse('unescape',El.NoUnescape);
+end;
+
+procedure TTestMustacheParser.TestDottedVariable;
+begin
+  Template:='{{a.b}}';
+  CallParser;
+  AssertResultCount(1);
+  AssertElement(0,metVariable,'a.b');
+end;
+
+procedure TTestMustacheParser.TestVariableNoUnescape;
+Var
+  el : TMustacheVariableElement;
+
+begin
+  Template:='{{{a}}}';
+  CallParser;
+  AssertResultCount(1);
+  el:=AssertElement(0,metVariable,'a',TMustacheVariableElement) as TMustacheVariableElement;
+  AssertTrue('unescape',El.NoUnescape);
+end;
+
+procedure TTestMustacheParser.TestVariableNoUnescapeErrNonClosed;
+begin
+  Template:='{{{a';
+  AssertException('Have error',EMustache,@CallParser,'Tag {{ opened on position 1 but not closed.');
+  Template:='{{{a}';
+  AssertException('Have error',EMustache,@CallParser,'Tag {{ opened on position 1 but not closed.');
+  Template:='{{{a}}';
+  AssertException('Have error',EMustache,@CallParser,'Tag {{ opened on position 1 but not closed.');
+end;
+
+procedure TTestMustacheParser.TestVariableNoUnescapeAlternateStartStop;
+
+Var
+  el : TMustacheVariableElement;
+
+begin
+  Parser.StartTag:='<<';
+  Parser.StopTag:='>>';
+  Template:='<<{a}>>';
+  CallParser;
+  AssertResultCount(1);
+  el:=AssertElement(0,metVariable,'a',TMustacheVariableElement) as TMustacheVariableElement;
+  AssertTrue('unescape',El.NoUnescape);
+end;
+
+procedure TTestMustacheParser.TestComment;
+
+begin
+  Parser.StartTag:='<<';
+  Parser.StopTag:='>>';
+  Template:='<<! a comment>>';
+  CallParser;
+  AssertResultCount(1);
+  AssertElement(0,metComment,' a comment',TMustacheTextElement);
+end;
+
+procedure TTestMustacheParser.TestCommentSurround;
+begin
+  Template:='ab{{! a comment}}cd';
+  CallParser;
+  AssertResultCount(3);
+  AssertElement(0,metText,'ab',TMustacheTextElement);
+  AssertElement(1,metComment,' a comment',TMustacheTextElement);
+  AssertElement(2,metText,'cd',TMustacheTextElement);
+end;
+
+procedure TTestMustacheParser.TestCommentStandalone;
+begin
+  Template:='a'+sLineBreak+'{{! a comment}}'+sLineBreak+'b';
+  CallParser;
+  AssertResultCount(3);
+  AssertElement(0,metText,'a'+sLineBreak,TMustacheTextElement);
+  AssertElement(1,metComment,' a comment',TMustacheTextElement);
+  AssertElement(2,metText,'b',TMustacheTextElement);
+end;
+
+procedure TTestMustacheParser.TestCommentStandaloneSpaced;
+begin
+  Template:='a'+sLineBreak+'  {{! a comment}}  '+sLineBreak+'b';
+  CallParser;
+  AssertResultCount(3);
+  AssertElement(0,metText,'a'+sLineBreak,TMustacheTextElement);
+  AssertElement(1,metComment,' a comment',TMustacheTextElement);
+  AssertElement(2,metText,'b',TMustacheTextElement);
+end;
+
+procedure TTestMustacheParser.TestSetDelimiter;
+
+begin
+  Template:='{{=<< >>=}}<<! a comment>>';
+  CallParser;
+  AssertResultCount(1);
+  AssertElement(0,metComment,' a comment',TMustacheTextElement);
+end;
+
+procedure TTestMustacheParser.TestSetDelimiterErrInvalid;
+begin
+  Template:='{{=== ===}}';
+  AssertException('Have error',EMustache,@CallParser,'Invalid set delimiter Stop value: == in "== =="');
+end;
+
+procedure TTestMustacheParser.TestSection;
+
+Var
+  el : TMustacheSectionElement;
+
+begin
+  Template:='{{#a}}{{/a}}';
+  CallParser;
+  AssertResultCount(1);
+  el:=AssertElement(0,metSection,'a',TMustacheSectionElement) as TMustacheSectionElement;
+  AssertEquals('No elements in section',0,el.ChildCount);
+end;
+
+procedure TTestMustacheParser.TestSectionNested;
+
+Var
+  el : TMustacheSectionElement;
+
+begin
+  Template:='{{#a}}{{#b}}{{/b}}{{/a}}';
+  CallParser;
+  AssertResultCount(1);
+  el:=AssertElement(0,metSection,'a',TMustacheSectionElement) as TMustacheSectionElement;
+  AssertEquals('elements in section',1,el.ChildCount);
+  el:=AssertElement(el,0,metSection,'b',TMustacheSectionElement) as TMustacheSectionElement;
+  AssertEquals('elements in section sub',0,el.ChildCount);
+end;
+
+procedure TTestMustacheParser.TestSectionErrNotClosed;
+
+begin
+  Template:='{{#a}}';
+  AssertException('Have error',EMustache,@CallParser,'Structural error: Section "a" on position 1 is not closed.');
+end;
+
+procedure TTestMustacheParser.TestSectionErrWrongClosed;
+begin
+  Template:='{{#a}}{{#b}}{{/a}}{{/b}}';
+  AssertException('Have error',EMustache,@CallParser,'Structural error: Section "b" on position 7 is closed by tag "a" on position 13.');
+end;
+
+procedure TTestMustacheParser.TestSectionErrNotStarted;
+begin
+  Template:='{{/a}}';
+  AssertException('Have error',EMustache,@CallParser,'Structural error: Section "a" on position 1 was never opened.');
+end;
+
+procedure TTestMustacheParser.TestTextSection;
+
+Var
+  el : TMustacheSectionElement;
+
+begin
+  Template:='{{#a}}bbb{{/a}}';
+  CallParser;
+  AssertResultCount(1);
+  el:=AssertElement(0,metSection,'a',TMustacheSectionElement) as TMustacheSectionElement;
+  AssertEquals('No elements in section',1,el.ChildCount);
+  AssertElement(el,0,metText,'bbb');
+end;
+
+procedure TTestMustacheParser.TestPartial;
+
+Var
+  el : TMustachePartialElement;
+
+begin
+  AddPartial('part','bcd');
+  Template:='a{{>part}}e';
+  CallParser;
+  AssertResultCount(3);
+  AssertElement(0,metText,'a',TMustacheTextElement);
+  el:=AssertElement(1,metPartial,'part',TMustachePartialElement) as TMustachePartialElement;
+  AssertElement(2,metText,'e',TMustacheTextElement);
+  AssertEquals('Correct partial','part',El.Partial.Data);
+  AssertEquals('Correct partial',1,El.Partial.ChildCount);
+  AssertElement(el.Partial,0,metText,'bcd',TMustacheTextElement);
+end;
+
+
+initialization
+  RegisterTests([TTestMustacheParser,TTestMustacheOutput,TTestMustacheElement]);
+end.
+

+ 188 - 0
packages/fcl-mustache/tests/tcspecs.pas

@@ -0,0 +1,188 @@
+{
+    This file is part of the Free Pascal Run time library.
+    Copyright (c) 2021 by Michael Van Canneyt ([email protected])
+
+    testcase for official Mustache tests
+
+    See the File COPYING.FPC, included in this distribution,
+    for details about the copyright.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+
+ **********************************************************************}
+unit tcspecs;
+
+{$mode objfpc}{$H+}
+
+interface
+
+uses
+  Classes, SysUtils, fpcunit, testregistry, fpmustache, fpjson, jsonparser;
+
+Type
+
+  { TTestMustacheSpecs }
+
+  TTestMustacheSpecs = class(TTestCase)
+  private
+    FTests: TJSONArray;
+    procedure RunMustacheTest(aIndex: Integer; aTest: TJSONObject);
+  Public
+    class var BaseDir : string;
+  Public
+    Procedure Setup; override;
+    Procedure TearDown; override;
+    Procedure DoTest(aFileName : string);
+    Property Tests : TJSONArray Read FTests;
+  Published
+    Procedure TestComments;
+    Procedure TestDelimiters;
+    Procedure TestInterpolation;
+    Procedure TestInverted;
+    Procedure TestPartials;
+    Procedure TestSections;
+  end;
+
+
+implementation
+
+{ TTestMustacheSpecs }
+
+procedure TTestMustacheSpecs.RunMustacheTest(aIndex : Integer; aTest : TJSONObject);
+
+Var
+  M : TMustache;
+  aTempl,aErr,aRes,aName : TMustacheString;
+  Parts : TJSONObject;
+  I : Integer;
+  Ok : Boolean;
+
+  Procedure TreeDump;
+
+  begin
+    if not OK then
+      begin
+      Writeln('Tree dump:');
+      Writeln(M.Dump);
+      end;
+  end;
+
+  Procedure InputDump;
+
+  begin
+    Writeln('Test : ',aIndex);
+    writeln(aTempl);
+    writeln(StringReplace(StringReplace(aTempl,#10,' ',[rfReplaceAll]),#13,' ',[rfReplaceAll]));
+    aName:='';
+    While Length(aName)<Length(aTempl) do
+      aName:=AName+'1234567890';
+    Writeln(aName);
+  end;
+
+begin
+  OK:=False;
+  aTempl:=aTest.Get('template','');
+  // InputDump;
+  M:=TMustache.CreateMustache(Nil,aTempl);
+  try
+    // Load partials
+    Parts:=aTest.Get('partials',TJSONObject(Nil));
+    if Assigned(Parts) then
+      for I:=0 to Parts.Count-1 do
+        M.Partials.Add(Parts.Names[i]+'='+Parts.Items[i].AsString);
+    // Set test name and run tests
+    aName:='Test '+IntToStr(aIndex)+': '+aTest.Get('name','');
+    Try
+      aErr:='';
+      aRes:=m.Render(aTest.Get('data',TJSONObject(Nil)));
+    except
+      on e : exception do
+        aErr:=E.ClassName+' '+E.message;
+    end;
+    if aErr<>'' then
+      Fail(aName+': Unexpected error: '+aErr);
+    AssertEquals(aName,aTest.Get('expected',''),aRes);
+    OK:=true;
+  finally
+    // TreeDump;
+    M.Free;
+  end;
+end;
+
+procedure TTestMustacheSpecs.Setup;
+begin
+  inherited Setup;
+end;
+
+procedure TTestMustacheSpecs.TearDown;
+begin
+  inherited TearDown;
+end;
+
+procedure TTestMustacheSpecs.DoTest(aFileName: string);
+
+Var
+  I : Integer;
+  F : TFileStream;
+  D : TJSONData;
+  FN : String;
+
+begin
+  D:=Nil;
+  FN:=IncludeTrailingPathDelimiter(BaseDir)+aFileName+'.json';
+  F:=TFileStream.Create(FN,fmOpenRead or fmShareDenyWrite);
+  try
+    D:=GetJSON(F);
+    if D is TJSONObject then
+      begin
+      Ftests:=(D as TJSONObject).Get('tests',TJSONArray(Nil));
+      if (FTests=Nil) then
+        Fail('Invalid mustache tests in '+FN);
+      end
+    else
+      Fail('Invalid JSON object in '+FN);
+    For I:=0 to Tests.Count-1 do
+      RunMustacheTest(I,Tests.Items[i] as TJSONObject);
+  finally
+    D.Free;
+    F.Free;
+  end;
+end;
+
+procedure TTestMustacheSpecs.TestComments;
+begin
+  DoTest('comments');
+end;
+
+procedure TTestMustacheSpecs.TestDelimiters;
+begin
+  DoTest('delimiters');
+end;
+
+procedure TTestMustacheSpecs.TestInterpolation;
+begin
+  DoTest('interpolation');
+end;
+
+procedure TTestMustacheSpecs.TestInverted;
+begin
+  DoTest('inverted');
+end;
+
+procedure TTestMustacheSpecs.TestPartials;
+begin
+  DoTest('partials');
+end;
+
+procedure TTestMustacheSpecs.TestSections;
+begin
+  DoTest('sections');
+end;
+
+begin
+  TTestMustacheSpecs.BaseDir:='spec/';
+  RegisterTest(TTestMustacheSpecs);
+end.
+

+ 88 - 0
packages/fcl-mustache/tests/testmustache.lpi

@@ -0,0 +1,88 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<CONFIG>
+  <ProjectOptions>
+    <Version Value="12"/>
+    <General>
+      <Flags>
+        <SaveOnlyProjectUnits Value="True"/>
+        <MainUnitHasCreateFormStatements Value="False"/>
+        <MainUnitHasTitleStatement Value="False"/>
+        <MainUnitHasScaledStatement Value="False"/>
+      </Flags>
+      <SessionStorage Value="InProjectDir"/>
+      <Title Value="testmustache"/>
+      <UseAppBundle Value="False"/>
+      <ResourceType Value="res"/>
+    </General>
+    <BuildModes>
+      <Item Name="Default" Default="True"/>
+    </BuildModes>
+    <PublishOptions>
+      <Version Value="2"/>
+      <UseFileFilters Value="True"/>
+    </PublishOptions>
+    <RunParams>
+      <FormatVersion Value="2"/>
+    </RunParams>
+    <RequiredPackages>
+      <Item>
+        <PackageName Value="FCL"/>
+      </Item>
+    </RequiredPackages>
+    <Units>
+      <Unit>
+        <Filename Value="testmustache.lpr"/>
+        <IsPartOfProject Value="True"/>
+      </Unit>
+      <Unit>
+        <Filename Value="tcmustache.pas"/>
+        <IsPartOfProject Value="True"/>
+      </Unit>
+      <Unit>
+        <Filename Value="tcspecs.pas"/>
+        <IsPartOfProject Value="True"/>
+      </Unit>
+      <Unit>
+        <Filename Value="tcexmustache.pas"/>
+        <IsPartOfProject Value="True"/>
+      </Unit>
+      <Unit>
+        <Filename Value="tcbasemustache.pas"/>
+        <IsPartOfProject Value="True"/>
+      </Unit>
+      <Unit>
+        <Filename Value="tcdbmustache.pas"/>
+        <IsPartOfProject Value="True"/>
+      </Unit>
+    </Units>
+  </ProjectOptions>
+  <CompilerOptions>
+    <Version Value="11"/>
+    <Target>
+      <Filename Value="testmustache"/>
+    </Target>
+    <SearchPaths>
+      <IncludeFiles Value="$(ProjOutDir)"/>
+      <OtherUnitFiles Value="../src"/>
+      <UnitOutputDirectory Value="lib/$(TargetCPU)-$(TargetOS)"/>
+    </SearchPaths>
+    <Linking>
+      <Debugging>
+        <UseHeaptrc Value="True"/>
+      </Debugging>
+    </Linking>
+  </CompilerOptions>
+  <Debugging>
+    <Exceptions>
+      <Item>
+        <Name Value="EAbort"/>
+      </Item>
+      <Item>
+        <Name Value="ECodetoolError"/>
+      </Item>
+      <Item>
+        <Name Value="EFOpenError"/>
+      </Item>
+    </Exceptions>
+  </Debugging>
+</CONFIG>

+ 29 - 0
packages/fcl-mustache/tests/testmustache.lpr

@@ -0,0 +1,29 @@
+program testmustache;
+
+{$mode objfpc}{$H+}
+
+uses
+  Classes, consoletestrunner, tcmustache, tcspecs,
+  tcexmustache, tcbasemustache, tcdbmustache;
+
+type
+
+  { TMyTestRunner }
+
+  TMyTestRunner = class(TTestRunner)
+  protected
+  // override the protected methods of TTestRunner to customize its behavior
+  end;
+
+var
+  Application: TMyTestRunner;
+
+begin
+  DefaultFormat:=fPlain;
+  DefaultRunAllTests:=True;
+  Application := TMyTestRunner.Create(nil);
+  Application.Initialize;
+  Application.Title := 'FPCUnit Console test runner';
+  Application.Run;
+  Application.Free;
+end.

+ 50 - 9
packages/fcl-passrc/src/pasresolver.pp

@@ -1690,6 +1690,7 @@ type
     procedure FinishMethodImplHeader(ImplProc: TPasProcedure); virtual;
     procedure FinishExceptOnExpr; virtual;
     procedure FinishExceptOnStatement; virtual;
+    procedure FinishParserSpecializeType(El: TPasSpecializeType); virtual;
     procedure FinishWithDo(El: TPasImplWithDo); virtual;
     procedure FinishForLoopHeader(Loop: TPasImplForLoop); virtual;
     procedure FinishDeclaration(El: TPasElement); virtual;
@@ -2153,6 +2154,7 @@ type
     function PushHelperDotScope(HiType: TPasType): TPasDotBaseScope;
     function PushTemplateDotScope(TemplType: TPasGenericTemplateType; ErrorEl: TPasElement): TPasDotBaseScope;
     function PushDotScope(HiType: TPasType): TPasDotBaseScope;
+    function PushParserSpecializeType(SpecType: TPasSpecializeType): TPasDotBaseScope;
     function PushWithExprScope(Expr: TPasExpr): TPasWithExprScope;
     function StashScopes(NewScopeCnt: integer): integer; // returns old StashDepth
     function StashSubExprScopes: integer; // returns old StashDepth
@@ -5238,6 +5240,9 @@ begin
       begin
       // El is the first element found -> raise error
       // ToDo: use the ( as error position
+      {$IFDEF VerbosePasResolver}
+      writeln('TPasResolver.OnFindCallElements El=',GetObjPath(El));
+      {$ENDIF}
       RaiseMsg(20170216151525,nIllegalQualifierAfter,sIllegalQualifierAfter,
         ['(',El.ElementTypeName],Data^.Params);
       end;
@@ -7606,6 +7611,12 @@ begin
   PopScope;
 end;
 
+procedure TPasResolver.FinishParserSpecializeType(El: TPasSpecializeType);
+begin
+  if El=nil then ;
+  PopScope;
+end;
+
 procedure TPasResolver.FinishWithDo(El: TPasImplWithDo);
 begin
   PopWithScope(El);
@@ -18120,6 +18131,13 @@ begin
   SpecializeElList(GenEl,SpecEl,GenEl.Params,SpecEl.Params,true
     {$IFDEF CheckPasTreeRefCount},'TPasSpecializeType.Params'{$ENDIF});
 
+  if GenEl.SubType<>nil then
+    begin
+    PushParserSpecializeType(SpecEl);
+    SpecializeElType(GenEl,SpecEl,GenEl.SubType,SpecEl.SubType);
+    PopScope;
+    end;
+
   FinishSpecializeType(SpecEl);
   {$IFDEF VerbosePasResolver}
   //writeln('TPasResolver.SpecializeSpecializeType ',GetObjName(SpecEl.DestType),' ',GetObjName(SpecEl.CustomData));
@@ -21807,6 +21825,7 @@ end;
 procedure TPasResolver.BeginScope(ScopeType: TPasScopeType; El: TPasElement);
 begin
   case ScopeType of
+  stSpecializeType: PushParserSpecializeType(El as TPasSpecializeType);
   stWithExpr: PushWithExprScope(El as TPasExpr);
   else
     RaiseMsg(20181210163324,nNotYetImplemented,sNotYetImplemented+' BeginScope',[IntToStr(ord(ScopeType))],nil);
@@ -21824,9 +21843,10 @@ begin
   stResourceString: FinishResourcestring(El as TPasResString);
   stProcedure: FinishProcedure(El as TPasProcedure);
   stProcedureHeader: FinishProcedureType(El as TPasProcedureType);
+  stSpecializeType: FinishParserSpecializeType(El as TPasSpecializeType);
+  stWithExpr: FinishWithDo(El as TPasImplWithDo);
   stExceptOnExpr: FinishExceptOnExpr;
   stExceptOnStatement: FinishExceptOnStatement;
-  stWithExpr: FinishWithDo(El as TPasImplWithDo);
   stForLoopHeader: FinishForLoopHeader(El as TPasImplForLoop);
   stDeclaration: FinishDeclaration(El);
   stAncestors: FinishAncestors(El as TPasClassType);
@@ -22784,6 +22804,12 @@ begin
     Result:=PushHelperDotScope(HiType);
 end;
 
+function TPasResolver.PushParserSpecializeType(SpecType: TPasSpecializeType
+  ): TPasDotBaseScope;
+begin
+  Result:=PushDotScope(SpecType.DestType);
+end;
+
 function TPasResolver.PushWithExprScope(Expr: TPasExpr): TPasWithExprScope;
 var
   WithEl: TPasImplWithDo;
@@ -25520,6 +25546,7 @@ function TPasResolver.ResolvedElIsClassOrRecordInstance(
   const ResolvedEl: TPasResolverResult): boolean;
 var
   TypeEl: TPasType;
+  C: TClass;
 begin
   Result:=false;
   if ResolvedEl.BaseType<>btContext then exit;
@@ -25532,10 +25559,14 @@ begin
   else if TypeEl.ClassType=TPasRecordType then
   else
     exit;
-  if (ResolvedEl.IdentEl is TPasVariable)
-      or (ResolvedEl.IdentEl.ClassType=TPasArgument)
-      or (ResolvedEl.IdentEl.ClassType=TPasResultElement) then
-    exit(true);
+  if ResolvedEl.IdentEl<>nil then
+    begin
+    C:=ResolvedEl.IdentEl.ClassType;
+    if C.InheritsFrom(TPasVariable)
+        or (C=TPasArgument)
+        or (C=TPasResultElement) then
+      exit(true);
+    end;
 end;
 
 function TPasResolver.GetResolver(El: TPasElement): TPasResolver;
@@ -27709,7 +27740,9 @@ procedure TPasResolver.ComputeElement(El: TPasElement; out
   var
     TypeEl: TPasType;
   begin
-    if SpecType.CustomData is TPasSpecializeTypeData then
+    if SpecType.SubType<>nil then
+      ComputeElement(SpecType.SubType,ResolvedEl,Flags,StartEl)
+    else if SpecType.CustomData is TPasSpecializeTypeData then
       begin
       TypeEl:=TPasSpecializeTypeData(SpecType.CustomData).SpecializedType;
       if TypeEl=nil then
@@ -28393,6 +28426,7 @@ function TPasResolver.ResolveAliasType(aType: TPasType; SkipTypeAlias: boolean
   ): TPasType;
 var
   C: TClass;
+  SpecType: TPasSpecializeType;
 begin
   while aType<>nil do
     begin
@@ -28406,9 +28440,16 @@ begin
       aType:=NoNil(TResolvedReference(aType.CustomData).Declaration) as TPasType
     else if C=TPasSpecializeType then
       begin
-      if aType.CustomData is TPasSpecializeTypeData then
-        exit(TPasSpecializeTypeData(aType.CustomData).SpecializedType);
-      aType:=TPasSpecializeType(aType).DestType;
+      SpecType:=TPasSpecializeType(aType);
+      if SpecType.SubType<>nil then
+        // e.g. a<b>.c
+        aType:=SpecType.SubType
+      else
+        begin
+        if SpecType.CustomData is TPasSpecializeTypeData then
+          exit(TPasSpecializeTypeData(SpecType.CustomData).SpecializedType);
+        aType:=SpecType.DestType;
+        end;
       end
     else
       exit(aType);

+ 1 - 0
packages/fcl-passrc/src/pasuseanalyzer.pas

@@ -2471,6 +2471,7 @@ begin
     if Param is TPasGenericTemplateType then continue;
     UseElement(Param,rraRead,false);
     end;
+  UseElType(El,El.SubType,Mode);
 end;
 
 procedure TPasAnalyzer.UseVariable(El: TPasVariable;

+ 5 - 2
packages/fcl-passrc/src/pparser.pp

@@ -157,6 +157,7 @@ type
     stResourceString, // e.g. TPasResString
     stProcedure, // also method, procedure, constructor, destructor, ...
     stProcedureHeader,
+    stSpecializeType, // calls BeginScope to resolve c in a<b>.c
     stWithExpr, // calls BeginScope after parsing every WITH-expression
     stExceptOnExpr,
     stExceptOnStatement,
@@ -1766,6 +1767,8 @@ begin
     ReadSpecializeArguments(ST,ST.Params);
     if CurToken<>tkGreaterThan then
       ParseExcTokenError('[20190801113005]');
+    // Important: resolve type reference AFTER args, because arg count is needed
+    ST.DestType:=ResolveTypeReference(GenName,ST,ST.Params.Count);
 
     // Check for cascaded specialize A<B>.C or A<B>.C<D>
     NextToken;
@@ -1774,10 +1777,10 @@ begin
     else
       begin
       NextToken;
+      Engine.BeginScope(stSpecializeType,ST);
       ST.SubType:=ParseSimpleType(ST,CurSourcePos,GenName,False);
+      Engine.FinishScope(stSpecializeType,ST);
       end;
-    // Important: resolve type reference AFTER args, because arg count is needed
-    ST.DestType:=ResolveTypeReference(GenName,ST,ST.Params.Count);
 
     Engine.FinishScope(stTypeDef,ST);
     Result:=ST;

+ 27 - 0
packages/fcl-passrc/tests/tcresolvegenerics.pas

@@ -157,6 +157,7 @@ type
     procedure TestGenProc_TypeParamCntOverloadNoParams;
     procedure TestGenProc_TypeParamWithDefaultParamDelphiFail;
     procedure TestGenProc_ParamSpecWithT;
+    procedure TestGenProc_ParamSpecWithTNestedType;
     // ToDo: NestedResultAssign
 
     // generic function infer types
@@ -2554,6 +2555,32 @@ begin
   ParseProgram;
 end;
 
+procedure TTestResolveGenerics.TestGenProc_ParamSpecWithTNestedType;
+begin
+  StartProgram(false);
+  Add([
+  '{$mode delphi}',
+  'type',
+  '  TObject = class end;',
+  '  TBird<T> = class',
+  '  type',
+  '    TEvent = procedure(aSender: T);',
+  '  end;',
+  'procedure Fly<T>(Event: TBird<T>.TEvent; Sender: T);',
+  'begin',
+  '  Event(Sender);',
+  'end;',
+  'procedure Run(aSender: TObject);',
+  'begin',
+  'end;',
+  'var',
+  '  Bird: TBird<TObject>;',
+  'begin',
+  '  Fly<TObject>(@Run,Bird);',
+  '']);
+  ParseProgram;
+end;
+
 procedure TTestResolveGenerics.TestGenProc_Infer_NeedExplicitFail;
 begin
   StartProgram(false);

+ 13 - 3
packages/fcl-web/src/base/custhttpapp.pp

@@ -21,7 +21,7 @@ unit custhttpapp;
 Interface
 
 uses
-  Classes, SysUtils, httpdefs, custweb, ssockets,  fphttpserver;
+  Classes, SysUtils, httpdefs, custweb, ssockets,  fphttpserver, sslbase;
 
 Type
   TCustomHTTPApplication = Class;
@@ -76,7 +76,6 @@ Type
     Procedure InitResponse(AResponse : TResponse); override;
     function WaitForRequest(out ARequest : TRequest; out AResponse : TResponse) : boolean; override;
     Function CreateServer : TEmbeddedHttpServer; virtual;
-    Property HTTPServer : TEmbeddedHttpServer Read FServer;
   Public
     Procedure Run; override;
     Procedure Terminate; override;
@@ -104,6 +103,8 @@ Type
     Property UseSSL : Boolean Read GetUseSSL Write SetUseSSL;
     // HostName to use when using SSL
     Property HostName : String Read GetHostName Write SetHostName;
+    // Access to server so you can set certificate data
+    Property HTTPServer : TEmbeddedHttpServer Read FServer;
   end;
 
   { TCustomHTTPApplication }
@@ -111,6 +112,7 @@ Type
   TCustomHTTPApplication = Class(TCustomWebApplication)
   private
     procedure FakeConnect;
+    function GetCertificateData: TCertificateData;
     function GetHostName: String;
     function GetIdle: TNotifyEvent;
     function GetIDleTimeOut: Cardinal;
@@ -133,9 +135,10 @@ Type
     procedure SetUseSSL(AValue: Boolean);
   protected
     function InitializeWebHandler: TWebHandler; override;
-    Function HTTPHandler : TFPHTTPServerHandler;
   Public
     procedure Terminate; override;
+    // Access to HTTP handler
+    Function HTTPHandler : TFPHTTPServerHandler;
     Property Address : string Read GetAddress Write SetAddress;
     Property Port : Word Read GetPort Write SetPort Default 80;
     // Max connections on queue (for Listen call)
@@ -154,6 +157,8 @@ Type
     Property UseSSL : Boolean Read GetUseSSL Write SetUseSSL;
     // Hostname to use when using SSL
     Property HostName : String Read GetHostName Write SetHostName;
+    // Access to certificate data
+    Property CertificateData : TCertificateData Read GetCertificateData;
   end;
 
 
@@ -296,6 +301,11 @@ begin
   end
 end;
 
+function TCustomHTTPApplication.GetCertificateData: TCertificateData;
+begin
+  Result:=HTTPHandler.HTTPServer.CertificateData;
+end;
+
 function TCustomHTTPApplication.GetHostName: String;
 begin
   Result:=HTTPHandler.HostName;

+ 1 - 0
packages/fpmake_add.inc

@@ -145,4 +145,5 @@
   add_ide(ADirectory+IncludeTrailingPathDelimiter('ide'));
   add_vclcompat(ADirectory+IncludeTrailingPathDelimiter('vcl-compat'));
   add_qlunits(ADirectory+IncludeTrailingPathDelimiter('qlunits'));
+  add_mustache(ADirectory+IncludeTrailingPathDelimiter('fcl-mustache'));
   

+ 6 - 0
packages/fpmake_proc.inc

@@ -821,4 +821,10 @@ begin
 {$include qlunits/fpmake.pp}
 end;
 
+procedure add_mustache(const ADirectory: string);
+begin
+  with Installer do
+{$include fcl-mustache/fpmake.pp}
+end;
+
 {$include ide/fpmake.pp}

+ 85 - 30
packages/fpmkunit/src/fpmkunit.pp

@@ -1230,6 +1230,7 @@ Type
     FInteractive : boolean;
     FProgressMax : integer;
     FProgressCount : integer;
+    FIndentCount : integer;
     FExternalPackages : TPackages;
     // Events
     FOnLog: TLogEvent;
@@ -1238,7 +1239,9 @@ Type
     FOnFinishCopy: TNotifyEvent;
 
     FCachedlibcPath: string;
+{$ifndef NO_THREADING}
     FGeneralCriticalSection: TRTLCriticalSection;
+{$endif NO_THREADING}
 {$ifdef HAS_UNIT_ZIPPER}
     FZipper: TZipper;
     FGZFileStream: TGZFileStream;
@@ -1452,6 +1455,7 @@ Type
     FCompilationOK: boolean;
     FDone: boolean;
     FErrorMessage: string;
+    FWorkerPrefix: string;
     FNotifyMainThreadEvent: PRTLEvent;
     FNotifyStartTask: PRTLEvent;
     FPackage: TPackage;
@@ -3407,17 +3411,17 @@ begin
       begin
       { synchronise with ReadWriteBarrier in mainthread for same reason as above }
       ReadWriteBarrier;
-      FBuildEngine.log(vlInfo,'Compiling: '+APackage.Name);
+      FBuildEngine.log(vlInfo,FWorkerPrefix+'Compiling: '+APackage.Name);
       FCompilationOK:=false;
       try
         FBuildEngine.Compile(APackage);
         FCompilationOK:=true;
-        FBuildEngine.log(vlInfo,'Done compiling: '+APackage.Name);
+        FBuildEngine.log(vlInfo,FWorkerPrefix+'Done compiling: '+APackage.Name);
         RaiseMainEvent;
       except
         on E: Exception do
           begin
-            FErrorMessage := 'Failed compiling: '+APackage.Name+': '+E.Message;
+            FErrorMessage := FWorkerPrefix+'Failed compiling: '+APackage.Name+': '+E.Message;
             FBuildEngine.log(vlInfo,FErrorMessage);
             RaiseMainEvent;
           end;
@@ -6007,6 +6011,7 @@ begin
   // With --start-dir=/path/to/sources.
   FStartDir:=includeTrailingPathDelimiter(GetCurrentDir);
   FExternalPackages:=TPackages.Create(TPackage);
+  FIndentCount:=0;
   FNotifyEventCollection := TNotifyEventCollection.create([neaAfterCompile, neaBeforeCompile, neaAfterInstall, neaBeforeInstall,
                                                            neaAfterClean, neaBeforeClean, neaAfterArchive, neaBeforeArchive,
                                                            neaAfterManifest, neaBeforeManifest, neaAfterPkgList, neaBeforePkgList,
@@ -6021,6 +6026,8 @@ destructor TBuildEngine.Destroy;
 begin
   FreeAndNil(FExternalPackages);
   FreeAndNil(FNotifyEventCollection);
+  If FIndentCount<>0 then
+    Log(vlDebug,Format('Log level at exit is %d',[FIndentCount]));
 
 {$ifndef NO_THREADING}
   DoneCriticalsection(FGeneralCriticalSection);
@@ -6419,13 +6426,33 @@ end;
 
 procedure TBuildEngine.LogIndent;
 begin
-  GLogPrefix:=GLogPrefix+'  ';
+{$ifndef NO_THREADING}
+  EnterCriticalSection(FGeneralCriticalSection);
+{$endif NO_THREADING}
+  Inc(FIndentCount);
+  if not (vlDebug in Installer.FLogLevels) then
+    GLogPrefix:=GLogPrefix+'  '
+  else
+    GLogPrefix:=IntToStr(FIndentCount)+'> ';
+{$ifndef NO_THREADING}
+  LeaveCriticalSection(FGeneralCriticalSection);
+{$endif NO_THREADING}
 end;
 
 
 procedure TBuildEngine.LogUnIndent;
 begin
-  Delete(GLogPrefix,1,2);
+{$ifndef NO_THREADING}
+  EnterCriticalSection(FGeneralCriticalSection);
+{$endif NO_THREADING}
+  Dec(FIndentCount);
+  if not (vlDebug in Installer.FLogLevels) then
+    Delete(GLogPrefix,1,2)
+  else
+    GLogPrefix:=IntToStr(FIndentCount)+'> ';
+{$ifndef NO_THREADING}
+  LeaveCriticalSection(FGeneralCriticalSection);
+{$endif NO_THREADING}
 end;
 
 
@@ -6433,10 +6460,19 @@ procedure TBuildEngine.Log(Level: TVerboseLevel; Msg: String);
 begin
   If Assigned(FOnLog) then
     begin
+{$ifndef NO_THREADING}
+      EnterCriticalSection(FGeneralCriticalSection);
+      try
+{$endif NO_THREADING}
       if Level in [vlInfo,vlDebug] then
         FOnLog(Level,GLogPrefix+Msg)
       else
         FOnLog(Level,Msg);
+{$ifndef NO_THREADING}
+      finally
+        LeaveCriticalSection(FGeneralCriticalSection);
+      end;
+{$endif NO_THREADING}
     end;
 end;
 
@@ -6905,32 +6941,34 @@ begin
           begin
             // Debug information
             Log(vlDebug,SDbgResolvingSourcesOfTarget,[T.Name,MakeTargetString(ACPU,AOS)]);
-            LogIndent;
+            try
+              LogIndent;
 
-            case T.TargetType of
-              ttProgram,
-              ttSharedLibrary,
-              ttUnit,
-              ttImplicitUnit :
-                begin
-                  if T.FTargetSourceFileName<>'' then
-                    Log(vlDebug,SDbgSourceAlreadyResolved,[T.Name])
-                  else
-                    FindMainSource(T);
-                  if T.Dependencies.Count>0 then
-                    FindIncludeSources(T);
-                end;
-              ttExampleUnit,
-              ttExampleProgram :
-                begin
-                  if T.FTargetSourceFileName<>'' then
-                    Log(vlDebug,SDbgSourceAlreadyResolved,[T.Name])
-                  else
-                    FindExampleSource(T);
-                end;
+              case T.TargetType of
+                ttProgram,
+                ttSharedLibrary,
+                ttUnit,
+                ttImplicitUnit :
+                  begin
+                    if T.FTargetSourceFileName<>'' then
+                      Log(vlDebug,SDbgSourceAlreadyResolved,[T.Name])
+                    else
+                      FindMainSource(T);
+                    if T.Dependencies.Count>0 then
+                      FindIncludeSources(T);
+                  end;
+                ttExampleUnit,
+                ttExampleProgram :
+                  begin
+                    if T.FTargetSourceFileName<>'' then
+                      Log(vlDebug,SDbgSourceAlreadyResolved,[T.Name])
+                    else
+                      FindExampleSource(T);
+                  end;
+              end;
+            finally
+              LogUnIndent;
             end;
-
-            LogUnIndent;
           end;
       end;
   finally
@@ -7484,6 +7522,7 @@ Var
   Env : TStrings;
 begin
   Log(vlInfo,SInfoCompilingTarget,[ATarget.Name]);
+  try
   LogIndent;
   ExecuteCommands(ATarget.Commands,caBeforeCompile);
   If Assigned(ATarget.BeforeCompile) then
@@ -7514,7 +7553,9 @@ begin
         ATarget.AfterCompile(ATarget);
       ExecuteCommands(ATarget.Commands,caAfterCompile);
     end;
+  finally
   LogUnIndent;
+  end;
 end;
 
 
@@ -7525,6 +7566,7 @@ Var
   D : TDependency;
 begin
   Log(vlDebug, Format(SDbgCompilingDependenciesOfTarget, [ATarget.Name]));
+  try
   LogIndent;
   For I:=0 to ATarget.Dependencies.Count-1 do
     begin
@@ -7558,7 +7600,9 @@ begin
             Error(SErrDepUnknownTarget,[D.Value, ATarget.Name, APackage.Name]);
         end;
     end;
+  finally
   LogUnIndent;
+  end;
 end;
 
 
@@ -7567,6 +7611,7 @@ begin
   if ATarget.State<>tsNeutral then
     Error(SErrInvalidState,[ATarget.Name]);
   Log(vlDebug, Format(SDbgConsideringTarget, [ATarget.Name]));
+  try
   LogIndent;
   ATarget.FTargetState:=tsConsidering;
   ResolveDependencies(ATarget.Dependencies,ATarget.Collection as TTargets);
@@ -7578,7 +7623,9 @@ begin
     end
   else
     ATarget.FTargetState:=tsNoCompile;
+  finally
   LogUnIndent;
+  end;
 end;
 
 
@@ -8037,6 +8084,7 @@ begin
   if APackage.State<>tsNeutral then
     Error(SErrInvalidState,[APackage.Name]);
   Log(vlDebug,SDbgConsideringPackage,[APackage.Name]);
+  try
   LogIndent;
   if Defaults.ThreadsAmount=-1 then
     APackage.FTargetState:=tsConsidering;
@@ -8049,6 +8097,7 @@ begin
   else if CheckDependencies(APackage, true)=cdNotYetAvailable then
     begin
       log(vlInfo,'Delaying package '+apackage.name);
+      //LogUnIndent; Done in Finally below
       result := False;
       Exit;
     end;
@@ -8062,7 +8111,9 @@ begin
       APackage.FTargetState:=tsNoCompile;
       inc(FProgressCount);
     end;
+  finally
   LogUnIndent;
+  end;
 end;
 
 
@@ -8696,7 +8747,10 @@ begin
             begin
               Threads[Thr] := TCompileWorkerThread.Create(self,NotifyThreadWaiting);
               if assigned(Threads[Thr]) then
-                inc(ThreadCount);
+                begin
+                  inc(ThreadCount);
+                  Threads[Thr].FWorkerPrefix:=Format('(%d/%d) ',[Thr,Defaults.ThreadsAmount]);
+                end;
             end;
         except
           on E: Exception do
@@ -8712,6 +8766,7 @@ begin
           while not Finished do
             begin
               RTLeventWaitFor(NotifyThreadWaiting);
+              RTLeventResetEvent(NotifyThreadWaiting);
               for Thr:=0 to Defaults.ThreadsAmount-1 do
                 if assigned(Threads[Thr]) and not Finished then
                   ProcessThreadResult(Threads[Thr]);

+ 1 - 1
packages/openssl/src/openssl.pas

@@ -90,7 +90,7 @@ Type
 const
 // SSL and Crypto DLL arrays must have the same length and contain
 // matched pairs of DLL filenames. Place newer versions at the beginning.
-{$IFDEF WIN64}
+{$IF DEFINED(WIN64)}
   SSL_DLL_Names:    array[1..3] of string = ('libssl-1_1-x64',    'ssleay32', 'libssl32');
   Crypto_DLL_Names: array[1..3] of string = ('libcrypto-1_1-x64', 'libeay32', 'libeay32');
 {$ELSEIF DEFINED(WINDOWS)}

+ 76 - 39
packages/pastojs/src/fppas2js.pp

@@ -2082,8 +2082,8 @@ type
     Procedure CreateInitSection(El: TPasModule; Src: TJSSourceElements; AContext: TConvertContext); virtual;
     Procedure AddHeaderStatement(JS: TJSElement; PosEl: TPasElement; aContext: TConvertContext); virtual;
     Procedure AddImplHeaderStatement(JS: TJSElement; PosEl: TPasElement; aContext: TConvertContext); virtual;
-    Procedure AddDelayedInits(El: TPasModule; Src: TJSSourceElements; AContext: TConvertContext); virtual;
-    Procedure AddDelaySpecializeInit(El: TPasGenericType; Src: TJSSourceElements; AContext: TConvertContext); virtual;
+    function AddDelayedInits(El: TPasModule; Src: TJSSourceElements; AContext: TConvertContext): boolean; virtual;
+    function CreateDelaySpecializeInit(El: TPasGenericType; AContext: TConvertContext): TJSElement; virtual;
     // enum and sets
     Function CreateReferencedSet(El: TPasElement; SetExpr: TJSElement): TJSElement; virtual;
     // record
@@ -2314,6 +2314,7 @@ type
       pfVarargs = 2;
       pfExternal = 4;
       pfSafeCall = 8;
+      pfAsync = $10;
       // PropertyFlag
       pfGetFunction = 1; // getter is a function
       pfSetProcedure = 2; // setter is a function
@@ -4488,6 +4489,9 @@ begin
       AddElevatedLocal(El);
       end;
     end
+  else if ParentC=TPasImplExceptOn then
+    // except on var
+    RaiseVarModifierNotSupported(LocalVarModifiersAllowed)
   else if ParentC=TImplementationSection then
     // implementation var
     RaiseVarModifierNotSupported(ImplementationVarModifiersAllowed)
@@ -4499,7 +4503,7 @@ begin
   else
     begin
     {$IFDEF VerbosePas2JS}
-    writeln('TPas2JSResolver.FinishVariable ',GetObjName(El),' Parent=',GetObjName(El.Parent));
+    writeln('TPas2JSResolver.FinishVariable ',GetObjPath(El));
     {$ENDIF}
     RaiseNotYetImplemented(20170324151259,El);
     end;
@@ -8199,7 +8203,7 @@ Var
   ModuleName, ModVarName: String;
   IntfContext: TSectionContext;
   ImplVarSt: TJSVariableStatement;
-  HasImplUsesClause, ok, NeedRTLCheckVersion: Boolean;
+  HasImplCode, ok, NeedRTLCheckVersion: Boolean;
   Prg: TPasProgram;
   Lib: TPasLibrary;
   ImplFuncAssignSt: TJSSimpleAssignStatement;
@@ -8280,7 +8284,7 @@ begin
       Prg:=TPasProgram(El);
       if Assigned(Prg.ProgramSection) then
         AddToSourceElements(Src,ConvertDeclarations(Prg.ProgramSection,IntfContext));
-      AddDelayedInits(Prg,Src,IntfContext);
+      HasImplCode:=AddDelayedInits(Prg,Src,IntfContext);
       CreateInitSection(Prg,Src,IntfContext);
       end
     else if El is TPasLibrary then
@@ -8288,7 +8292,7 @@ begin
       Lib:=TPasLibrary(El);
       if Assigned(Lib.LibrarySection) then
         AddToSourceElements(Src,ConvertDeclarations(Lib.LibrarySection,IntfContext));
-      AddDelayedInits(Lib,Src,IntfContext);
+      HasImplCode:=AddDelayedInits(Lib,Src,IntfContext);
       CreateInitSection(Lib,Src,IntfContext);
       // ToDo: append exports
       end
@@ -8317,7 +8321,9 @@ begin
       // append initialization section
       CreateInitSection(El,Src,IntfSecCtx);
 
-      if TJSSourceElements(ImplFunc.AFunction.Body.A).Statements.Count=0 then
+      if TJSSourceElements(ImplFunc.AFunction.Body.A).Statements.Count>0 then
+        HasImplCode:=true
+      else
         begin
         // empty implementation
 
@@ -8325,18 +8331,14 @@ begin
         RemoveFromSourceElements(Src,ImplVarSt);
         // remove unneeded $mod.$implcode = function(){}
         RemoveFromSourceElements(Src,ImplFuncAssignSt);
-        HasImplUsesClause:=(El.ImplementationSection<>nil)
+        // keep impl uses section
+        HasImplCode:=(El.ImplementationSection<>nil)
                        and (length(El.ImplementationSection.UsesClause)>0);
-        end
-      else
-        begin
-        HasImplUsesClause:=true;
         end;
 
-      if HasImplUsesClause then
+      if HasImplCode then
         // add implementation uses list: [<implementation uses1>,<uses2>, ...]
         ArgArray.AddElement(CreateUsesList(El.ImplementationSection,AContext));
-
       end; // end unit
 
     if (ModScope<>nil) and (coStoreImplJS in Options) then
@@ -16564,14 +16566,20 @@ begin
       if ResultTypeInfo<>nil then
         InnerCall.AddArg(ResultTypeInfo);
       end;
-    // add param flags
+    // add procedure flags
     Flags:=0;
     if ptmVarargs in El.Modifiers then
       inc(Flags,pfVarargs);
+    if ptmAsync in El.Modifiers then
+      inc(Flags,pfAsync);
     if El.CallingConvention=ccSafeCall then
       inc(Flags,pfSafeCall);
     if Flags>0 then
+      begin
+      if not (El is TPasFunctionType) then
+        InnerCall.AddArg(CreateLiteralNull(El));
       InnerCall.AddArg(CreateLiteralNumber(El,Flags));
+      end;
 
     if El.IsOfObject then
       begin
@@ -17846,13 +17854,18 @@ begin
   IntfSec.AddImplHeaderStatement(JS);
 end;
 
-procedure TPasToJSConverter.AddDelayedInits(El: TPasModule;
-  Src: TJSSourceElements; AContext: TConvertContext);
+function TPasToJSConverter.AddDelayedInits(El: TPasModule;
+  Src: TJSSourceElements; AContext: TConvertContext): boolean;
 var
   aResolver: TPas2JSResolver;
   Hub: TPas2JSResolverHub;
   i: Integer;
+  JS: TJSElement;
+  AssignSt: TJSSimpleAssignStatement;
+  FunDecl: TJSFunctionDeclarationStatement;
+  ImplSrc: TJSSourceElements;
 begin
+  Result:=false;
   aResolver:=AContext.Resolver;
   if aResolver=nil then exit;
   if El=nil then ;
@@ -17860,12 +17873,29 @@ begin
   {$IFDEF VerbosePas2JS}
   writeln('TPasToJSConverter.AddDelayedInits Hub.JSDelaySpecializeCount=',Hub.JSDelaySpecializeCount);
   {$ENDIF}
+  ImplSrc:=nil;
   for i:=0 to Hub.JSDelaySpecializeCount-1 do
-    AddDelaySpecializeInit(Hub.JSDelaySpecializes[i],Src,AContext);
+    begin
+    JS:=CreateDelaySpecializeInit(Hub.JSDelaySpecializes[i],AContext);
+    if JS=nil then continue;
+    if ImplSrc=nil then
+      begin
+      // create  "$mod.$implcode = function(){ }"
+      AssignSt:=TJSSimpleAssignStatement(CreateElement(TJSSimpleAssignStatement,El));
+      AddToSourceElements(Src,AssignSt);
+      AssignSt.LHS:=CreateMemberExpression([GetBIName(pbivnModule),GetBIName(pbivnImplCode)]);
+      // create function(){}
+      FunDecl:=CreateFunctionSt(El,true,true);
+      AssignSt.Expr:=FunDecl;
+      ImplSrc:=TJSSourceElements(FunDecl.AFunction.Body.A);
+      end;
+    AddToSourceElements(ImplSrc,JS);
+    Result:=true;
+    end;
 end;
 
-procedure TPasToJSConverter.AddDelaySpecializeInit(El: TPasGenericType;
-  Src: TJSSourceElements; AContext: TConvertContext);
+function TPasToJSConverter.CreateDelaySpecializeInit(El: TPasGenericType;
+  AContext: TConvertContext): TJSElement;
 var
   C: TClass;
   Path: String;
@@ -17876,6 +17906,7 @@ var
   ElTypeHi, ElTypeLo: TPasType;
   aResolver: TPas2JSResolver;
 begin
+  Result:=nil;
   if not IsElementUsed(El) then exit;
   if not AContext.Resolver.IsFullySpecialized(El) then
     RaiseNotSupported(El,AContext,20201202145045,'not fully specialized, probably a bug in the analyzer');
@@ -17889,7 +17920,7 @@ begin
     Path:=CreateReferencePath(El,AContext,rpkPathAndName)+'.'+GetBIName(pbifnClassInitSpecialize);
     Call:=CreateCallExpression(El);
     Call.Expr:=CreatePrimitiveDotExpr(Path,El);
-    AddToSourceElements(Src,Call);
+    Result:=Call;
     end
   else if (C=TPasProcedureType) or (C=TPasFunctionType) then
     begin
@@ -17901,7 +17932,7 @@ begin
     DotExpr.Name:=TJSString(GetBIName(pbivnRTTIProc_InitSpec));
     Call:=CreateCallExpression(El);
     Call.Expr:=DotExpr;
-    AddToSourceElements(Src,Call);
+    Result:=Call;
     end
   else if (C=TPasArrayType) then
     begin
@@ -17928,7 +17959,7 @@ begin
     AssignSt.LHS:=CreateDotNameExpr(El,CreateTypeInfoRef(El,AContext,El),
                                    TJSString(GetBIName(pbivnRTTIArray_ElType)));
     AssignSt.Expr:=CreateTypeInfoRef(ElTypeHi,AContext,El);
-    AddToSourceElements(Src,AssignSt);
+    Result:=AssignSt;
     end
   else
     RaiseNotSupported(El,AContext,20200831115251);
@@ -19801,9 +19832,8 @@ var
 begin
   Result:=nil;
   if Args.Count=0 then
-    Result:=CreateLiteralNull(Parent)
+    Result:=TJSArrayLiteral(CreateElement(TJSArrayLiteral,Parent))
   else
-    begin
     try
       Params:=TJSArrayLiteral(CreateElement(TJSArrayLiteral,Parent));
       for i:=0 to Args.Count-1 do
@@ -19813,7 +19843,6 @@ begin
       if Result=nil then
         Params.Free;
     end;
-  end;
 end;
 
 procedure TPasToJSConverter.AddRTTIArgument(Arg: TPasArgument;
@@ -20119,6 +20148,7 @@ var
   OptionsEl: TJSObjectLiteral;
   ResultTypeInfo: TJSElement;
   Call: TJSCallExpression;
+  Flags: Integer;
 
   procedure AddOption(const aName: String; JS: TJSElement);
   var
@@ -20128,8 +20158,6 @@ var
     if OptionsEl=nil then
       begin
       OptionsEl:=TJSObjectLiteral(CreateElement(TJSObjectLiteral,Proc));
-      if ResultTypeInfo=nil then
-        Call.AddArg(CreateLiteralNull(Proc));
       Call.AddArg(OptionsEl);
       end;
     ObjLit:=OptionsEl.Elements.AddElement;
@@ -20140,7 +20168,7 @@ var
 var
   FunName: String;
   C: TClass;
-  MethodKind, Flags: Integer;
+  MethodKind: Integer;
   ResultEl: TPasResultElement;
   ProcScope, OverriddenProcScope: TPasProcedureScope;
   OverriddenClass: TPasClassType;
@@ -20198,6 +20226,19 @@ begin
     // param params as []
     Call.AddArg(CreateRTTIArgList(Proc,Proc.ProcType.Args,AContext));
 
+    // optional params:
+    ResultTypeInfo:=nil;
+    Flags:=0;
+    if Proc.IsStatic then
+      inc(Flags,pfStatic);
+    if ptmVarargs in Proc.ProcType.Modifiers then
+      inc(Flags,pfVarargs);
+    if ptmAsync in Proc.ProcType.Modifiers then
+      inc(Flags,pfAsync);
+    if Proc.IsExternal then
+      inc(Flags,pfExternal);
+    Attr:=aResolver.GetAttributeCalls(Members,Index);
+
     // param resulttype as typeinfo reference
     if C.InheritsFrom(TPasFunction) then
       begin
@@ -20206,18 +20247,14 @@ begin
       if ResultTypeInfo<>nil then
         Call.AddArg(ResultTypeInfo);
       end;
+    if (ResultTypeInfo=nil) and ((Flags>0) or (length(Attr)>0)) then
+      Call.AddArg(CreateLiteralNull(Proc));
+
+    // flags if needed
+    if (Flags>0) or (length(Attr)>0) then
+      Call.AddArg(CreateLiteralNumber(Proc,Flags));
 
     // param options if needed as {}
-    Flags:=0;
-    if Proc.IsStatic then
-      inc(Flags,pfStatic);
-    if ptmVarargs in Proc.ProcType.Modifiers then
-      inc(Flags,pfVarargs);
-    if Proc.IsExternal then
-      inc(Flags,pfExternal);
-    if Flags>0 then
-      AddOption(GetBIName(pbivnRTTIProcFlags),CreateLiteralNumber(Proc,Flags));
-    Attr:=aResolver.GetAttributeCalls(Members,Index);
     if length(Attr)>0 then
       AddOption(GetBIName(pbivnRTTIMemberAttributes),
                 CreateRTTIAttributes(Attr,Proc,AContext));

+ 140 - 10
packages/pastojs/tests/tcgenerics.pas

@@ -20,7 +20,7 @@ type
     Procedure TestGen_Record_ClassVarRecord_Program;
     Procedure TestGen_Record_ClassVarRecord_UnitImpl;
     Procedure TestGen_Record_RTTI_UnitImpl;
-    // ToDo: delay RTTI with anonymous array  a:array of T, array[1..2] of T
+    procedure TestGen_Record_Delay_UsedByImplUses;
     // ToDo: type alias type as parameter, TBird = type word;
 
     // generic class
@@ -62,6 +62,7 @@ type
     Procedure TestGen_CallUnitImplProc;
     Procedure TestGen_IntAssignTemplVar;
     Procedure TestGen_TypeCastDotField;
+    Procedure TestGen_Except;
 
     // generic helper
     procedure TestGen_HelperForArray;
@@ -288,7 +289,9 @@ begin
     '}, []);']));
   CheckSource('TestGen_Record_ClassVarRecord_UnitImpl',
     LinesToStr([ // statements
-    'pas.UnitA.TAnt$G1.$initSpec();',
+    '$mod.$implcode = function () {',
+    '  pas.UnitA.TAnt$G1.$initSpec();',
+    '};',
     '']),
     LinesToStr([ // $mod.$main
     '']));
@@ -355,6 +358,53 @@ begin
     '']));
 end;
 
+procedure TTestGenerics.TestGen_Record_Delay_UsedByImplUses;
+begin
+  WithTypeInfo:=true;
+  StartProgram(true,[supTObject]);
+  AddModuleWithIntfImplSrc('UnitA.pas',
+  LinesToStr([
+    '{$modeswitch AdvancedRecords}',
+    'type',
+    '  generic TBird<T> = record',
+    '    class var a: T;',
+    '  end;',
+    '']),
+  LinesToStr([
+    '']));
+  AddModuleWithIntfImplSrc('UnitB.pas',
+  LinesToStr([
+    'procedure Fly;',
+    '']),
+  LinesToStr([
+    'uses UnitA;',
+    'type',
+    '  TFox = record',
+    '    B: word;',
+    '  end;',
+    'procedure Fly;',
+    'var Bird: specialize TBird<TFox>;',
+    'begin',
+    '  if typeinfo(Bird)<>nil then ;',
+    '  Bird.a:=Bird.a;',
+    'end;',
+    '']));
+  Add([
+  'uses UnitB;',
+  'begin',
+  '  Fly;']);
+  ConvertProgram;
+  CheckSource('TestGen_Record_Delay_UsedByImplUses',
+    LinesToStr([ // statements
+    '$mod.$implcode = function () {',
+    '  pas.UnitA.TBird$G1.$initSpec();',
+    '};',
+    '']),
+    LinesToStr([ // $mod.$main
+    'pas.UnitB.Fly();'
+    ]));
+end;
+
 procedure TTestGenerics.TestGen_ClassEmpty;
 begin
   StartProgram(false);
@@ -1201,7 +1251,9 @@ begin
     '']));
   CheckSource('TestGen_Class_ClassVarRecord_UnitImpl',
     LinesToStr([ // statements
-    'pas.UnitA.TAnt$G1.$initSpec();',
+    '$mod.$implcode = function () {',
+    '  pas.UnitA.TAnt$G1.$initSpec();',
+    '};',
     '']),
     LinesToStr([ // $mod.$main
     '']));
@@ -1453,7 +1505,6 @@ begin
     '}, []);']));
   CheckSource('TestGen_Class_ClassVarRecord_UnitImpl',
     LinesToStr([ // statements
-    //'pas.UnitA.TAnt$G1.$initSpec();',
     '']),
     LinesToStr([ // $mod.$main
     '']));
@@ -1706,7 +1757,9 @@ begin
     '  rtl.addIntf(this, pas.system.IUnknown);',
     '});',
     'this.i = null;',
-    'pas.UnitA.TAnt$G1.$initSpec();',
+    '$mod.$implcode = function () {',
+    '  pas.UnitA.TAnt$G1.$initSpec();',
+    '};',
     '']),
     LinesToStr([ // $mod.$main
     'rtl.setIntfP($mod, "i", rtl.queryIntfT($mod.TBird.$create("Create"), pas.UnitA.TAnt$G1), true);',
@@ -1898,6 +1951,77 @@ begin
     '']));
 end;
 
+procedure TTestGenerics.TestGen_Except;
+begin
+  StartProgram(false);
+  Add([
+  'type',
+  '  TObject = class end;',
+  '  generic TBird<T> = class',
+  '    Field: T;',
+  '    procedure Fly;',
+  '  end;',
+  '  Exception = class',
+  '  end;',
+  '  generic EBird<T> = class(Exception)',
+  '    Id: T;',
+  '  end;',
+  'var',
+  '  b: specialize TBird<word>;',
+  'procedure TBird.Fly;',
+  'begin',
+  '  try',
+  '  except',
+  '    on E: Exception do Fly;',
+  '    on EBird: specialize EBird<word> do EBird.Id:=3;',
+  '  else',
+  '    Fly;',
+  '  end;',
+  'end;',
+  'begin',
+  '']);
+  ConvertProgram;
+  CheckSource('TestGen_Except',
+    LinesToStr([ // statements
+    'rtl.createClass(this, "TObject", null, function () {',
+    '  this.$init = function () {',
+    '  };',
+    '  this.$final = function () {',
+    '  };',
+    '});',
+    'rtl.createClass(this, "Exception", this.TObject, function () {',
+    '});',
+    'rtl.createClass(this, "TBird$G1", this.TObject, function () {',
+    '  this.$init = function () {',
+    '    $mod.TObject.$init.call(this);',
+    '    this.Field = 0;',
+    '  };',
+    '  this.Fly = function () {',
+    '    try {} catch ($e) {',
+    '      if ($mod.Exception.isPrototypeOf($e)) {',
+    '        var E = $e;',
+    '        this.Fly();',
+    '      } else if ($mod.EBird$G1.isPrototypeOf($e)) {',
+    '        var EBird = $e;',
+    '        EBird.Id = 3;',
+    '      } else {',
+    '        this.Fly();',
+    '      }',
+    '    };',
+    '  };',
+    '}, "TBird<System.Word>");',
+    'this.b = null;',
+    'rtl.createClass(this, "EBird$G1", this.Exception, function () {',
+    '  this.$init = function () {',
+    '    $mod.Exception.$init.call(this);',
+    '    this.Id = 0;',
+    '  };',
+    '}, "EBird<System.Word>");',
+    '']),
+    LinesToStr([ // $mod.$main
+    '']));
+end;
+
 procedure TTestGenerics.TestGen_HelperForArray;
 begin
   StartProgram(false);
@@ -2424,7 +2548,9 @@ begin
     '});']));
   CheckSource('TestGen_Array_OtherUnit',
     LinesToStr([ // statements
-    'pas.UnitA.$rtti["TDyn<UnitB.TAnt>"].eltype = pas.UnitB.$rtti["TAnt"];',
+    '$mod.$implcode = function () {',
+    '  pas.UnitA.$rtti["TDyn<UnitB.TAnt>"].eltype = pas.UnitB.$rtti["TAnt"];',
+    '};',
     '']),
     LinesToStr([ // $mod.$main
     '  pas.UnitB.Run();',
@@ -2504,9 +2630,11 @@ begin
     '}, []);']));
   CheckSource('TestGen_ArrayOfUnitImplRec',
     LinesToStr([ // statements
-    'pas.UnitA.$rtti["TDyn<UnitA.TAnt>"].eltype = pas.UnitA.$rtti["TAnt"];',
-    'pas.UnitA.$rtti["TDyn<UnitA.TBird>"].eltype = pas.UnitA.$rtti["TBird"];',
-    'pas.UnitA.$rtti["TStatic<UnitA.TBird>"].eltype = pas.UnitA.$rtti["TBird"];',
+    '$mod.$implcode = function () {',
+    '  pas.UnitA.$rtti["TDyn<UnitA.TAnt>"].eltype = pas.UnitA.$rtti["TAnt"];',
+    '  pas.UnitA.$rtti["TDyn<UnitA.TBird>"].eltype = pas.UnitA.$rtti["TBird"];',
+    '  pas.UnitA.$rtti["TStatic<UnitA.TBird>"].eltype = pas.UnitA.$rtti["TBird"];',
+    '};',
     '']),
     LinesToStr([ // $mod.$main
     '']));
@@ -2673,7 +2801,9 @@ begin
     '}, []);']));
   CheckSource('TestGen_Class_ClassVarRecord_UnitImpl',
     LinesToStr([ // statements
-    'pas.UnitA.$rtti["TAnt<UnitA.TBird>"].init();',
+    '$mod.$implcode = function () {',
+    '  pas.UnitA.$rtti["TAnt<UnitA.TBird>"].init();',
+    '};',
     '']),
     LinesToStr([ // $mod.$main
     '']));

+ 38 - 37
packages/pastojs/tests/tcmodules.pas

@@ -29256,20 +29256,20 @@ begin
   CheckSource('TestRTTI_ProcType',
     LinesToStr([ // statements
     'this.$rtti.$ProcVar("TProcA", {',
-    '  procsig: rtl.newTIProcSig(null)',
+    '  procsig: rtl.newTIProcSig([])',
     '});',
     'this.$rtti.$MethodVar("TMethodB", {',
-    '  procsig: rtl.newTIProcSig(null),',
+    '  procsig: rtl.newTIProcSig([]),',
     '  methodkind: 0',
     '});',
     'this.$rtti.$ProcVar("TProcC", {',
-    '  procsig: rtl.newTIProcSig(null, 2)',
+    '  procsig: rtl.newTIProcSig([], null, 2)',
     '});',
     'this.$rtti.$ProcVar("TProcD", {',
     '  procsig: rtl.newTIProcSig([["i", rtl.longint], ["j", rtl.string, 2], ["c", rtl.char, 1], ["d", rtl.double, 4]])',
     '});',
     'this.$rtti.$ProcVar("TProcE", {',
-    '  procsig: rtl.newTIProcSig(null, rtl.nativeint)',
+    '  procsig: rtl.newTIProcSig([], rtl.nativeint)',
     '});',
     'this.$rtti.$ProcVar("TProcF", {',
     '  procsig: rtl.newTIProcSig([["p", this.$rtti["TProcA"], 2]], rtl.nativeuint)',
@@ -29578,13 +29578,13 @@ begin
     '  this.Fly = function () {',
     '  };',
     '  var $r = this.$rtti;',
-    '  $r.addMethod("Fly", 0, null);',
+    '  $r.addMethod("Fly", 0, []);',
     '});',
     'rtl.createClass(this, "TEagle", this.TBird, function () {',
     '  this.Fly = function () {',
     '  };',
     '  var $r = this.$rtti;',
-    '  $r.addMethod("Fly", 0, null);',
+    '  $r.addMethod("Fly", 0, []);',
     '});',
     '']),
     LinesToStr([ // $mod.$main
@@ -29738,17 +29738,19 @@ procedure TTestModule.TestRTTI_Class_Method;
 begin
   WithTypeInfo:=true;
   StartProgram(false);
-  Add('type');
-  Add('  TObject = class');
-  Add('  private');
-  Add('    procedure Internal; external name ''$intern'';');
-  Add('  published');
-  Add('    procedure Click; virtual; abstract;');
-  Add('    procedure Notify(Sender: TObject); virtual; abstract;');
-  Add('    function GetNotify: boolean; external name ''GetNotify'';');
-  Add('    procedure Println(a,b: longint); varargs; virtual; abstract;');
-  Add('  end;');
-  Add('begin');
+  Add([
+  'type',
+  '  TObject = class',
+  '  private',
+  '    procedure Internal; external name ''$intern'';',
+  '  published',
+  '    procedure Click; virtual; abstract;',
+  '    procedure Notify(Sender: TObject); virtual; abstract;',
+  '    function GetNotify: boolean; external name ''GetNotify'';',
+  '    procedure Println(a,b: longint); varargs; virtual; abstract;',
+  '    function Fetch(URL: string): word; async; external name ''Fetch'';',
+  '  end;',
+  'begin']);
   ConvertProgram;
   CheckSource('TestRTTI_Class_Method',
     LinesToStr([ // statements
@@ -29758,12 +29760,11 @@ begin
     '  this.$final = function () {',
     '  };',
     '  var $r = this.$rtti;',
-    '  $r.addMethod("Click", 0, null);',
+    '  $r.addMethod("Click", 0, []);',
     '  $r.addMethod("Notify", 0, [["Sender", $r]]);',
-    '  $r.addMethod("GetNotify", 1, null, rtl.boolean,{flags: 4});',
-    '  $r.addMethod("Println", 0, [["a", rtl.longint], ["b", rtl.longint]], null, {',
-    '    flags: 2',
-    '  });',
+    '  $r.addMethod("GetNotify", 1, [], rtl.boolean, 4);',
+    '  $r.addMethod("Println", 0, [["a", rtl.longint], ["b", rtl.longint]], null, 2);',
+    '  $r.addMethod("Fetch", 1, [["URL", rtl.string]], rtl.word, 20);',
     '});',
     '']),
     LinesToStr([ // $mod.$main
@@ -30507,7 +30508,7 @@ begin
     '  this.$final = function () {',
     '  };',
     '  var $r = this.$rtti;',
-    '  $r.addMethod("DoIt", 0, null);',
+    '  $r.addMethod("DoIt", 0, []);',
     '});',
     'rtl.createClass(this, "TSky", this.TObject, function () {',
     '  this.DoIt = function () {',
@@ -30549,14 +30550,14 @@ begin
     '  this.DoIt = function () {',
     '  };',
     '  var $r = this.$rtti;',
-    '  $r.addMethod("DoIt", 0, null);',
+    '  $r.addMethod("DoIt", 0, []);',
     '});',
     'rtl.createClass(this, "TSky", this.TObject, function () {',
     '  this.DoIt = function () {',
     '    $mod.TObject.DoIt.call(this);',
     '  };',
     '  var $r = this.$rtti;',
-    '  $r.addMethod("DoIt", 0, null);',
+    '  $r.addMethod("DoIt", 0, []);',
     '});',
     '']),
     LinesToStr([ // $mod.$main
@@ -30633,7 +30634,7 @@ begin
     '});',
     'this.$rtti.$Class("TBridge");',
     'this.$rtti.$ProcVar("TProc", {',
-    '  procsig: rtl.newTIProcSig(null, this.$rtti["TBridge"])',
+    '  procsig: rtl.newTIProcSig([], this.$rtti["TBridge"])',
     '});',
     'rtl.createClass(this, "TOger", this.TObject, function () {',
     '  this.$init = function () {',
@@ -30696,7 +30697,7 @@ begin
     '  instancetype: this.$rtti["TObject"]',
     '});',
     'this.$rtti.$ProcVar("TProcA", {',
-    '  procsig: rtl.newTIProcSig(null, this.$rtti["TClass"])',
+    '  procsig: rtl.newTIProcSig([], this.$rtti["TClass"])',
     '});',
     'rtl.createClass(this, "TObject", null, function () {',
     '  this.$init = function () {',
@@ -31169,10 +31170,10 @@ begin
     '  eltype: rtl.string',
     '});',
     'this.$rtti.$ProcVar("TProc", {',
-    '  procsig: rtl.newTIProcSig(null)',
+    '  procsig: rtl.newTIProcSig([])',
     '});',
     'this.$rtti.$MethodVar("TMethod", {',
-    '  procsig: rtl.newTIProcSig(null),',
+    '  procsig: rtl.newTIProcSig([]),',
     '  methodkind: 0',
     '});',
     'this.StaticArray = rtl.arraySetLength(null,"",2);',
@@ -31457,7 +31458,7 @@ begin
     '  null,',
     '  function () {',
     '    var $r = this.$rtti;',
-    '    $r.addMethod("GetItem", 1, null, rtl.longint);',
+    '    $r.addMethod("GetItem", 1, [], rtl.longint);',
     '    $r.addMethod("SetItem", 0, [["Value", rtl.longint]]);',
     '    $r.addProperty("Item", 3, rtl.longint, "GetItem", "SetItem");',
     '  }',
@@ -31524,8 +31525,8 @@ begin
     '    this.$kind = "com";',
     '    var $r = this.$rtti;',
     '    $r.addMethod("QueryInterface", 1, [["iid", $mod.$rtti["TGuid"], 2], ["obj", null, 4]], rtl.longint);',
-    '    $r.addMethod("_AddRef", 1, null, rtl.longint);',
-    '    $r.addMethod("_Release", 1, null, rtl.longint);',
+    '    $r.addMethod("_AddRef", 1, [], rtl.longint);',
+    '    $r.addMethod("_Release", 1, [], rtl.longint);',
     '  }',
     ');',
     'rtl.createInterface(',
@@ -31536,7 +31537,7 @@ begin
     '  this.IUnknown,',
     '  function () {',
     '    var $r = this.$rtti;',
-    '    $r.addMethod("GetItem", 1, null, rtl.longint);',
+    '    $r.addMethod("GetItem", 1, [], rtl.longint);',
     '    $r.addMethod("SetItem", 0, [["Value", rtl.longint]]);',
     '    $r.addProperty("Item", 3, rtl.longint, "GetItem", "SetItem");',
     '  }',
@@ -31588,7 +31589,7 @@ begin
     '    return Result;',
     '  };',
     '  var $r = this.$rtti;',
-    '  $r.addMethod("GetItem", 1, null, rtl.longint);',
+    '  $r.addMethod("GetItem", 1, [], rtl.longint);',
     '  $r.addProperty("Item", 1, rtl.longint, "GetItem", "");',
     '});',
     'this.t = null;',
@@ -31676,8 +31677,8 @@ begin
     '  pas.system.IUnknown,',
     '  function () {',
     '    var $r = this.$rtti;',
-    '    $r.addMethod("Swoop", 1, null, pas.unit2.$rtti["TWordArray"]);',
-    '    $r.addMethod("Glide", 1, null, pas.unit2.$rtti["TArray<System.Word>"]);',
+    '    $r.addMethod("Swoop", 1, [], pas.unit2.$rtti["TWordArray"]);',
+    '    $r.addMethod("Glide", 1, [], pas.unit2.$rtti["TArray<System.Word>"]);',
     '  }',
     ');',
     'this.Fly = function () {',
@@ -31882,7 +31883,7 @@ begin
     '      attr: [$mod.TCustomAttribute, "Create$1", [14]]',
     '    }',
     '  );',
-    '  $r.addMethod("Fly", 0, null, null, {',
+    '  $r.addMethod("Fly", 0, [], null, 0, {',
     '    attr: [$mod.TCustomAttribute, "Create$1", [15]]',
     '  });',
     '});',

+ 5 - 2
packages/winunits-base/src/comserv.pp

@@ -205,9 +205,12 @@ end;
 function GetModuleFileName: String;
 const
   MAX_PATH_SIZE = 2048;
+var
+  FileName: WideString;
 begin
-  SetLength(Result, MAX_PATH_SIZE);
-  SetLength(Result, Windows.GetModuleFileName(HInstance, @Result[1], MAX_PATH_SIZE));
+  SetLength(FileName, MAX_PATH_SIZE);
+  SetLength(FileName, Windows.GetModuleFileNameW(HInstance, @FileName[1], MAX_PATH_SIZE));
+  Result := FileName;
 end;
 
 function GetModuleName: String;

+ 13 - 2
rtl/aarch64/math.inc

@@ -51,6 +51,19 @@
     {$endif FPC_SYSTEM_HAS_SQRT}
 
 
+{$ifndef VER3_2}
+    {$ifndef FPC_SYSTEM_HAS_FRAC}
+    {$define FPC_SYSTEM_HAS_FRAC}
+    function fpc_frac_real(d : ValReal) : ValReal;compilerproc;
+      begin
+        { Function is handled internal in the compiler }
+        runerror(207);
+        result:=0;
+      end;
+    {$endif FPC_SYSTEM_HAS_FRAC}
+{$endif VER3_2}
+
+
     {$ifndef FPC_SYSTEM_HAS_INT}
     {$define FPC_SYSTEM_HAS_INT}
     function fpc_int_real(d : ValReal) : ValReal;assembler;nostackframe;compilerproc;
@@ -82,5 +95,3 @@
         fcvtzs x0,d0
       end;
     {$endif FPC_SYSTEM_HAS_ROUND}
-
-

+ 8 - 8
rtl/aarch64/mathu.inc

@@ -14,25 +14,25 @@
 
 {$asmmode gas}
 
-function getfpcr: dword; nostackframe; assembler;
+function getfpcr: qword; nostackframe; assembler;
   asm
     mrs x0,fpcr
   end;
 
 
-procedure setfpcr(val: dword); nostackframe; assembler;
+procedure setfpcr(val: qword); nostackframe; assembler;
   asm
     msr fpcr,x0
   end;
 
 
-function getfpsr: dword; nostackframe; assembler;
+function getfpsr: qword; nostackframe; assembler;
   asm
     mrs x0,fpsr
   end;
 
 
-procedure setfpsr(val: dword); nostackframe; assembler;
+procedure setfpsr(val: qword); nostackframe; assembler;
   asm
     msr fpsr, x0
   end;
@@ -75,7 +75,7 @@ const
   fpu_ufe = 1 shl 11;
   fpu_ixe = 1 shl 12;
   fpu_ide = 1 shl 15;
-  fpu_exception_mask = fpu_ioe or fpu_dze or fpu_ofe or fpu_ufe or fpu_ixe or fpu_ide;
+  fpu_exception_mask = qword(fpu_ioe or fpu_dze or fpu_ofe or fpu_ufe or fpu_ixe or fpu_ide);
   fpu_exception_mask_to_status_mask_shift = 8;
 
 
@@ -111,13 +111,13 @@ function GetExceptionMask: TFPUExceptionMask;
 
 function SetExceptionMask(const Mask: TFPUExceptionMask): TFPUExceptionMask;
   var
-    newfpcr: dword;
+    newfpcr: qword;
   begin
     { clear "exception happened" flags }
     ClearExceptions(false);
     softfloat_exception_mask:=mask;
 
-    { at least the ThunderX AArch64 support apperently hardware exceptions,
+    { at least the ThunderX AArch64 support apparently hardware exceptions,
       so set fpcr correctly, thought it might be WI on most implementations it does not hurt
     }
     newfpcr:=fpu_exception_mask;
@@ -143,7 +143,7 @@ function SetExceptionMask(const Mask: TFPUExceptionMask): TFPUExceptionMask;
 
 procedure ClearExceptions(RaisePending: Boolean);
   var
-    fpsr: dword;
+    fpsr: qword;
     f: TFPUException;
   begin
     fpsr:=getfpsr;

+ 1 - 1
rtl/darwin/Makefile

@@ -3293,7 +3293,7 @@ include $(INC)/makefile.inc
 SYSINCDEPS=$(addprefix $(INC)/,$(SYSINCNAMES))
 include $(PROCINC)/makefile.cpu
 SYSCPUDEPS=$(addprefix $(PROCINC)/,$(CPUINCNAMES))
-SYSDEPS=$(SYSINCDEPS) $(SYSCPUDEPS)
+SYSDEPS=$(SYSINCDEPS) $(SYSCPUDEPS) sighnd.inc sig_cpu.inc
 $(SYSTEMUNIT)$(PPUEXT) : $(BSDINC)/$(SYSTEMUNIT).pp $(SYSDEPS)
 	$(COMPILER) $(FPC_SYSTEM_OPT) -Us -Sg $(BSDINC)/$(SYSTEMUNIT).pp
 sysinit$(PPUEXT) : sysinit.pas $(SYSTEMUNIT)$(PPUEXT)

+ 1 - 1
rtl/darwin/Makefile.fpc

@@ -116,7 +116,7 @@ include $(PROCINC)/makefile.cpu
 SYSCPUDEPS=$(addprefix $(PROCINC)/,$(CPUINCNAMES))
 
 # Put system unit dependencies together.
-SYSDEPS=$(SYSINCDEPS) $(SYSCPUDEPS)
+SYSDEPS=$(SYSINCDEPS) $(SYSCPUDEPS) sighnd.inc sig_cpu.inc
 
 
 #

+ 7 - 5
rtl/objpas/sysutils/dati.inc

@@ -75,18 +75,20 @@ end;
 {   MSecsToTimeStamp   }
 
 function MSecsToTimeStamp(MSecs: comp): TTimeStamp;
+var
+  D1:Int64;
 begin
-  result.Date := Trunc(msecs / msecsperday);
-  msecs:= msecs-comp(result.date)*msecsperday;
-  result.Time := Round(MSecs);
-end ;
+  D1:=Trunc(msecs);
+  result.Date := D1 div msecsperday;
+  result.Time := D1 - result.date * msecsperday;
+end;
 
 {   TimeStampToMSecs   }
 
 function TimeStampToMSecs(const TimeStamp: TTimeStamp): comp;
 begin
   result := TimeStamp.Time + comp(timestamp.date)*msecsperday;
-end ;
+end;
 
 Function TryEncodeDate(Year,Month,Day : Word; Out Date : TDateTime) : Boolean;
 

+ 1 - 1
rtl/objpas/sysutils/filutilh.inc

@@ -17,7 +17,7 @@ Type
 
 
   // Some operating systems need FindHandle to be a Pointer
-{$if defined(unix) or defined(msdos) or defined(hasamiga) or defined(atari) or defined(win16)}
+{$if defined(unix) or defined(msdos) or defined(hasamiga) or defined(atari) or defined(win16) or defined(sinclairql)}
     {$define FINDHANDLE_IS_POINTER}
 {$endif}
 

+ 1 - 1
rtl/objpas/sysutils/osutil.inc

@@ -245,7 +245,7 @@ begin
     Repeat
       Result:=Format('%s%.5d.tmp',[Start,I]);
       Inc(I);
-    Until not FileExists(Result);
+    Until not (FileExists(Result) or DirectoryExists(Result));
     end;
 end;
 {$endif}

+ 4 - 3
rtl/sinclairql/buildrtl.pp

@@ -4,11 +4,12 @@ unit buildrtl;
 
     uses
       si_prc,
+      sysutils,
 
       ctypes, strings,
-      rtlconsts, {sysconst,} {math,} {types,}
-      {typinfo,} sortbase, {fgl,} {classes,}
-      charset, {character,} {getopts,}
+      rtlconsts, sysconst, math, types,
+      typinfo, sortbase, fgl, classes,
+      charset, character, getopts,
       fpwidestring;
 
   implementation

+ 50 - 0
rtl/sinclairql/classes.pp

@@ -0,0 +1,50 @@
+{
+    This file is part of the Free Component Library (FCL)
+    Copyright (c) 2021 by the Free Pascal development team
+
+    Classes unit for the Sinclair QL
+
+    See the file COPYING.FPC, included in this distribution,
+    for details about the copyright.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+
+ **********************************************************************}
+
+{$mode objfpc}
+
+{ determine the type of the resource/form file }
+{$define Win16Res}
+
+unit Classes;
+
+interface
+
+uses
+  sysutils,
+  rtlconsts,
+  types,
+  sortbase,
+{$ifdef FPC_TESTGENERICS}
+  fgl,
+{$endif}
+  typinfo;
+
+{$i classesh.inc}
+
+
+implementation
+
+{ OS - independent class implementations are in /inc directory. }
+{$i classes.inc}
+
+
+initialization
+  CommonInit;
+
+finalization
+  CommonCleanup;
+
+end.

+ 1 - 1
rtl/sinclairql/si_prc.pp

@@ -107,7 +107,7 @@ asm
     add.l d1,d2
     add.l d0,(a0,d2)
     subq.l #1,d7
-    bpl @relocloop
+    bgt @relocloop
 {$ENDIF PACKEDRELOCS}
 
 @noreloc:

+ 501 - 0
rtl/sinclairql/sysutils.pp

@@ -0,0 +1,501 @@
+{
+    This file is part of the Free Pascal run time library.
+    Copyright (c) 2021 by Free Pascal development team
+
+    Sysutils unit for Sinclair QL
+
+    See the file COPYING.FPC, included in this distribution,
+    for details about the copyright.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+
+ **********************************************************************}
+
+unit sysutils;
+
+interface
+
+{$MODE objfpc}
+{$MODESWITCH OUT}
+{ force ansistrings }
+{$H+}
+{$modeswitch typehelpers}
+{$modeswitch advancedrecords}
+
+{$DEFINE OS_FILESETDATEBYNAME}
+{$DEFINE HAS_SLEEP}
+{$DEFINE HAS_OSERROR}
+
+{OS has only 1 byte version for ExecuteProcess}
+{$define executeprocuni}
+
+{ used OS file system APIs use ansistring }
+{$define SYSUTILS_HAS_ANSISTR_FILEUTIL_IMPL}
+{ OS has an ansistring/single byte environment variable API }
+{$define SYSUTILS_HAS_ANSISTR_ENVVAR_IMPL}
+
+{ Include platform independent interface part }
+{$i sysutilh.inc}
+
+{ Platform dependent calls }
+
+
+implementation
+
+uses
+  sysconst;
+
+{$DEFINE FPC_FEXPAND_UNC} (* UNC paths are supported *)
+{$DEFINE FPC_FEXPAND_DRIVES} (* Full paths begin with drive specification *)
+
+{ Include platform independent implementation part }
+{$i sysutils.inc}
+
+{$i qdosfuncs.inc}
+{$i smsfuncs.inc}
+
+{****************************************************************************
+                              File Functions
+****************************************************************************}
+{$I-}{ Required for correct usage of these routines }
+
+
+(****** non portable routines ******)
+
+function FileOpen(const FileName: rawbytestring; Mode: Integer): THandle;
+begin
+  FileOpen:=-1;
+  if FileOpen < -1 then
+    FileOpen:=-1;
+end;
+
+
+function FileGetDate(Handle: THandle) : Int64;
+begin
+  result:=-1;
+end;
+
+
+function FileSetDate(Handle: THandle; Age: Int64) : LongInt;
+begin
+  result:=0;
+end;
+
+
+function FileSetDate(const FileName: RawByteString; Age: Int64) : LongInt;
+var
+  f: THandle;
+begin
+  result:=-1;
+  f:=FileOpen(FileName,fmOpenReadWrite);
+  if f < 0 then
+    exit;
+  result:=FileSetDate(f,Age);
+  FileClose(f);
+end;
+
+
+function FileCreate(const FileName: RawByteString) : THandle;
+begin
+  FileCreate:=-1;
+  if FileCreate < -1 then
+    FileCreate:=-1;
+end;
+
+function FileCreate(const FileName: RawByteString; Rights: integer): THandle;
+begin
+  { Rights don't exist on the QL, so we simply map this to FileCreate() }
+  FileCreate:=FileCreate(FileName);
+end;
+
+function FileCreate(const FileName: RawByteString; ShareMode: integer; Rights : integer): THandle;
+begin
+  { Rights and ShareMode don't exist on the QL so we simply map this to FileCreate() }
+  FileCreate:=FileCreate(FileName);
+end;
+
+
+function FileRead(Handle: THandle; out Buffer; Count: LongInt): LongInt;
+begin
+  FileRead:=-1;
+  if (Count<=0) then
+    exit;
+
+  FileRead:=-1;
+  if FileRead < -1 then
+    FileRead:=-1;
+end;
+
+
+function FileWrite(Handle: THandle; const Buffer; Count: LongInt): LongInt;
+begin
+  FileWrite:=-1;
+  if (Count<=0) then 
+    exit;
+
+  FileWrite:=-1;
+  if FileWrite < -1 then
+    FileWrite:=-1;
+end;
+
+
+function FileSeek(Handle: THandle; FOffset, Origin: LongInt) : LongInt;
+var
+  dosResult: longint;
+begin
+  FileSeek:=-1;
+
+  dosResult:=-1;
+  if dosResult < 0 then
+    exit;
+
+  FileSeek:=dosResult;
+end;
+
+function FileSeek(Handle: THandle; FOffset: Int64; Origin: Longint): Int64;
+begin
+  FileSeek:=FileSeek(Handle,LongInt(FOffset),Origin);
+end;
+
+
+procedure FileClose(Handle: THandle);
+begin
+end;
+
+
+function FileTruncate(Handle: THandle; Size: Int64): Boolean;
+begin
+  FileTruncate:=False;
+end;
+
+
+function DeleteFile(const FileName: RawByteString) : Boolean;
+begin
+  DeleteFile:=false;
+end;
+
+
+function RenameFile(const OldName, NewName: RawByteString): Boolean;
+begin
+  RenameFile:=false;
+end;
+
+
+
+(****** end of non portable routines ******)
+
+
+function FileAge (const FileName : RawByteString): Int64;
+var
+  f: THandle;
+begin
+  FileAge:=-1;
+  f:=FileOpen(FileName,fmOpenRead);
+  if f < 0 then
+    exit;
+  FileAge:=FileGetDate(f);
+  FileClose(f);
+end;
+
+
+function FileGetSymLinkTarget(const FileName: RawByteString; out SymLinkRec: TRawbyteSymLinkRec): Boolean;
+begin
+  Result := False;
+end;
+
+
+function FileExists (const FileName : RawByteString; FollowLink : Boolean) : Boolean;
+var
+  Attr: longint;
+begin
+  FileExists:=false;
+  Attr:=FileGetAttr(FileName);
+  if Attr < 0 then
+    exit;
+
+  result:=(Attr and (faVolumeID or faDirectory)) = 0;
+end;
+
+
+type
+  PInternalFindData = ^TInternalFindData;
+  TInternalFindData = record
+    dummy: pointer;
+  end;
+
+
+Function InternalFindFirst (Const Path : RawByteString; Attr : Longint; out Rslt : TAbstractSearchRec; var Name: RawByteString) : Longint;
+var
+  dosResult: longint;
+  IFD: PInternalFindData;
+begin
+  result:=-1; { We emulate Linux/Unix behaviour, and return -1 on errors. }
+
+  new(IFD);
+  IFD^.dummy:=nil;
+
+  Rslt.FindHandle:=nil;
+  dosResult:=-1; { add findfirst here }
+  if dosResult < 0 then
+    begin
+      InternalFindClose(IFD);
+      exit;
+    end;
+
+  Rslt.FindHandle:=IFD;
+
+  Name:='';
+  SetCodePage(Name,DefaultFileSystemCodePage,false);
+
+  Rslt.Time:=0;
+  Rslt.Size:=0;
+
+  { "128" is Windows "NORMALFILE" attribute. Some buggy code depend on this... :( (KB) }
+  Rslt.Attr := 128 or 0;
+
+  result:=0;
+end;
+
+
+Function InternalFindNext (var Rslt : TAbstractSearchRec; var Name : RawByteString) : Longint;
+var
+  dosResult: longint;
+  IFD: PInternalFindData;
+begin
+  result:=-1;
+  IFD:=PInternalFindData(Rslt.FindHandle);
+  if not assigned(IFD) then
+    exit;
+
+  dosResult:=-1;
+  if dosResult < 0 then
+    exit;
+
+  Name:='';
+  SetCodePage(Name,DefaultFileSystemCodePage,false);
+
+  Rslt.Time:=0;
+  Rslt.Size:=0;
+
+  { "128" is Windows "NORMALFILE" attribute. Some buggy code depend on this... :( (KB) }
+  Rslt.Attr := 128 or 0;
+
+  result:=0;
+end;
+
+
+Procedure InternalFindClose(var Handle: Pointer);
+var
+  IFD: PInternalFindData;
+begin
+  IFD:=PInternalFindData(Handle);
+  if not assigned(IFD) then
+    exit;
+
+  dispose(IFD);
+end;
+
+
+(****** end of non portable routines ******)
+
+Function FileGetAttr (Const FileName : RawByteString) : Longint;
+begin
+  FileGetAttr:=0;
+end;
+
+
+Function FileSetAttr (Const Filename : RawByteString; Attr: longint) : Longint;
+begin
+  FileSetAttr:=-1;
+
+  if FileSetAttr < -1 then
+    FileSetAttr:=-1
+  else
+    FileSetAttr:=0;
+end;
+
+
+
+{****************************************************************************
+                              Disk Functions
+****************************************************************************}
+
+function DiskSize(Drive: Byte): Int64;
+var
+  dosResult: longint;
+begin
+  DiskSize := -1;
+
+  dosResult:=-1;
+  if dosResult < 0 then
+    exit;
+
+  DiskSize:=0;
+end;
+
+function DiskFree(Drive: Byte): Int64;
+var
+  dosResult: longint;
+begin
+  DiskFree := -1;
+
+  dosResult:=-1;
+  if dosResult < 0 then
+    exit;
+
+  DiskFree:=0;
+end;
+
+function DirectoryExists(const Directory: RawByteString; FollowLink : Boolean): Boolean;
+var
+  Attr: longint;
+begin
+  DirectoryExists:=false;
+  Attr:=FileGetAttr(Directory);
+  if Attr < 0 then
+    exit;
+
+  result:=(Attr and faDirectory) <> 0;
+end;
+
+
+
+{****************************************************************************
+                              Locale Functions
+****************************************************************************}
+
+Procedure GetLocalTime(var SystemTime: TSystemTime);
+begin
+   DateTimeToSystemTime(FileDateToDateTime(0),SystemTime);
+end;
+
+
+Procedure InitAnsi;
+Var
+  i : longint;
+begin
+  {  Fill table entries 0 to 127  }
+  for i := 0 to 96 do
+    UpperCaseTable[i] := chr(i);
+  for i := 97 to 122 do
+    UpperCaseTable[i] := chr(i - 32);
+  for i := 123 to 191 do
+    UpperCaseTable[i] := chr(i);
+  Move (CPISO88591UCT,UpperCaseTable[192],SizeOf(CPISO88591UCT));
+
+  for i := 0 to 64 do
+    LowerCaseTable[i] := chr(i);
+  for i := 65 to 90 do
+    LowerCaseTable[i] := chr(i + 32);
+  for i := 91 to 191 do
+    LowerCaseTable[i] := chr(i);
+  Move (CPISO88591LCT,UpperCaseTable[192],SizeOf(CPISO88591UCT));
+end;
+
+
+Procedure InitInternational;
+begin
+  InitInternationalGeneric;
+  InitAnsi;
+end;
+
+function SysErrorMessage(ErrorCode: Integer): String;
+begin
+  Result:=Format(SUnknownErrorCode,[ErrorCode]);
+end;
+
+function GetLastOSError: Integer;
+begin
+  result:=-1;
+end;
+
+{****************************************************************************
+                              OS utility functions
+****************************************************************************}
+
+function GetPathString: String;
+begin
+  {writeln('Unimplemented GetPathString');}
+  result := '';
+end;
+
+Function GetEnvironmentVariable(Const EnvVar : String) : String;
+begin
+  {writeln('Unimplemented GetEnvironmentVariable');}
+  result:='';
+end;
+
+Function GetEnvironmentVariableCount : Integer;
+begin
+  {writeln('Unimplemented GetEnvironmentVariableCount');}
+  result:=0;
+end;
+
+Function GetEnvironmentString(Index : Integer) : {$ifdef FPC_RTL_UNICODE}UnicodeString{$else}AnsiString{$endif};
+begin
+  {writeln('Unimplemented GetEnvironmentString');}
+  result:='';
+end;
+
+function ExecuteProcess (const Path: RawByteString; const ComLine: RawByteString;Flags:TExecuteFlags=[]):
+                                                                       integer;
+var
+  tmpPath: RawByteString;
+  pcmdline: ShortString;
+  CommandLine: RawByteString;
+  E: EOSError;
+begin
+  tmpPath:=ToSingleByteFileSystemEncodedFileName(Path);
+  pcmdline:=ToSingleByteFileSystemEncodedFileName(ComLine);
+
+  result:=-1; { execute here }
+
+  if result < 0 then begin
+    if ComLine = '' then
+      CommandLine := Path
+    else
+      CommandLine := Path + ' ' + ComLine;
+
+    E := EOSError.CreateFmt (SExecuteProcessFailed, [CommandLine, result]);
+    E.ErrorCode := result;
+    raise E;
+  end;
+end;
+
+function ExecuteProcess (const Path: RawByteString;
+                                  const ComLine: array of RawByteString;Flags:TExecuteFlags=[]): integer;
+var
+  CommandLine: RawByteString;
+  I: integer;
+
+begin
+  Commandline := '';
+  for I := 0 to High (ComLine) do
+   if Pos (' ', ComLine [I]) <> 0 then
+    CommandLine := CommandLine + ' ' + '"' + ToSingleByteFileSystemEncodedFileName(ComLine [I]) + '"'
+   else
+    CommandLine := CommandLine + ' ' + ToSingleByteFileSystemEncodedFileName(Comline [I]);
+  ExecuteProcess := ExecuteProcess (Path, CommandLine);
+end;
+
+procedure Sleep(Milliseconds: cardinal);
+begin
+  {writeln('Unimplemented sleep');}
+end;
+
+
+{****************************************************************************
+                              Initialization code
+****************************************************************************}
+
+Initialization
+  InitExceptions;
+  InitInternational;    { Initialize internationalization settings }
+  OnBeep:=Nil;          { No SysBeep() on the QL for now. }
+
+Finalization
+  FreeTerminateProcs;
+  DoneExceptions;
+end.

+ 9 - 4
rtl/win/sysutils.pp

@@ -1037,14 +1037,19 @@ end;
                               Locale Functions
 ****************************************************************************}
 
-function GetLocaleStr(LID, LT: Longint; const Def: string): ShortString;
+function GetLocaleStr(LID, LT: Longint; const Def: string): AnsiString;
 var
   L: Integer;
-  Buf: array[0..255] of Char;
+  Buf: unicodestring;
 begin
-  L := GetLocaleInfoA(LID, LT, Buf, SizeOf(Buf));
+  L := GetLocaleInfoW(LID, LT, nil, 0);
   if L > 0 then
-    SetString(Result, @Buf[0], L - 1)
+    begin
+      SetLength(Buf,L-1); // L includes terminating NULL
+      if l>1 Then
+        L := GetLocaleInfoW(LID, LT, @Buf[1], L);
+      result:=buf;
+    end
   else
     Result := Def;
 end;

+ 8 - 0
tests/tbf/tb0274.pp

@@ -0,0 +1,8 @@
+{ %fail }
+{ %opt=-Sew }
+var 
+  c : comp;
+
+begin
+  c:=123.123;
+end.

+ 22 - 0
tests/webtbf/tw38771.pp

@@ -0,0 +1,22 @@
+{ %FAIL }
+{$mode objfpc}
+
+program tw38771;
+
+type
+  TMyClass = class
+    generic procedure DoThis<T>(msg: T);
+    generic procedure DoThat<T>(msg: T); virtual;
+  end;
+
+generic procedure TMyClass.DoThis<T>(msg:T);
+begin
+  specialize DoThat<T>(msg);
+end;
+
+generic procedure TMyClass.DoThat<T>(msg: T);
+begin
+end;
+
+begin
+end.

+ 42 - 0
tests/webtbs/tw38353.pp

@@ -0,0 +1,42 @@
+{ %OPT=-Cg -O2 }
+{ %CPU=x86_64 }
+
+{ -Cg and -O2 options together lead to 
+  the generation of instruction:
+  testq   $15,U_$P$VECTORCALL_HVA_TEST1_$$_HVA@GOTPCREL(%rip)
+  for which the relocation was not correctly generated
+  in the internal assembler }
+
+program tw38353;
+
+{$IFNDEF CPUX86_64}
+  {$FATAL This test program can only be compiled on Windows or Linux 64-bit with an Intel processor }
+{$ENDIF}
+
+{$ASMMODE Intel}
+{$PUSH}
+{$CODEALIGN RECORDMIN=16}
+{$PACKRECORDS C}
+type
+  TM128 = record
+    case Byte of
+      0: (M128_F32: array[0..3] of Single);
+      1: (M128_F64: array[0..1] of Double);
+  end;
+{$POP}
+
+var
+  HVA: TM128;
+
+begin
+{$ifdef verbose}
+  writeln('@HVA=',hexstr(ptruint(@HVA),2*sizeof(ptruint)));
+{$endif verbose}
+  if (PtrUInt(@HVA) and $F) <> 0 then
+  begin
+{$ifdef verbose}
+    WriteLn('FAIL: HVA is not correctly aligned.');
+{$endif verbose}
+    Halt(1);
+  end;
+end.

+ 23 - 0
tests/webtbs/tw38631.pp

@@ -0,0 +1,23 @@
+{$mode objfpc}
+program msec_test1;
+uses sysutils;
+
+var
+  D: TDateTime;
+  T, T1, T2: TTimeStamp;
+  MS: Comp;
+begin
+  D:=EncodeDate(2021, 03, 16) + EncodeTime(14, 02, 15, 1);
+  WriteLn('DATE: ', DateTimeToStr(D));
+
+  T:=DateTimeToTimeStamp(D);
+  WriteLn(' T.Date=',T.Date,'   T.Time=', T.Time);
+  MS:=TimeStampToMSecs(T);
+  T1:=MSecsToTimeStamp(MS);
+  WriteLn('T1.Date=',T1.Date,'  T1.Time=', T1.Time);
+  
+  WriteLn('DATE1: ', DateTimeToStr(TimeStampToDateTime(T1)));
+  if TimeStampToDateTime(T1)<>D then
+    halt(1);
+  writeln('ok')
+end.

+ 28 - 0
tests/webtbs/tw38766.pp

@@ -0,0 +1,28 @@
+{$mode objfpc}
+
+type
+  trec = record
+    x, y: longint;
+  end;
+
+function max(x,y: longint): longint;
+begin
+  if x>y then
+    result:=x
+  else
+    result:=y;
+end;
+
+function test: trec; inline;
+begin
+ result.x:=1;
+ result.y:=2;
+ result.x:=max(result.x,result.y);
+end;
+    
+begin
+  if test.x<>2 then
+    halt(1);
+  if test.y<>2 then
+    halt(2);
+end.

Some files were not shown because too many files changed in this diff