xref: /openbsd-src/gnu/llvm/clang/tools/scan-build-py/lib/libear/__init__.py (revision a9ac8606c53d55cee9c3a39778b249c51df111ef)
1*a9ac8606Spatrick# -*- coding: utf-8 -*-
2*a9ac8606Spatrick# Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
3*a9ac8606Spatrick# See https://llvm.org/LICENSE.txt for license information.
4*a9ac8606Spatrick# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
5*a9ac8606Spatrick""" This module compiles the intercept library. """
6*a9ac8606Spatrick
7*a9ac8606Spatrickimport sys
8*a9ac8606Spatrickimport os
9*a9ac8606Spatrickimport os.path
10*a9ac8606Spatrickimport re
11*a9ac8606Spatrickimport tempfile
12*a9ac8606Spatrickimport shutil
13*a9ac8606Spatrickimport contextlib
14*a9ac8606Spatrickimport logging
15*a9ac8606Spatrick
16*a9ac8606Spatrick__all__ = ['build_libear']
17*a9ac8606Spatrick
18*a9ac8606Spatrick
19*a9ac8606Spatrickdef build_libear(compiler, dst_dir):
20*a9ac8606Spatrick    """ Returns the full path to the 'libear' library. """
21*a9ac8606Spatrick
22*a9ac8606Spatrick    try:
23*a9ac8606Spatrick        src_dir = os.path.dirname(os.path.realpath(__file__))
24*a9ac8606Spatrick        toolset = make_toolset(src_dir)
25*a9ac8606Spatrick        toolset.set_compiler(compiler)
26*a9ac8606Spatrick        toolset.set_language_standard('c99')
27*a9ac8606Spatrick        toolset.add_definitions(['-D_GNU_SOURCE'])
28*a9ac8606Spatrick
29*a9ac8606Spatrick        configure = do_configure(toolset)
30*a9ac8606Spatrick        configure.check_function_exists('execve', 'HAVE_EXECVE')
31*a9ac8606Spatrick        configure.check_function_exists('execv', 'HAVE_EXECV')
32*a9ac8606Spatrick        configure.check_function_exists('execvpe', 'HAVE_EXECVPE')
33*a9ac8606Spatrick        configure.check_function_exists('execvp', 'HAVE_EXECVP')
34*a9ac8606Spatrick        configure.check_function_exists('execvP', 'HAVE_EXECVP2')
35*a9ac8606Spatrick        configure.check_function_exists('exect', 'HAVE_EXECT')
36*a9ac8606Spatrick        configure.check_function_exists('execl', 'HAVE_EXECL')
37*a9ac8606Spatrick        configure.check_function_exists('execlp', 'HAVE_EXECLP')
38*a9ac8606Spatrick        configure.check_function_exists('execle', 'HAVE_EXECLE')
39*a9ac8606Spatrick        configure.check_function_exists('posix_spawn', 'HAVE_POSIX_SPAWN')
40*a9ac8606Spatrick        configure.check_function_exists('posix_spawnp', 'HAVE_POSIX_SPAWNP')
41*a9ac8606Spatrick        configure.check_symbol_exists('_NSGetEnviron', 'crt_externs.h',
42*a9ac8606Spatrick                                      'HAVE_NSGETENVIRON')
43*a9ac8606Spatrick        configure.write_by_template(
44*a9ac8606Spatrick            os.path.join(src_dir, 'config.h.in'),
45*a9ac8606Spatrick            os.path.join(dst_dir, 'config.h'))
46*a9ac8606Spatrick
47*a9ac8606Spatrick        target = create_shared_library('ear', toolset)
48*a9ac8606Spatrick        target.add_include(dst_dir)
49*a9ac8606Spatrick        target.add_sources('ear.c')
50*a9ac8606Spatrick        target.link_against(toolset.dl_libraries())
51*a9ac8606Spatrick        target.link_against(['pthread'])
52*a9ac8606Spatrick        target.build_release(dst_dir)
53*a9ac8606Spatrick
54*a9ac8606Spatrick        return os.path.join(dst_dir, target.name)
55*a9ac8606Spatrick
56*a9ac8606Spatrick    except Exception:
57*a9ac8606Spatrick        logging.info("Could not build interception library.", exc_info=True)
58*a9ac8606Spatrick        return None
59*a9ac8606Spatrick
60*a9ac8606Spatrick
61*a9ac8606Spatrickdef execute(cmd, *args, **kwargs):
62*a9ac8606Spatrick    """ Make subprocess execution silent. """
63*a9ac8606Spatrick
64*a9ac8606Spatrick    import subprocess
65*a9ac8606Spatrick    kwargs.update({'stdout': subprocess.PIPE, 'stderr': subprocess.STDOUT})
66*a9ac8606Spatrick    return subprocess.check_call(cmd, *args, **kwargs)
67*a9ac8606Spatrick
68*a9ac8606Spatrick
69*a9ac8606Spatrick@contextlib.contextmanager
70*a9ac8606Spatrickdef TemporaryDirectory(**kwargs):
71*a9ac8606Spatrick    name = tempfile.mkdtemp(**kwargs)
72*a9ac8606Spatrick    try:
73*a9ac8606Spatrick        yield name
74*a9ac8606Spatrick    finally:
75*a9ac8606Spatrick        shutil.rmtree(name)
76*a9ac8606Spatrick
77*a9ac8606Spatrick
78*a9ac8606Spatrickclass Toolset(object):
79*a9ac8606Spatrick    """ Abstract class to represent different toolset. """
80*a9ac8606Spatrick
81*a9ac8606Spatrick    def __init__(self, src_dir):
82*a9ac8606Spatrick        self.src_dir = src_dir
83*a9ac8606Spatrick        self.compiler = None
84*a9ac8606Spatrick        self.c_flags = []
85*a9ac8606Spatrick
86*a9ac8606Spatrick    def set_compiler(self, compiler):
87*a9ac8606Spatrick        """ part of public interface """
88*a9ac8606Spatrick        self.compiler = compiler
89*a9ac8606Spatrick
90*a9ac8606Spatrick    def set_language_standard(self, standard):
91*a9ac8606Spatrick        """ part of public interface """
92*a9ac8606Spatrick        self.c_flags.append('-std=' + standard)
93*a9ac8606Spatrick
94*a9ac8606Spatrick    def add_definitions(self, defines):
95*a9ac8606Spatrick        """ part of public interface """
96*a9ac8606Spatrick        self.c_flags.extend(defines)
97*a9ac8606Spatrick
98*a9ac8606Spatrick    def dl_libraries(self):
99*a9ac8606Spatrick        raise NotImplementedError()
100*a9ac8606Spatrick
101*a9ac8606Spatrick    def shared_library_name(self, name):
102*a9ac8606Spatrick        raise NotImplementedError()
103*a9ac8606Spatrick
104*a9ac8606Spatrick    def shared_library_c_flags(self, release):
105*a9ac8606Spatrick        extra = ['-DNDEBUG', '-O3'] if release else []
106*a9ac8606Spatrick        return extra + ['-fPIC'] + self.c_flags
107*a9ac8606Spatrick
108*a9ac8606Spatrick    def shared_library_ld_flags(self, release, name):
109*a9ac8606Spatrick        raise NotImplementedError()
110*a9ac8606Spatrick
111*a9ac8606Spatrick
112*a9ac8606Spatrickclass DarwinToolset(Toolset):
113*a9ac8606Spatrick    def __init__(self, src_dir):
114*a9ac8606Spatrick        Toolset.__init__(self, src_dir)
115*a9ac8606Spatrick
116*a9ac8606Spatrick    def dl_libraries(self):
117*a9ac8606Spatrick        return []
118*a9ac8606Spatrick
119*a9ac8606Spatrick    def shared_library_name(self, name):
120*a9ac8606Spatrick        return 'lib' + name + '.dylib'
121*a9ac8606Spatrick
122*a9ac8606Spatrick    def shared_library_ld_flags(self, release, name):
123*a9ac8606Spatrick        extra = ['-dead_strip'] if release else []
124*a9ac8606Spatrick        return extra + ['-dynamiclib', '-install_name', '@rpath/' + name]
125*a9ac8606Spatrick
126*a9ac8606Spatrick
127*a9ac8606Spatrickclass UnixToolset(Toolset):
128*a9ac8606Spatrick    def __init__(self, src_dir):
129*a9ac8606Spatrick        Toolset.__init__(self, src_dir)
130*a9ac8606Spatrick
131*a9ac8606Spatrick    def dl_libraries(self):
132*a9ac8606Spatrick        return []
133*a9ac8606Spatrick
134*a9ac8606Spatrick    def shared_library_name(self, name):
135*a9ac8606Spatrick        return 'lib' + name + '.so'
136*a9ac8606Spatrick
137*a9ac8606Spatrick    def shared_library_ld_flags(self, release, name):
138*a9ac8606Spatrick        extra = [] if release else []
139*a9ac8606Spatrick        return extra + ['-shared', '-Wl,-soname,' + name]
140*a9ac8606Spatrick
141*a9ac8606Spatrick
142*a9ac8606Spatrickclass LinuxToolset(UnixToolset):
143*a9ac8606Spatrick    def __init__(self, src_dir):
144*a9ac8606Spatrick        UnixToolset.__init__(self, src_dir)
145*a9ac8606Spatrick
146*a9ac8606Spatrick    def dl_libraries(self):
147*a9ac8606Spatrick        return ['dl']
148*a9ac8606Spatrick
149*a9ac8606Spatrick
150*a9ac8606Spatrickdef make_toolset(src_dir):
151*a9ac8606Spatrick    platform = sys.platform
152*a9ac8606Spatrick    if platform in {'win32', 'cygwin'}:
153*a9ac8606Spatrick        raise RuntimeError('not implemented on this platform')
154*a9ac8606Spatrick    elif platform == 'darwin':
155*a9ac8606Spatrick        return DarwinToolset(src_dir)
156*a9ac8606Spatrick    elif platform in {'linux', 'linux2'}:
157*a9ac8606Spatrick        return LinuxToolset(src_dir)
158*a9ac8606Spatrick    else:
159*a9ac8606Spatrick        return UnixToolset(src_dir)
160*a9ac8606Spatrick
161*a9ac8606Spatrick
162*a9ac8606Spatrickclass Configure(object):
163*a9ac8606Spatrick    def __init__(self, toolset):
164*a9ac8606Spatrick        self.ctx = toolset
165*a9ac8606Spatrick        self.results = {'APPLE': sys.platform == 'darwin'}
166*a9ac8606Spatrick
167*a9ac8606Spatrick    def _try_to_compile_and_link(self, source):
168*a9ac8606Spatrick        try:
169*a9ac8606Spatrick            with TemporaryDirectory() as work_dir:
170*a9ac8606Spatrick                src_file = 'check.c'
171*a9ac8606Spatrick                with open(os.path.join(work_dir, src_file), 'w') as handle:
172*a9ac8606Spatrick                    handle.write(source)
173*a9ac8606Spatrick
174*a9ac8606Spatrick                execute([self.ctx.compiler, src_file] + self.ctx.c_flags,
175*a9ac8606Spatrick                        cwd=work_dir)
176*a9ac8606Spatrick                return True
177*a9ac8606Spatrick        except Exception:
178*a9ac8606Spatrick            return False
179*a9ac8606Spatrick
180*a9ac8606Spatrick    def check_function_exists(self, function, name):
181*a9ac8606Spatrick        template = "int FUNCTION(); int main() { return FUNCTION(); }"
182*a9ac8606Spatrick        source = template.replace("FUNCTION", function)
183*a9ac8606Spatrick
184*a9ac8606Spatrick        logging.debug('Checking function %s', function)
185*a9ac8606Spatrick        found = self._try_to_compile_and_link(source)
186*a9ac8606Spatrick        logging.debug('Checking function %s -- %s', function,
187*a9ac8606Spatrick                      'found' if found else 'not found')
188*a9ac8606Spatrick        self.results.update({name: found})
189*a9ac8606Spatrick
190*a9ac8606Spatrick    def check_symbol_exists(self, symbol, include, name):
191*a9ac8606Spatrick        template = """#include <INCLUDE>
192*a9ac8606Spatrick                      int main() { return ((int*)(&SYMBOL))[0]; }"""
193*a9ac8606Spatrick        source = template.replace('INCLUDE', include).replace("SYMBOL", symbol)
194*a9ac8606Spatrick
195*a9ac8606Spatrick        logging.debug('Checking symbol %s', symbol)
196*a9ac8606Spatrick        found = self._try_to_compile_and_link(source)
197*a9ac8606Spatrick        logging.debug('Checking symbol %s -- %s', symbol,
198*a9ac8606Spatrick                      'found' if found else 'not found')
199*a9ac8606Spatrick        self.results.update({name: found})
200*a9ac8606Spatrick
201*a9ac8606Spatrick    def write_by_template(self, template, output):
202*a9ac8606Spatrick        def transform(line, definitions):
203*a9ac8606Spatrick
204*a9ac8606Spatrick            pattern = re.compile(r'^#cmakedefine\s+(\S+)')
205*a9ac8606Spatrick            m = pattern.match(line)
206*a9ac8606Spatrick            if m:
207*a9ac8606Spatrick                key = m.group(1)
208*a9ac8606Spatrick                if key not in definitions or not definitions[key]:
209*a9ac8606Spatrick                    return '/* #undef {0} */{1}'.format(key, os.linesep)
210*a9ac8606Spatrick                else:
211*a9ac8606Spatrick                    return '#define {0}{1}'.format(key, os.linesep)
212*a9ac8606Spatrick            return line
213*a9ac8606Spatrick
214*a9ac8606Spatrick        with open(template, 'r') as src_handle:
215*a9ac8606Spatrick            logging.debug('Writing config to %s', output)
216*a9ac8606Spatrick            with open(output, 'w') as dst_handle:
217*a9ac8606Spatrick                for line in src_handle:
218*a9ac8606Spatrick                    dst_handle.write(transform(line, self.results))
219*a9ac8606Spatrick
220*a9ac8606Spatrick
221*a9ac8606Spatrickdef do_configure(toolset):
222*a9ac8606Spatrick    return Configure(toolset)
223*a9ac8606Spatrick
224*a9ac8606Spatrick
225*a9ac8606Spatrickclass SharedLibrary(object):
226*a9ac8606Spatrick    def __init__(self, name, toolset):
227*a9ac8606Spatrick        self.name = toolset.shared_library_name(name)
228*a9ac8606Spatrick        self.ctx = toolset
229*a9ac8606Spatrick        self.inc = []
230*a9ac8606Spatrick        self.src = []
231*a9ac8606Spatrick        self.lib = []
232*a9ac8606Spatrick
233*a9ac8606Spatrick    def add_include(self, directory):
234*a9ac8606Spatrick        self.inc.extend(['-I', directory])
235*a9ac8606Spatrick
236*a9ac8606Spatrick    def add_sources(self, source):
237*a9ac8606Spatrick        self.src.append(source)
238*a9ac8606Spatrick
239*a9ac8606Spatrick    def link_against(self, libraries):
240*a9ac8606Spatrick        self.lib.extend(['-l' + lib for lib in libraries])
241*a9ac8606Spatrick
242*a9ac8606Spatrick    def build_release(self, directory):
243*a9ac8606Spatrick        for src in self.src:
244*a9ac8606Spatrick            logging.debug('Compiling %s', src)
245*a9ac8606Spatrick            execute(
246*a9ac8606Spatrick                [self.ctx.compiler, '-c', os.path.join(self.ctx.src_dir, src),
247*a9ac8606Spatrick                 '-o', src + '.o'] + self.inc +
248*a9ac8606Spatrick                self.ctx.shared_library_c_flags(True),
249*a9ac8606Spatrick                cwd=directory)
250*a9ac8606Spatrick        logging.debug('Linking %s', self.name)
251*a9ac8606Spatrick        execute(
252*a9ac8606Spatrick            [self.ctx.compiler] + [src + '.o' for src in self.src] +
253*a9ac8606Spatrick            ['-o', self.name] + self.lib +
254*a9ac8606Spatrick            self.ctx.shared_library_ld_flags(True, self.name),
255*a9ac8606Spatrick            cwd=directory)
256*a9ac8606Spatrick
257*a9ac8606Spatrick
258*a9ac8606Spatrickdef create_shared_library(name, toolset):
259*a9ac8606Spatrick    return SharedLibrary(name, toolset)
260