summaryrefslogtreecommitdiff
path: root/cffi
diff options
context:
space:
mode:
authorArmin Rigo <arigo@tunes.org>2019-01-31 12:14:50 +0100
committerArmin Rigo <arigo@tunes.org>2019-01-31 12:14:50 +0100
commitacfd2aef58cd40f4ee06bcc551b8549bb16974e8 (patch)
tree6114742a3b3647f3a6d1a938c2a067038c38fbcc /cffi
parent93925db3b1736fcdc8026a96346ba065c070daa4 (diff)
downloadcffi-acfd2aef58cd40f4ee06bcc551b8549bb16974e8.tar.gz
Tweaks to the pkgconfig support
Diffstat (limited to 'cffi')
-rw-r--r--cffi/__init__.py1
-rw-r--r--cffi/api.py8
-rw-r--r--cffi/error.py13
-rw-r--r--cffi/pkgconfig.py126
4 files changed, 84 insertions, 64 deletions
diff --git a/cffi/__init__.py b/cffi/__init__.py
index ec479b7..157a215 100644
--- a/cffi/__init__.py
+++ b/cffi/__init__.py
@@ -3,6 +3,7 @@ __all__ = ['FFI', 'VerificationError', 'VerificationMissing', 'CDefError',
from .api import FFI
from .error import CDefError, FFIError, VerificationError, VerificationMissing
+from .error import PkgConfigError
__version__ = "1.12.0"
__version_info__ = (1, 12, 0)
diff --git a/cffi/api.py b/cffi/api.py
index e9d442c..48b6e8a 100644
--- a/cffi/api.py
+++ b/cffi/api.py
@@ -2,7 +2,6 @@ import sys, types
from .lock import allocate_lock
from .error import CDefError
from . import model
-from . import pkgconfig
try:
callable
@@ -642,8 +641,11 @@ class FFI(object):
raise ValueError("'module_name' must not contain '/': use a dotted "
"name to make a 'package.module' location")
if "pkgconfig" in kwds:
- pkgconfig.merge_flags(kwds, pkgconfig.flags(kwds["pkgconfig"]))
- del kwds["pkgconfig"]
+ from . import pkgconfig
+ libs = kwds.pop("pkgconfig")
+ if not isinstance(libs, (list, tuple)):
+ libs = [libs]
+ pkgconfig.merge_flags(kwds, pkgconfig.flags_from_pkgconfig(libs))
self._assigned_source = (str(module_name), source,
source_extension, kwds)
diff --git a/cffi/error.py b/cffi/error.py
index e360089..0a27247 100644
--- a/cffi/error.py
+++ b/cffi/error.py
@@ -1,8 +1,9 @@
class FFIError(Exception):
- pass
+ __module__ = 'cffi'
class CDefError(Exception):
+ __module__ = 'cffi'
def __str__(self):
try:
current_decl = self.args[1]
@@ -16,15 +17,15 @@ class CDefError(Exception):
class VerificationError(Exception):
""" An error raised when verification fails
"""
+ __module__ = 'cffi'
class VerificationMissing(Exception):
""" An error raised when incomplete structures are passed into
cdef, but no verification has been done
"""
+ __module__ = 'cffi'
class PkgConfigError(Exception):
- """ An error raised for all pkg-config related errors
- except version mismatch"""
-
-class PkgConfigModuleVersionNotFound(Exception):
- """ An error raised when requested version was not found"""
+ """ An error raised for missing modules in pkg-config
+ """
+ __module__ = 'cffi'
diff --git a/cffi/pkgconfig.py b/cffi/pkgconfig.py
index ccf7908..5c93f15 100644
--- a/cffi/pkgconfig.py
+++ b/cffi/pkgconfig.py
@@ -1,51 +1,63 @@
# pkg-config, https://www.freedesktop.org/wiki/Software/pkg-config/ integration for cffi
-import subprocess
-import sys
-import re
+import sys, os, subprocess
-from .error import PkgConfigModuleVersionNotFound
from .error import PkgConfigError
+
def merge_flags(cfg1, cfg2):
"""Merge values from cffi config flags cfg2 to cf1
Example:
- merge_flags({"libraries": ["one"]}, {"libraries": "two"})
- {"libraries}" : ["one", "two"]}
+ merge_flags({"libraries": ["one"]}, {"libraries": ["two"]})
+ {"libraries": ["one", "two"]}
"""
for key, value in cfg2.items():
- if not key in cfg1:
- cfg1 [key] = value
+ if key not in cfg1:
+ cfg1[key] = value
else:
- cfg1 [key].extend(value)
+ if not isinstance(cfg1[key], list):
+ raise TypeError("cfg1[%r] should be a list of strings" % (key,))
+ if not isinstance(value, list):
+ raise TypeError("cfg2[%r] should be a list of strings" % (key,))
+ cfg1[key].extend(value)
return cfg1
-def call(libname, flag):
- """Calls pkg-config and returing the output if found
+def call(libname, flag, encoding=sys.getfilesystemencoding()):
+ """Calls pkg-config and returns the output if found
"""
a = ["pkg-config", "--print-errors"]
a.append(flag)
a.append(libname)
- pc = None
try:
pc = subprocess.Popen(a, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
- except FileNotFoundError:
- pass
- if pc is None:
- raise PkgConfigError("pkg-config was not found on this system")
-
+ except EnvironmentError as e:
+ raise PkgConfigError("cannot run pkg-config: %s" % (str(e).strip(),))
+
bout, berr = pc.communicate()
- if berr is not None:
- err = berr.decode(sys.getfilesystemencoding())
- if re.search("Requested '.*' but version of ", err, re.MULTILINE) is not None:
- raise PkgConfigModuleVersionNotFound(err)
- else:
- PkgConfigError(err)
+ if pc.returncode != 0:
+ try:
+ berr = berr.decode(encoding)
+ except Exception:
+ pass
+ raise PkgConfigError(berr.strip())
+
+ if sys.version_info >= (3,) and not isinstance(bout, str): # Python 3.x
+ try:
+ bout = bout.decode(encoding)
+ except UnicodeDecodeError:
+ raise PkgConfigError("pkg-config %s %s returned bytes that cannot "
+ "be decoded with encoding %r:\n%r" %
+ (flag, libname, encoding, bout))
+
+ if os.altsep != '\\' and '\\' in bout:
+ raise PkgConfigError("pkg-config %s %s returned an unsupported "
+ "backslash-escaped output:\n%r" %
+ (flag, libname, bout))
return bout
-def flags(libs):
+def flags_from_pkgconfig(libs):
r"""Return compiler line flags for FFI.set_source based on pkg-config output
Usage
@@ -57,49 +69,53 @@ def flags(libs):
extra_link_args are extended with an output of pkg-config for libfoo and
libbar.
- Raises
- * PkgConfigModuleVersionNotFound if requested version does not match
- * PkgConfigError for all other errors
+ Raises PkgConfigError in case the pkg-config call fails.
"""
- subprocess.check_output(["pkg-config", "--version"])
+ def get_include_dirs(string):
+ return [x[2:] for x in string.split() if x.startswith("-I")]
- # make API great again!
- if isinstance(libs, (str, bytes)):
- libs = (libs, )
-
- # drop starting -I -L -l from cflags
- def dropILl(string):
- def _dropILl(string):
- if string.startswith("-I") or string.startswith("-L") or string.startswith("-l"):
- return string [2:]
- return [_dropILl(x) for x in string.split()]
+ def get_library_dirs(string):
+ return [x[2:] for x in string.split() if x.startswith("-L")]
- # convert -Dfoo=bar to list of tuples [("foo", "bar")] expected by cffi
- def macros(string):
- def _macros(string):
- return tuple(string [2:].split("=", 2))
- return [_macros(x) for x in string.split() if x.startswith("-D")]
+ def get_libraries(string):
+ return [x[2:] for x in string.split() if x.startswith("-l")]
- def drop_macros(string):
- return [x for x in string.split() if not x.startswith("-D")]
+ # convert -Dfoo=bar to list of tuples [("foo", "bar")] expected by distutils
+ def get_macros(string):
+ def _macro(x):
+ x = x[2:] # drop "-D"
+ if '=' in x:
+ return tuple(x.split("=", 1)) # "-Dfoo=bar" => ("foo", "bar")
+ else:
+ return (x, None) # "-Dfoo" => ("foo", None)
+ return [_macro(x) for x in string.split() if x.startswith("-D")]
+
+ def get_other_cflags(string):
+ return [x for x in string.split() if not x.startswith("-I") and
+ not x.startswith("-D")]
+
+ def get_other_libs(string):
+ return [x for x in string.split() if not x.startswith("-L") and
+ not x.startswith("-l")]
# return kwargs for given libname
def kwargs(libname):
fse = sys.getfilesystemencoding()
+ all_cflags = call(libname, "--cflags")
+ all_libs = call(libname, "--libs")
return {
- "include_dirs" : dropILl(call(libname, "--cflags-only-I").decode(fse)),
- "library_dirs" : dropILl(call(libname, "--libs-only-L").decode(fse)),
- "libraries" : dropILl(call(libname, "--libs-only-l").decode(fse)),
- "define_macros" : macros(call(libname, "--cflags-only-other").decode('ascii')),
- "extra_compile_args" : drop_macros(call(libname, "--cflags-only-other").decode('ascii')),
- "extra_link_args" : call(libname, "--libs-only-other").decode('ascii').split()
- }
+ "include_dirs": get_include_dirs(all_cflags),
+ "library_dirs": get_library_dirs(all_libs),
+ "libraries": get_libraries(all_libs),
+ "define_macros": get_macros(all_cflags),
+ "extra_compile_args": get_other_cflags(all_cflags),
+ "extra_link_args": get_other_libs(all_libs),
+ }
# merge all arguments together
ret = {}
for libname in libs:
- foo = kwargs(libname)
- merge_flags(ret, foo)
-
+ lib_flags = kwargs(libname)
+ merge_flags(ret, lib_flags)
return ret