Index: tests/Makefile.am
===================================================================
--- tests/Makefile.am	(revision 655c5fa432b0d3c06bbbd9fb099aa0f6212d9551)
+++ tests/Makefile.am	(revision 81e60f78be23ef2411ac7fc6ebe5521e62bddc9f)
@@ -35,4 +35,5 @@
 
 # applies to both programs
+# since automake doesn't have support for CFA we have to
 AM_CFLAGS = $(if $(test), 2> $(test), ) \
 	-g \
@@ -42,6 +43,7 @@
 	-DIN_DIR="${abs_srcdir}/.in/"
 
-AM_CFLAGS += ${DEBUG_FLAGS} ${INSTALL_FLAGS} ${ARCH_FLAGS}
-CC = @CFACC@
+CC = $(if $(DISTCC_CFA_PATH),distcc $(DISTCC_CFA_PATH) -dist-tree -in-tree,@CFACC@ ${DEBUG_FLAGS} ${INSTALL_FLAGS} ${ARCH_FLAGS})
+CFACCBIN = @CFACC@
+CFACC = $(CC)
 
 PRETTY_PATH=mkdir -p $(dir $(abspath ${@})) && cd ${srcdir} &&
@@ -57,8 +59,8 @@
 #----------------------------------------------------------------------------------------------------------------
 all-local :
-	@+${TEST_PY} --debug=${debug}  --install=${installed} --archive-errors=${archiveerrors} ${concurrent} ${timeouts} ${quick_test}
+	@+${TEST_PY} --debug=${debug} --install=${installed} --archive-errors=${archiveerrors} ${concurrent} ${timeouts} ${quick_test}
 
 all-tests :
-	@+${TEST_PY} --debug=${debug}  --install=${installed} --archive-errors=${archiveerrors} ${concurrent} ${timeouts} --all # '@' => do not echo command (SILENT), '+' => allows recursive make from within python program
+	@+${TEST_PY} --debug=${debug} --install=${installed} --archive-errors=${archiveerrors} ${concurrent} ${timeouts} --all # '@' => do not echo command (SILENT), '+' => allows recursive make from within python program
 
 clean-local :
@@ -87,7 +89,7 @@
 
 # Use for all tests, make sure the path are correct and all flags are added
-CFACOMPILETEST=$(PRETTY_PATH) $(CFACOMPILE) $(shell realpath --relative-to=${srcdir} ${<}) $($(shell echo "${@}_FLAGS" | sed 's/-\|\//_/g'))
+CFACOMPILETEST=$(PRETTY_PATH) $(CFACOMPILE) -c $(shell realpath --relative-to=${srcdir} ${<}) $($(shell echo "${@}_FLAGS" | sed 's/-\|\//_/g'))
 
-# Use for tests that either generate an executable, print directyl to stdout or the make command is expected to fail
+# Use for tests that either generate an executable, print directly to stdout or the make command is expected to fail
 CFATEST_STDOUT=$(CFACOMPILETEST) -o $(abspath ${@})
 
@@ -98,6 +100,8 @@
 
 # implicit rule so not all test require a rule
-% : %.cfa $(CFACC)
-	$(CFATEST_STDOUT)
+% : %.cfa $(CFACCBIN)
+	$(CFACOMPILETEST) -o $(abspath ${@}).o
+	$(CFACC) $(abspath ${@}).o -o $(abspath ${@})
+
 
 % : %.cpp
@@ -123,33 +127,33 @@
 # CUSTOM TARGET
 #------------------------------------------------------------------------------
-typedefRedef-ERR1: typedefRedef.cfa $(CFACC)
+typedefRedef-ERR1: typedefRedef.cfa $(CFACCBIN)
 	$(CFATEST_STDOUT) -DERR1
 
-alloc-ERROR: alloc.cfa $(CFACC)
+alloc-ERROR: alloc.cfa $(CFACCBIN)
 	$(CFATEST_STDOUT) -DERR1
 
-nested-types-ERR1: nested-types.cfa $(CFACC)
+nested-types-ERR1: nested-types.cfa $(CFACCBIN)
 	$(CFATEST_STDOUT) -DERR1
 
-nested-types-ERR2: nested-types.cfa $(CFACC)
+nested-types-ERR2: nested-types.cfa $(CFACCBIN)
 	$(CFATEST_STDOUT) -DERR2
 
-raii/dtor-early-exit-ERR1: raii/dtor-early-exit.cfa $(CFACC)
+raii/dtor-early-exit-ERR1: raii/dtor-early-exit.cfa $(CFACCBIN)
 	$(CFATEST_STDOUT) -DERR1
 
-raii/dtor-early-exit-ERR2: raii/dtor-early-exit.cfa $(CFACC)
+raii/dtor-early-exit-ERR2: raii/dtor-early-exit.cfa $(CFACCBIN)
 	$(CFATEST_STDOUT) -DERR2
 
-raii/memberCtors-ERR1: raii/memberCtors.cfa $(CFACC)
+raii/memberCtors-ERR1: raii/memberCtors.cfa $(CFACCBIN)
 	$(CFATEST_STDOUT) -DERR1
 
-raii/ctor-autogen-ERR1: raii/ctor-autogen.cfa $(CFACC)
+raii/ctor-autogen-ERR1: raii/ctor-autogen.cfa $(CFACCBIN)
 	$(CFATEST_STDOUT) -DERR1
 
 #builtins
-builtins/sync: builtins/sync.cfa $(CFACC)
+builtins/sync: builtins/sync.cfa $(CFACCBIN)
 	$(CFATEST_STDERR) -fsyntax-only
 
 # Warnings
-warnings/self-assignment: warnings/self-assignment.cfa $(CFACC)
+warnings/self-assignment: warnings/self-assignment.cfa $(CFACCBIN)
 	$(CFATEST_STDERR) -fsyntax-only
Index: tests/Makefile.in
===================================================================
--- tests/Makefile.in	(revision 655c5fa432b0d3c06bbbd9fb099aa0f6212d9551)
+++ tests/Makefile.in	(revision 81e60f78be23ef2411ac7fc6ebe5521e62bddc9f)
@@ -212,10 +212,10 @@
 AWK = @AWK@
 BUILD_IN_TREE_FLAGS = @BUILD_IN_TREE_FLAGS@
-CC = @CFACC@
+CC = $(if $(DISTCC_CFA_PATH),distcc $(DISTCC_CFA_PATH) -dist-tree -in-tree,@CFACC@ ${DEBUG_FLAGS} ${INSTALL_FLAGS} ${ARCH_FLAGS})
 CCAS = @CCAS@
 CCASDEPMODE = @CCASDEPMODE@
 CCASFLAGS = @CCASFLAGS@
 CCDEPMODE = @CCDEPMODE@
-CFACC = @CFACC@
+CFACC = $(CC)
 CFACPP = @CFACPP@
 CFA_BACKEND_CC = @CFA_BACKEND_CC@
@@ -248,4 +248,5 @@
 FGREP = @FGREP@
 GREP = @GREP@
+HAS_DISTCC = @HAS_DISTCC@
 HOST_FLAGS = @HOST_FLAGS@
 INSTALL = @INSTALL@
@@ -386,7 +387,13 @@
 
 # applies to both programs
-AM_CFLAGS = $(if $(test), 2> $(test), ) -g -Wall -Wno-unused-function \
-	-quiet @CFA_FLAGS@ -DIN_DIR="${abs_srcdir}/.in/" \
-	${DEBUG_FLAGS} ${INSTALL_FLAGS} ${ARCH_FLAGS}
+# since automake doesn't have support for CFA we have to
+AM_CFLAGS = $(if $(test), 2> $(test), ) \
+	-g \
+	-Wall \
+	-Wno-unused-function \
+	-quiet @CFA_FLAGS@ \
+	-DIN_DIR="${abs_srcdir}/.in/"
+
+CFACCBIN = @CFACC@
 PRETTY_PATH = mkdir -p $(dir $(abspath ${@})) && cd ${srcdir} &&
 avl_test_SOURCES = avltree/avl_test.cfa avltree/avl0.cfa avltree/avl1.cfa avltree/avl2.cfa avltree/avl3.cfa avltree/avl4.cfa avltree/avl-private.cfa
@@ -397,7 +404,7 @@
 
 # Use for all tests, make sure the path are correct and all flags are added
-CFACOMPILETEST = $(PRETTY_PATH) $(CFACOMPILE) $(shell realpath --relative-to=${srcdir} ${<}) $($(shell echo "${@}_FLAGS" | sed 's/-\|\//_/g'))
-
-# Use for tests that either generate an executable, print directyl to stdout or the make command is expected to fail
+CFACOMPILETEST = $(PRETTY_PATH) $(CFACOMPILE) -c $(shell realpath --relative-to=${srcdir} ${<}) $($(shell echo "${@}_FLAGS" | sed 's/-\|\//_/g'))
+
+# Use for tests that either generate an executable, print directly to stdout or the make command is expected to fail
 CFATEST_STDOUT = $(CFACOMPILETEST) -o $(abspath ${@})
 
@@ -771,8 +778,8 @@
 #----------------------------------------------------------------------------------------------------------------
 all-local :
-	@+${TEST_PY} --debug=${debug}  --install=${installed} --archive-errors=${archiveerrors} ${concurrent} ${timeouts} ${quick_test}
+	@+${TEST_PY} --debug=${debug} --install=${installed} --archive-errors=${archiveerrors} ${concurrent} ${timeouts} ${quick_test}
 
 all-tests :
-	@+${TEST_PY} --debug=${debug}  --install=${installed} --archive-errors=${archiveerrors} ${concurrent} ${timeouts} --all # '@' => do not echo command (SILENT), '+' => allows recursive make from within python program
+	@+${TEST_PY} --debug=${debug} --install=${installed} --archive-errors=${archiveerrors} ${concurrent} ${timeouts} --all # '@' => do not echo command (SILENT), '+' => allows recursive make from within python program
 
 clean-local :
@@ -801,6 +808,7 @@
 
 # implicit rule so not all test require a rule
-% : %.cfa $(CFACC)
-	$(CFATEST_STDOUT)
+% : %.cfa $(CFACCBIN)
+	$(CFACOMPILETEST) -o $(abspath ${@}).o
+	$(CFACC) $(abspath ${@}).o -o $(abspath ${@})
 
 % : %.cpp
@@ -810,34 +818,34 @@
 # CUSTOM TARGET
 #------------------------------------------------------------------------------
-typedefRedef-ERR1: typedefRedef.cfa $(CFACC)
+typedefRedef-ERR1: typedefRedef.cfa $(CFACCBIN)
 	$(CFATEST_STDOUT) -DERR1
 
-alloc-ERROR: alloc.cfa $(CFACC)
+alloc-ERROR: alloc.cfa $(CFACCBIN)
 	$(CFATEST_STDOUT) -DERR1
 
-nested-types-ERR1: nested-types.cfa $(CFACC)
+nested-types-ERR1: nested-types.cfa $(CFACCBIN)
 	$(CFATEST_STDOUT) -DERR1
 
-nested-types-ERR2: nested-types.cfa $(CFACC)
+nested-types-ERR2: nested-types.cfa $(CFACCBIN)
 	$(CFATEST_STDOUT) -DERR2
 
-raii/dtor-early-exit-ERR1: raii/dtor-early-exit.cfa $(CFACC)
+raii/dtor-early-exit-ERR1: raii/dtor-early-exit.cfa $(CFACCBIN)
 	$(CFATEST_STDOUT) -DERR1
 
-raii/dtor-early-exit-ERR2: raii/dtor-early-exit.cfa $(CFACC)
+raii/dtor-early-exit-ERR2: raii/dtor-early-exit.cfa $(CFACCBIN)
 	$(CFATEST_STDOUT) -DERR2
 
-raii/memberCtors-ERR1: raii/memberCtors.cfa $(CFACC)
+raii/memberCtors-ERR1: raii/memberCtors.cfa $(CFACCBIN)
 	$(CFATEST_STDOUT) -DERR1
 
-raii/ctor-autogen-ERR1: raii/ctor-autogen.cfa $(CFACC)
+raii/ctor-autogen-ERR1: raii/ctor-autogen.cfa $(CFACCBIN)
 	$(CFATEST_STDOUT) -DERR1
 
 #builtins
-builtins/sync: builtins/sync.cfa $(CFACC)
+builtins/sync: builtins/sync.cfa $(CFACCBIN)
 	$(CFATEST_STDERR) -fsyntax-only
 
 # Warnings
-warnings/self-assignment: warnings/self-assignment.cfa $(CFACC)
+warnings/self-assignment: warnings/self-assignment.cfa $(CFACCBIN)
 	$(CFATEST_STDERR) -fsyntax-only
 
Index: tests/config.py.in
===================================================================
--- tests/config.py.in	(revision 655c5fa432b0d3c06bbbd9fb099aa0f6212d9551)
+++ tests/config.py.in	(revision 81e60f78be23ef2411ac7fc6ebe5521e62bddc9f)
@@ -8,2 +8,3 @@
 BUILDDIR = "@abs_builddir@"
 HOSTARCH = "@host_cpu@"
+DISTRIBUTE = @HAS_DISTCC@
Index: tests/pybin/settings.py
===================================================================
--- tests/pybin/settings.py	(revision 655c5fa432b0d3c06bbbd9fb099aa0f6212d9551)
+++ tests/pybin/settings.py	(revision 81e60f78be23ef2411ac7fc6ebe5521e62bddc9f)
@@ -14,4 +14,5 @@
 	SRCDIR = os.path.abspath(config.SRCDIR)
 	BUILDDIR = os.path.abspath(config.BUILDDIR)
+	distribute = config.DISTRIBUTE
 	os.chdir(testpath)
 
@@ -88,7 +89,11 @@
 		self.string = "debug" if value else "no debug"
 		self.flags  = """DEBUG_FLAGS=%s""" % ("-debug -O0" if value else "-nodebug -O2")
+		self.path   = "debug" if value else "nodebug"
 
 class Install:
 	def __init__(self, value):
+		if value:
+			distribute = False
+
 		self.string = "installed" if value else "in-tree"
 		self.flags  = """INSTALL_FLAGS=%s""" % ("" if value else "-in-tree")
@@ -109,23 +114,29 @@
 def init( options ):
 	global arch
+	global archive
+	global debug
+	global distcc
 	global dry_run
 	global generating
+	global install
 	global make
-	global debug
-	global install
+	global output_width
 	global timeout
-	global output_width
-	global archive
 
+	arch         = Architecture(options.arch)
+	archive      = os.path.abspath(os.path.join(original_path, options.archive_errors)) if options.archive_errors else None
+	debug        = Debug(options.debug)
+	distcc       = "DISTCC_CFA_PATH=~/.cfadistcc/%s/cfa" % tools.config_hash()
 	dry_run      = options.dry_run
 	generating   = options.regenerate_expected
+	install      = Install(options.install)
 	make         = ['make']
-	debug        = Debug(options.debug)
-	install      = Install(options.install)
-	arch         = Architecture(options.arch)
+	output_width = 24
 	timeout      = Timeouts(options.timeout, options.global_timeout)
-	output_width = 24
-	archive      = os.path.abspath(os.path.join(original_path, options.archive_errors)) if options.archive_errors else None
 
+	# if we distribute, distcc errors will fail tests, use log file for distcc
+	# don't use "'DISTCC_LOG' not in os.environ" because it can be set to ''
+	if distribute and not os.environ.get('DISTCC_LOG'):
+		os.putenv('DISTCC_LOG', os.path.join(BUILDDIR, 'distcc_error.log'))
 
 def update_make_cmd(force, jobs):
@@ -136,5 +147,5 @@
 def validate():
 	errf = os.path.join(BUILDDIR, ".validate.err")
-	make_ret, out = tools.make( ".validate", error_file = errf, output=subprocess.DEVNULL, error=subprocess.DEVNULL )
+	make_ret, out = tools.make( ".validate", error_file = errf, output_file=subprocess.DEVNULL, error=subprocess.DEVNULL )
 	if make_ret != 0:
 		with open (errf, "r") as myfile:
Index: tests/pybin/tools.py
===================================================================
--- tests/pybin/tools.py	(revision 655c5fa432b0d3c06bbbd9fb099aa0f6212d9551)
+++ tests/pybin/tools.py	(revision 81e60f78be23ef2411ac7fc6ebe5521e62bddc9f)
@@ -23,13 +23,16 @@
 
 # helper functions to run terminal commands
-def sh(*cmd, timeout = False, output = None, input = None, error = subprocess.STDOUT):
+def sh(*cmd, timeout = False, output_file = None, input_file = None, input_text = None, error = subprocess.STDOUT):
 	cmd = list(cmd)
+
+	if input_file and input_text:
+		return 401, "Cannot use both text and file inputs"
 
 	# if this is a dry_run, only print the commands that would be ran
 	if settings.dry_run :
 		cmd = "{} cmd: {}".format(os.getcwd(), ' '.join(cmd))
-		if output and not isinstance(output, int):
+		if output_file and not isinstance(output_file, int):
 			cmd += " > "
-			cmd += output
+			cmd += output_file
 
 		if error and not isinstance(error, int):
@@ -37,7 +40,7 @@
 			cmd += error
 
-		if input and not isinstance(input, int) and os.path.isfile(input):
+		if input_file and not isinstance(input_file, int) and os.path.isfile(input_file):
 			cmd += " < "
-			cmd += input
+			cmd += input_file
 
 		print(cmd)
@@ -46,8 +49,8 @@
 	with contextlib.ExitStack() as onexit:
 		# add input redirection if needed
-		input = openfd(input, 'r', onexit, True)
+		input_file = openfd(input_file, 'r', onexit, True)
 
 		# add output redirection if needed
-		output = openfd(output, 'w', onexit, False)
+		output_file = openfd(output_file, 'w', onexit, False)
 
 		# add error redirection if needed
@@ -58,9 +61,10 @@
 			proc = subprocess.run(
 				cmd,
-				stdin =input,
-				stdout=output,
-				stderr=error,
-				timeout=settings.timeout.single if timeout else None
+				**({'input' : bytes(input_text, encoding='utf-8')} if input_text else {'stdin' : input_file}),
+				stdout  = output_file,
+				stderr  = error,
+				timeout = settings.timeout.single if timeout else None
 			)
+
 			return proc.returncode, proc.stdout.decode("utf-8") if proc.stdout else None
 		except subprocess.TimeoutExpired:
@@ -75,5 +79,5 @@
 		return False
 
-	code, out = sh("file %s" % fname, output=subprocess.PIPE)
+	code, out = sh("file %s" % fname, output_file=subprocess.PIPE)
 	if code != 0:
 		return False
@@ -107,5 +111,5 @@
 	if isinstance(files, str ): files = [ files ]
 	for file in files:
-		sh( 'rm', '-f', file, output=subprocess.DEVNULL, error=subprocess.DEVNULL )
+		sh( 'rm', '-f', file, output_file=subprocess.DEVNULL, error=subprocess.DEVNULL )
 
 # Create 1 or more directory
@@ -115,5 +119,5 @@
 		p = os.path.normpath( file )
 		d = os.path.dirname ( p )
-		sh( 'mkdir', '-p', d, output=subprocess.DEVNULL, error=subprocess.DEVNULL )
+		sh( 'mkdir', '-p', d, output_file=subprocess.DEVNULL, error=subprocess.DEVNULL )
 
 
@@ -138,9 +142,9 @@
 		lhs,
 		rhs,
-		output=subprocess.PIPE
+		output_file=subprocess.PIPE
 	)
 
 # call make
-def make(target, *, flags = '', output = None, error = None, error_file = None, silent = False):
+def make(target, *, flags = '', output_file = None, error = None, error_file = None, silent = False):
 	test_param = """test="%s" """ % (error_file) if error_file else None
 	cmd = [
@@ -151,9 +155,10 @@
 		settings.debug.flags,
 		settings.install.flags,
+		settings.distcc if settings.distribute else None,
 		flags,
 		target
 	]
 	cmd = [s for s in cmd if s]
-	return sh(*cmd, output=output, error=error)
+	return sh(*cmd, output_file=output_file, error=error)
 
 def which(program):
@@ -201,5 +206,5 @@
 # cat one file into the other
 def cat(source, dest):
-	ret, _ = sh("cat", source, output=dest)
+	ret, _ = sh("cat", source, output_file=dest)
 	return ret
 
@@ -274,4 +279,16 @@
 ################################################################################
 
+# get hash for given configuration
+def config_hash():
+	path = os.path.normpath(os.path.join(
+		settings.SRCDIR,
+	))
+
+	distcc_hash = os.path.join(settings.SRCDIR, '../tools/build/distcc_hash')
+	config = "%s-%s" % (settings.arch.target, settings.debug.path)
+	_, out = sh(distcc_hash, config, output_file=subprocess.PIPE)
+	return out.strip()
+
+# get pretty string for time of day
 def pretty_now():
 	ts = time.time()
@@ -308,5 +325,5 @@
 		return 1, "ERR No core dump"
 
-	return sh('gdb', '-n', path, core, '-batch', '-x', cmd, output=subprocess.PIPE)
+	return sh('gdb', '-n', path, core, '-batch', '-x', cmd, output_file=subprocess.PIPE)
 
 def core_archive(dst, name, exe):
Index: tests/test.py
===================================================================
--- tests/test.py	(revision 655c5fa432b0d3c06bbbd9fb099aa0f6212d9551)
+++ tests/test.py	(revision 81e60f78be23ef2411ac7fc6ebe5521e62bddc9f)
@@ -143,5 +143,5 @@
 	# build, skipping to next test on error
 	with Timed() as comp_dur:
-		make_ret, _ = make( test.target(), output=subprocess.DEVNULL, error=out_file, error_file = err_file )
+		make_ret, _ = make( test.target(), output_file=subprocess.DEVNULL, error=out_file, error_file = err_file )
 
 	run_dur = None
@@ -153,5 +153,5 @@
 				if settings.dry_run or is_exe(exe_file):
 					# run test
-					retcode, _ = sh(exe_file, output=out_file, input=in_file, timeout=True)
+					retcode, _ = sh(exe_file, output_file=out_file, input_file=in_file, timeout=True)
 				else :
 					# simply cat the result into the output
@@ -219,5 +219,5 @@
 def run_tests(tests, jobs) :
 	# clean the sandbox from previous commands
-	make('clean', output=subprocess.DEVNULL, error=subprocess.DEVNULL)
+	make('clean', output_file=subprocess.DEVNULL, error=subprocess.DEVNULL)
 
 	# create the executor for our jobs and handle the signal properly
@@ -260,5 +260,5 @@
 
 	# clean the workspace
-	make('clean', output=subprocess.DEVNULL, error=subprocess.DEVNULL)
+	make('clean', output_file=subprocess.DEVNULL, error=subprocess.DEVNULL)
 
 	return 1 if failed else 0
