diff --git a/loopy/target/c/__init__.py b/loopy/target/c/__init__.py index 177daa02948b9c07ef1d9856dc04019e69e24897..8e69793e8079864a7e4c3117f267a20d6db3962f 100644 --- a/loopy/target/c/__init__.py +++ b/loopy/target/c/__init__.py @@ -314,11 +314,11 @@ class ExecutableCTarget(CTarget): """ An executable CTarget that uses (by default) JIT compilation of C-code """ - from loopy.target.c.c_execution import CCompiler - def __init__(self, compiler=CCompiler(), fortran_abi=False): + def __init__(self, compiler=None, fortran_abi=False): super(ExecutableCTarget, self).__init__(fortran_abi=fortran_abi) - self.compiler = compiler + from loopy.target.c.c_execution import CCompiler + self.compiler = compiler or CCompiler() def get_kernel_executor(self, knl, *args, **kwargs): from loopy.target.c.c_execution import CKernelExecutor diff --git a/loopy/target/c/c_execution.py b/loopy/target/c/c_execution.py index 2c8ab16f2e789438bf7040909b96484c4fa93954..3634cc71aa73f9079ce4f0180f546cfc4f344b3d 100644 --- a/loopy/target/c/c_execution.py +++ b/loopy/target/c/c_execution.py @@ -227,9 +227,17 @@ class CCompiler(object): logger = logging.getLogger(__name__) logger.warn('Default toolchain guessed from python config ' 'not found, replacing with default GCCToolchain.') - self.toolchain = GCCToolchain() + # this is ugly, but I'm not sure there's a clean way to copy the + # default args + self.toolchain = GCCToolchain( + cc='gcc', + cflags='-std=c99 -O3 -fPIC'.split(), + ldflags='-shared'.split(), + libraries=[], + library_dirs=[], + defines=[], + source_suffix='c') - self.source_suffix = source_suffix if toolchain is None: # copy in all differing values diff = {'cc': cc, @@ -241,9 +249,8 @@ class CCompiler(object): 'defines': defines} # filter empty and those equal to toolchain defaults diff = dict((k, v) for k, v in six.iteritems(diff) - if v and - not hasattr(self.toolchain, k) or - getattr(self.toolchain, k) != v) + if v and (not hasattr(self.toolchain, k) or + getattr(self.toolchain, k) != v)) self.toolchain = self.toolchain.copy(**diff) self.tempdir = tempfile.mkdtemp(prefix="tmp_loopy") self.source_suffix = source_suffix diff --git a/test/test_c_execution.py b/test/test_c_execution.py index d1b3c95caa034191b4b29c49076fc101cd318950..20725d69120e900e2342b3ad2f9b6fb11c5e8867 100644 --- a/test/test_c_execution.py +++ b/test/test_c_execution.py @@ -257,9 +257,10 @@ def test_c_caching(): # setup test logger to check logs tl = TestingLogger() tl.start_capture() - # remake kernel to clear cache - knl = __get_knl() - assert np.allclose(knl(b=np.arange(10))[1], np.arange(10)) + # copy kernel such that we share the same executor cache + knl = knl.copy() + # but use different args, so we can't cache the result + assert np.allclose(knl(b=np.arange(1, 11))[1], np.arange(1, 11)) # and get logs logs = tl.stop_capture() # check that we didn't recompile @@ -291,6 +292,54 @@ def test_c_execution_with_global_temporaries(): assert np.allclose(knl(a=np.zeros(10, dtype=np.int32))[1], np.arange(10)) +def test_missing_compilers(): + from loopy.target.c import ExecutableCTarget, CTarget + from loopy.target.c.c_execution import CCompiler + from codepy.toolchain import GCCToolchain + + def __test(evalfunc, target, **targetargs): + n = 10 + + knl = lp.make_kernel('{[i]: 0 <= i < n}', + """ + a[i] = b[i] + """, + [lp.GlobalArg('a', shape=(n,), dtype=np.int32), + lp.GlobalArg('b', shape=(n,), dtype=np.int32)], + target=target(**targetargs)) + + knl = lp.fix_parameters(knl, n=n) + return evalfunc(knl) + + assert __test(lambda knl: lp.generate_code_v2(knl).device_code(), CTarget) + + from pytools.prefork import ExecError + + def eval_tester(knl): + return np.allclose(knl(a=np.zeros(10, dtype=np.int32), + b=np.arange(10, dtype=np.int32))[1], np.arange(10)) + import os + path_store = os.environ["PATH"] + try: + # test with path wiped out such that we can't find gcc + with pytest.raises(ExecError): + os.environ["PATH"] = '' + __test(eval_tester, ExecutableCTarget) + finally: + # make sure we restore the path regardless for future testing + os.environ["PATH"] = path_store + + # next test that some made up compiler can be specified + ccomp = CCompiler(cc='foo') + assert isinstance(ccomp.toolchain, GCCToolchain) + assert ccomp.toolchain.cc == 'foo' + + # and that said made up compiler errors out + + with pytest.raises(ExecError): + __test(eval_tester, ExecutableCTarget, compiler=ccomp) + + if __name__ == "__main__": if len(sys.argv) > 1: exec(sys.argv[1])