Spaces:
Sleeping
Sleeping
| # pkg-config, https://www.freedesktop.org/wiki/Software/pkg-config/ integration for cffi | |
| import sys, os, subprocess | |
| 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"]} | |
| """ | |
| for key, value in cfg2.items(): | |
| if key not in cfg1: | |
| cfg1[key] = value | |
| else: | |
| 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, encoding=sys.getfilesystemencoding()): | |
| """Calls pkg-config and returns the output if found | |
| """ | |
| a = ["pkg-config", "--print-errors"] | |
| a.append(flag) | |
| a.append(libname) | |
| try: | |
| pc = subprocess.Popen(a, stdout=subprocess.PIPE, stderr=subprocess.PIPE) | |
| except EnvironmentError as e: | |
| raise PkgConfigError("cannot run pkg-config: %s" % (str(e).strip(),)) | |
| bout, berr = pc.communicate() | |
| 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_from_pkgconfig(libs): | |
| r"""Return compiler line flags for FFI.set_source based on pkg-config output | |
| Usage | |
| ... | |
| ffibuilder.set_source("_foo", pkgconfig = ["libfoo", "libbar >= 1.8.3"]) | |
| If pkg-config is installed on build machine, then arguments include_dirs, | |
| library_dirs, libraries, define_macros, extra_compile_args and | |
| extra_link_args are extended with an output of pkg-config for libfoo and | |
| libbar. | |
| Raises PkgConfigError in case the pkg-config call fails. | |
| """ | |
| def get_include_dirs(string): | |
| return [x[2:] for x in string.split() if x.startswith("-I")] | |
| def get_library_dirs(string): | |
| return [x[2:] for x in string.split() if x.startswith("-L")] | |
| def get_libraries(string): | |
| return [x[2:] for x in string.split() if x.startswith("-l")] | |
| # 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": 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: | |
| lib_flags = kwargs(libname) | |
| merge_flags(ret, lib_flags) | |
| return ret | |