codekingpro/portable-devtools
114k
1# pkg-config, https://www.freedesktop.org/wiki/Software/pkg-config/ integration for cffi2import sys, os, subprocess3 4from .error import PkgConfigError5 6 7def merge_flags(cfg1, cfg2):8 """Merge values from cffi config flags cfg2 to cf19 10 Example:11 merge_flags({"libraries": ["one"]}, {"libraries": ["two"]})12 {"libraries": ["one", "two"]}13 """14 for key, value in cfg2.items():15 if key not in cfg1:16 cfg1[key] = value17 else:18 if not isinstance(cfg1[key], list):19 raise TypeError("cfg1[%r] should be a list of strings" % (key,))20 if not isinstance(value, list):21 raise TypeError("cfg2[%r] should be a list of strings" % (key,))22 cfg1[key].extend(value)23 return cfg124 25 26def call(libname, flag, encoding=sys.getfilesystemencoding()):27 """Calls pkg-config and returns the output if found28 """29 a = ["pkg-config", "--print-errors"]30 a.append(flag)31 a.append(libname)32 try:33 pc = subprocess.Popen(a, stdout=subprocess.PIPE, stderr=subprocess.PIPE)34 except EnvironmentError as e:35 raise PkgConfigError("cannot run pkg-config: %s" % (str(e).strip(),))36 37 bout, berr = pc.communicate()38 if pc.returncode != 0:39 try:40 berr = berr.decode(encoding)41 except Exception:42 pass43 raise PkgConfigError(berr.strip())44 45 if sys.version_info >= (3,) and not isinstance(bout, str): # Python 3.x46 try:47 bout = bout.decode(encoding)48 except UnicodeDecodeError:49 raise PkgConfigError("pkg-config %s %s returned bytes that cannot "50 "be decoded with encoding %r:\n%r" %51 (flag, libname, encoding, bout))52 53 if os.altsep != '\\' and '\\' in bout:54 raise PkgConfigError("pkg-config %s %s returned an unsupported "55 "backslash-escaped output:\n%r" %56 (flag, libname, bout))57 return bout58 59 60def flags_from_pkgconfig(libs):61 r"""Return compiler line flags for FFI.set_source based on pkg-config output62 63 Usage64 ...65 ffibuilder.set_source("_foo", pkgconfig = ["libfoo", "libbar >= 1.8.3"])66 67 If pkg-config is installed on build machine, then arguments include_dirs,68 library_dirs, libraries, define_macros, extra_compile_args and69 extra_link_args are extended with an output of pkg-config for libfoo and70 libbar.71 72 Raises PkgConfigError in case the pkg-config call fails.73 """74 75 def get_include_dirs(string):76 return [x[2:] for x in string.split() if x.startswith("-I")]77 78 def get_library_dirs(string):79 return [x[2:] for x in string.split() if x.startswith("-L")]80 81 def get_libraries(string):82 return [x[2:] for x in string.split() if x.startswith("-l")]83 84 # convert -Dfoo=bar to list of tuples [("foo", "bar")] expected by distutils85 def get_macros(string):86 def _macro(x):87 x = x[2:] # drop "-D"88 if '=' in x:89 return tuple(x.split("=", 1)) # "-Dfoo=bar" => ("foo", "bar")90 else:91 return (x, None) # "-Dfoo" => ("foo", None)92 return [_macro(x) for x in string.split() if x.startswith("-D")]93 94 def get_other_cflags(string):95 return [x for x in string.split() if not x.startswith("-I") and96 not x.startswith("-D")]97 98 def get_other_libs(string):99 return [x for x in string.split() if not x.startswith("-L") and100 not x.startswith("-l")]101 102 # return kwargs for given libname103 def kwargs(libname):104 fse = sys.getfilesystemencoding()105 all_cflags = call(libname, "--cflags")106 all_libs = call(libname, "--libs")107 return {108 "include_dirs": get_include_dirs(all_cflags),109 "library_dirs": get_library_dirs(all_libs),110 "libraries": get_libraries(all_libs),111 "define_macros": get_macros(all_cflags),112 "extra_compile_args": get_other_cflags(all_cflags),113 "extra_link_args": get_other_libs(all_libs),114 }115 116 # merge all arguments together117 ret = {}118 for libname in libs:119 lib_flags = kwargs(libname)120 merge_flags(ret, lib_flags)121 return ret122 