Subversion Repositories QNX 8.QNX8 LLVM/Clang compiler suite

Rev

Details | Last modification | View Log | RSS feed

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