mirror of
https://github.com/llvm/llvm-project.git
synced 2025-05-03 06:36:07 +00:00

This is an ongoing series of commits that are reformatting our Python code. Reformatting is done with `black`. If you end up having problems merging this commit because you have made changes to a python file, the best way to handle that is to run git checkout --ours <yourfile> and then reformat it with black. If you run into any problems, post to discourse about it and we will try to help. RFC Thread below: https://discourse.llvm.org/t/rfc-document-and-standardize-python-code-style Reviewed By: MatzeB Differential Revision: https://reviews.llvm.org/D150761
273 lines
8.4 KiB
Python
273 lines
8.4 KiB
Python
# -*- coding: utf-8 -*-
|
|
# Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
|
# See https://llvm.org/LICENSE.txt for license information.
|
|
# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
|
""" This module compiles the intercept library. """
|
|
|
|
import sys
|
|
import os
|
|
import os.path
|
|
import re
|
|
import tempfile
|
|
import shutil
|
|
import contextlib
|
|
import logging
|
|
|
|
__all__ = ["build_libear"]
|
|
|
|
|
|
def build_libear(compiler, dst_dir):
|
|
"""Returns the full path to the 'libear' library."""
|
|
|
|
try:
|
|
src_dir = os.path.dirname(os.path.realpath(__file__))
|
|
toolset = make_toolset(src_dir)
|
|
toolset.set_compiler(compiler)
|
|
toolset.set_language_standard("c99")
|
|
toolset.add_definitions(["-D_GNU_SOURCE"])
|
|
|
|
configure = do_configure(toolset)
|
|
configure.check_function_exists("execve", "HAVE_EXECVE")
|
|
configure.check_function_exists("execv", "HAVE_EXECV")
|
|
configure.check_function_exists("execvpe", "HAVE_EXECVPE")
|
|
configure.check_function_exists("execvp", "HAVE_EXECVP")
|
|
configure.check_function_exists("execvP", "HAVE_EXECVP2")
|
|
configure.check_function_exists("exect", "HAVE_EXECT")
|
|
configure.check_function_exists("execl", "HAVE_EXECL")
|
|
configure.check_function_exists("execlp", "HAVE_EXECLP")
|
|
configure.check_function_exists("execle", "HAVE_EXECLE")
|
|
configure.check_function_exists("posix_spawn", "HAVE_POSIX_SPAWN")
|
|
configure.check_function_exists("posix_spawnp", "HAVE_POSIX_SPAWNP")
|
|
configure.check_symbol_exists(
|
|
"_NSGetEnviron", "crt_externs.h", "HAVE_NSGETENVIRON"
|
|
)
|
|
configure.write_by_template(
|
|
os.path.join(src_dir, "config.h.in"), os.path.join(dst_dir, "config.h")
|
|
)
|
|
|
|
target = create_shared_library("ear", toolset)
|
|
target.add_include(dst_dir)
|
|
target.add_sources("ear.c")
|
|
target.link_against(toolset.dl_libraries())
|
|
target.link_against(["pthread"])
|
|
target.build_release(dst_dir)
|
|
|
|
return os.path.join(dst_dir, target.name)
|
|
|
|
except Exception:
|
|
logging.info("Could not build interception library.", exc_info=True)
|
|
return None
|
|
|
|
|
|
def execute(cmd, *args, **kwargs):
|
|
"""Make subprocess execution silent."""
|
|
|
|
import subprocess
|
|
|
|
kwargs.update({"stdout": subprocess.PIPE, "stderr": subprocess.STDOUT})
|
|
return subprocess.check_call(cmd, *args, **kwargs)
|
|
|
|
|
|
@contextlib.contextmanager
|
|
def TemporaryDirectory(**kwargs):
|
|
name = tempfile.mkdtemp(**kwargs)
|
|
try:
|
|
yield name
|
|
finally:
|
|
shutil.rmtree(name)
|
|
|
|
|
|
class Toolset(object):
|
|
"""Abstract class to represent different toolset."""
|
|
|
|
def __init__(self, src_dir):
|
|
self.src_dir = src_dir
|
|
self.compiler = None
|
|
self.c_flags = []
|
|
|
|
def set_compiler(self, compiler):
|
|
"""part of public interface"""
|
|
self.compiler = compiler
|
|
|
|
def set_language_standard(self, standard):
|
|
"""part of public interface"""
|
|
self.c_flags.append("-std=" + standard)
|
|
|
|
def add_definitions(self, defines):
|
|
"""part of public interface"""
|
|
self.c_flags.extend(defines)
|
|
|
|
def dl_libraries(self):
|
|
raise NotImplementedError()
|
|
|
|
def shared_library_name(self, name):
|
|
raise NotImplementedError()
|
|
|
|
def shared_library_c_flags(self, release):
|
|
extra = ["-DNDEBUG", "-O3"] if release else []
|
|
return extra + ["-fPIC"] + self.c_flags
|
|
|
|
def shared_library_ld_flags(self, release, name):
|
|
raise NotImplementedError()
|
|
|
|
|
|
class DarwinToolset(Toolset):
|
|
def __init__(self, src_dir):
|
|
Toolset.__init__(self, src_dir)
|
|
|
|
def dl_libraries(self):
|
|
return []
|
|
|
|
def shared_library_name(self, name):
|
|
return "lib" + name + ".dylib"
|
|
|
|
def shared_library_ld_flags(self, release, name):
|
|
extra = ["-dead_strip"] if release else []
|
|
return extra + ["-dynamiclib", "-install_name", "@rpath/" + name]
|
|
|
|
|
|
class UnixToolset(Toolset):
|
|
def __init__(self, src_dir):
|
|
Toolset.__init__(self, src_dir)
|
|
|
|
def dl_libraries(self):
|
|
return []
|
|
|
|
def shared_library_name(self, name):
|
|
return "lib" + name + ".so"
|
|
|
|
def shared_library_ld_flags(self, release, name):
|
|
extra = [] if release else []
|
|
return extra + ["-shared", "-Wl,-soname," + name]
|
|
|
|
|
|
class LinuxToolset(UnixToolset):
|
|
def __init__(self, src_dir):
|
|
UnixToolset.__init__(self, src_dir)
|
|
|
|
def dl_libraries(self):
|
|
return ["dl"]
|
|
|
|
|
|
def make_toolset(src_dir):
|
|
platform = sys.platform
|
|
if platform in {"win32", "cygwin"}:
|
|
raise RuntimeError("not implemented on this platform")
|
|
elif platform == "darwin":
|
|
return DarwinToolset(src_dir)
|
|
elif platform in {"linux", "linux2"}:
|
|
return LinuxToolset(src_dir)
|
|
else:
|
|
return UnixToolset(src_dir)
|
|
|
|
|
|
class Configure(object):
|
|
def __init__(self, toolset):
|
|
self.ctx = toolset
|
|
self.results = {"APPLE": sys.platform == "darwin"}
|
|
|
|
def _try_to_compile_and_link(self, source):
|
|
try:
|
|
with TemporaryDirectory() as work_dir:
|
|
src_file = "check.c"
|
|
with open(os.path.join(work_dir, src_file), "w") as handle:
|
|
handle.write(source)
|
|
|
|
execute([self.ctx.compiler, src_file] + self.ctx.c_flags, cwd=work_dir)
|
|
return True
|
|
except Exception:
|
|
return False
|
|
|
|
def check_function_exists(self, function, name):
|
|
template = "int FUNCTION(); int main() { return FUNCTION(); }"
|
|
source = template.replace("FUNCTION", function)
|
|
|
|
logging.debug("Checking function %s", function)
|
|
found = self._try_to_compile_and_link(source)
|
|
logging.debug(
|
|
"Checking function %s -- %s", function, "found" if found else "not found"
|
|
)
|
|
self.results.update({name: found})
|
|
|
|
def check_symbol_exists(self, symbol, include, name):
|
|
template = """#include <INCLUDE>
|
|
int main() { return ((int*)(&SYMBOL))[0]; }"""
|
|
source = template.replace("INCLUDE", include).replace("SYMBOL", symbol)
|
|
|
|
logging.debug("Checking symbol %s", symbol)
|
|
found = self._try_to_compile_and_link(source)
|
|
logging.debug(
|
|
"Checking symbol %s -- %s", symbol, "found" if found else "not found"
|
|
)
|
|
self.results.update({name: found})
|
|
|
|
def write_by_template(self, template, output):
|
|
def transform(line, definitions):
|
|
|
|
pattern = re.compile(r"^#cmakedefine\s+(\S+)")
|
|
m = pattern.match(line)
|
|
if m:
|
|
key = m.group(1)
|
|
if key not in definitions or not definitions[key]:
|
|
return "/* #undef {0} */{1}".format(key, os.linesep)
|
|
else:
|
|
return "#define {0}{1}".format(key, os.linesep)
|
|
return line
|
|
|
|
with open(template, "r") as src_handle:
|
|
logging.debug("Writing config to %s", output)
|
|
with open(output, "w") as dst_handle:
|
|
for line in src_handle:
|
|
dst_handle.write(transform(line, self.results))
|
|
|
|
|
|
def do_configure(toolset):
|
|
return Configure(toolset)
|
|
|
|
|
|
class SharedLibrary(object):
|
|
def __init__(self, name, toolset):
|
|
self.name = toolset.shared_library_name(name)
|
|
self.ctx = toolset
|
|
self.inc = []
|
|
self.src = []
|
|
self.lib = []
|
|
|
|
def add_include(self, directory):
|
|
self.inc.extend(["-I", directory])
|
|
|
|
def add_sources(self, source):
|
|
self.src.append(source)
|
|
|
|
def link_against(self, libraries):
|
|
self.lib.extend(["-l" + lib for lib in libraries])
|
|
|
|
def build_release(self, directory):
|
|
for src in self.src:
|
|
logging.debug("Compiling %s", src)
|
|
execute(
|
|
[
|
|
self.ctx.compiler,
|
|
"-c",
|
|
os.path.join(self.ctx.src_dir, src),
|
|
"-o",
|
|
src + ".o",
|
|
]
|
|
+ self.inc
|
|
+ self.ctx.shared_library_c_flags(True),
|
|
cwd=directory,
|
|
)
|
|
logging.debug("Linking %s", self.name)
|
|
execute(
|
|
[self.ctx.compiler]
|
|
+ [src + ".o" for src in self.src]
|
|
+ ["-o", self.name]
|
|
+ self.lib
|
|
+ self.ctx.shared_library_ld_flags(True, self.name),
|
|
cwd=directory,
|
|
)
|
|
|
|
|
|
def create_shared_library(name, toolset):
|
|
return SharedLibrary(name, toolset)
|