From f8b509bc2be0a824233bc84dddcd71453b778e54 Mon Sep 17 00:00:00 2001
From: Martin Bauer <martin.bauer@fau.de>
Date: Wed, 25 Jul 2018 16:05:39 +0200
Subject: [PATCH] Support for compiler flag modification at runtime & option to
 turn cache off

---
 cpu/cpujit.py | 118 +++++++++++++++++++++++++++++++-------------------
 1 file changed, 74 insertions(+), 44 deletions(-)

diff --git a/cpu/cpujit.py b/cpu/cpujit.py
index e40473f53..9b17d3be5 100644
--- a/cpu/cpujit.py
+++ b/cpu/cpujit.py
@@ -49,6 +49,8 @@ import json
 import platform
 import shutil
 import textwrap
+from tempfile import TemporaryDirectory
+
 import numpy as np
 import subprocess
 from appdirs import user_config_dir, user_cache_dir
@@ -167,15 +169,12 @@ def read_config():
         create_folder(config_path, True)
         json.dump(config, open(config_path, 'w'), indent=4)
 
-    config['cache']['object_cache'] = os.path.expanduser(config['cache']['object_cache']).format(pid=os.getpid())
-
-    if config['cache']['clear_cache_on_start']:
-        clear_cache()
+    if config['cache']['object_cache'] is not False:
+        config['cache']['object_cache'] = os.path.expanduser(config['cache']['object_cache']).format(pid=os.getpid())
 
-    create_folder(config['cache']['object_cache'], False)
+        if config['cache']['clear_cache_on_start']:
+            clear_cache()
 
-    if 'env' not in config['compiler']:
-        shutil.rmtree(config['cache']['object_cache'], ignore_errors=True)
         create_folder(config['cache']['object_cache'], False)
 
     if config['compiler']['os'] == 'windows':
@@ -204,10 +203,31 @@ def hash_to_function_name(h):
     return res.replace('-', 'm')
 
 
+def add_or_change_compiler_flags(flags):
+    if not isinstance(flags, list) and not isinstance(flags, tuple):
+        flags = [flags]
+
+    compiler_config = get_compiler_config()
+    cache_config = get_cache_config()
+    cache_config['object_cache'] = False  # disable cache
+
+    for flag in flags:
+        flag = flag.strip()
+        if '=' in flag:
+            base = flag.split('=')[0].strip()
+        else:
+            base = flag
+
+        new_flags = [c for c in compiler_config['flags'].split() if not c.startswith(base)]
+        new_flags.append(flag)
+        compiler_config['flags'] = ' '.join(new_flags)
+
+
 def clear_cache():
     cache_config = get_cache_config()
-    shutil.rmtree(cache_config['object_cache'], ignore_errors=True)
-    create_folder(cache_config['object_cache'], False)
+    if cache_config['object_cache'] is not False:
+        shutil.rmtree(cache_config['object_cache'], ignore_errors=True)
+        create_folder(cache_config['object_cache'], False)
 
 
 type_mapping = {
@@ -449,10 +469,9 @@ class KernelWrapper:
         return self.kernel(**kwargs)
 
 
-def compile_and_load(ast):
-    from pystencils.cpu.cpujit import get_cache_config, get_compiler_config
-    cache_config = get_cache_config()
+def compile_module(code, code_hash, base_dir):
     compiler_config = get_compiler_config()
+    extra_flags = ['-I' + get_paths()['include']]
 
     if compiler_config['os'].lower() == 'windows':
         function_prefix = '__declspec(dllexport)'
@@ -465,39 +484,50 @@ def compile_and_load(ast):
         object_suffix = '.o'
         windows = False
 
+    src_file = os.path.join(base_dir, code_hash + ".cpp")
+    lib_file = os.path.join(base_dir, code_hash + lib_suffix)
+    object_file = os.path.join(base_dir, code_hash + object_suffix)
+
+    if not os.path.exists(object_file):
+        with file_handle_for_atomic_write(src_file) as f:
+            code.write_to_file(compiler_config['restrict_qualifier'], function_prefix, f)
+
+        if windows:
+            compile_cmd = ['cl.exe', '/c', '/EHsc'] + compiler_config['flags'].split()
+            compile_cmd += [*extra_flags, src_file, '/Fo' + object_file]
+            run_compile_step(compile_cmd)
+        else:
+            with atomic_file_write(object_file) as file_name:
+                compile_cmd = [compiler_config['command'], '-c'] + compiler_config['flags'].split()
+                compile_cmd += [*extra_flags, '-o', file_name, src_file]
+                run_compile_step(compile_cmd)
+
+        # Linking
+        if windows:
+            import sysconfig
+            config_vars = sysconfig.get_config_vars()
+            py_lib = os.path.join(config_vars["installed_base"], "libs",
+                                  "python{}.lib".format(config_vars["py_version_nodot"]))
+            run_compile_step(['link.exe', py_lib, '/DLL', '/out:' + lib_file, object_file])
+        else:
+            with atomic_file_write(lib_file) as file_name:
+                run_compile_step([compiler_config['command'], '-shared', object_file, '-o', file_name] +
+                                 compiler_config['flags'].split())
+    return lib_file
+
+
+def compile_and_load(ast):
+    cache_config = get_cache_config()
     code_hash_str = "mod_" + hashlib.sha256(generate_c(ast).encode()).hexdigest()
     code = ExtensionModuleCode(module_name=code_hash_str)
     code.add_function(ast, ast.function_name)
-    src_file = os.path.join(cache_config['object_cache'], code_hash_str + ".cpp")
-    lib_file = os.path.join(cache_config['object_cache'], code_hash_str + lib_suffix)
-    if not os.path.exists(lib_file):
-        extra_flags = ['-I' + get_paths()['include']]
-        object_file = os.path.join(cache_config['object_cache'], code_hash_str + object_suffix)
-        if not os.path.exists(object_file):
-            with file_handle_for_atomic_write(src_file) as f:
-                code.write_to_file(compiler_config['restrict_qualifier'], function_prefix, f)
-
-            if windows:
-                compile_cmd = ['cl.exe', '/c', '/EHsc'] + compiler_config['flags'].split()
-                compile_cmd += [*extra_flags, src_file, '/Fo' + object_file]
-                run_compile_step(compile_cmd)
-            else:
-                with atomic_file_write(object_file) as file_name:
-                    compile_cmd = [compiler_config['command'], '-c'] + compiler_config['flags'].split()
-                    compile_cmd += [*extra_flags, '-o', file_name, src_file]
-                    run_compile_step(compile_cmd)
-
-            # Linking
-            if windows:
-                import sysconfig
-                config_vars = sysconfig.get_config_vars()
-                py_lib = os.path.join(config_vars["installed_base"], "libs",
-                                      "python{}.lib".format(config_vars["py_version_nodot"]))
-                run_compile_step(['link.exe', py_lib, '/DLL', '/out:' + lib_file, object_file])
-            else:
-                with atomic_file_write(lib_file) as file_name:
-                    run_compile_step([compiler_config['command'], '-shared', object_file, '-o', file_name] +
-                                     compiler_config['flags'].split())
-
-    result = load_kernel_from_file(code_hash_str, ast.function_name, lib_file)
+
+    if cache_config['object_cache'] is False:
+        with TemporaryDirectory() as base_dir:
+            lib_file = compile_module(code, code_hash_str, base_dir)
+            result = load_kernel_from_file(code_hash_str, ast.function_name, lib_file)
+    else:
+        lib_file = compile_module(code, code_hash_str, base_dir=cache_config['object_cache'])
+        result = load_kernel_from_file(code_hash_str, ast.function_name, lib_file)
+
     return KernelWrapper(result, ast.parameters, ast)
-- 
GitLab