From e16a579db0ac4ee2ca79e69fea286cb51210d2dc Mon Sep 17 00:00:00 2001 From: mloubout Date: Thu, 16 Apr 2026 14:17:25 -0400 Subject: [PATCH] compiler: load so directly when source is missing (e.g deleted c file) --- devito/arch/compiler.py | 8 ++++++++ tests/test_cinterface.py | 30 ++++++++++++++++++++++++++++++ 2 files changed, 38 insertions(+) diff --git a/devito/arch/compiler.py b/devito/arch/compiler.py index a0b4ce8a75..17cdc91a0f 100644 --- a/devito/arch/compiler.py +++ b/devito/arch/compiler.py @@ -386,6 +386,14 @@ def jit_compile(self, soname, code): # Typically we end up here # Make a suite of cache directories based on the soname cache_dir.mkdir(parents=True, exist_ok=True) + + # Has the library already been compiled? + try: + self.load(target) + return False, src_file + except OSError: + # The .so file isn't present, we need to compile it + pass else: # Warning: dropping `code` on the floor in favor to whatever is written # within `src_file` diff --git a/tests/test_cinterface.py b/tests/test_cinterface.py index 1c9d1968cb..e3b39994f0 100644 --- a/tests/test_cinterface.py +++ b/tests/test_cinterface.py @@ -1,4 +1,6 @@ import os +import sys +from contextlib import suppress from devito import Eq, Grid, Operator, TimeFunction, configuration from devito.types import Timer @@ -37,3 +39,31 @@ def test_basic(): assert isinstance(timers, Timer) assert 'struct profiler\n{' not in ccode assert 'struct profiler\n{' in hcode + + +def test_load_without_source(): + grid = Grid(shape=(4, 4)) + + f = TimeFunction(name='f', grid=grid) + + eq = Eq(f.forward, f + 1.) + + name = "foo" + op = Operator(eq, name=name) + + # Make sure the so file is not present + dirname = op._compiler.get_jit_dir() + soname = op._soname + ext = '.dylib' if sys.platform == "darwin" else '.so' + with suppress(FileNotFoundError): + os.remove(f"{os.path.join(dirname, soname)}{ext}") + + # Trigger compilation + recompiled, src_file = op._compiler.jit_compile(op._soname, str(op)) + assert recompiled + + os.remove(src_file) + # Trigger compilation again. It should skip the C part and directly load the .so file + recompiled, src_file = op._compiler.jit_compile(op._soname, str(op)) + assert not recompiled + assert not os.path.isfile(src_file)