]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - tools/scan-build-py/libear/__init__.py
Vendor import of clang trunk r300422:
[FreeBSD/FreeBSD.git] / tools / scan-build-py / libear / __init__.py
1 # -*- coding: utf-8 -*-
2 #                     The LLVM Compiler Infrastructure
3 #
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. """
7
8 import sys
9 import os
10 import os.path
11 import re
12 import tempfile
13 import shutil
14 import contextlib
15 import logging
16
17 __all__ = ['build_libear']
18
19
20 def build_libear(compiler, dst_dir):
21     """ Returns the full path to the 'libear' library. """
22
23     try:
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'])
29
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',
43                                       'HAVE_NSGETENVIRON')
44         configure.write_by_template(
45             os.path.join(src_dir, 'config.h.in'),
46             os.path.join(dst_dir, 'config.h'))
47
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)
54
55         return os.path.join(dst_dir, target.name)
56
57     except Exception:
58         logging.info("Could not build interception library.", exc_info=True)
59         return None
60
61
62 def execute(cmd, *args, **kwargs):
63     """ Make subprocess execution silent. """
64
65     import subprocess
66     kwargs.update({'stdout': subprocess.PIPE, 'stderr': subprocess.STDOUT})
67     return subprocess.check_call(cmd, *args, **kwargs)
68
69
70 @contextlib.contextmanager
71 def TemporaryDirectory(**kwargs):
72     name = tempfile.mkdtemp(**kwargs)
73     try:
74         yield name
75     finally:
76         shutil.rmtree(name)
77
78
79 class Toolset(object):
80     """ Abstract class to represent different toolset. """
81
82     def __init__(self, src_dir):
83         self.src_dir = src_dir
84         self.compiler = None
85         self.c_flags = []
86
87     def set_compiler(self, compiler):
88         """ part of public interface """
89         self.compiler = compiler
90
91     def set_language_standard(self, standard):
92         """ part of public interface """
93         self.c_flags.append('-std=' + standard)
94
95     def add_definitions(self, defines):
96         """ part of public interface """
97         self.c_flags.extend(defines)
98
99     def dl_libraries(self):
100         raise NotImplementedError()
101
102     def shared_library_name(self, name):
103         raise NotImplementedError()
104
105     def shared_library_c_flags(self, release):
106         extra = ['-DNDEBUG', '-O3'] if release else []
107         return extra + ['-fPIC'] + self.c_flags
108
109     def shared_library_ld_flags(self, release, name):
110         raise NotImplementedError()
111
112
113 class DarwinToolset(Toolset):
114     def __init__(self, src_dir):
115         Toolset.__init__(self, src_dir)
116
117     def dl_libraries(self):
118         return []
119
120     def shared_library_name(self, name):
121         return 'lib' + name + '.dylib'
122
123     def shared_library_ld_flags(self, release, name):
124         extra = ['-dead_strip'] if release else []
125         return extra + ['-dynamiclib', '-install_name', '@rpath/' + name]
126
127
128 class UnixToolset(Toolset):
129     def __init__(self, src_dir):
130         Toolset.__init__(self, src_dir)
131
132     def dl_libraries(self):
133         return []
134
135     def shared_library_name(self, name):
136         return 'lib' + name + '.so'
137
138     def shared_library_ld_flags(self, release, name):
139         extra = [] if release else []
140         return extra + ['-shared', '-Wl,-soname,' + name]
141
142
143 class LinuxToolset(UnixToolset):
144     def __init__(self, src_dir):
145         UnixToolset.__init__(self, src_dir)
146
147     def dl_libraries(self):
148         return ['dl']
149
150
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)
159     else:
160         return UnixToolset(src_dir)
161
162
163 class Configure(object):
164     def __init__(self, toolset):
165         self.ctx = toolset
166         self.results = {'APPLE': sys.platform == 'darwin'}
167
168     def _try_to_compile_and_link(self, source):
169         try:
170             with TemporaryDirectory() as work_dir:
171                 src_file = 'check.c'
172                 with open(os.path.join(work_dir, src_file), 'w') as handle:
173                     handle.write(source)
174
175                 execute([self.ctx.compiler, src_file] + self.ctx.c_flags,
176                         cwd=work_dir)
177                 return True
178         except Exception:
179             return False
180
181     def check_function_exists(self, function, name):
182         template = "int FUNCTION(); int main() { return FUNCTION(); }"
183         source = template.replace("FUNCTION", function)
184
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})
190
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)
195
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})
201
202     def write_by_template(self, template, output):
203         def transform(line, definitions):
204
205             pattern = re.compile(r'^#cmakedefine\s+(\S+)')
206             m = pattern.match(line)
207             if m:
208                 key = m.group(1)
209                 if key not in definitions or not definitions[key]:
210                     return '/* #undef {0} */{1}'.format(key, os.linesep)
211                 else:
212                     return '#define {0}{1}'.format(key, os.linesep)
213             return line
214
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))
220
221
222 def do_configure(toolset):
223     return Configure(toolset)
224
225
226 class SharedLibrary(object):
227     def __init__(self, name, toolset):
228         self.name = toolset.shared_library_name(name)
229         self.ctx = toolset
230         self.inc = []
231         self.src = []
232         self.lib = []
233
234     def add_include(self, directory):
235         self.inc.extend(['-I', directory])
236
237     def add_sources(self, source):
238         self.src.append(source)
239
240     def link_against(self, libraries):
241         self.lib.extend(['-l' + lib for lib in libraries])
242
243     def build_release(self, directory):
244         for src in self.src:
245             logging.debug('Compiling %s', src)
246             execute(
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),
250                 cwd=directory)
251         logging.debug('Linking %s', self.name)
252         execute(
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),
256             cwd=directory)
257
258
259 def create_shared_library(name, toolset):
260     return SharedLibrary(name, toolset)