瀏覽代碼

* more finegrained parallelism support for testsuite runs: the tests are no
longer split per directory, but in chunks of 100 tests (configurable via
the new CHUNKSIZE=xxx Makefile parameter), which then all can be checked
in parallel
o dependencies between tests are handled by always putting tests whose
name is the same except for the last character into the same chunk
o as previously, -jx can only be used for the "all" target, other targets
cannot be executed in parallel
o the "units" directory has been renamed "tstunits" because otherwise
"make distclean" will remove it entirely now that the main tests
makefile also compiles a program

git-svn-id: trunk@21479 -

Jonas Maebe 13 年之前
父節點
當前提交
04df0d537e

+ 7 - 6
.gitattributes

@@ -11445,12 +11445,12 @@ tests/test/uunit2b.pp svneol=native#text/plain
 tests/test/uunit3a.pp svneol=native#text/plain
 tests/test/uunit3b.pp svneol=native#text/plain
 tests/test/uunit3c.pp svneol=native#text/plain
-tests/units/MPWMake -text
-tests/units/Makefile svneol=native#text/plain
-tests/units/Makefile.fpc svneol=native#text/plain
-tests/units/erroru.pp svneol=native#text/plain
-tests/units/popuperr.pp svneol=native#text/plain
-tests/units/win32err.pp svneol=native#text/plain
+tests/tstunits/MPWMake -text
+tests/tstunits/Makefile svneol=native#text/plain
+tests/tstunits/Makefile.fpc svneol=native#text/plain
+tests/tstunits/erroru.pp svneol=native#text/plain
+tests/tstunits/popuperr.pp svneol=native#text/plain
+tests/tstunits/win32err.pp svneol=native#text/plain
 tests/utils/Makefile svneol=native#text/plain
 tests/utils/Makefile.fpc svneol=native#text/plain
 tests/utils/bench.pp svneol=native#text/plain
@@ -11461,6 +11461,7 @@ tests/utils/digest.pp svneol=native#text/plain
 tests/utils/dotest.pp svneol=native#text/plain
 tests/utils/fail.pp svneol=native#text/plain
 tests/utils/fptime.pp svneol=native#text/plain
+tests/utils/gparmake.pp svneol=native#text/plain
 tests/utils/libtar.pas svneol=native#text/plain
 tests/utils/macos/LinkRunDir -text
 tests/utils/macos/LinkRunTests -text

+ 8 - 8
.gitignore

@@ -7594,14 +7594,14 @@ tests/test/units/variants/*.ppu
 tests/test/units/variants/*.s
 tests/test/units/variants/fpcmade.*
 tests/test/units/variants/units
-tests/units/*-stamp.*
-tests/units/*.bak
-tests/units/*.exe
-tests/units/*.o
-tests/units/*.ppu
-tests/units/*.s
-tests/units/fpcmade.*
-tests/units/units
+tests/tstunits/*-stamp.*
+tests/tstunits/*.bak
+tests/tstunits/*.exe
+tests/tstunits/*.o
+tests/tstunits/*.ppu
+tests/tstunits/*.s
+tests/tstunits/fpcmade.*
+tests/tstunits/units
 tests/utils/*.bak
 tests/utils/*.exe
 tests/utils/*.o

+ 669 - 43
tests/Makefile

@@ -294,6 +294,216 @@ FPMAKE_SKIP_CONFIG=-n
 FPCFPMAKE=$(FPC)
 endif
 endif
+ifeq ($(FULL_TARGET),i386-linux)
+override TARGET_PROGRAMS+=gparmake
+endif
+ifeq ($(FULL_TARGET),i386-go32v2)
+override TARGET_PROGRAMS+=gparmake
+endif
+ifeq ($(FULL_TARGET),i386-win32)
+override TARGET_PROGRAMS+=gparmake
+endif
+ifeq ($(FULL_TARGET),i386-os2)
+override TARGET_PROGRAMS+=gparmake
+endif
+ifeq ($(FULL_TARGET),i386-freebsd)
+override TARGET_PROGRAMS+=gparmake
+endif
+ifeq ($(FULL_TARGET),i386-beos)
+override TARGET_PROGRAMS+=gparmake
+endif
+ifeq ($(FULL_TARGET),i386-haiku)
+override TARGET_PROGRAMS+=gparmake
+endif
+ifeq ($(FULL_TARGET),i386-netbsd)
+override TARGET_PROGRAMS+=gparmake
+endif
+ifeq ($(FULL_TARGET),i386-solaris)
+override TARGET_PROGRAMS+=gparmake
+endif
+ifeq ($(FULL_TARGET),i386-qnx)
+override TARGET_PROGRAMS+=gparmake
+endif
+ifeq ($(FULL_TARGET),i386-netware)
+override TARGET_PROGRAMS+=gparmake
+endif
+ifeq ($(FULL_TARGET),i386-openbsd)
+override TARGET_PROGRAMS+=gparmake
+endif
+ifeq ($(FULL_TARGET),i386-wdosx)
+override TARGET_PROGRAMS+=gparmake
+endif
+ifeq ($(FULL_TARGET),i386-darwin)
+override TARGET_PROGRAMS+=gparmake
+endif
+ifeq ($(FULL_TARGET),i386-emx)
+override TARGET_PROGRAMS+=gparmake
+endif
+ifeq ($(FULL_TARGET),i386-watcom)
+override TARGET_PROGRAMS+=gparmake
+endif
+ifeq ($(FULL_TARGET),i386-netwlibc)
+override TARGET_PROGRAMS+=gparmake
+endif
+ifeq ($(FULL_TARGET),i386-wince)
+override TARGET_PROGRAMS+=gparmake
+endif
+ifeq ($(FULL_TARGET),i386-embedded)
+override TARGET_PROGRAMS+=gparmake
+endif
+ifeq ($(FULL_TARGET),i386-symbian)
+override TARGET_PROGRAMS+=gparmake
+endif
+ifeq ($(FULL_TARGET),i386-nativent)
+override TARGET_PROGRAMS+=gparmake
+endif
+ifeq ($(FULL_TARGET),i386-iphonesim)
+override TARGET_PROGRAMS+=gparmake
+endif
+ifeq ($(FULL_TARGET),m68k-linux)
+override TARGET_PROGRAMS+=gparmake
+endif
+ifeq ($(FULL_TARGET),m68k-freebsd)
+override TARGET_PROGRAMS+=gparmake
+endif
+ifeq ($(FULL_TARGET),m68k-netbsd)
+override TARGET_PROGRAMS+=gparmake
+endif
+ifeq ($(FULL_TARGET),m68k-amiga)
+override TARGET_PROGRAMS+=gparmake
+endif
+ifeq ($(FULL_TARGET),m68k-atari)
+override TARGET_PROGRAMS+=gparmake
+endif
+ifeq ($(FULL_TARGET),m68k-openbsd)
+override TARGET_PROGRAMS+=gparmake
+endif
+ifeq ($(FULL_TARGET),m68k-palmos)
+override TARGET_PROGRAMS+=gparmake
+endif
+ifeq ($(FULL_TARGET),m68k-embedded)
+override TARGET_PROGRAMS+=gparmake
+endif
+ifeq ($(FULL_TARGET),powerpc-linux)
+override TARGET_PROGRAMS+=gparmake
+endif
+ifeq ($(FULL_TARGET),powerpc-netbsd)
+override TARGET_PROGRAMS+=gparmake
+endif
+ifeq ($(FULL_TARGET),powerpc-amiga)
+override TARGET_PROGRAMS+=gparmake
+endif
+ifeq ($(FULL_TARGET),powerpc-macos)
+override TARGET_PROGRAMS+=gparmake
+endif
+ifeq ($(FULL_TARGET),powerpc-darwin)
+override TARGET_PROGRAMS+=gparmake
+endif
+ifeq ($(FULL_TARGET),powerpc-morphos)
+override TARGET_PROGRAMS+=gparmake
+endif
+ifeq ($(FULL_TARGET),powerpc-embedded)
+override TARGET_PROGRAMS+=gparmake
+endif
+ifeq ($(FULL_TARGET),powerpc-wii)
+override TARGET_PROGRAMS+=gparmake
+endif
+ifeq ($(FULL_TARGET),powerpc-aix)
+override TARGET_PROGRAMS+=gparmake
+endif
+ifeq ($(FULL_TARGET),sparc-linux)
+override TARGET_PROGRAMS+=gparmake
+endif
+ifeq ($(FULL_TARGET),sparc-netbsd)
+override TARGET_PROGRAMS+=gparmake
+endif
+ifeq ($(FULL_TARGET),sparc-solaris)
+override TARGET_PROGRAMS+=gparmake
+endif
+ifeq ($(FULL_TARGET),sparc-embedded)
+override TARGET_PROGRAMS+=gparmake
+endif
+ifeq ($(FULL_TARGET),x86_64-linux)
+override TARGET_PROGRAMS+=gparmake
+endif
+ifeq ($(FULL_TARGET),x86_64-freebsd)
+override TARGET_PROGRAMS+=gparmake
+endif
+ifeq ($(FULL_TARGET),x86_64-netbsd)
+override TARGET_PROGRAMS+=gparmake
+endif
+ifeq ($(FULL_TARGET),x86_64-solaris)
+override TARGET_PROGRAMS+=gparmake
+endif
+ifeq ($(FULL_TARGET),x86_64-openbsd)
+override TARGET_PROGRAMS+=gparmake
+endif
+ifeq ($(FULL_TARGET),x86_64-darwin)
+override TARGET_PROGRAMS+=gparmake
+endif
+ifeq ($(FULL_TARGET),x86_64-win64)
+override TARGET_PROGRAMS+=gparmake
+endif
+ifeq ($(FULL_TARGET),x86_64-embedded)
+override TARGET_PROGRAMS+=gparmake
+endif
+ifeq ($(FULL_TARGET),arm-linux)
+override TARGET_PROGRAMS+=gparmake
+endif
+ifeq ($(FULL_TARGET),arm-palmos)
+override TARGET_PROGRAMS+=gparmake
+endif
+ifeq ($(FULL_TARGET),arm-darwin)
+override TARGET_PROGRAMS+=gparmake
+endif
+ifeq ($(FULL_TARGET),arm-wince)
+override TARGET_PROGRAMS+=gparmake
+endif
+ifeq ($(FULL_TARGET),arm-gba)
+override TARGET_PROGRAMS+=gparmake
+endif
+ifeq ($(FULL_TARGET),arm-nds)
+override TARGET_PROGRAMS+=gparmake
+endif
+ifeq ($(FULL_TARGET),arm-embedded)
+override TARGET_PROGRAMS+=gparmake
+endif
+ifeq ($(FULL_TARGET),arm-symbian)
+override TARGET_PROGRAMS+=gparmake
+endif
+ifeq ($(FULL_TARGET),powerpc64-linux)
+override TARGET_PROGRAMS+=gparmake
+endif
+ifeq ($(FULL_TARGET),powerpc64-darwin)
+override TARGET_PROGRAMS+=gparmake
+endif
+ifeq ($(FULL_TARGET),powerpc64-embedded)
+override TARGET_PROGRAMS+=gparmake
+endif
+ifeq ($(FULL_TARGET),powerpc64-aix)
+override TARGET_PROGRAMS+=gparmake
+endif
+ifeq ($(FULL_TARGET),avr-embedded)
+override TARGET_PROGRAMS+=gparmake
+endif
+ifeq ($(FULL_TARGET),armeb-linux)
+override TARGET_PROGRAMS+=gparmake
+endif
+ifeq ($(FULL_TARGET),armeb-embedded)
+override TARGET_PROGRAMS+=gparmake
+endif
+ifeq ($(FULL_TARGET),mips-linux)
+override TARGET_PROGRAMS+=gparmake
+endif
+ifeq ($(FULL_TARGET),mipsel-linux)
+override TARGET_PROGRAMS+=gparmake
+endif
+ifeq ($(FULL_TARGET),jvm-java)
+override TARGET_PROGRAMS+=gparmake
+endif
+ifeq ($(FULL_TARGET),jvm-android)
+override TARGET_PROGRAMS+=gparmake
+endif
 override INSTALL_FPCPACKAGE=y
 ifdef REQUIRE_UNITSDIR
 override UNITSDIR+=$(REQUIRE_UNITSDIR)
@@ -905,6 +1115,255 @@ else
 TAROPT=vz
 TAREXT=.tar.gz
 endif
+override REQUIRE_PACKAGES=rtl 
+ifeq ($(FULL_TARGET),i386-linux)
+REQUIRE_PACKAGES_RTL=1
+endif
+ifeq ($(FULL_TARGET),i386-go32v2)
+REQUIRE_PACKAGES_RTL=1
+endif
+ifeq ($(FULL_TARGET),i386-win32)
+REQUIRE_PACKAGES_RTL=1
+endif
+ifeq ($(FULL_TARGET),i386-os2)
+REQUIRE_PACKAGES_RTL=1
+endif
+ifeq ($(FULL_TARGET),i386-freebsd)
+REQUIRE_PACKAGES_RTL=1
+endif
+ifeq ($(FULL_TARGET),i386-beos)
+REQUIRE_PACKAGES_RTL=1
+endif
+ifeq ($(FULL_TARGET),i386-haiku)
+REQUIRE_PACKAGES_RTL=1
+endif
+ifeq ($(FULL_TARGET),i386-netbsd)
+REQUIRE_PACKAGES_RTL=1
+endif
+ifeq ($(FULL_TARGET),i386-solaris)
+REQUIRE_PACKAGES_RTL=1
+endif
+ifeq ($(FULL_TARGET),i386-qnx)
+REQUIRE_PACKAGES_RTL=1
+endif
+ifeq ($(FULL_TARGET),i386-netware)
+REQUIRE_PACKAGES_RTL=1
+endif
+ifeq ($(FULL_TARGET),i386-openbsd)
+REQUIRE_PACKAGES_RTL=1
+endif
+ifeq ($(FULL_TARGET),i386-wdosx)
+REQUIRE_PACKAGES_RTL=1
+endif
+ifeq ($(FULL_TARGET),i386-darwin)
+REQUIRE_PACKAGES_RTL=1
+endif
+ifeq ($(FULL_TARGET),i386-emx)
+REQUIRE_PACKAGES_RTL=1
+endif
+ifeq ($(FULL_TARGET),i386-watcom)
+REQUIRE_PACKAGES_RTL=1
+endif
+ifeq ($(FULL_TARGET),i386-netwlibc)
+REQUIRE_PACKAGES_RTL=1
+endif
+ifeq ($(FULL_TARGET),i386-wince)
+REQUIRE_PACKAGES_RTL=1
+endif
+ifeq ($(FULL_TARGET),i386-embedded)
+REQUIRE_PACKAGES_RTL=1
+endif
+ifeq ($(FULL_TARGET),i386-symbian)
+REQUIRE_PACKAGES_RTL=1
+endif
+ifeq ($(FULL_TARGET),i386-nativent)
+REQUIRE_PACKAGES_RTL=1
+endif
+ifeq ($(FULL_TARGET),i386-iphonesim)
+REQUIRE_PACKAGES_RTL=1
+endif
+ifeq ($(FULL_TARGET),m68k-linux)
+REQUIRE_PACKAGES_RTL=1
+endif
+ifeq ($(FULL_TARGET),m68k-freebsd)
+REQUIRE_PACKAGES_RTL=1
+endif
+ifeq ($(FULL_TARGET),m68k-netbsd)
+REQUIRE_PACKAGES_RTL=1
+endif
+ifeq ($(FULL_TARGET),m68k-amiga)
+REQUIRE_PACKAGES_RTL=1
+endif
+ifeq ($(FULL_TARGET),m68k-atari)
+REQUIRE_PACKAGES_RTL=1
+endif
+ifeq ($(FULL_TARGET),m68k-openbsd)
+REQUIRE_PACKAGES_RTL=1
+endif
+ifeq ($(FULL_TARGET),m68k-palmos)
+REQUIRE_PACKAGES_RTL=1
+endif
+ifeq ($(FULL_TARGET),m68k-embedded)
+REQUIRE_PACKAGES_RTL=1
+endif
+ifeq ($(FULL_TARGET),powerpc-linux)
+REQUIRE_PACKAGES_RTL=1
+endif
+ifeq ($(FULL_TARGET),powerpc-netbsd)
+REQUIRE_PACKAGES_RTL=1
+endif
+ifeq ($(FULL_TARGET),powerpc-amiga)
+REQUIRE_PACKAGES_RTL=1
+endif
+ifeq ($(FULL_TARGET),powerpc-macos)
+REQUIRE_PACKAGES_RTL=1
+endif
+ifeq ($(FULL_TARGET),powerpc-darwin)
+REQUIRE_PACKAGES_RTL=1
+endif
+ifeq ($(FULL_TARGET),powerpc-morphos)
+REQUIRE_PACKAGES_RTL=1
+endif
+ifeq ($(FULL_TARGET),powerpc-embedded)
+REQUIRE_PACKAGES_RTL=1
+endif
+ifeq ($(FULL_TARGET),powerpc-wii)
+REQUIRE_PACKAGES_RTL=1
+endif
+ifeq ($(FULL_TARGET),powerpc-aix)
+REQUIRE_PACKAGES_RTL=1
+endif
+ifeq ($(FULL_TARGET),sparc-linux)
+REQUIRE_PACKAGES_RTL=1
+endif
+ifeq ($(FULL_TARGET),sparc-netbsd)
+REQUIRE_PACKAGES_RTL=1
+endif
+ifeq ($(FULL_TARGET),sparc-solaris)
+REQUIRE_PACKAGES_RTL=1
+endif
+ifeq ($(FULL_TARGET),sparc-embedded)
+REQUIRE_PACKAGES_RTL=1
+endif
+ifeq ($(FULL_TARGET),x86_64-linux)
+REQUIRE_PACKAGES_RTL=1
+endif
+ifeq ($(FULL_TARGET),x86_64-freebsd)
+REQUIRE_PACKAGES_RTL=1
+endif
+ifeq ($(FULL_TARGET),x86_64-netbsd)
+REQUIRE_PACKAGES_RTL=1
+endif
+ifeq ($(FULL_TARGET),x86_64-solaris)
+REQUIRE_PACKAGES_RTL=1
+endif
+ifeq ($(FULL_TARGET),x86_64-openbsd)
+REQUIRE_PACKAGES_RTL=1
+endif
+ifeq ($(FULL_TARGET),x86_64-darwin)
+REQUIRE_PACKAGES_RTL=1
+endif
+ifeq ($(FULL_TARGET),x86_64-win64)
+REQUIRE_PACKAGES_RTL=1
+endif
+ifeq ($(FULL_TARGET),x86_64-embedded)
+REQUIRE_PACKAGES_RTL=1
+endif
+ifeq ($(FULL_TARGET),arm-linux)
+REQUIRE_PACKAGES_RTL=1
+endif
+ifeq ($(FULL_TARGET),arm-palmos)
+REQUIRE_PACKAGES_RTL=1
+endif
+ifeq ($(FULL_TARGET),arm-darwin)
+REQUIRE_PACKAGES_RTL=1
+endif
+ifeq ($(FULL_TARGET),arm-wince)
+REQUIRE_PACKAGES_RTL=1
+endif
+ifeq ($(FULL_TARGET),arm-gba)
+REQUIRE_PACKAGES_RTL=1
+endif
+ifeq ($(FULL_TARGET),arm-nds)
+REQUIRE_PACKAGES_RTL=1
+endif
+ifeq ($(FULL_TARGET),arm-embedded)
+REQUIRE_PACKAGES_RTL=1
+endif
+ifeq ($(FULL_TARGET),arm-symbian)
+REQUIRE_PACKAGES_RTL=1
+endif
+ifeq ($(FULL_TARGET),powerpc64-linux)
+REQUIRE_PACKAGES_RTL=1
+endif
+ifeq ($(FULL_TARGET),powerpc64-darwin)
+REQUIRE_PACKAGES_RTL=1
+endif
+ifeq ($(FULL_TARGET),powerpc64-embedded)
+REQUIRE_PACKAGES_RTL=1
+endif
+ifeq ($(FULL_TARGET),powerpc64-aix)
+REQUIRE_PACKAGES_RTL=1
+endif
+ifeq ($(FULL_TARGET),avr-embedded)
+REQUIRE_PACKAGES_RTL=1
+endif
+ifeq ($(FULL_TARGET),armeb-linux)
+REQUIRE_PACKAGES_RTL=1
+endif
+ifeq ($(FULL_TARGET),armeb-embedded)
+REQUIRE_PACKAGES_RTL=1
+endif
+ifeq ($(FULL_TARGET),mips-linux)
+REQUIRE_PACKAGES_RTL=1
+endif
+ifeq ($(FULL_TARGET),mipsel-linux)
+REQUIRE_PACKAGES_RTL=1
+endif
+ifeq ($(FULL_TARGET),jvm-java)
+REQUIRE_PACKAGES_RTL=1
+endif
+ifeq ($(FULL_TARGET),jvm-android)
+REQUIRE_PACKAGES_RTL=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
 ifndef NOCPUDEF
 override FPCOPTDEF=$(ARCH)
 endif
@@ -1059,10 +1518,170 @@ EXECPPAS:=@$(PPAS)
 endif
 endif
 endif
+.PHONY: fpc_exes
+ifndef CROSSINSTALL
+ifneq ($(TARGET_PROGRAMS),)
+override EXEFILES=$(addsuffix $(EXEEXT),$(TARGET_PROGRAMS))
+override EXEOFILES:=$(addsuffix $(OEXT),$(TARGET_PROGRAMS)) $(addprefix $(STATICLIBPREFIX),$(addsuffix $(STATICLIBEXT),$(TARGET_PROGRAMS))) $(addprefix $(IMPORTLIBPREFIX),$(addsuffix $(STATICLIBEXT),$(TARGET_PROGRAMS)))
+override EXEDBGFILES:=$(addsuffix $(EXEDBGEXT),$(TARGET_PROGRAMS))
+override ALLTARGET+=fpc_exes
+override INSTALLEXEFILES+=$(EXEFILES)
+override CLEANEXEFILES+=$(EXEFILES) $(EXEOFILES)
+override CLEANEXEDBGFILES+=$(EXEDBGFILES)
+ifeq ($(OS_TARGET),os2)
+override CLEANEXEFILES+=$(addsuffix $(AOUTEXT),$(TARGET_PROGRAMS))
+endif
+ifeq ($(OS_TARGET),emx)
+override CLEANEXEFILES+=$(addsuffix $(AOUTEXT),$(TARGET_PROGRAMS))
+endif
+endif
+endif
+fpc_exes: $(COMPILER_TARGETDIR) $(COMPILER_UNITTARGETDIR) $(EXEFILES)
 ifdef TARGET_RSTS
 override RSTFILES=$(addsuffix $(RSTEXT),$(TARGET_RSTS))
 override CLEANRSTFILES+=$(RSTFILES)
 endif
+.PHONY: fpc_all fpc_smart fpc_debug fpc_release fpc_shared
+$(FPCMADE): $(ALLDEPENDENCIES) $(ALLTARGET)
+	@$(ECHOREDIR) Compiled > $(FPCMADE)
+fpc_all: $(FPCMADE)
+fpc_smart:
+	$(MAKE) all LINKSMART=1 CREATESMART=1
+fpc_debug:
+	$(MAKE) all DEBUG=1
+fpc_release:
+	$(MAKE) all RELEASE=1
+.SUFFIXES: $(EXEEXT) $(PPUEXT) $(OEXT) .pas .lpr .dpr .pp .rc .res
+$(COMPILER_UNITTARGETDIR):
+	$(MKDIRTREE) $(COMPILER_UNITTARGETDIR)
+$(COMPILER_TARGETDIR):
+	$(MKDIRTREE) $(COMPILER_TARGETDIR)
+%$(PPUEXT): %.pp
+	$(COMPILER) $<
+	$(EXECPPAS)
+%$(PPUEXT): %.pas
+	$(COMPILER) $<
+	$(EXECPPAS)
+%$(EXEEXT): %.pp
+	$(COMPILER) $<
+	$(EXECPPAS)
+%$(EXEEXT): %.pas
+	$(COMPILER) $<
+	$(EXECPPAS)
+%$(EXEEXT): %.lpr
+	$(COMPILER) $<
+	$(EXECPPAS)
+%$(EXEEXT): %.dpr
+	$(COMPILER) $<
+	$(EXECPPAS)
+%.res: %.rc
+	windres -i $< -o $@
+vpath %.pp $(COMPILER_SOURCEDIR) $(COMPILER_INCLUDEDIR)
+vpath %.pas $(COMPILER_SOURCEDIR) $(COMPILER_INCLUDEDIR)
+vpath %.lpr $(COMPILER_SOURCEDIR) $(COMPILER_INCLUDEDIR)
+vpath %.dpr $(COMPILER_SOURCEDIR) $(COMPILER_INCLUDEDIR)
+vpath %.inc $(COMPILER_INCLUDEDIR)
+vpath %$(OEXT) $(COMPILER_UNITTARGETDIR)
+vpath %$(PPUEXT) $(COMPILER_UNITTARGETDIR)
+.PHONY: fpc_shared
+override INSTALLTARGET+=fpc_shared_install
+ifndef SHARED_LIBVERSION
+SHARED_LIBVERSION=$(FPC_VERSION)
+endif
+ifndef SHARED_LIBNAME
+SHARED_LIBNAME=$(PACKAGE_NAME)
+endif
+ifndef SHARED_FULLNAME
+SHARED_FULLNAME=$(SHAREDLIBPREFIX)$(SHARED_LIBNAME)-$(SHARED_LIBVERSION)$(SHAREDLIBEXT)
+endif
+ifndef SHARED_LIBUNITS
+SHARED_LIBUNITS:=$(TARGET_UNITS) $(TARGET_IMPLICITUNITS)
+override SHARED_LIBUNITS:=$(filter-out $(INSTALL_BUILDUNIT),$(SHARED_LIBUNITS))
+endif
+fpc_shared:
+ifdef HASSHAREDLIB
+	$(MAKE) all CREATESHARED=1 LINKSHARED=1 CREATESMART=1
+ifneq ($(SHARED_BUILD),n)
+	$(PPUMOVE) -q $(SHARED_LIBUNITS) -i$(COMPILER_UNITTARGETDIR) -o$(SHARED_FULLNAME) -d$(COMPILER_UNITTARGETDIR)
+endif
+else
+	@$(ECHO) Shared Libraries not supported
+endif
+fpc_shared_install:
+ifneq ($(SHARED_BUILD),n)
+ifneq ($(SHARED_LIBUNITS),)
+ifneq ($(wildcard $(COMPILER_UNITTARGETDIR)/$(SHARED_FULLNAME)),)
+	$(INSTALL) $(COMPILER_UNITTARGETDIR)/$(SHARED_FULLNAME) $(INSTALL_SHAREDDIR)
+endif
+endif
+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
+override INSTALLPPULINKFILES:=$(subst $(PPUEXT),$(OEXT),$(INSTALLPPUFILES)) $(addprefix $(STATICLIBPREFIX),$(subst $(PPUEXT),$(STATICLIBEXT),$(INSTALLPPUFILES))) $(addprefix $(IMPORTLIBPREFIX),$(subst $(PPUEXT),$(STATICLIBEXT),$(INSTALLPPUFILES)))
+ifneq ($(UNITTARGETDIRPREFIX),)
+override INSTALLPPUFILES:=$(addprefix $(UNITTARGETDIRPREFIX),$(notdir $(INSTALLPPUFILES)))
+override INSTALLPPULINKFILES:=$(wildcard $(addprefix $(UNITTARGETDIRPREFIX),$(notdir $(INSTALLPPULINKFILES))))
+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: $(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_clean fpc_cleanall fpc_distclean
 ifdef EXEFILES
 override CLEANEXEFILES:=$(addprefix $(TARGETDIRPREFIX),$(CLEANEXEFILES))
@@ -1247,22 +1866,23 @@ 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
-debug:
-smart:
-release:
+debug: fpc_debug
+smart: fpc_smart
+release: fpc_release
+units: fpc_units
 examples:
-shared:
-install:
-sourceinstall:
-exampleinstall:
+shared: fpc_shared
+install: fpc_install
+sourceinstall: fpc_sourceinstall
+exampleinstall: fpc_exampleinstall
 distinstall:
 zipinstall:
 zipsourceinstall:
 zipexampleinstall:
 zipdistinstall:
-cleanall:
+cleanall: fpc_cleanall
 makefiles: fpc_makefiles
-.PHONY: debug smart release examples shared install sourceinstall exampleinstall distinstall zipinstall zipsourceinstall zipexampleinstall zipdistinstall cleanall makefiles
+.PHONY: debug smart release units examples shared install sourceinstall exampleinstall distinstall zipinstall zipsourceinstall zipexampleinstall zipdistinstall cleanall makefiles
 ifneq ($(wildcard fpcmake.loc),)
 include fpcmake.loc
 endif
@@ -1369,8 +1989,11 @@ export QUICKTEST
 else
 override TESTSUBDIRS+=$(TESTPACKAGESUBDIRS) alglib
 endif
+ifndef CHUNKSIZE
+export CHUNKSIZE:=100
+endif
 TESTDIRS:=test $(addprefix test/,$(TESTSUBDIRS))
-.PHONY: utils units copyfiles testprep
+.PHONY: utils tstunits copyfiles testprep
 utils:
 	$(MAKE) -C utils utils
 utilsdb:
@@ -1384,11 +2007,14 @@ endif
 ifndef CONCAT
 CONCAT=utils/concat
 endif
+ifndef GPARMAKE
+GPARMAKE=./gparmake$(EXEEXT)
+endif
 ifndef PREPUP
 PREPUP=utils/prepup
 endif
-units:
-	$(MAKE) -C units FPC_VERSION= FPC=$(TEST_FPC) CPU_TARGET=$(TEST_CPU_TARGET) OS_TARGET=$(TEST_OS_TARGET) OPT="$(TEST_OPT)" CCOMPILER=$(TEST_CCOMPILER) BINUTILSPREFIX=$(TEST_BINUTILSPREFIX)
+tstunits:
+	$(MAKE) -C tstunits FPC_VERSION= FPC=$(TEST_FPC) CPU_TARGET=$(TEST_CPU_TARGET) OS_TARGET=$(TEST_OS_TARGET) OPT="$(TEST_OPT)" CCOMPILER=$(TEST_CCOMPILER) BINUTILSPREFIX=$(TEST_BINUTILSPREFIX)
 .PHONY: create_c_objects delete_c_objects copyfiles
 C_SOURCE_DIR=test/cg/obj
 C_SOURCES=ctest.c tcext3.c tcext4.c tcext5.c tcext6.c
@@ -1427,9 +2053,8 @@ copyfiles: $(TEST_OUTPUTDIR)
 	-$(COPY) $(C_OBJECTS) $(TEST_OUTPUTDIR)/test/cg
 	-$(COPY) $(CPP_OBJECTS) $(TEST_OUTPUTDIR)/test/cg
 	-$(MKDIRTREE) $(TEST_OUTPUTDIR)/test/units/system
-	$(COPY) test/units/system/test*.txt $(TEST_OUTPUTDIR)/test/units/system
 testprep: testprep-stamp.$(TEST_FULL_TARGET)
-testprep-stamp.$(TEST_FULL_TARGET): utils units copyfiles
+testprep-stamp.$(TEST_FULL_TARGET): utils tstunits copyfiles
 	$(ECHOREDIR) $(DATE) > testprep-stamp.$(TEST_FULL_TARGET)
 $(TEST_OUTPUTDIR):
 	$(MKDIRTREE) $@
@@ -1546,6 +2171,7 @@ ifndef SINGLEDOTESTRUNS
 	$(Q)$(DOTEST) $(DOTESTOPT) -e $(wildcard $(addsuffix /t*.pp,$(TESTDIRS)))
 endif
 alltests: alltest alltbs alltbf allwebtbs allwebtbf
+ifdef SINGLEDOTESTRUNS
 .PHONY: allexectbs allexectbf allexecwebtbs allexecwebtbf allexectest allexectests
 allexectbs: $(addsuffix .tbslog, $(LOGFILES))
 allexectbf: $(addsuffix .tbflog, $(LOGFILES))
@@ -1555,50 +2181,50 @@ allexectest: $(addsuffix .testlog, $(LOGFILES))
 $(TEST_OUTPUTDIR)/%.mergedlog : $(TEST_OUTPUTDIR)/%.testlog $(TEST_OUTPUTDIR)/%.tbslog $(TEST_OUTPUTDIR)/%.tbflog $(TEST_OUTPUTDIR)/%.webtbslog $(TEST_OUTPUTDIR)/%.webtbflog
 	$(Q)$(CONCAT) $^ $@
 	$(Q)$(COPY) $@ $(basename $@)
-$(TEST_OUTPUTDIR)/tbsdir-stamp.$(TEST_FULL_TARGET): testprep-stamp.$(TEST_FULL_TARGET)
-	$(Q)$(DOTEST) $(DOTESTOPT) -Ltbslog -e $(sort $(wildcard tbs/t*.pp))
-	$(ECHOREDIR) $(DATE) > $@
 $(TEST_OUTPUTDIR)/%.tbslog : $(TBSREQ)
-	$(Q)$(ECHOREDIR) -n >> $(TEST_OUTPUTDIR)/faillist.tbslog
-	$(Q)$(ECHOREDIR) -n >> $(TEST_OUTPUTDIR)/longlog.tbslog
-$(TEST_OUTPUTDIR)/tbfdir-stamp.$(TEST_FULL_TARGET): testprep-stamp.$(TEST_FULL_TARGET)
-	$(Q)$(DOTEST) $(DOTESTOPT) -Ltbflog -e $(sort $(wildcard tbf/t*.pp))
-	$(ECHOREDIR) $(DATE) > $@
+	$(Q)$(ECHO) -n >> $(TEST_OUTPUTDIR)/faillist.tbslog
+	$(Q)$(ECHO) -n >> $(TEST_OUTPUTDIR)/longlog.tbslog
 $(TEST_OUTPUTDIR)/%.tbflog : $(TBFREQ)
-	$(Q)$(ECHOREDIR) -n >> $(TEST_OUTPUTDIR)/faillist.tbflog
-	$(Q)$(ECHOREDIR) -n >> $(TEST_OUTPUTDIR)/longlog.tbflog
-$(TEST_OUTPUTDIR)/webtbsdir-stamp.$(TEST_FULL_TARGET): testprep-stamp.$(TEST_FULL_TARGET)
-	$(Q)$(DOTEST) $(DOTESTOPT) -Lwebtbslog -e $(sort $(wildcard webtbs/t*.pp))
-	$(Q)$(ECHOREDIR) $(DATE) > $@
+	$(Q)$(ECHO) -n >> $(TEST_OUTPUTDIR)/faillist.tbflog
+	$(Q)$(ECHO) -n >> $(TEST_OUTPUTDIR)/longlog.tbflog
 $(TEST_OUTPUTDIR)/%.webtbslog : $(WEBTBSREQ)
-	$(Q)$(ECHOREDIR) -n >> $(TEST_OUTPUTDIR)/faillist.webtbslog
-	$(Q)$(ECHOREDIR) -n >> $(TEST_OUTPUTDIR)/longlog.webtbslog
-$(TEST_OUTPUTDIR)/webtbfdir-stamp.$(TEST_FULL_TARGET): testprep-stamp.$(TEST_FULL_TARGET)
-	$(Q)$(DOTEST) $(DOTESTOPT) -Lwebtbflog -e $(sort $(wildcard webtbf/t*.pp))
-	$(ECHOREDIR) $(DATE) > $@
+	$(Q)$(ECHO) -n >> $(TEST_OUTPUTDIR)/faillist.webtbslog
+	$(Q)$(ECHO) -n >> $(TEST_OUTPUTDIR)/longlog.webtbslog
 $(TEST_OUTPUTDIR)/%.webtbflog : $(WEBTBFREQ)
-	$(Q)$(ECHOREDIR) -n >> $(TEST_OUTPUTDIR)/faillist.webtbflog
-	$(Q)$(ECHOREDIR) -n >> $(TEST_OUTPUTDIR)/longlog.webtbflog
-$(TEST_OUTPUTDIR)/testdir-stamp.$(TEST_FULL_TARGET): testprep-stamp.$(TEST_FULL_TARGET)
-	$(Q)$(DOTEST) $(DOTESTOPT)  -Ltestlog -e $(sort $(wildcard $(addsuffix /t*.pp,$(TESTDIRS))))
-	$(ECHOREDIR) $(DATE) > $@
+	$(Q)$(ECHO) -n >> $(TEST_OUTPUTDIR)/faillist.webtbflog
+	$(Q)$(ECHO) -n >> $(TEST_OUTPUTDIR)/longlog.webtbflog
 $(TEST_OUTPUTDIR)/%.testlog : $(TESTREQ)
-	$(Q)$(ECHOREDIR) -n >> $(TEST_OUTPUTDIR)/faillist.testlog
-	$(Q)$(ECHOREDIR) -n >> $(TEST_OUTPUTDIR)/longlog.testlog
-allexectests: $(TEST_OUTPUTDIR)/log.webtbslog $(TEST_OUTPUTDIR)/log.testlog $(TEST_OUTPUTDIR)/log.tbslog $(TEST_OUTPUTDIR)/log.tbflog $(TEST_OUTPUTDIR)/log.webtbflog  $(addprefix $(TEST_OUTPUTDIR)/,$(foreach EXT, $(LOGEXT), $(addsuffix $(EXT), faillist longlog))) $(addsuffix .mergedlog, $(LOGFILES))
+	$(Q)$(ECHO) -n >> $(TEST_OUTPUTDIR)/faillist.testlog
+	$(Q)$(ECHO) -n >> $(TEST_OUTPUTDIR)/longlog.testlog
+allexectests:  $(TEST_OUTPUTDIR)/log.testlog $(TEST_OUTPUTDIR)/log.tbslog $(TEST_OUTPUTDIR)/log.tbflog $(TEST_OUTPUTDIR)/log.webtbslog $(TEST_OUTPUTDIR)/log.webtbflog $(addprefix $(TEST_OUTPUTDIR)/,$(foreach EXT, $(LOGEXT), $(addsuffix $(EXT), faillist longlog))) $(addsuffix .mergedlog, $(LOGFILES))
+else
+.PHONY: allexectests
+$(GPARMAKE): $(COMPILER_UNITTARGETDIR) utils/gparmake.pp
+	$(FPC) $(FPC_OPT) -FE. utils/gparmake.pp
+$(TEST_OUTPUTDIR)/MakeChunks-$(TEST_TARGETSUFFIX).inc: $(GPARMAKE) $(TEST_OUTPUTDIR)
+	$(Q)$(GPARMAKE) $(TEST_OUTPUTDIR)/MakeChunks-$(TEST_TARGETSUFFIX).inc 1 $(CHUNKSIZE) $(sort $(wildcard $(addsuffix /t*.pp,$(TESTDIRS))))
+	$(Q)$(GPARMAKE) -a $(TEST_OUTPUTDIR)/MakeChunks-$(TEST_TARGETSUFFIX).inc 100000 $(CHUNKSIZE) $(sort $(wildcard tbs/t*.pp))
+	$(Q)$(GPARMAKE) -a $(TEST_OUTPUTDIR)/MakeChunks-$(TEST_TARGETSUFFIX).inc 150000 $(CHUNKSIZE) $(sort $(wildcard tbf/t*.pp))
+	$(Q)$(GPARMAKE) -a $(TEST_OUTPUTDIR)/MakeChunks-$(TEST_TARGETSUFFIX).inc 200000 $(CHUNKSIZE) $(sort $(wildcard webtbs/t*.pp))
+	$(Q)$(GPARMAKE) -a $(TEST_OUTPUTDIR)/MakeChunks-$(TEST_TARGETSUFFIX).inc 300000 $(CHUNKSIZE) $(sort $(wildcard webtbf/t*.pp))
+ifneq (,$(findstring all,$(MAKECMDGOALS)))
+include $(TEST_OUTPUTDIR)/MakeChunks-$(TEST_TARGETSUFFIX).inc
+endif
+allexectests: $(TEST_OUTPUTDIR)/MakeChunks-$(TEST_TARGETSUFFIX).inc
+endif
 .PHONY: clean distclean clean_test clean_sources
 clean_sources:
 	-$(DEL) $(wildcard $(patsubst %.pp,%$(PPUEXT),$(wildcard $(addsuffix /*.pp,$(DIRS)))))
 clean_test:
 	-$(DELTREE) $(TEST_OUTPUTDIR)
 	-$(DEL) core gmon.out testprep-stamp.$(TEST_FULL_TARGET) dotgz.bat
-clean: clean_sources
+clean: clean_sources fpc_clean
 	$(MAKE) clean_test CPU_TARGET=$(TEST_CPU_TARGET) OS_TARGET=$(TEST_OS_TARGET)
-	$(MAKE) -C units clean CPU_TARGET=$(TEST_CPU_TARGET) OS_TARGET=$(TEST_OS_TARGET)
+	$(MAKE) -C tstunits clean CPU_TARGET=$(TEST_CPU_TARGET) OS_TARGET=$(TEST_OS_TARGET)
 distclean: clean fpc_distclean
 	$(DELTREE) output
 	$(MAKE) -C utils distclean
-	$(MAKE) -C units distclean
+	$(MAKE) -C tstunits distclean
 	-$(DEL) testprep-stamp.*
 .PHONY: all full digest onlyknown onlygraph onlyinteractive
 digest : utils

+ 76 - 41
tests/Makefile.fpc

@@ -9,6 +9,9 @@ fpcpackage=y
 fpcdir=..
 rule=allexectests
 
+[target]
+programs=gparmake
+
 [rules]
 unexport FPC_VERSION
 
@@ -147,10 +150,15 @@ else
 override TESTSUBDIRS+=$(TESTPACKAGESUBDIRS) alglib
 endif
 
+# number of tests to run per (potentially parallel) chunk/dotest invocation
+ifndef CHUNKSIZE
+export CHUNKSIZE:=100
+endif
+
 # All full dirnames in the test/ dir including the subdir self
 TESTDIRS:=test $(addprefix test/,$(TESTSUBDIRS))
 
-.PHONY: utils units copyfiles testprep
+.PHONY: utils tstunits copyfiles testprep
 
 ################################
 # Utilities
@@ -174,6 +182,10 @@ ifndef CONCAT
 CONCAT=utils/concat
 endif
 
+ifndef GPARMAKE
+GPARMAKE=./gparmake$(EXEEXT)
+endif
+
 ifndef PREPUP
 PREPUP=utils/prepup
 endif
@@ -183,8 +195,8 @@ endif
 # Units
 #
 
-units:
-        $(MAKE) -C units FPC_VERSION= FPC=$(TEST_FPC) CPU_TARGET=$(TEST_CPU_TARGET) OS_TARGET=$(TEST_OS_TARGET) OPT="$(TEST_OPT)" CCOMPILER=$(TEST_CCOMPILER) BINUTILSPREFIX=$(TEST_BINUTILSPREFIX)
+tstunits:
+        $(MAKE) -C tstunits FPC_VERSION= FPC=$(TEST_FPC) CPU_TARGET=$(TEST_CPU_TARGET) OS_TARGET=$(TEST_OS_TARGET) OPT="$(TEST_OPT)" CCOMPILER=$(TEST_CCOMPILER) BINUTILSPREFIX=$(TEST_BINUTILSPREFIX)
 
 ################################
 # Copy test environment dependent files ctest.o, cext3.o, cext4.o to test/cg etc
@@ -235,14 +247,13 @@ copyfiles: $(TEST_OUTPUTDIR)
         -$(COPY) $(C_OBJECTS) $(TEST_OUTPUTDIR)/test/cg
         -$(COPY) $(CPP_OBJECTS) $(TEST_OUTPUTDIR)/test/cg
         -$(MKDIRTREE) $(TEST_OUTPUTDIR)/test/units/system
-        $(COPY) test/units/system/test*.txt $(TEST_OUTPUTDIR)/test/units/system
 
 ################################
 # Preparation for tests
 #
 
 testprep: testprep-stamp.$(TEST_FULL_TARGET)
-testprep-stamp.$(TEST_FULL_TARGET): utils units copyfiles
+testprep-stamp.$(TEST_FULL_TARGET): utils tstunits copyfiles
         $(ECHOREDIR) $(DATE) > testprep-stamp.$(TEST_FULL_TARGET)
 
 $(TEST_OUTPUTDIR):
@@ -406,6 +417,7 @@ alltests: alltest alltbs alltbf allwebtbs allwebtbf
 # Compile and Run tests
 #
 
+ifdef SINGLEDOTESTRUNS
 .PHONY: allexectbs allexectbf allexecwebtbs allexecwebtbf allexectest allexectests
 
 allexectbs: $(addsuffix .tbslog, $(LOGFILES))
@@ -418,51 +430,74 @@ $(TEST_OUTPUTDIR)/%.mergedlog : $(TEST_OUTPUTDIR)/%.testlog $(TEST_OUTPUTDIR)/%.
 	$(Q)$(CONCAT) $^ $@
 	$(Q)$(COPY) $@ $(basename $@)
 
-# the extra ECHOREDIR's below are in case a particular dir doesn't have any failures
+$(TEST_OUTPUTDIR)/%.tbslog : $(TBSREQ)
+	$(Q)$(ECHO) -n >> $(TEST_OUTPUTDIR)/faillist.tbslog
+	$(Q)$(ECHO) -n >> $(TEST_OUTPUTDIR)/longlog.tbslog
 
-$(TEST_OUTPUTDIR)/tbsdir-stamp.$(TEST_FULL_TARGET): testprep-stamp.$(TEST_FULL_TARGET)
-	$(Q)$(DOTEST) $(DOTESTOPT) -Ltbslog -e $(sort $(wildcard tbs/t*.pp))
-	$(ECHOREDIR) $(DATE) > $@
+$(TEST_OUTPUTDIR)/%.tbflog : $(TBFREQ)
+	$(Q)$(ECHO) -n >> $(TEST_OUTPUTDIR)/faillist.tbflog
+	$(Q)$(ECHO) -n >> $(TEST_OUTPUTDIR)/longlog.tbflog
 
-$(TEST_OUTPUTDIR)/%.tbslog : $(TBSREQ)
-	$(Q)$(ECHOREDIR) -n >> $(TEST_OUTPUTDIR)/faillist.tbslog
-	$(Q)$(ECHOREDIR) -n >> $(TEST_OUTPUTDIR)/longlog.tbslog
+$(TEST_OUTPUTDIR)/%.webtbslog : $(WEBTBSREQ)
+	$(Q)$(ECHO) -n >> $(TEST_OUTPUTDIR)/faillist.webtbslog
+	$(Q)$(ECHO) -n >> $(TEST_OUTPUTDIR)/longlog.webtbslog
+
+$(TEST_OUTPUTDIR)/%.webtbflog : $(WEBTBFREQ)
+	$(Q)$(ECHO) -n >> $(TEST_OUTPUTDIR)/faillist.webtbflog
+	$(Q)$(ECHO) -n >> $(TEST_OUTPUTDIR)/longlog.webtbflog
 
-$(TEST_OUTPUTDIR)/tbfdir-stamp.$(TEST_FULL_TARGET): testprep-stamp.$(TEST_FULL_TARGET)
-	$(Q)$(DOTEST) $(DOTESTOPT) -Ltbflog -e $(sort $(wildcard tbf/t*.pp))
-	$(ECHOREDIR) $(DATE) > $@
+$(TEST_OUTPUTDIR)/%.testlog : $(TESTREQ)
+	$(Q)$(ECHO) -n >> $(TEST_OUTPUTDIR)/faillist.testlog
+	$(Q)$(ECHO) -n >> $(TEST_OUTPUTDIR)/longlog.testlog
 
-$(TEST_OUTPUTDIR)/%.tbflog : $(TBFREQ)
-	$(Q)$(ECHOREDIR) -n >> $(TEST_OUTPUTDIR)/faillist.tbflog
-	$(Q)$(ECHOREDIR) -n >> $(TEST_OUTPUTDIR)/longlog.tbflog
+# run all tests, then merge log files
+allexectests:  $(TEST_OUTPUTDIR)/log.testlog $(TEST_OUTPUTDIR)/log.tbslog $(TEST_OUTPUTDIR)/log.tbflog $(TEST_OUTPUTDIR)/log.webtbslog $(TEST_OUTPUTDIR)/log.webtbflog $(addprefix $(TEST_OUTPUTDIR)/,$(foreach EXT, $(LOGEXT), $(addsuffix $(EXT), faillist longlog))) $(addsuffix .mergedlog, $(LOGFILES))
 
-$(TEST_OUTPUTDIR)/webtbsdir-stamp.$(TEST_FULL_TARGET): testprep-stamp.$(TEST_FULL_TARGET)
-	$(Q)$(DOTEST) $(DOTESTOPT) -Lwebtbslog -e $(sort $(wildcard webtbs/t*.pp))
-	$(Q)$(ECHOREDIR) $(DATE) > $@
+# SINGLEDOTESTRUNS
+else
 
-$(TEST_OUTPUTDIR)/%.webtbslog : $(WEBTBSREQ)
-	$(Q)$(ECHOREDIR) -n >> $(TEST_OUTPUTDIR)/faillist.webtbslog
-	$(Q)$(ECHOREDIR) -n >> $(TEST_OUTPUTDIR)/longlog.webtbslog
+.PHONY: allexectests
 
-$(TEST_OUTPUTDIR)/webtbfdir-stamp.$(TEST_FULL_TARGET): testprep-stamp.$(TEST_FULL_TARGET)
-	$(Q)$(DOTEST) $(DOTESTOPT) -Lwebtbflog -e $(sort $(wildcard webtbf/t*.pp))
-	$(ECHOREDIR) $(DATE) > $@
+$(GPARMAKE): $(COMPILER_UNITTARGETDIR) utils/gparmake.pp
+        $(FPC) $(FPC_OPT) -FE. utils/gparmake.pp
 
-$(TEST_OUTPUTDIR)/%.webtbflog : $(WEBTBFREQ)
-	$(Q)$(ECHOREDIR) -n >> $(TEST_OUTPUTDIR)/faillist.webtbflog
-	$(Q)$(ECHOREDIR) -n >> $(TEST_OUTPUTDIR)/longlog.webtbflog
+# Can't have testprep as prerequisite, because that is a phony target and
+# phony targets are always remade. Since the makefile will be reparsed 
+# after making the MakeChunks file (because it has to be included) and all
+# up-to-date checks will be re-evaluated, this means that the testprep rule
+# (or testprep timestamp file, which depends on phony rules and hence has
+# the same behaviour) will always be considered newer than the MakeChunks
+# file and hence the MakeChunks will be regenerated in an infinite loop
+# (although some make versions appear to contain a bug that prevents this
+# from happening)
+# As a result, we list TEST_OUTPUTDIR as a dependency (that just creates
+# the directory) and have an explicit rule to build GPARMAKE rather than
+# building it via the utils Makefile
+$(TEST_OUTPUTDIR)/MakeChunks-$(TEST_TARGETSUFFIX).inc: $(GPARMAKE) $(TEST_OUTPUTDIR)
+# generate rules for parallel executions of dotest
+	$(Q)$(GPARMAKE) $(TEST_OUTPUTDIR)/MakeChunks-$(TEST_TARGETSUFFIX).inc 1 $(CHUNKSIZE) $(sort $(wildcard $(addsuffix /t*.pp,$(TESTDIRS))))
+	$(Q)$(GPARMAKE) -a $(TEST_OUTPUTDIR)/MakeChunks-$(TEST_TARGETSUFFIX).inc 100000 $(CHUNKSIZE) $(sort $(wildcard tbs/t*.pp))
+	$(Q)$(GPARMAKE) -a $(TEST_OUTPUTDIR)/MakeChunks-$(TEST_TARGETSUFFIX).inc 150000 $(CHUNKSIZE) $(sort $(wildcard tbf/t*.pp))
+	$(Q)$(GPARMAKE) -a $(TEST_OUTPUTDIR)/MakeChunks-$(TEST_TARGETSUFFIX).inc 200000 $(CHUNKSIZE) $(sort $(wildcard webtbs/t*.pp))
+	$(Q)$(GPARMAKE) -a $(TEST_OUTPUTDIR)/MakeChunks-$(TEST_TARGETSUFFIX).inc 300000 $(CHUNKSIZE) $(sort $(wildcard webtbf/t*.pp))
 
-$(TEST_OUTPUTDIR)/testdir-stamp.$(TEST_FULL_TARGET): testprep-stamp.$(TEST_FULL_TARGET)
-	$(Q)$(DOTEST) $(DOTESTOPT)  -Ltestlog -e $(sort $(wildcard $(addsuffix /t*.pp,$(TESTDIRS))))
-	$(ECHOREDIR) $(DATE) > $@
 
-$(TEST_OUTPUTDIR)/%.testlog : $(TESTREQ)
-	$(Q)$(ECHOREDIR) -n >> $(TEST_OUTPUTDIR)/faillist.testlog
-	$(Q)$(ECHOREDIR) -n >> $(TEST_OUTPUTDIR)/longlog.testlog
+# only include the targets to compile/run the tests when we want to
+# run them (in particular: not when cleaning)
+ifneq (,$(findstring all,$(MAKECMDGOALS)))
+# incude the rules we just generated
+include $(TEST_OUTPUTDIR)/MakeChunks-$(TEST_TARGETSUFFIX).inc
+endif
+
 
-# run all tests (ordered by longest running to shortest), then merge log files
-allexectests: $(TEST_OUTPUTDIR)/log.webtbslog $(TEST_OUTPUTDIR)/log.testlog $(TEST_OUTPUTDIR)/log.tbslog $(TEST_OUTPUTDIR)/log.tbflog $(TEST_OUTPUTDIR)/log.webtbflog  $(addprefix $(TEST_OUTPUTDIR)/,$(foreach EXT, $(LOGEXT), $(addsuffix $(EXT), faillist longlog))) $(addsuffix .mergedlog, $(LOGFILES))
 
+# this is pretty cool: MakeChunks-$(TEST_TARGETSUFFIX).inc will contain additional prerequisites
+# for the allexectests target, and after it is generated it will be included by the above "include"
+# statement, and those additional prerquisites will be added on the fly and also be evaluated
+allexectests: $(TEST_OUTPUTDIR)/MakeChunks-$(TEST_TARGETSUFFIX).inc
+
+# SINGLEDOTESTRUNS
+endif
   
 
 ################################
@@ -479,14 +514,14 @@ clean_test:
         -$(DELTREE) $(TEST_OUTPUTDIR)
         -$(DEL) core gmon.out testprep-stamp.$(TEST_FULL_TARGET) dotgz.bat
 
-clean: clean_sources
+clean: clean_sources fpc_clean
         $(MAKE) clean_test CPU_TARGET=$(TEST_CPU_TARGET) OS_TARGET=$(TEST_OS_TARGET)
-        $(MAKE) -C units clean CPU_TARGET=$(TEST_CPU_TARGET) OS_TARGET=$(TEST_OS_TARGET)
+        $(MAKE) -C tstunits clean CPU_TARGET=$(TEST_CPU_TARGET) OS_TARGET=$(TEST_OS_TARGET)
 
 distclean: clean fpc_distclean
         $(DELTREE) output
         $(MAKE) -C utils distclean
-        $(MAKE) -C units distclean
+        $(MAKE) -C tstunits distclean
         -$(DEL) testprep-stamp.*
 
 #cleanall: clean

+ 21 - 0
tests/readme.txt

@@ -21,6 +21,25 @@ directory. Then webtbs/webtbf/test/tbs/tbf are searched for t*.pp to be
 compiled and executed as tests.
 
 
+Parallel test suite runs
+------------------------
+
+It is possible to run the test suite in parallel, but only when using the
+"all" target and only if SINGLEDOTESTRUNS is not used. Under those
+circumstances, it is safe to use -jx, with x the number of tests that can
+be compiled and run in parallel. E.g.
+
+  make all TEST_FPC=path_to_your_compiler -j 2
+
+After running the tests in parallel, you will probably want to get the
+summary. This can be achieved making the "digest" target after the "all"
+target has finished, e.g.
+
+  make digest TEST_FPC=path_to_your_compiler
+
+Make sure to clean the test suite between two runs.
+
+
 Directories
 -----------
 webtbs...........Tests for web-bug-database bugs (should compile/run)
@@ -135,6 +154,8 @@ TEST_ABI               test a certain abi, this influences where the
                        c object files are taken from: TEST_ABI=eabi
                        takes the c*.o files from
                        test/cg/obj/linux/arm-eabi
+CHUNKSIZE              Number of tests per chunk that can be potentially executed
+                       in parallel with other chunks
 
   (Please add more test options if needed)
 

+ 0 - 0
tests/units/MPWMake → tests/tstunits/MPWMake


+ 0 - 0
tests/units/Makefile → tests/tstunits/Makefile


+ 0 - 0
tests/units/Makefile.fpc → tests/tstunits/Makefile.fpc


+ 0 - 0
tests/units/erroru.pp → tests/tstunits/erroru.pp


+ 0 - 0
tests/units/popuperr.pp → tests/tstunits/popuperr.pp


+ 0 - 0
tests/units/win32err.pp → tests/tstunits/win32err.pp


+ 48 - 11
tests/utils/concat.pp

@@ -3,15 +3,20 @@
 program concat;
 
 uses
-  SysUtils;
+  SysUtils, Classes;
 
 var
   Dst: TextFile;
+  FileList: TStringList;
+  IgnoreNonExisting: boolean;
 
 
 procedure usage;
   begin
-    Writeln('Usage: concat <srcfile1> [<srcfile2> ..] <dstfile>');
+    Writeln('Usage: concat [-i] <srcfile1> [<srcfile2> ..] <dstfile>');
+    Writeln;
+    Writeln('Options:');
+    Writeln('  -i      Ignore non-existent files');
     Writeln;
     halt(1);
   end;
@@ -22,11 +27,23 @@ procedure DoConcat;
     Src: TextFile;
     I: Longint;
     Line: Ansistring;
+    OldFilemode: byte;
   begin
-    for I:=1 to ParamCount-1 do
+    OldFilemode:=FileMode;
+    Filemode:=0;
+    for I:=0 to FileList.Count-1 do
       begin
-        Assign(Src,ParamStr(I));
+        Assign(Src,FileList[i]);
+       {$i-}
         Reset(Src);
+        while ioresult<>0 do
+          begin
+            { wait for lingering locks to disappear }
+            Sleep(200);
+            Reset(Src);
+          end;
+       {$i+}
+
         while not Eof(Src) do
           begin
             ReadLn(Src,Line);
@@ -34,17 +51,28 @@ procedure DoConcat;
           end;
         Close(Src);
       end;
+    Filemode:=OldFilemode;
     Close(Dst);
   end;
 
 
 procedure CheckParas;
   var
+    FirstFile,
     I: Longint;
+    Exists: boolean;
   begin
     { enough parameters? }
     if ParamCount<2 then
       Usage;
+
+    FirstFile:=1;
+    if UpperCase(ParamStr(1))='-i' then
+        begin
+          IgnoreNonExisting:=true;
+          Inc(FirstFile);
+        end;
+
     { check destination }
     if DirectoryExists(ParamStr(ParamCount)) then
       begin
@@ -61,23 +89,32 @@ procedure CheckParas;
         halt(2);
       end;
     { check source(s) }
-    for I:=1 to ParamCount-1 do
+    for I:=FirstFile to ParamCount-1 do
       begin
+        Exists:=True;
         if not FileExists(ParamStr(I)) then
           begin
-            Writeln('File "',ParamStr(I),'" does not exist');
-            halt(2);
-          end;
-        if DirectoryExists(ParamStr(I)) then
+            if not IgnoreNonExisting then
+              begin
+                Writeln('File "',ParamStr(I),'" does not exist');
+                halt(2);
+              end;
+            Exists:=False;
+          end
+        else if DirectoryExists(ParamStr(I)) then
           begin
             Writeln('"',ParamStr(I),'" is a directory');
             halt(2);
-          end;
-        end;
+          end
+        else if Exists then
+          FileList.Add(ParamStr(I));
+        end
   end;
 
 
 begin
+  FileList:=TStringList.Create;
   CheckParas;
   DoConcat;
+  FileList.Free;
 end.

+ 122 - 35
tests/utils/dotest.pp

@@ -431,6 +431,26 @@ begin
 end;
 
 
+procedure ForceLog(const logfile:string);
+var
+  t : text;
+begin
+  assign(t,logfile);
+  {$I-}
+   append(t);
+  {$I+}
+  if ioresult<>0 then
+   begin
+     {$I-}
+      rewrite(t);
+     {$I+}
+     if ioresult<>0 then
+       Verbose(V_Abort,'Can''t Create '+logfile);
+   end;
+  close(t);
+end;
+
+
 function GetCompilerInfo(c:tcompinfo):boolean;
 
   function GetToken(var s:string):string;
@@ -697,6 +717,23 @@ begin
 end;
 
 
+function TestLogFileName(Const pref,base,ext:String):String;
+var
+  LogDir: String;
+begin
+  LogDir:=TestOutputDir;
+{$ifndef macos}
+  if UniqueSuffix<>'' then
+    LogDir:=LogDir+'/..';
+  TestLogFileName:=LogDir+'/'+ForceExtension(pref+SplitFileName(base),ext);
+{$else macos}
+  if UniqueSuffix<>'' then
+    LogDir:=LogDir+'::';
+  TestLogFileName:=ConcatMacPath(LogDir,ForceExtension(pref+SplitFileName(base),ext));
+{$endif macos}
+end;
+
+
 function ExitWithInternalError(const OutName:string):boolean;
 var
   t : text;
@@ -855,6 +892,9 @@ var
 begin
   RunCompiler:=false;
   args:='-n -T'+CompilerTarget+' -Fu'+RTLUnitsDir;
+  { the helper object files have been copied to the common directory }
+  if UniqueSuffix<>'' then
+    args:=args+' -Fo'+TestOutputDir+'/..';
   args:=args+' -FE'+TestOutputDir;
   if TargetIsMacOS then
     args:=args+' -WT ';  {tests should be compiled as MPWTool}
@@ -1092,6 +1132,7 @@ begin
     end;
   LibraryExists:=false;
 end;
+
 function ExecuteRemote(const prog,args:string;out StartTicks,EndTicks : int64):boolean;
 const
   MaxTrials = 5;
@@ -1134,18 +1175,60 @@ end;
 function MaybeCopyFiles(const FileToCopy : string) : boolean;
 var
   TestRemoteExe,
-  s : string;
   pref     : string;
   LocalFile, RemoteFile: string;
   LocalPath: string;
-  index    : integer;
+  i       : integer;
   execres : boolean;
   EndTicks,
   StartTicks : int64;
+  FileList   : TStringList;
+
+  function BuildFileList: TStringList;
+    var
+      s      : string;
+      index  : longint;
+    begin
+      s:=Config.Files;
+      if length(s) = 0 then
+        begin
+          Result:=nil;
+          exit;
+        end;
+      Result:=TStringList.Create;
+      repeat
+        index:=pos(' ',s);
+        if index=0 then
+          LocalFile:=s
+        else
+          LocalFile:=copy(s,1,index-1);
+        Result.Add(LocalFile);
+        if index=0 then
+          break;
+        s:=copy(s,index+1,length(s)-index);
+      until false;
+    end;
+
 begin
   if RemoteAddr='' then
     begin
-      exit(false);
+      If UniqueSuffix<>'' then
+        begin
+          FileList:=BuildFileList;
+          if assigned(FileList) then
+            begin
+              LocalPath:=SplitPath(PPFile[current]);
+              if Length(LocalPath) > 0 then
+                LocalPath:=LocalPath+'/';
+              for i:=0 to FileList.count-1 do
+                begin
+                  LocalFile:=FileList[i];
+                  CopyFile(LocalPath+LocalFile,TestOutputDir+'/'+LocalFile,false);
+                end;
+              FileList.Free;
+            end;
+        end;
+      exit(true);
     end;
   execres:=true;
   { We don't want to create subdirs, remove paths from the test }
@@ -1160,36 +1243,32 @@ begin
     Verbose(V_normal, 'Could not copy executable '+FileToCopy);
     exit(execres);
   end;
-  s:=Config.Files;
-  if length(s) > 0 then
+  FileList:=BuildFileList;
+  if assigned(FileList) then
   begin
     LocalPath:=SplitPath(PPFile[current]);
     if Length(LocalPath) > 0 then
       LocalPath:=LocalPath+'/';
-    repeat
-      index:=pos(' ',s);
-      if index=0 then
-        LocalFile:=s
-      else
-        LocalFile:=copy(s,1,index-1);
-      RemoteFile:=RemotePath+'/'+SplitFileName(LocalFile);
-      LocalFile:=LocalPath+LocalFile;
-      if DoVerbose and (rcpprog='pscp') then
-        pref:='-v '
-      else
-        pref:='';
-      execres:=ExecuteRemote(rcpprog,pref+RemotePara+' '+LocalFile+' '+
-                             RemoteAddr+':'+RemoteFile,StartTicks,EndTicks);
-      if not execres then
+    for i:=0 to FileList.count-1 do
       begin
-        Verbose(V_normal, 'Could not copy required file '+LocalFile);
-        exit(false);
+        LocalFile:=FileList[i];
+        RemoteFile:=RemotePath+'/'+SplitFileName(LocalFile);
+        LocalFile:=LocalPath+LocalFile;
+        if DoVerbose and (rcpprog='pscp') then
+          pref:='-v '
+        else
+          pref:='';
+        execres:=ExecuteRemote(rcpprog,pref+RemotePara+' '+LocalFile+' '+
+                               RemoteAddr+':'+RemoteFile,StartTicks,EndTicks);
+        if not execres then
+        begin
+          Verbose(V_normal, 'Could not copy required file '+LocalFile);
+          FileList.Free;
+          exit(false);
+        end;
       end;
-      if index=0 then
-        break;
-      s:=copy(s,index+1,length(s)-index);
-    until false;
   end;
+  FileList.Free;
   MaybeCopyFiles:=execres;
 end;
 
@@ -1213,8 +1292,9 @@ begin
   RunExecutable:=false;
   execres:=true;
 
-  TestExe:=OutputFileName(PPFile[current],ExeExt);
+  TestExe:=TestOutputFilename('',PPFile[current],ExeExt);
 
+  execres:=MaybeCopyFiles(TestExe);
   if EmulatorName<>'' then
     begin
       { Get full name out log file, because we change the directory during
@@ -1233,7 +1313,6 @@ begin
     end
   else if RemoteAddr<>'' then
     begin
-      execres:=MaybeCopyFiles(TestExe);
       TestRemoteExe:=RemotePath+'/'+SplitFileName(TestExe);
       { rsh doesn't pass the exitcode, use a second command to print the exitcode
         on the remoteshell to stdout }
@@ -1609,9 +1688,9 @@ begin
       Res:=GetCompilerCPU;
       Res:=GetCompilerTarget;
 {$ifndef MACOS}
-      RTLUnitsDir:='units/'+CompilerFullTarget;
+      RTLUnitsDir:='tstunits/'+CompilerFullTarget;
 {$else MACOS}
-      RTLUnitsDir:=':units:'+CompilerFullTarget;
+      RTLUnitsDir:=':tstunits:'+CompilerFullTarget;
 {$endif MACOS}
       if not PathExists(RTLUnitsDir) then
         Verbose(V_Abort,'Unit path "'+RTLUnitsDir+'" does not exists');
@@ -1630,22 +1709,30 @@ begin
         begin
 {$ifndef MACOS}
           TestOutputDir:=OutputDir+'/'+PPDir;
+          if UniqueSuffix<>'' then
+            TestOutputDir:=TestOutputDir+'/'+UniqueSuffix;
 {$else MACOS}
           TestOutputDir:=OutputDir+PPDir;
+          if UniqueSuffix<>'' then
+            TestOutputDir:=TestOutputDir+':'+UniqueSuffix;
 {$endif MACOS}
           mkdirtree(TestOutputDir);
         end
       else
         TestOutputDir:=OutputDir;
-      { Global log files (don't use UniqueSuffix here, it's not set in case of
-        SINGLEDOTESTRUNS) }
-      LogSuffix:=SplitBasePath(PPDir)+'log';
+      if UniqueSuffix<>'' then
+        LogSuffix:=UniqueSuffix
+      else
+        LogSuffix:=SplitBasePath(PPDir)+'log';
       ResLogFile:=OutputFileName('log',LogSuffix);
       LongLogFile:=OutputFileName('longlog',LogSuffix);
       FailLogFile:=OutputFileName('faillist',LogSuffix);
+      ForceLog(ResLogFile);
+      ForceLog(LongLogFile);
+      ForceLog(FailLogFile);
       { Per test logfiles }
-      CompilerLogFile:=TestOutputFileName('',SplitFileName(PPFile[current]),'log');
-      ExeLogFile:=TestOutputFileName('',SplitFileName(PPFile[current]),'elg');
+      CompilerLogFile:=TestLogFileName('',SplitFileName(PPFile[current]),'log');
+      ExeLogFile:=TestLogFileName('',SplitFileName(PPFile[current]),'elg');
       Verbose(V_Debug,'Using Compiler logfile: '+CompilerLogFile);
       Verbose(V_Debug,'Using Execution logfile: '+ExeLogFile);
     end;

+ 166 - 0
tests/utils/gparmake.pp

@@ -0,0 +1,166 @@
+{ See procedure "Usage". This code is in the public domain. }
+
+Program GParMake;
+
+procedure Usage;
+  begin
+    writeln('GParMake: create make rules for parallel execution of testsuite');
+    writeln('Usage: gparmake [-a] <outputfile>  <startchunk> <tests_per_chunk> <test1> [<test2> ...]');
+    writeln('Output: makefile fragment with rules to run the tests in sequences of <tests_per_chunk>');
+    writeln;
+    halt(1);
+  end;
+
+{ make all numbers of the same string length so they can be sorted
+  lexographically }
+function rulenr2str(rulenr: longint): string;
+  var
+    i: longint;
+  begin
+    str(rulenr:9,rulenr2str);
+    for i:=1 to length(rulenr2str)-1 do
+      if rulenr2str[i]=' ' then
+        rulenr2str[i]:='0';
+  end;
+
+procedure WriteChunkRule(rulenr: longint; const files: ansistring);
+  var
+    rulestr: string;
+  begin
+    rulestr:=rulenr2str(rulenr);
+    writeln('$(TEST_OUTPUTDIR)/testchunk_',rulestr,'-stamp.$(TEST_FULL_TARGET): testprep-stamp.$(TEST_FULL_TARGET)');
+    writeln(#9'$(Q)$(DOTEST) $(DOTESTOPT) -Lchunk',rulestr,' -e ',files);
+    writeln(#9'$(ECHOREDIR) $(DATE) > $@');
+    writeln;
+    writeln('$(addsuffix .chunk',rulestr,', $(LOGFILES)) : $(TEST_OUTPUTDIR)/testchunk_',rulestr,'-stamp.$(TEST_FULL_TARGET)');
+    writeln;
+    writeln('.INTERMEDIATE: $(addsuffix .chunk',rulestr,', $(LOGFILES)) $(TEST_OUTPUTDIR)/testchunk_',rulestr,'-stamp.$(TEST_FULL_TARGET)');
+    writeln;
+  end;
+
+
+var
+  startchunk: longint;
+  doappend: boolean;
+
+Function ProcessArgs: longint;
+  var
+    i,
+    paramnr,
+    chunktargetsize,
+    chunksize,
+    chunknr,
+    error: longint;
+    testname,
+    nexttestname,
+    testlist,
+    outputname: ansistring;
+
+  procedure FlushChunk;
+    begin
+      WriteChunkRule(chunknr,testlist);
+      inc(chunknr);
+      testlist:='';
+      chunksize:=0;
+    end;
+
+  begin
+    if paramcount < 3 then
+      Usage;
+
+    doappend:=false;
+
+    paramnr:=1;
+    if paramstr(paramnr)='-a' then
+      begin
+        doappend:=true;
+        inc(paramnr);
+      end;
+
+    outputname:=paramstr(paramnr);
+    inc(paramnr);
+
+    val(paramstr(paramnr),startchunk,error);
+    if error<>0 then
+      Usage;
+    inc(paramnr);
+
+    val(paramstr(paramnr),chunktargetsize,error);
+    if error<>0 then
+      Usage;
+    inc(paramnr);
+
+    { only redirect output after all possible cases where we may have to write
+      the usage screen }
+    assign(output,outputname);
+    if doappend then
+      append(output)
+    else
+      rewrite(output);
+
+    chunknr:=startchunk;
+    chunksize:=0;
+    testlist:='';
+    for i := paramnr to paramcount do
+      begin
+        testname:=paramstr(i);
+        testlist:=testlist+' '+testname;
+        inc(chunksize);
+        if chunksize>=chunktargetsize then
+          begin
+            if (i=paramcount) then
+              FlushChunk
+            else
+              begin
+                { keep tests with the same name except for the last character in the same chunk,
+                  because they may have to be executed in order (skip ".pp" suffix and last char) }
+                nexttestname:=paramstr(i+1);
+                if lowercase(copy(testname,1,length(testname)-4))<>lowercase(copy(nexttestname,1,length(nexttestname)-4)) then
+                  FlushChunk;
+              end;
+          end;
+      end;
+    if chunksize<>0 then
+      FlushChunk;
+    ProcessArgs:=chunknr-1;
+  end;
+
+
+procedure WriteWrapperRules(totalchunks: longint);
+  const
+    lognames: array[1..3] of string[11] = ('log','faillist','longlog');
+  var
+    logi,
+    i: longint;
+  begin
+    for logi:=1 to 3 do
+      begin
+        write('$(TEST_OUTPUTDIR)/',lognames[logi],' :');
+        for i:=startchunk to totalchunks do
+          write(' $(TEST_OUTPUTDIR)/',lognames[logi],'.chunk',rulenr2str(i));
+        writeln;
+        { if you have multiple rules for one (non-pattern) target, all
+          prerequisites will be merged, but only one of the rules can have a
+          recipe }
+        if not doappend then
+          begin
+            writeln(#9'$(Q)$(CONCAT) $(sort $^) $@');
+            writeln;
+          end;
+        writeln;
+      end;
+    if not doappend then
+      begin
+        writeln('allexectests : $(LOGFILES)');
+        writeln;
+      end;
+  end;
+
+
+var
+  totalchunks: longint;
+begin
+  totalchunks:=ProcessArgs;
+  WriteWrapperRules(totalchunks);
+  close(output);
+end.