From fe5a282ae8cdc7d5ad91cb4c30448332eab20b0a 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                     | 249 ++++++++++++++++++---------------
 src/SConscript                 |  87 +++++++-----
 src/backends/{ => llvm}/llvm.c |   4 +-
 3 files changed, 188 insertions(+), 152 deletions(-)
 rename src/backends/{ => llvm}/llvm.c (99%)

diff --git a/SConstruct b/SConstruct
index 0eb3605e..e768a019 100644
--- a/SConstruct
+++ b/SConstruct
@@ -56,7 +56,6 @@ if 'DESTDIR' in env:
         print('--!!-- you want; files will be installed in', file=sys.stderr)
         print('--!!--    %s' % (calcInstallPath('$prefix'),), file=sys.stderr)
 
-env['LLVM_CONFIG'] = "llvm-config"
 if 'includedir' in env:
     env['incpath'] = calcInstallPath("$includedir", "hammer")
 else:
@@ -98,9 +97,20 @@ AddOption('--tests',
           action='store_true',
           help='Build tests')
 
+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)")
+
 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 os.getenv('CC') == 'clang' or env['PLATFORM'] == 'darwin':
     env.Replace(CC='clang',
@@ -133,6 +143,11 @@ elif env['PLATFORM'] == 'win32':
 else:
     env.MergeFlags('-lrt')
 
+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'],
                CXXFLAGS=['--coverage'],
@@ -165,113 +180,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'))
 
-# Set up LLVM config stuff to export
+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)
+    # 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"
+    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)
@@ -288,16 +312,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 ef8fdb05..8913224f 100644
--- a/src/SConscript
+++ b/src/SConscript
@@ -9,14 +9,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',
@@ -67,7 +68,12 @@ parsers = ['parsers/%s.c'%s for s in
             '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',
@@ -109,29 +115,33 @@ if env['PLATFORM'] == 'win32':
     # prevent collision between .lib from dll and .lib for static lib
     static_library_name = 'hammer_s'
 
-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(static_library_name, 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)
 
 if build_shared_library:
@@ -150,24 +160,25 @@ if GetOption('with_tests'):
     testenv.Append(LIBS=['hammer'])
     testenv.Prepend(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)
     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