Index: tests/pybin/settings.py
===================================================================
--- tests/pybin/settings.py	(revision a8d4b592d6ba9a45a01127ff6b84417756735aa4)
+++ tests/pybin/settings.py	(revision d65f92c77f5cc157071fd474ca2ad557b54f447b)
@@ -11,4 +11,5 @@
 	SRCDIR = os.path.abspath(config.SRCDIR)
 	BUILDDIR = os.path.abspath(config.BUILDDIR)
+	distribute = config.DISTRIBUTE
 	os.chdir(testpath)
 
@@ -85,7 +86,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")
@@ -113,4 +118,5 @@
 	global timeout
 	global output_width
+	global distcc
 
 	dry_run      = options.dry_run
@@ -122,5 +128,8 @@
 	timeout      = Timeouts(options.timeout, options.global_timeout)
 	output_width = 24
+	distcc       = "DISTCC_CFA_PATH=~/.cfadistcc/%s/cfa" % tools.config_hash()
 
+	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):
@@ -131,5 +140,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 a8d4b592d6ba9a45a01127ff6b84417756735aa4)
+++ tests/pybin/tools.py	(revision d65f92c77f5cc157071fd474ca2ad557b54f447b)
@@ -22,13 +22,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):
@@ -36,7 +39,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)
@@ -45,8 +48,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
@@ -57,9 +60,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:
@@ -74,5 +78,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
@@ -106,5 +110,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
@@ -114,5 +118,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 )
 
 
@@ -137,9 +141,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 = [
@@ -150,9 +154,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):
@@ -200,5 +205,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
 
@@ -273,4 +278,15 @@
 ################################################################################
 
+# 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()
+
 # check if arguments is yes or no
 def yes_no(string):
@@ -302,5 +318,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)
 
 class Timed:
