setupinfo.py 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409
  1. import sys, os, os.path
  2. from distutils.core import Extension
  3. from distutils.errors import DistutilsOptionError
  4. from versioninfo import get_base_dir, split_version
  5. try:
  6. from Cython.Distutils import build_ext as build_pyx
  7. import Cython.Compiler.Version
  8. CYTHON_INSTALLED = True
  9. except ImportError:
  10. CYTHON_INSTALLED = False
  11. EXT_MODULES = ["lxml.etree", "lxml.objectify"]
  12. PACKAGE_PATH = "src%slxml%s" % (os.path.sep, os.path.sep)
  13. INCLUDE_PACKAGE_PATH = PACKAGE_PATH + 'includes'
  14. if sys.version_info[0] >= 3:
  15. _system_encoding = sys.getdefaultencoding()
  16. if _system_encoding is None:
  17. _system_encoding = "iso-8859-1" # :-)
  18. def decode_input(data):
  19. if isinstance(data, str):
  20. return data
  21. return data.decode(_system_encoding)
  22. else:
  23. def decode_input(data):
  24. return data
  25. def env_var(name):
  26. value = os.getenv(name)
  27. if value:
  28. value = decode_input(value)
  29. if sys.platform == 'win32' and ';' in value:
  30. return value.split(';')
  31. else:
  32. return value.split()
  33. else:
  34. return []
  35. def ext_modules(static_include_dirs, static_library_dirs,
  36. static_cflags, static_binaries):
  37. global XML2_CONFIG, XSLT_CONFIG
  38. if OPTION_BUILD_LIBXML2XSLT:
  39. from buildlibxml import build_libxml2xslt, get_prebuilt_libxml2xslt
  40. if sys.platform.startswith('win'):
  41. get_prebuilt_libxml2xslt(
  42. OPTION_DOWNLOAD_DIR, static_include_dirs, static_library_dirs)
  43. else:
  44. XML2_CONFIG, XSLT_CONFIG = build_libxml2xslt(
  45. OPTION_DOWNLOAD_DIR, 'build/tmp',
  46. static_include_dirs, static_library_dirs,
  47. static_cflags, static_binaries,
  48. libiconv_version=OPTION_LIBICONV_VERSION,
  49. libxml2_version=OPTION_LIBXML2_VERSION,
  50. libxslt_version=OPTION_LIBXSLT_VERSION,
  51. multicore=OPTION_MULTICORE)
  52. if OPTION_WITHOUT_OBJECTIFY:
  53. modules = [ entry for entry in EXT_MODULES
  54. if 'objectify' not in entry ]
  55. else:
  56. modules = EXT_MODULES
  57. c_files_exist = [ os.path.exists('%s%s.c' % (PACKAGE_PATH, module)) for module in modules ]
  58. if CYTHON_INSTALLED and (OPTION_WITH_CYTHON or False in c_files_exist):
  59. source_extension = ".pyx"
  60. print("Building with Cython %s." % Cython.Compiler.Version.version)
  61. # generate module cleanup code
  62. from Cython.Compiler import Options
  63. Options.generate_cleanup_code = 3
  64. Options.clear_to_none = False
  65. elif not OPTION_WITHOUT_CYTHON and False in c_files_exist:
  66. for exists, module in zip(c_files_exist, modules):
  67. if not exists:
  68. raise RuntimeError(
  69. "ERROR: Trying to build without Cython, but pre-generated '%s%s.c' "
  70. "is not available (pass --without-cython to ignore this error)." % (
  71. PACKAGE_PATH, module))
  72. else:
  73. if False in c_files_exist:
  74. for exists, module in zip(c_files_exist, modules):
  75. if not exists:
  76. print("WARNING: Trying to build without Cython, but pre-generated "
  77. "'%s%s.c' is not available." % (PACKAGE_PATH, module))
  78. source_extension = ".c"
  79. print("Building without Cython.")
  80. lib_versions = get_library_versions()
  81. versions_ok = True
  82. if lib_versions[0]:
  83. print("Using build configuration of libxml2 %s and libxslt %s" %
  84. lib_versions)
  85. versions_ok = check_min_version(lib_versions[0], (2, 7, 0), 'libxml2')
  86. else:
  87. print("Using build configuration of libxslt %s" %
  88. lib_versions[1])
  89. versions_ok |= check_min_version(lib_versions[1], (1, 1, 23), 'libxslt')
  90. if not versions_ok:
  91. raise RuntimeError("Dependency missing")
  92. _include_dirs = include_dirs(static_include_dirs)
  93. _library_dirs = library_dirs(static_library_dirs)
  94. _cflags = cflags(static_cflags)
  95. _define_macros = define_macros()
  96. _libraries = libraries()
  97. _include_dirs.append(os.path.join(get_base_dir(), INCLUDE_PACKAGE_PATH))
  98. if _library_dirs:
  99. message = "Building against libxml2/libxslt in "
  100. if len(_library_dirs) > 1:
  101. print(message + "one of the following directories:")
  102. for dir in _library_dirs:
  103. print(" " + dir)
  104. else:
  105. print(message + "the following directory: " +
  106. _library_dirs[0])
  107. if OPTION_AUTO_RPATH:
  108. runtime_library_dirs = _library_dirs
  109. else:
  110. runtime_library_dirs = []
  111. if CYTHON_INSTALLED and OPTION_SHOW_WARNINGS:
  112. from Cython.Compiler import Errors
  113. Errors.LEVEL = 0
  114. result = []
  115. for module in modules:
  116. main_module_source = PACKAGE_PATH + module + source_extension
  117. result.append(
  118. Extension(
  119. module,
  120. sources = [main_module_source],
  121. depends = find_dependencies(module),
  122. extra_compile_args = _cflags,
  123. extra_objects = static_binaries,
  124. define_macros = _define_macros,
  125. include_dirs = _include_dirs,
  126. library_dirs = _library_dirs,
  127. runtime_library_dirs = runtime_library_dirs,
  128. libraries = _libraries,
  129. ))
  130. if CYTHON_INSTALLED and OPTION_WITH_CYTHON_GDB:
  131. for ext in result:
  132. ext.cython_gdb = True
  133. if CYTHON_INSTALLED and source_extension == '.pyx':
  134. # build .c files right now and convert Extension() objects
  135. from Cython.Build import cythonize
  136. result = cythonize(result)
  137. return result
  138. def find_dependencies(module):
  139. if not CYTHON_INSTALLED:
  140. return []
  141. base_dir = get_base_dir()
  142. package_dir = os.path.join(base_dir, PACKAGE_PATH)
  143. includes_dir = os.path.join(base_dir, INCLUDE_PACKAGE_PATH)
  144. pxd_files = [ os.path.join(includes_dir, filename)
  145. for filename in os.listdir(includes_dir)
  146. if filename.endswith('.pxd') ]
  147. if 'etree' in module:
  148. pxi_files = [ os.path.join(PACKAGE_PATH, filename)
  149. for filename in os.listdir(package_dir)
  150. if filename.endswith('.pxi')
  151. and 'objectpath' not in filename ]
  152. pxd_files = [ filename for filename in pxd_files
  153. if 'etreepublic' not in filename ]
  154. elif 'objectify' in module:
  155. pxi_files = [ os.path.join(PACKAGE_PATH, 'objectpath.pxi') ]
  156. else:
  157. pxi_files = []
  158. return pxd_files + pxi_files
  159. def extra_setup_args():
  160. result = {}
  161. if CYTHON_INSTALLED:
  162. result['cmdclass'] = {'build_ext': build_pyx}
  163. return result
  164. def libraries():
  165. if sys.platform in ('win32',):
  166. libs = ['libxslt', 'libexslt', 'libxml2', 'iconv']
  167. if OPTION_STATIC:
  168. libs = ['%s_a' % lib for lib in libs]
  169. libs.extend(['zlib', 'WS2_32'])
  170. elif OPTION_STATIC:
  171. libs = ['z', 'm']
  172. else:
  173. libs = ['xslt', 'exslt', 'xml2', 'z', 'm']
  174. return libs
  175. def library_dirs(static_library_dirs):
  176. if OPTION_STATIC:
  177. if not static_library_dirs:
  178. static_library_dirs = env_var('LIBRARY')
  179. assert static_library_dirs, "Static build not configured, see doc/build.txt"
  180. return static_library_dirs
  181. # filter them from xslt-config --libs
  182. result = []
  183. possible_library_dirs = flags('libs')
  184. for possible_library_dir in possible_library_dirs:
  185. if possible_library_dir.startswith('-L'):
  186. result.append(possible_library_dir[2:])
  187. return result
  188. def include_dirs(static_include_dirs):
  189. if OPTION_STATIC:
  190. if not static_include_dirs:
  191. static_include_dirs = env_var('INCLUDE')
  192. return static_include_dirs
  193. # filter them from xslt-config --cflags
  194. result = []
  195. possible_include_dirs = flags('cflags')
  196. for possible_include_dir in possible_include_dirs:
  197. if possible_include_dir.startswith('-I'):
  198. result.append(possible_include_dir[2:])
  199. return result
  200. def cflags(static_cflags):
  201. result = []
  202. if not OPTION_SHOW_WARNINGS:
  203. result.append('-w')
  204. if OPTION_DEBUG_GCC:
  205. result.append('-g2')
  206. if OPTION_STATIC:
  207. if not static_cflags:
  208. static_cflags = env_var('CFLAGS')
  209. result.extend(static_cflags)
  210. else:
  211. # anything from xslt-config --cflags that doesn't start with -I
  212. possible_cflags = flags('cflags')
  213. for possible_cflag in possible_cflags:
  214. if not possible_cflag.startswith('-I'):
  215. result.append(possible_cflag)
  216. if sys.platform in ('darwin',):
  217. for opt in result:
  218. if 'flat_namespace' in opt:
  219. break
  220. else:
  221. result.append('-flat_namespace')
  222. return result
  223. def define_macros():
  224. macros = []
  225. if OPTION_WITHOUT_ASSERT:
  226. macros.append(('PYREX_WITHOUT_ASSERTIONS', None))
  227. if OPTION_WITHOUT_THREADING:
  228. macros.append(('WITHOUT_THREADING', None))
  229. if OPTION_WITH_REFNANNY:
  230. macros.append(('CYTHON_REFNANNY', None))
  231. if OPTION_WITH_UNICODE_STRINGS:
  232. macros.append(('LXML_UNICODE_STRINGS', '1'))
  233. return macros
  234. _ERROR_PRINTED = False
  235. def run_command(cmd, *args):
  236. if not cmd:
  237. return ''
  238. if args:
  239. cmd = ' '.join((cmd,) + args)
  240. try:
  241. import subprocess
  242. except ImportError:
  243. # Python 2.3
  244. sf, rf, ef = os.popen3(cmd)
  245. sf.close()
  246. errors = ef.read()
  247. stdout_data = rf.read()
  248. else:
  249. # Python 2.4+
  250. p = subprocess.Popen(cmd, shell=True,
  251. stdout=subprocess.PIPE, stderr=subprocess.PIPE)
  252. stdout_data, errors = p.communicate()
  253. global _ERROR_PRINTED
  254. if errors and not _ERROR_PRINTED:
  255. _ERROR_PRINTED = True
  256. print("ERROR: %s" % errors)
  257. print("** make sure the development packages of libxml2 and libxslt are installed **\n")
  258. return decode_input(stdout_data).strip()
  259. def check_min_version(version, min_version, error_name):
  260. if not version:
  261. # this is ok for targets like sdist etc.
  262. return True
  263. version = tuple(map(int, version.split('.')[:3]))
  264. min_version = tuple(min_version)
  265. if version < min_version:
  266. print("Minimum required version of %s is %s, found %s" % (
  267. error_name, '.'.join(map(str, version)), '.'.join(map(str, min_version))))
  268. return False
  269. return True
  270. def get_library_versions():
  271. xml2_version = run_command(find_xml2_config(), "--version")
  272. xslt_version = run_command(find_xslt_config(), "--version")
  273. return xml2_version, xslt_version
  274. def flags(option):
  275. xml2_flags = run_command(find_xml2_config(), "--%s" % option)
  276. xslt_flags = run_command(find_xslt_config(), "--%s" % option)
  277. flag_list = xml2_flags.split()
  278. for flag in xslt_flags.split():
  279. if flag not in flag_list:
  280. flag_list.append(flag)
  281. return flag_list
  282. XSLT_CONFIG = None
  283. XML2_CONFIG = None
  284. def find_xml2_config():
  285. global XML2_CONFIG
  286. if XML2_CONFIG:
  287. return XML2_CONFIG
  288. option = '--with-xml2-config='
  289. for arg in sys.argv:
  290. if arg.startswith(option):
  291. sys.argv.remove(arg)
  292. XML2_CONFIG = arg[len(option):]
  293. return XML2_CONFIG
  294. else:
  295. # default: do nothing, rely only on xslt-config
  296. XML2_CONFIG = os.getenv('XML2_CONFIG', '')
  297. return XML2_CONFIG
  298. def find_xslt_config():
  299. global XSLT_CONFIG
  300. if XSLT_CONFIG:
  301. return XSLT_CONFIG
  302. option = '--with-xslt-config='
  303. for arg in sys.argv:
  304. if arg.startswith(option):
  305. sys.argv.remove(arg)
  306. XSLT_CONFIG = arg[len(option):]
  307. return XSLT_CONFIG
  308. else:
  309. XSLT_CONFIG = os.getenv('XSLT_CONFIG', 'xslt-config')
  310. return XSLT_CONFIG
  311. ## Option handling:
  312. def has_option(name):
  313. try:
  314. sys.argv.remove('--%s' % name)
  315. return True
  316. except ValueError:
  317. pass
  318. # allow passing all cmd line options also as environment variables
  319. env_val = os.getenv(name.upper().replace('-', '_'), 'false').lower()
  320. if env_val == "true":
  321. return True
  322. return False
  323. def option_value(name):
  324. for index, option in enumerate(sys.argv):
  325. if option == '--' + name:
  326. if index+1 >= len(sys.argv):
  327. raise DistutilsOptionError(
  328. 'The option %s requires a value' % option)
  329. value = sys.argv[index+1]
  330. sys.argv[index:index+2] = []
  331. return value
  332. if option.startswith('--' + name + '='):
  333. value = option[len(name)+3:]
  334. sys.argv[index:index+1] = []
  335. return value
  336. env_val = os.getenv(name.upper().replace('-', '_'))
  337. return env_val
  338. staticbuild = bool(os.environ.get('STATICBUILD', ''))
  339. # pick up any commandline options and/or env variables
  340. OPTION_WITHOUT_OBJECTIFY = has_option('without-objectify')
  341. OPTION_WITH_UNICODE_STRINGS = has_option('with-unicode-strings')
  342. OPTION_WITHOUT_ASSERT = has_option('without-assert')
  343. OPTION_WITHOUT_THREADING = has_option('without-threading')
  344. OPTION_WITHOUT_CYTHON = has_option('without-cython')
  345. OPTION_WITH_CYTHON = has_option('with-cython')
  346. OPTION_WITH_CYTHON_GDB = has_option('cython-gdb')
  347. OPTION_WITH_REFNANNY = has_option('with-refnanny')
  348. if OPTION_WITHOUT_CYTHON:
  349. CYTHON_INSTALLED = False
  350. OPTION_STATIC = staticbuild or has_option('static')
  351. OPTION_DEBUG_GCC = has_option('debug-gcc')
  352. OPTION_SHOW_WARNINGS = has_option('warnings')
  353. OPTION_AUTO_RPATH = has_option('auto-rpath')
  354. OPTION_BUILD_LIBXML2XSLT = staticbuild or has_option('static-deps')
  355. if OPTION_BUILD_LIBXML2XSLT:
  356. OPTION_STATIC = True
  357. OPTION_LIBXML2_VERSION = option_value('libxml2-version')
  358. OPTION_LIBXSLT_VERSION = option_value('libxslt-version')
  359. OPTION_LIBICONV_VERSION = option_value('libiconv-version')
  360. OPTION_MULTICORE = option_value('multicore')
  361. OPTION_DOWNLOAD_DIR = option_value('download-dir')
  362. if OPTION_DOWNLOAD_DIR is None:
  363. OPTION_DOWNLOAD_DIR = 'libs'