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