| | |
| | 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): |
| | 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")] |
| |
|
| | |
| | def get_macros(string): |
| | def _macro(x): |
| | x = x[2:] |
| | if '=' in x: |
| | return tuple(x.split("=", 1)) |
| | else: |
| | return (x, 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")] |
| |
|
| | |
| | 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), |
| | } |
| |
|
| | |
| | ret = {} |
| | for libname in libs: |
| | lib_flags = kwargs(libname) |
| | merge_flags(ret, lib_flags) |
| | return ret |
| |
|