Skip to content

Commit

Permalink
Fix
Browse files Browse the repository at this point in the history
  • Loading branch information
zacikpa committed Aug 12, 2024
1 parent ef66d41 commit 06fe454
Show file tree
Hide file tree
Showing 3 changed files with 130 additions and 65 deletions.
71 changes: 12 additions & 59 deletions python/src/libcpuid/_ffi_build.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,71 +2,24 @@
Module for compiling the C FFI.
"""

import subprocess
import os
import sys
from cffi import FFI

sys.path.append(os.path.dirname(os.path.abspath(__file__)))

class FFIBuildException(Exception):
"""Generic exception for errors occuring during the CFFI build."""


def find_header():
"""
Obtains libcpuid header file location via pkg-config.
"""
try:
cflags = (
subprocess.check_output(["pkg-config", "libcpuid", "--cflags-only-I"])
.decode()
.strip()
.split()
)
except subprocess.CalledProcessError as e:
if e.returncode == 127:
raise FFIBuildException(
"The pkg-config command is necessary to build python-libcpuid."
) from e
if e.returncode == 1:
raise FFIBuildException(
"The libcpuid C library (devel) was not found."
) from e
raise FFIBuildException("Error looking for the libcpuid library") from e

# Find an existing libcpuid header file
header_path = None # pylint: disable=invalid-name
for cflag in cflags:
header_candidate = os.path.join(cflag[2:], "libcpuid.h")
if os.path.isfile(header_candidate):
header_path = header_candidate
break
if header_path is None:
raise FFIBuildException("Could not find header file of the libcpuid library.")
return header_path


def preprocess_header(header_path):
"""
Preprocesses the header file (python-cffi only accepts preprocessed C definitions).
"""
try:
return subprocess.check_output(
["gcc", "-U __GNUC__", "-E", header_path]
).decode()
except subprocess.CalledProcessError as e:
if e.returncode == 127:
raise FFIBuildException(
"The gcc compiler is necessary to build python-libcpuid."
) from e
raise FFIBuildException(
f"Error preprocessing the libcpuid header file: {e.stderr}"
) from e


header = find_header()
from _ffi_build_utils import ( # pylint: disable=import-error, wrong-import-position
get_include_flags,
find_header_file,
preprocess_header,
eval_sizeofs,
)

include_flags = get_include_flags()
preprocessed_header = preprocess_header(find_header_file(include_flags))
no_sizeof_header = eval_sizeofs(preprocessed_header, include_flags)
ffibuilder = FFI()
ffibuilder.cdef(preprocess_header(header))
ffibuilder.cdef(no_sizeof_header)
ffibuilder.set_source_pkgconfig(
"libcpuid._libcpuid_cffi", ["libcpuid"], "#include <libcpuid.h>"
)
17 changes: 11 additions & 6 deletions python/src/libcpuid/_ffi_build_rtd.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,25 +2,30 @@
Script for compiling the C FFI for the live documentation.
"""

import subprocess
import sys
import os
from cffi import FFI

sys.path.append(os.path.dirname(os.path.abspath(__file__)))

from _ffi_build_utils import ( # pylint: disable=import-error, wrong-import-position
preprocess_header,
eval_sizeofs,
)

if __name__ == "__main__":
header = sys.argv[1]
header_path = sys.argv[1]
install_dir = sys.argv[2]
library_dir = os.path.join(os.getcwd(), install_dir, "lib")
include_dir = os.path.join(install_dir, "include", "libcpuid")
ffibuilder = FFI()
ffibuilder.cdef(
subprocess.check_output(["gcc", "-U __GNUC__", "-E", header]).decode()
)
ffibuilder.cdef(eval_sizeofs(preprocess_header(header_path), [f"-I{include_dir}"]))
ffibuilder.set_source(
"python.src.libcpuid._libcpuid_cffi",
"#include <libcpuid.h>",
libraries=["cpuid"],
library_dirs=[library_dir],
include_dirs=[os.path.join(install_dir, "include", "libcpuid")],
include_dirs=[include_dir],
extra_link_args=[f"-Wl,-rpath={library_dir}"],
)
ffibuilder.compile(verbose=True)
107 changes: 107 additions & 0 deletions python/src/libcpuid/_ffi_build_utils.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
"""
Utility functions for building the FFI.
"""

import subprocess
import os
import re
import tempfile


class FFIBuildException(Exception):
"""Generic exception for errors occuring during the CFFI build."""


def get_include_flags():
"""
Obtains libcpuid include flags via pkg-config.
"""
try:
cflags = (
subprocess.check_output(["pkg-config", "libcpuid", "--cflags-only-I"])
.decode()
.strip()
.split()
)
return cflags
except subprocess.CalledProcessError as e:
if e.returncode == 127:
raise FFIBuildException(
"The pkg-config command is necessary to build python-libcpuid."
) from e
if e.returncode == 1:
raise FFIBuildException(
"The libcpuid C library (devel) was not found."
) from e
raise FFIBuildException("Error looking for the libcpuid library") from e


def find_header_file(include_flags):
"""
Obtains main libcpuid header file location from include flags.
"""
header_path = None # pylint: disable=invalid-name
for cflag in include_flags:
header_candidate = os.path.join(cflag[2:], "libcpuid.h")
if os.path.isfile(header_candidate):
header_path = header_candidate
break
if header_path is None:
raise FFIBuildException("Could not find header file of the libcpuid library.")
return header_path


def preprocess_header(header_path):
"""
Preprocesses the header file (python-cffi only accepts preprocessed C definitions)
at the given path and returns it as a string.
"""
try:
return subprocess.check_output(
["gcc", "-U __GNUC__", "-E", header_path]
).decode()
except subprocess.CalledProcessError as e:
if e.returncode == 127:
raise FFIBuildException(
"The gcc compiler is necessary to build python-libcpuid."
) from e
raise FFIBuildException(
f"Error preprocessing the libcpuid header file: {e.stderr}"
) from e


def _get_sizeof_eval_source(sizeof):
return f"""
#include <libcpuid.h>
#include <stdio.h>
int main() {{
printf("%d", {sizeof});
return 0;
}}
"""


def eval_sizeofs(header, cflags):
"""
Evaluates each sizeof found in the given C header and replaces all
occurences of the sizeof with its computed value.
"""
sizeofs = set(re.findall(r"sizeof\([^\)]*\)", header))
tmp_dir = tempfile.mkdtemp()
c_program_path = os.path.join(tmp_dir, "sizeof.c")
executable_path = os.path.join(tmp_dir, "sizeof")

for sizeof in sizeofs:
with open(c_program_path, "w", encoding="UTF-8") as c_program_file:
c_program_file.write(_get_sizeof_eval_source(sizeof))
subprocess.check_call(
["gcc", c_program_path, *cflags, "-o", executable_path]
)
size = subprocess.check_output([executable_path]).decode()
header = header.replace(sizeof, size)

os.remove(c_program_path)
os.remove(executable_path)
os.rmdir(tmp_dir)
return header

0 comments on commit 06fe454

Please sign in to comment.