[TargetVersion] Only enable on RISC-V and AArch64 (#115991)
[llvm-project.git] / clang / tools / scan-build-py / lib / libear / __init__.py
blobb3a8e6cf125180d1c151c2c4d92cf7ff4ff2cdc1
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. """
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
16 __all__ = ["build_libear"]
19 def build_libear(compiler, dst_dir):
20 """Returns the full path to the 'libear' library."""
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"])
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(
42 "_NSGetEnviron", "crt_externs.h", "HAVE_NSGETENVIRON"
44 configure.write_by_template(
45 os.path.join(src_dir, "config.h.in"), 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)
57 except Exception:
58 logging.info("Could not build interception library.", exc_info=True)
59 return None
62 def execute(cmd, *args, **kwargs):
63 """Make subprocess execution silent."""
65 import subprocess
67 kwargs.update({"stdout": subprocess.PIPE, "stderr": subprocess.STDOUT})
68 return subprocess.check_call(cmd, *args, **kwargs)
71 @contextlib.contextmanager
72 def TemporaryDirectory(**kwargs):
73 name = tempfile.mkdtemp(**kwargs)
74 try:
75 yield name
76 finally:
77 shutil.rmtree(name)
80 class Toolset(object):
81 """Abstract class to represent different toolset."""
83 def __init__(self, src_dir):
84 self.src_dir = src_dir
85 self.compiler = None
86 self.c_flags = []
88 def set_compiler(self, compiler):
89 """part of public interface"""
90 self.compiler = compiler
92 def set_language_standard(self, standard):
93 """part of public interface"""
94 self.c_flags.append("-std=" + standard)
96 def add_definitions(self, defines):
97 """part of public interface"""
98 self.c_flags.extend(defines)
100 def dl_libraries(self):
101 raise NotImplementedError()
103 def shared_library_name(self, name):
104 raise NotImplementedError()
106 def shared_library_c_flags(self, release):
107 extra = ["-DNDEBUG", "-O3"] if release else []
108 return extra + ["-fPIC"] + self.c_flags
110 def shared_library_ld_flags(self, release, name):
111 raise NotImplementedError()
114 class DarwinToolset(Toolset):
115 def __init__(self, src_dir):
116 Toolset.__init__(self, src_dir)
118 def dl_libraries(self):
119 return []
121 def shared_library_name(self, name):
122 return "lib" + name + ".dylib"
124 def shared_library_ld_flags(self, release, name):
125 extra = ["-dead_strip"] if release else []
126 return extra + ["-dynamiclib", "-install_name", "@rpath/" + name]
129 class UnixToolset(Toolset):
130 def __init__(self, src_dir):
131 Toolset.__init__(self, src_dir)
133 def dl_libraries(self):
134 return []
136 def shared_library_name(self, name):
137 return "lib" + name + ".so"
139 def shared_library_ld_flags(self, release, name):
140 extra = [] if release else []
141 return extra + ["-shared", "-Wl,-soname," + name]
144 class LinuxToolset(UnixToolset):
145 def __init__(self, src_dir):
146 UnixToolset.__init__(self, src_dir)
148 def dl_libraries(self):
149 return ["dl"]
152 def make_toolset(src_dir):
153 platform = sys.platform
154 if platform in {"win32", "cygwin"}:
155 raise RuntimeError("not implemented on this platform")
156 elif platform == "darwin":
157 return DarwinToolset(src_dir)
158 elif platform in {"linux", "linux2"}:
159 return LinuxToolset(src_dir)
160 else:
161 return UnixToolset(src_dir)
164 class Configure(object):
165 def __init__(self, toolset):
166 self.ctx = toolset
167 self.results = {"APPLE": sys.platform == "darwin"}
169 def _try_to_compile_and_link(self, source):
170 try:
171 with TemporaryDirectory() as work_dir:
172 src_file = "check.c"
173 with open(os.path.join(work_dir, src_file), "w") as handle:
174 handle.write(source)
176 execute([self.ctx.compiler, src_file] + self.ctx.c_flags, cwd=work_dir)
177 return True
178 except Exception:
179 return False
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(
188 "Checking function %s -- %s", function, "found" if found else "not found"
190 self.results.update({name: found})
192 def check_symbol_exists(self, symbol, include, name):
193 template = """#include <INCLUDE>
194 int main() { return ((int*)(&SYMBOL))[0]; }"""
195 source = template.replace("INCLUDE", include).replace("SYMBOL", symbol)
197 logging.debug("Checking symbol %s", symbol)
198 found = self._try_to_compile_and_link(source)
199 logging.debug(
200 "Checking symbol %s -- %s", symbol, "found" if found else "not found"
202 self.results.update({name: found})
204 def write_by_template(self, template, output):
205 def transform(line, definitions):
207 pattern = re.compile(r"^#cmakedefine\s+(\S+)")
208 m = pattern.match(line)
209 if m:
210 key = m.group(1)
211 if key not in definitions or not definitions[key]:
212 return "/* #undef {0} */{1}".format(key, os.linesep)
213 else:
214 return "#define {0}{1}".format(key, os.linesep)
215 return line
217 with open(template, "r") as src_handle:
218 logging.debug("Writing config to %s", output)
219 with open(output, "w") as dst_handle:
220 for line in src_handle:
221 dst_handle.write(transform(line, self.results))
224 def do_configure(toolset):
225 return Configure(toolset)
228 class SharedLibrary(object):
229 def __init__(self, name, toolset):
230 self.name = toolset.shared_library_name(name)
231 self.ctx = toolset
232 self.inc = []
233 self.src = []
234 self.lib = []
236 def add_include(self, directory):
237 self.inc.extend(["-I", directory])
239 def add_sources(self, source):
240 self.src.append(source)
242 def link_against(self, libraries):
243 self.lib.extend(["-l" + lib for lib in libraries])
245 def build_release(self, directory):
246 for src in self.src:
247 logging.debug("Compiling %s", src)
248 execute(
250 self.ctx.compiler,
251 "-c",
252 os.path.join(self.ctx.src_dir, src),
253 "-o",
254 src + ".o",
256 + self.inc
257 + self.ctx.shared_library_c_flags(True),
258 cwd=directory,
260 logging.debug("Linking %s", self.name)
261 execute(
262 [self.ctx.compiler]
263 + [src + ".o" for src in self.src]
264 + ["-o", self.name]
265 + self.lib
266 + self.ctx.shared_library_ld_flags(True, self.name),
267 cwd=directory,
271 def create_shared_library(name, toolset):
272 return SharedLibrary(name, toolset)