From 01f516fd7d3d42512b4ef8e4a9825598f24292c8 Mon Sep 17 00:00:00 2001
From: Andrea Shepard <andrea@persephoneslair.org>
Date: Sun, 20 Nov 2016 22:07:07 +0000
Subject: [PATCH] Add --disable-llvm-backend/--enable-llvm-backend build-time
 options, and move llvm.c to src/backends/llvm/ subdir

---
 SConstruct                     | 243 ++++++++++++++++++---------------
 src/SConscript                 |  91 ++++++------
 src/backends/{ => llvm}/llvm.c |   4 +-
 3 files changed, 187 insertions(+), 151 deletions(-)
 rename src/backends/{ => llvm}/llvm.c (99%)

diff --git a/SConstruct b/SConstruct
index 072e966b..707690c8 100644
--- a/SConstruct
+++ b/SConstruct
@@ -46,7 +46,6 @@ if 'DESTDIR' in env:
         print >>sys.stderr, "--!!-- you want; files will be installed in"
         print >>sys.stderr, "--!!--    %s" % (calcInstallPath("$prefix"),)
 
-env['LLVM_CONFIG'] = "llvm-config"
 if 'includedir' in env:
     env['incpath'] = calcInstallPath("$includedir", "hammer")
 else:
@@ -91,6 +90,16 @@ AddOption("--in-place",
           action="store_true",
           help="Build in-place, rather than in the build/<variant> tree")
 
+AddOption("--disable-llvm-backend",
+          dest="use_llvm",
+          default=False,
+          action="store_false",
+          help="Disable the LLVM backend (and don't require LLVM library dependencies)")
+AddOption("--enable-llvm-backend",
+          dest="use_llvm",
+          default=False,
+          action="store_true",
+          help="Enable the LLVM backend (and require LLVM library dependencies)")
 
 dbg = env.Clone(VARIANT='debug')
 dbg.MergeFlags("-g -O0")
@@ -105,7 +114,11 @@ else:
 
 env["CC"] = os.getenv("CC") or env["CC"]
 env["CXX"] = os.getenv("CXX") or env["CXX"]
-env["LLVM_CONFIG"] = os.getenv("LLVM_CONFIG") or env["LLVM_CONFIG"]
+
+if GetOption("use_llvm"):
+    # Overridable default path to llvm-config
+    env['LLVM_CONFIG'] = "llvm-config"
+    env["LLVM_CONFIG"] = os.getenv("LLVM_CONFIG") or env["LLVM_CONFIG"]
 
 if GetOption("coverage"):
     env.Append(CFLAGS=["--coverage"],
@@ -126,113 +139,122 @@ env["ENV"].update(x for x in os.environ.items() if x[0].startswith("CCC_"))
 #rootpath = env['ROOTPATH'] = os.path.abspath('.')
 #env.Append(CPPPATH=os.path.join('#', "hammer"))
 
+if GetOption("use_llvm"):
 # Set up LLVM config stuff to export
 
 # some llvm versions are old and will not work; some require --system-libs
 # with llvm-config, and some will break if given it
-llvm_config_version = subprocess.Popen('%s --version' % env["LLVM_CONFIG"], \
-                                       shell=True, \
-                                       stdin=subprocess.PIPE, stdout=subprocess.PIPE).communicate()
-if LooseVersion(llvm_config_version[0]) < LooseVersion("3.6"):
-   print "This LLVM version %s is too old" % llvm_config_version[0].strip()
-   Exit(1)
-
-if LooseVersion(llvm_config_version[0]) < LooseVersion("3.9") and \
-   LooseVersion(llvm_config_version[0]) >= LooseVersion("3.5"):
-    llvm_system_libs_flag = "--system-libs"
-else:
-    llvm_system_libs_flag = ""
-
-# Only keep one copy of this
-llvm_required_components = "core executionengine mcjit analysis x86codegen x86info"
-# Stubbing this out so we can implement static-only mode if needed later
-llvm_use_shared = True
-# Can we ask for shared/static from llvm-config?
-if LooseVersion(llvm_config_version[0]) < LooseVersion("3.9"):
-    # Nope
-    llvm_linkage_type_flag = ""
-    llvm_use_computed_shared_lib_name = True
-else:
-    # Woo, they finally fixed the dumb
-    llvm_use_computed_shared_lib_name = False
-    if llvm_use_shared:
-        llvm_linkage_type_flag = "--link-shared"
+    llvm_config_version = subprocess.Popen('%s --version' % env["LLVM_CONFIG"], \
+                                           shell=True, \
+                                           stdin=subprocess.PIPE, stdout=subprocess.PIPE).communicate()
+    if LooseVersion(llvm_config_version[0]) < LooseVersion("3.6"):
+        print "This LLVM version %s is too old" % llvm_config_version[0].strip()
+        Exit(1)
+
+    if LooseVersion(llvm_config_version[0]) < LooseVersion("3.9") and \
+        LooseVersion(llvm_config_version[0]) >= LooseVersion("3.5"):
+        llvm_system_libs_flag = "--system-libs"
     else:
-        llvm_linkage_type_flag = "--link-static"
-
-if llvm_use_computed_shared_lib_name:
-    # Okay, pull out the major and minor version numbers (barf barf)
-    p = re.compile("^(\d+)\.(\d+).*$")
-    m = p.match(llvm_config_version[0])
-    if m:
-        llvm_computed_shared_lib_name = "LLVM-%d.%d" % ((int)(m.group(1)), (int)(m.group(2)))
+        llvm_system_libs_flag = ""
+
+    # Only keep one copy of this
+    llvm_required_components = "core executionengine mcjit analysis x86codegen x86info"
+    # Stubbing this out so we can implement static-only mode if needed later
+    llvm_use_shared = True
+    # Can we ask for shared/static from llvm-config?
+    if LooseVersion(llvm_config_version[0]) < LooseVersion("3.9"):
+        # Nope
+        llvm_linkage_type_flag = ""
+        llvm_use_computed_shared_lib_name = True
     else:
-        print "Couldn't compute shared library name from LLVM version '%s', but needed to" % \
-            llvm_config_version[0]
-        Exit(1)
-else:
-    # We won't be needing it
-    llvm_computed_shared_lib_name = None
-
-# llvm-config 'helpfully' supplies -g and -O flags; educate it with this
-# custom ParseConfig function arg; make it a class with a method so we can
-# pass it around with scons export/import
-
-class LLVMConfigSanitizer:
-    def sanitize(self, env, cmd, unique=1):
-        # cmd is output from llvm-config
-        flags = cmd.split()
-        # match -g or -O flags
-        p = re.compile("^-[gO].*$")
-        filtered_flags = [flag for flag in flags if not p.match(flag)]
-        filtered_cmd = ' '.join(filtered_flags)
-        # print "llvm_config_sanitize: \"%s\" => \"%s\"" % (cmd, filtered_cmd)
-        env.MergeFlags(filtered_cmd, unique)
-llvm_config_sanitizer = LLVMConfigSanitizer()
-
-# LLVM defines, which the python bindings need
-try:
-    llvm_config_cflags = subprocess.Popen('%s --cflags' % env["LLVM_CONFIG"], \
-                                          shell=True, \
-                                          stdin=subprocess.PIPE, stdout=subprocess.PIPE).communicate()
-    flags = llvm_config_cflags[0].split()
-    # get just the -D ones
-    p = re.compile("^-D(.*)$")
-    llvm_defines = [p.match(flag).group(1) for flag in flags if p.match(flag)]
-except:
-    print "%s failed. Make sure you have LLVM and clang installed." % env["LLVM_CONFIG"]
-    Exit(1)
-
-# Get the llvm includedir, which the python bindings need
-try:
-    llvm_config_includes = subprocess.Popen('%s --includedir' % env["LLVM_CONFIG"], \
-                                            shell=True, \
-                                            stdin=subprocess.PIPE, stdout=subprocess.PIPE).communicate()
-    llvm_includes = llvm_config_includes[0].splitlines()
-except:
-    print "%s failed. Make sure you have LLVM and clang installed." % env["LLVM_CONFIG"]
-    Exit(1)
-
-# This goes here so we already know all the LLVM crap
-# Make a fresh environment to parse the config into, to read out just LLVM stuff
-llvm_dummy_env = Environment()
-# Get LLVM stuff into LIBS/LDFLAGS
-llvm_dummy_env.ParseConfig('%s --ldflags %s %s %s' % \
-                           (env["LLVM_CONFIG"], llvm_system_libs_flag, llvm_linkage_type_flag, \
-                            llvm_required_components), \
-                           function=llvm_config_sanitizer.sanitize)
-# Get the right -l lines in
-if llvm_use_shared:
+        # Woo, they finally fixed the dumb
+        llvm_use_computed_shared_lib_name = False
+        if llvm_use_shared:
+            llvm_linkage_type_flag = "--link-shared"
+        else:
+            llvm_linkage_type_flag = "--link-static"
+
     if llvm_use_computed_shared_lib_name:
-        llvm_dummy_env.Append(LIBS=[llvm_computed_shared_lib_name, ])
+        # Okay, pull out the major and minor version numbers (barf barf)
+        p = re.compile("^(\d+)\.(\d+).*$")
+        m = p.match(llvm_config_version[0])
+        if m:
+            llvm_computed_shared_lib_name = "LLVM-%d.%d" % ((int)(m.group(1)), (int)(m.group(2)))
+        else:
+            print "Couldn't compute shared library name from LLVM version '%s', but needed to" % \
+                llvm_config_version[0]
+            Exit(1)
     else:
-        llvm_dummy_env.ParseConfig('%s %s --libs %s' % \
-                                   (env["LLVM_CONFIG"], llvm_linkage_type_flag, llvm_required_components), \
-                                   function=llvm_config_sanitizer.sanitize)
-llvm_dummy_env.Append(LIBS=['stdc++', ], )
+        # We won't be needing it
+        llvm_computed_shared_lib_name = None
+
+    # llvm-config 'helpfully' supplies -g and -O flags; educate it with this
+    # custom ParseConfig function arg; make it a class with a method so we can
+    # pass it around with scons export/import
+
+    class LLVMConfigSanitizer:
+        def sanitize(self, env, cmd, unique=1):
+            # cmd is output from llvm-config
+            flags = cmd.split()
+            # match -g or -O flags
+            p = re.compile("^-[gO].*$")
+            filtered_flags = [flag for flag in flags if not p.match(flag)]
+            filtered_cmd = ' '.join(filtered_flags)
+            # print "llvm_config_sanitize: \"%s\" => \"%s\"" % (cmd, filtered_cmd)
+            env.MergeFlags(filtered_cmd, unique)
+    llvm_config_sanitizer = LLVMConfigSanitizer()
+
+    # LLVM defines, which the python bindings need
+    try:
+        llvm_config_cflags = subprocess.Popen('%s --cflags' % env["LLVM_CONFIG"], \
+                                              shell=True, \
+                                              stdin=subprocess.PIPE, stdout=subprocess.PIPE).communicate()
+        flags = llvm_config_cflags[0].split()
+        # get just the -D ones
+        p = re.compile("^-D(.*)$")
+        llvm_defines = [p.match(flag).group(1) for flag in flags if p.match(flag)]
+    except:
+        print "%s failed. Make sure you have LLVM and clang installed." % env["LLVM_CONFIG"]
+        Exit(1)
+
+    # Get the llvm includedir, which the python bindings need
+    try:
+        llvm_config_includes = subprocess.Popen('%s --includedir' % env["LLVM_CONFIG"], \
+                                                shell=True, \
+                                                stdin=subprocess.PIPE, stdout=subprocess.PIPE).communicate()
+        llvm_includes = llvm_config_includes[0].splitlines()
+    except:
+        print "%s failed. Make sure you have LLVM and clang installed." % env["LLVM_CONFIG"]
+        Exit(1)
+
+    # This goes here so we already know all the LLVM crap
+    # Make a fresh environment to parse the config into, to read out just LLVM stuff
+    llvm_dummy_env = Environment()
+    # Get LLVM stuff into LIBS/LDFLAGS
+    llvm_dummy_env.ParseConfig('%s --ldflags %s %s %s' % \
+                               (env["LLVM_CONFIG"], llvm_system_libs_flag, llvm_linkage_type_flag, \
+                                llvm_required_components), \
+                               function=llvm_config_sanitizer.sanitize)
+    # Get the right -l lines in
+    if llvm_use_shared:
+        if llvm_use_computed_shared_lib_name:
+            llvm_dummy_env.Append(LIBS=[llvm_computed_shared_lib_name, ])
+        else:
+            llvm_dummy_env.ParseConfig('%s %s --libs %s' % \
+                                       (env["LLVM_CONFIG"], llvm_linkage_type_flag, llvm_required_components), \
+                                       function=llvm_config_sanitizer.sanitize)
+    llvm_dummy_env.Append(LIBS=['stdc++', ], )
+#endif GetOption("use_llvm")
+
+# The .pc.in file has substs for llvm_lib_flags and llvm_libdir_flags, so if
+# we aren't using LLVM, set them to the empty string
+if GetOption("use_llvm"):
+    env['llvm_libdir_flags'] = llvm_dummy_env.subst('$_LIBDIRFLAGS')
+    env['llvm_lib_flags'] = llvm_dummy_env.subst('$_LIBFLAGS')
+else:
+    env['llvm_libdir_flags'] = ""
+    env['llvm_lib_flags'] = ""
 
-env['llvm_libdir_flags'] = llvm_dummy_env.subst('$_LIBDIRFLAGS')
-env['llvm_lib_flags'] = llvm_dummy_env.subst('$_LIBFLAGS')
 pkgconfig = env.ScanReplace('libhammer.pc.in')
 Default(pkgconfig)
 env.Install("$pkgconfigpath", pkgconfig)
@@ -249,16 +271,17 @@ Export('env')
 Export('testruns')
 Export('targets')
 # LLVM-related flags
-Export('llvm_computed_shared_lib_name')
-Export('llvm_config_sanitizer')
-Export('llvm_config_version')
-Export('llvm_defines')
-Export('llvm_includes')
-Export('llvm_linkage_type_flag')
-Export('llvm_required_components')
-Export('llvm_system_libs_flag')
-Export('llvm_use_computed_shared_lib_name')
-Export('llvm_use_shared')
+if GetOption("use_llvm"):
+    Export('llvm_computed_shared_lib_name')
+    Export('llvm_config_sanitizer')
+    Export('llvm_config_version')
+    Export('llvm_defines')
+    Export('llvm_includes')
+    Export('llvm_linkage_type_flag')
+    Export('llvm_required_components')
+    Export('llvm_system_libs_flag')
+    Export('llvm_use_computed_shared_lib_name')
+    Export('llvm_use_shared')
 
 if not GetOption("in_place"):
     env['BUILD_BASE'] = 'build/$VARIANT'
diff --git a/src/SConscript b/src/SConscript
index 80d96bf4..cdbe6c73 100644
--- a/src/SConscript
+++ b/src/SConscript
@@ -6,14 +6,15 @@ import subprocess
 
 Import('env testruns')
 # LLVM-related flags
-Import('llvm_computed_shared_lib_name')
-Import('llvm_config_sanitizer')
-Import('llvm_config_version')
-Import('llvm_linkage_type_flag')
-Import('llvm_required_components')
-Import('llvm_system_libs_flag')
-Import('llvm_use_computed_shared_lib_name')
-Import('llvm_use_shared')
+if GetOption("use_llvm"):
+    Import('llvm_computed_shared_lib_name')
+    Import('llvm_config_sanitizer')
+    Import('llvm_config_version')
+    Import('llvm_linkage_type_flag')
+    Import('llvm_required_components')
+    Import('llvm_system_libs_flag')
+    Import('llvm_use_computed_shared_lib_name')
+    Import('llvm_use_shared')
 
 dist_headers = [
     "hammer.h",
@@ -61,10 +62,15 @@ parsers = ['parsers/%s.c'%s for s in
             'unimplemented',
             'whitespace',
             'xor',
-            'value']] 
+            'value']]
 
 backends = ['backends/%s.c' % s for s in
-            ['packrat', 'llk', 'regex', 'glr', 'lalr', 'lr', 'lr0', 'llvm']]
+            ['packrat', 'llk', 'regex', 'glr', 'lalr', 'lr', 'lr0']]
+
+# Add LLVM backend if enabled
+if GetOption("use_llvm"):
+    llvm_backend_files = ['llvm.c']
+    backends = backends + ['backends/llvm/%s' % s for s in llvm_backend_files]
 
 misc_hammer_parts = [
     'allocator.c',
@@ -89,29 +95,33 @@ ctests = ['t_benchmark.c',
           't_misc.c',
 	  't_regression.c']
 
-env.ParseConfig('%s --cflags --ldflags' % env["LLVM_CONFIG"], function=llvm_config_sanitizer.sanitize)
+if GetOption("use_llvm"):
+    env.ParseConfig('%s --cflags --ldflags' % env["LLVM_CONFIG"], function=llvm_config_sanitizer.sanitize)
 libhammer_static = env.StaticLibrary('hammer', parsers + backends + misc_hammer_parts)
 
 # Use a cloned env for the shared library so we can have library dependencies
 shared_env = env.Clone()
-# Get LLVM stuff into LIBS/LDFLAGS
-shared_env.ParseConfig('%s --ldflags %s %s %s' % \
-                       (env["LLVM_CONFIG"], llvm_system_libs_flag, llvm_linkage_type_flag, llvm_required_components), \
-                       function=llvm_config_sanitizer.sanitize)
-# Get the right -l lines in
-if llvm_use_shared:
-    if llvm_use_computed_shared_lib_name:
-        shared_env.Append(LIBS=[llvm_computed_shared_lib_name, ])
+if GetOption("use_llvm"):
+    # Get LLVM stuff into LIBS/LDFLAGS
+    shared_env.ParseConfig('%s --ldflags %s %s %s' % \
+                           (env["LLVM_CONFIG"], llvm_system_libs_flag, \
+                            llvm_linkage_type_flag, llvm_required_components), \
+                           function=llvm_config_sanitizer.sanitize)
+    # Get the right -l lines in
+    if llvm_use_shared:
+        if llvm_use_computed_shared_lib_name:
+            shared_env.Append(LIBS=[llvm_computed_shared_lib_name, ])
+        else:
+            shared_env.ParseConfig('%s %s --libs %s' % \
+                                   (env["LLVM_CONFIG"], llvm_linkage_type_flag, llvm_required_components), \
+                                   function=llvm_config_sanitizer.sanitize)
     else:
+        # Just grab the statics regardless of version
         shared_env.ParseConfig('%s %s --libs %s' % \
                                (env["LLVM_CONFIG"], llvm_linkage_type_flag, llvm_required_components), \
                                function=llvm_config_sanitizer.sanitize)
-else:
-    # Just grab the statics regardless of version
-    shared_env.ParseConfig('%s %s --libs %s' % \
-                           (env["LLVM_CONFIG"], llvm_linkage_type_flag, llvm_required_components), \
-                           function=llvm_config_sanitizer.sanitize)
-shared_env.Append(LIBS=['stdc++', ], LIBPATH=['.'])
+    shared_env.Append(LIBS=['stdc++', ], LIBPATH=['.'])
+
 libhammer_shared = shared_env.SharedLibrary('hammer', parsers + backends + misc_hammer_parts)
 
 Default(libhammer_shared, libhammer_static)
@@ -124,24 +134,27 @@ env.Install("$backendsincpath", backends_headers)
 testenv = env.Clone()
 testenv.Append(LIBS=['hammer'], LIBPATH=['.'])
 testenv.ParseConfig('pkg-config --cflags --libs glib-2.0')
-# Get LLVM stuff into LIBS/LDFLAGS
-testenv.ParseConfig('%s --ldflags %s %s %s' % \
-                    (env["LLVM_CONFIG"], llvm_system_libs_flag, llvm_linkage_type_flag, llvm_required_components), \
-                    function=llvm_config_sanitizer.sanitize)
-# Get the right -l lines in
-if llvm_use_shared:
-    if llvm_use_computed_shared_lib_name:
-        testenv.Append(LIBS=[llvm_computed_shared_lib_name, ])
+if GetOption("use_llvm"):
+    # Get LLVM stuff into LIBS/LDFLAGS
+    testenv.ParseConfig('%s --ldflags %s %s %s' % \
+                        (env["LLVM_CONFIG"], llvm_system_libs_flag, \
+                         llvm_linkage_type_flag, llvm_required_components), \
+                        function=llvm_config_sanitizer.sanitize)
+    # Get the right -l lines in
+    if llvm_use_shared:
+        if llvm_use_computed_shared_lib_name:
+            testenv.Append(LIBS=[llvm_computed_shared_lib_name, ])
+        else:
+            testenv.ParseConfig('%s %s --libs %s' % \
+                                (env["LLVM_CONFIG"], llvm_linkage_type_flag, llvm_required_components), \
+                                function=llvm_config_sanitizer.sanitize)
     else:
+        # Just grab the statics regardless of version
         testenv.ParseConfig('%s %s --libs %s' % \
                             (env["LLVM_CONFIG"], llvm_linkage_type_flag, llvm_required_components), \
                             function=llvm_config_sanitizer.sanitize)
-else:
-    # Just grab the statics regardless of version
-    testenv.ParseConfig('%s %s --libs %s' % \
-                        (env["LLVM_CONFIG"], llvm_linkage_type_flag, llvm_required_components), \
-                        function=llvm_config_sanitizer.sanitize)
-testenv.Append(LIBS=['stdc++'], LIBPATH=['.'])
+    testenv.Append(LIBS=['stdc++'], LIBPATH=['.'])
+
 ctestexec = testenv.Program('test_suite', ctests + ['test_suite.c'], LINKFLAGS="--coverage" if testenv.GetOption("coverage") else None)
 ctest = Alias('testc', [ctestexec], "".join(["env LD_LIBRARY_PATH=", os.path.dirname(ctestexec[0].path), " ", ctestexec[0].path]))
 AlwaysBuild(ctest)
diff --git a/src/backends/llvm.c b/src/backends/llvm/llvm.c
similarity index 99%
rename from src/backends/llvm.c
rename to src/backends/llvm/llvm.c
index 8f53a932..d46d1d96 100644
--- a/src/backends/llvm.c
+++ b/src/backends/llvm/llvm.c
@@ -4,8 +4,8 @@
 #include <llvm-c/Core.h>
 #pragma GCC diagnostic pop
 #include <llvm-c/ExecutionEngine.h>
-#include "../internal.h"
-#include "../llvm.h"
+#include "../../internal.h"
+#include "../../llvm.h"
 
 typedef struct HLLVMParser_ {
   LLVMModuleRef mod;
-- 
GitLab