_cffi.py 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127
  1. # coding: utf-8
  2. """The main CFFI wrapping of libzmq"""
  3. # Copyright (C) PyZMQ Developers
  4. # Distributed under the terms of the Modified BSD License.
  5. import json
  6. import os
  7. from os.path import dirname, join
  8. from cffi import FFI
  9. from zmq.utils.constant_names import all_names, no_prefix
  10. base_zmq_version = (3,2,2)
  11. def load_compiler_config():
  12. """load pyzmq compiler arguments"""
  13. import zmq
  14. zmq_dir = dirname(zmq.__file__)
  15. zmq_parent = dirname(zmq_dir)
  16. fname = join(zmq_dir, 'utils', 'compiler.json')
  17. if os.path.exists(fname):
  18. with open(fname) as f:
  19. cfg = json.load(f)
  20. else:
  21. cfg = {}
  22. cfg.setdefault("include_dirs", [])
  23. cfg.setdefault("library_dirs", [])
  24. cfg.setdefault("runtime_library_dirs", [])
  25. cfg.setdefault("libraries", ["zmq"])
  26. # cast to str, because cffi can't handle unicode paths (?!)
  27. cfg['libraries'] = [str(lib) for lib in cfg['libraries']]
  28. for key in ("include_dirs", "library_dirs", "runtime_library_dirs"):
  29. # interpret paths relative to parent of zmq (like source tree)
  30. abs_paths = []
  31. for p in cfg[key]:
  32. if p.startswith('zmq'):
  33. p = join(zmq_parent, p)
  34. abs_paths.append(str(p))
  35. cfg[key] = abs_paths
  36. return cfg
  37. def zmq_version_info():
  38. """Get libzmq version as tuple of ints"""
  39. major = ffi.new('int*')
  40. minor = ffi.new('int*')
  41. patch = ffi.new('int*')
  42. C.zmq_version(major, minor, patch)
  43. return (int(major[0]), int(minor[0]), int(patch[0]))
  44. cfg = load_compiler_config()
  45. ffi = FFI()
  46. def _make_defines(names):
  47. _names = []
  48. for name in names:
  49. define_line = "#define %s ..." % (name)
  50. _names.append(define_line)
  51. return "\n".join(_names)
  52. c_constant_names = ['PYZMQ_DRAFT_API']
  53. for name in all_names:
  54. if no_prefix(name):
  55. c_constant_names.append(name)
  56. else:
  57. c_constant_names.append("ZMQ_" + name)
  58. # load ffi definitions
  59. here = os.path.dirname(__file__)
  60. with open(os.path.join(here, '_cdefs.h')) as f:
  61. _cdefs = f.read()
  62. with open(os.path.join(here, '_verify.c')) as f:
  63. _verify = f.read()
  64. ffi.cdef(_cdefs)
  65. ffi.cdef(_make_defines(c_constant_names))
  66. try:
  67. C = ffi.verify(_verify,
  68. modulename='_cffi_ext',
  69. libraries=cfg['libraries'],
  70. include_dirs=cfg['include_dirs'],
  71. library_dirs=cfg['library_dirs'],
  72. runtime_library_dirs=cfg['runtime_library_dirs'],
  73. )
  74. _version_info = zmq_version_info()
  75. except Exception as e:
  76. raise ImportError("PyZMQ CFFI backend couldn't find zeromq: %s\n"
  77. "Please check that you have zeromq headers and libraries." % e)
  78. if _version_info < (3,2,2):
  79. raise ImportError("PyZMQ CFFI backend requires zeromq >= 3.2.2,"
  80. " but found %i.%i.%i" % _version_info
  81. )
  82. nsp = new_sizet_pointer = lambda length: ffi.new('size_t*', length)
  83. new_uint64_pointer = lambda: (ffi.new('uint64_t*'),
  84. nsp(ffi.sizeof('uint64_t')))
  85. new_int64_pointer = lambda: (ffi.new('int64_t*'),
  86. nsp(ffi.sizeof('int64_t')))
  87. new_int_pointer = lambda: (ffi.new('int*'),
  88. nsp(ffi.sizeof('int')))
  89. new_binary_data = lambda length: (ffi.new('char[%d]' % (length)),
  90. nsp(ffi.sizeof('char') * length))
  91. value_uint64_pointer = lambda val : (ffi.new('uint64_t*', val),
  92. ffi.sizeof('uint64_t'))
  93. value_int64_pointer = lambda val: (ffi.new('int64_t*', val),
  94. ffi.sizeof('int64_t'))
  95. value_int_pointer = lambda val: (ffi.new('int*', val),
  96. ffi.sizeof('int'))
  97. value_binary_data = lambda val, length: (ffi.new('char[%d]' % (length + 1), val),
  98. ffi.sizeof('char') * length)
  99. IPC_PATH_MAX_LEN = C.get_ipc_path_max_len()