123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121 |
- # 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
|