diff options
author | Armin Rigo <arigo@tunes.org> | 2019-01-31 12:14:50 +0100 |
---|---|---|
committer | Armin Rigo <arigo@tunes.org> | 2019-01-31 12:14:50 +0100 |
commit | acfd2aef58cd40f4ee06bcc551b8549bb16974e8 (patch) | |
tree | 6114742a3b3647f3a6d1a938c2a067038c38fbcc /cffi | |
parent | 93925db3b1736fcdc8026a96346ba065c070daa4 (diff) | |
download | cffi-acfd2aef58cd40f4ee06bcc551b8549bb16974e8.tar.gz |
Tweaks to the pkgconfig support
Diffstat (limited to 'cffi')
-rw-r--r-- | cffi/__init__.py | 1 | ||||
-rw-r--r-- | cffi/api.py | 8 | ||||
-rw-r--r-- | cffi/error.py | 13 | ||||
-rw-r--r-- | cffi/pkgconfig.py | 126 |
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 |