1 # -*- coding: utf-8 -*-
2 # The LLVM Compiler Infrastructure
4 # This file is distributed under the University of Illinois Open Source
5 # License. See LICENSE.TXT for details.
6 """ This module compiles the intercept library. """
17 __all__ = ['build_libear']
20 def build_libear(compiler, dst_dir):
21 """ Returns the full path to the 'libear' library. """
24 src_dir = os.path.dirname(os.path.realpath(__file__))
25 toolset = make_toolset(src_dir)
26 toolset.set_compiler(compiler)
27 toolset.set_language_standard('c99')
28 toolset.add_definitions(['-D_GNU_SOURCE'])
30 configure = do_configure(toolset)
31 configure.check_function_exists('execve', 'HAVE_EXECVE')
32 configure.check_function_exists('execv', 'HAVE_EXECV')
33 configure.check_function_exists('execvpe', 'HAVE_EXECVPE')
34 configure.check_function_exists('execvp', 'HAVE_EXECVP')
35 configure.check_function_exists('execvP', 'HAVE_EXECVP2')
36 configure.check_function_exists('exect', 'HAVE_EXECT')
37 configure.check_function_exists('execl', 'HAVE_EXECL')
38 configure.check_function_exists('execlp', 'HAVE_EXECLP')
39 configure.check_function_exists('execle', 'HAVE_EXECLE')
40 configure.check_function_exists('posix_spawn', 'HAVE_POSIX_SPAWN')
41 configure.check_function_exists('posix_spawnp', 'HAVE_POSIX_SPAWNP')
42 configure.check_symbol_exists('_NSGetEnviron', 'crt_externs.h',
44 configure.write_by_template(
45 os.path.join(src_dir, 'config.h.in'),
46 os.path.join(dst_dir, 'config.h'))
48 target = create_shared_library('ear', toolset)
49 target.add_include(dst_dir)
50 target.add_sources('ear.c')
51 target.link_against(toolset.dl_libraries())
52 target.link_against(['pthread'])
53 target.build_release(dst_dir)
55 return os.path.join(dst_dir, target.name)
58 logging.info("Could not build interception library.", exc_info=True)
62 def execute(cmd, *args, **kwargs):
63 """ Make subprocess execution silent. """
66 kwargs.update({'stdout': subprocess.PIPE, 'stderr': subprocess.STDOUT})
67 return subprocess.check_call(cmd, *args, **kwargs)
70 @contextlib.contextmanager
71 def TemporaryDirectory(**kwargs):
72 name = tempfile.mkdtemp(**kwargs)
79 class Toolset(object):
80 """ Abstract class to represent different toolset. """
82 def __init__(self, src_dir):
83 self.src_dir = src_dir
87 def set_compiler(self, compiler):
88 """ part of public interface """
89 self.compiler = compiler
91 def set_language_standard(self, standard):
92 """ part of public interface """
93 self.c_flags.append('-std=' + standard)
95 def add_definitions(self, defines):
96 """ part of public interface """
97 self.c_flags.extend(defines)
99 def dl_libraries(self):
100 raise NotImplementedError()
102 def shared_library_name(self, name):
103 raise NotImplementedError()
105 def shared_library_c_flags(self, release):
106 extra = ['-DNDEBUG', '-O3'] if release else []
107 return extra + ['-fPIC'] + self.c_flags
109 def shared_library_ld_flags(self, release, name):
110 raise NotImplementedError()
113 class DarwinToolset(Toolset):
114 def __init__(self, src_dir):
115 Toolset.__init__(self, src_dir)
117 def dl_libraries(self):
120 def shared_library_name(self, name):
121 return 'lib' + name + '.dylib'
123 def shared_library_ld_flags(self, release, name):
124 extra = ['-dead_strip'] if release else []
125 return extra + ['-dynamiclib', '-install_name', '@rpath/' + name]
128 class UnixToolset(Toolset):
129 def __init__(self, src_dir):
130 Toolset.__init__(self, src_dir)
132 def dl_libraries(self):
135 def shared_library_name(self, name):
136 return 'lib' + name + '.so'
138 def shared_library_ld_flags(self, release, name):
139 extra = [] if release else []
140 return extra + ['-shared', '-Wl,-soname,' + name]
143 class LinuxToolset(UnixToolset):
144 def __init__(self, src_dir):
145 UnixToolset.__init__(self, src_dir)
147 def dl_libraries(self):
151 def make_toolset(src_dir):
152 platform = sys.platform
153 if platform in {'win32', 'cygwin'}:
154 raise RuntimeError('not implemented on this platform')
155 elif platform == 'darwin':
156 return DarwinToolset(src_dir)
157 elif platform in {'linux', 'linux2'}:
158 return LinuxToolset(src_dir)
160 return UnixToolset(src_dir)
163 class Configure(object):
164 def __init__(self, toolset):
166 self.results = {'APPLE': sys.platform == 'darwin'}
168 def _try_to_compile_and_link(self, source):
170 with TemporaryDirectory() as work_dir:
172 with open(os.path.join(work_dir, src_file), 'w') as handle:
175 execute([self.ctx.compiler, src_file] + self.ctx.c_flags,
181 def check_function_exists(self, function, name):
182 template = "int FUNCTION(); int main() { return FUNCTION(); }"
183 source = template.replace("FUNCTION", function)
185 logging.debug('Checking function %s', function)
186 found = self._try_to_compile_and_link(source)
187 logging.debug('Checking function %s -- %s', function,
188 'found' if found else 'not found')
189 self.results.update({name: found})
191 def check_symbol_exists(self, symbol, include, name):
192 template = """#include <INCLUDE>
193 int main() { return ((int*)(&SYMBOL))[0]; }"""
194 source = template.replace('INCLUDE', include).replace("SYMBOL", symbol)
196 logging.debug('Checking symbol %s', symbol)
197 found = self._try_to_compile_and_link(source)
198 logging.debug('Checking symbol %s -- %s', symbol,
199 'found' if found else 'not found')
200 self.results.update({name: found})
202 def write_by_template(self, template, output):
203 def transform(line, definitions):
205 pattern = re.compile(r'^#cmakedefine\s+(\S+)')
206 m = pattern.match(line)
209 if key not in definitions or not definitions[key]:
210 return '/* #undef {0} */{1}'.format(key, os.linesep)
212 return '#define {0}{1}'.format(key, os.linesep)
215 with open(template, 'r') as src_handle:
216 logging.debug('Writing config to %s', output)
217 with open(output, 'w') as dst_handle:
218 for line in src_handle:
219 dst_handle.write(transform(line, self.results))
222 def do_configure(toolset):
223 return Configure(toolset)
226 class SharedLibrary(object):
227 def __init__(self, name, toolset):
228 self.name = toolset.shared_library_name(name)
234 def add_include(self, directory):
235 self.inc.extend(['-I', directory])
237 def add_sources(self, source):
238 self.src.append(source)
240 def link_against(self, libraries):
241 self.lib.extend(['-l' + lib for lib in libraries])
243 def build_release(self, directory):
245 logging.debug('Compiling %s', src)
247 [self.ctx.compiler, '-c', os.path.join(self.ctx.src_dir, src),
248 '-o', src + '.o'] + self.inc +
249 self.ctx.shared_library_c_flags(True),
251 logging.debug('Linking %s', self.name)
253 [self.ctx.compiler] + [src + '.o' for src in self.src] +
254 ['-o', self.name] + self.lib +
255 self.ctx.shared_library_ld_flags(True, self.name),
259 def create_shared_library(name, toolset):
260 return SharedLibrary(name, toolset)