summaryrefslogtreecommitdiffstats
path: root/tools/scan-build-py/libear/__init__.py
diff options
context:
space:
mode:
Diffstat (limited to 'tools/scan-build-py/libear/__init__.py')
-rw-r--r--tools/scan-build-py/libear/__init__.py260
1 files changed, 260 insertions, 0 deletions
diff --git a/tools/scan-build-py/libear/__init__.py b/tools/scan-build-py/libear/__init__.py
new file mode 100644
index 0000000000..3e1c13cf2b
--- /dev/null
+++ b/tools/scan-build-py/libear/__init__.py
@@ -0,0 +1,260 @@
+# -*- coding: utf-8 -*-
+# The LLVM Compiler Infrastructure
+#
+# This file is distributed under the University of Illinois Open Source
+# License. See LICENSE.TXT for details.
+""" This module compiles the intercept library. """
+
+import sys
+import os
+import os.path
+import re
+import tempfile
+import shutil
+import contextlib
+import logging
+
+__all__ = ['build_libear']
+
+
+def build_libear(compiler, dst_dir):
+ """ Returns the full path to the 'libear' library. """
+
+ try:
+ src_dir = os.path.dirname(os.path.realpath(__file__))
+ toolset = make_toolset(src_dir)
+ toolset.set_compiler(compiler)
+ toolset.set_language_standard('c99')
+ toolset.add_definitions(['-D_GNU_SOURCE'])
+
+ configure = do_configure(toolset)
+ configure.check_function_exists('execve', 'HAVE_EXECVE')
+ configure.check_function_exists('execv', 'HAVE_EXECV')
+ configure.check_function_exists('execvpe', 'HAVE_EXECVPE')
+ configure.check_function_exists('execvp', 'HAVE_EXECVP')
+ configure.check_function_exists('execvP', 'HAVE_EXECVP2')
+ configure.check_function_exists('exect', 'HAVE_EXECT')
+ configure.check_function_exists('execl', 'HAVE_EXECL')
+ configure.check_function_exists('execlp', 'HAVE_EXECLP')
+ configure.check_function_exists('execle', 'HAVE_EXECLE')
+ configure.check_function_exists('posix_spawn', 'HAVE_POSIX_SPAWN')
+ configure.check_function_exists('posix_spawnp', 'HAVE_POSIX_SPAWNP')
+ configure.check_symbol_exists('_NSGetEnviron', 'crt_externs.h',
+ 'HAVE_NSGETENVIRON')
+ configure.write_by_template(
+ os.path.join(src_dir, 'config.h.in'),
+ os.path.join(dst_dir, 'config.h'))
+
+ target = create_shared_library('ear', toolset)
+ target.add_include(dst_dir)
+ target.add_sources('ear.c')
+ target.link_against(toolset.dl_libraries())
+ target.link_against(['pthread'])
+ target.build_release(dst_dir)
+
+ return os.path.join(dst_dir, target.name)
+
+ except Exception:
+ logging.info("Could not build interception library.", exc_info=True)
+ return None
+
+
+def execute(cmd, *args, **kwargs):
+ """ Make subprocess execution silent. """
+
+ import subprocess
+ kwargs.update({'stdout': subprocess.PIPE, 'stderr': subprocess.STDOUT})
+ return subprocess.check_call(cmd, *args, **kwargs)
+
+
+@contextlib.contextmanager
+def TemporaryDirectory(**kwargs):
+ name = tempfile.mkdtemp(**kwargs)
+ try:
+ yield name
+ finally:
+ shutil.rmtree(name)
+
+
+class Toolset(object):
+ """ Abstract class to represent different toolset. """
+
+ def __init__(self, src_dir):
+ self.src_dir = src_dir
+ self.compiler = None
+ self.c_flags = []
+
+ def set_compiler(self, compiler):
+ """ part of public interface """
+ self.compiler = compiler
+
+ def set_language_standard(self, standard):
+ """ part of public interface """
+ self.c_flags.append('-std=' + standard)
+
+ def add_definitions(self, defines):
+ """ part of public interface """
+ self.c_flags.extend(defines)
+
+ def dl_libraries(self):
+ raise NotImplementedError()
+
+ def shared_library_name(self, name):
+ raise NotImplementedError()
+
+ def shared_library_c_flags(self, release):
+ extra = ['-DNDEBUG', '-O3'] if release else []
+ return extra + ['-fPIC'] + self.c_flags
+
+ def shared_library_ld_flags(self, release, name):
+ raise NotImplementedError()
+
+
+class DarwinToolset(Toolset):
+ def __init__(self, src_dir):
+ Toolset.__init__(self, src_dir)
+
+ def dl_libraries(self):
+ return []
+
+ def shared_library_name(self, name):
+ return 'lib' + name + '.dylib'
+
+ def shared_library_ld_flags(self, release, name):
+ extra = ['-dead_strip'] if release else []
+ return extra + ['-dynamiclib', '-install_name', '@rpath/' + name]
+
+
+class UnixToolset(Toolset):
+ def __init__(self, src_dir):
+ Toolset.__init__(self, src_dir)
+
+ def dl_libraries(self):
+ return []
+
+ def shared_library_name(self, name):
+ return 'lib' + name + '.so'
+
+ def shared_library_ld_flags(self, release, name):
+ extra = [] if release else []
+ return extra + ['-shared', '-Wl,-soname,' + name]
+
+
+class LinuxToolset(UnixToolset):
+ def __init__(self, src_dir):
+ UnixToolset.__init__(self, src_dir)
+
+ def dl_libraries(self):
+ return ['dl']
+
+
+def make_toolset(src_dir):
+ platform = sys.platform
+ if platform in {'win32', 'cygwin'}:
+ raise RuntimeError('not implemented on this platform')
+ elif platform == 'darwin':
+ return DarwinToolset(src_dir)
+ elif platform in {'linux', 'linux2'}:
+ return LinuxToolset(src_dir)
+ else:
+ return UnixToolset(src_dir)
+
+
+class Configure(object):
+ def __init__(self, toolset):
+ self.ctx = toolset
+ self.results = {'APPLE': sys.platform == 'darwin'}
+
+ def _try_to_compile_and_link(self, source):
+ try:
+ with TemporaryDirectory() as work_dir:
+ src_file = 'check.c'
+ with open(os.path.join(work_dir, src_file), 'w') as handle:
+ handle.write(source)
+
+ execute([self.ctx.compiler, src_file] + self.ctx.c_flags,
+ cwd=work_dir)
+ return True
+ except Exception:
+ return False
+
+ def check_function_exists(self, function, name):
+ template = "int FUNCTION(); int main() { return FUNCTION(); }"
+ source = template.replace("FUNCTION", function)
+
+ logging.debug('Checking function %s', function)
+ found = self._try_to_compile_and_link(source)
+ logging.debug('Checking function %s -- %s', function,
+ 'found' if found else 'not found')
+ self.results.update({name: found})
+
+ def check_symbol_exists(self, symbol, include, name):
+ template = """#include <INCLUDE>
+ int main() { return ((int*)(&SYMBOL))[0]; }"""
+ source = template.replace('INCLUDE', include).replace("SYMBOL", symbol)
+
+ logging.debug('Checking symbol %s', symbol)
+ found = self._try_to_compile_and_link(source)
+ logging.debug('Checking symbol %s -- %s', symbol,
+ 'found' if found else 'not found')
+ self.results.update({name: found})
+
+ def write_by_template(self, template, output):
+ def transform(line, definitions):
+
+ pattern = re.compile(r'^#cmakedefine\s+(\S+)')
+ m = pattern.match(line)
+ if m:
+ key = m.group(1)
+ if key not in definitions or not definitions[key]:
+ return '/* #undef {} */\n'.format(key)
+ else:
+ return '#define {}\n'.format(key)
+ return line
+
+ with open(template, 'r') as src_handle:
+ logging.debug('Writing config to %s', output)
+ with open(output, 'w') as dst_handle:
+ for line in src_handle:
+ dst_handle.write(transform(line, self.results))
+
+
+def do_configure(toolset):
+ return Configure(toolset)
+
+
+class SharedLibrary(object):
+ def __init__(self, name, toolset):
+ self.name = toolset.shared_library_name(name)
+ self.ctx = toolset
+ self.inc = []
+ self.src = []
+ self.lib = []
+
+ def add_include(self, directory):
+ self.inc.extend(['-I', directory])
+
+ def add_sources(self, source):
+ self.src.append(source)
+
+ def link_against(self, libraries):
+ self.lib.extend(['-l' + lib for lib in libraries])
+
+ def build_release(self, directory):
+ for src in self.src:
+ logging.debug('Compiling %s', src)
+ execute(
+ [self.ctx.compiler, '-c', os.path.join(self.ctx.src_dir, src),
+ '-o', src + '.o'] + self.inc +
+ self.ctx.shared_library_c_flags(True),
+ cwd=directory)
+ logging.debug('Linking %s', self.name)
+ execute(
+ [self.ctx.compiler] + [src + '.o' for src in self.src] +
+ ['-o', self.name] + self.lib +
+ self.ctx.shared_library_ld_flags(True, self.name),
+ cwd=directory)
+
+
+def create_shared_library(name, toolset):
+ return SharedLibrary(name, toolset)