setupinfo.py 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339
  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/lxml/"
  13. if sys.version_info[0] >= 3:
  14. _system_encoding = sys.getdefaultencoding()
  15. if _system_encoding is None:
  16. _system_encoding = "iso-8859-1" # :-)
  17. def decode_input(data):
  18. if isinstance(data, str):
  19. return data
  20. return data.decode(_system_encoding)
  21. else:
  22. def decode_input(data):
  23. return data
  24. def env_var(name):
  25. value = os.getenv(name)
  26. if value:
  27. value = decode_input(value)
  28. if sys.platform == 'win32' and ';' in value:
  29. return value.split(';')
  30. else:
  31. return value.split()
  32. else:
  33. return []
  34. def ext_modules(static_include_dirs, static_library_dirs,
  35. static_cflags, static_binaries):
  36. global XML2_CONFIG, XSLT_CONFIG
  37. if OPTION_BUILD_LIBXML2XSLT:
  38. from buildlibxml import build_libxml2xslt
  39. XML2_CONFIG, XSLT_CONFIG = build_libxml2xslt(
  40. 'libs', 'build/tmp',
  41. static_include_dirs, static_library_dirs,
  42. static_cflags, static_binaries,
  43. libxml2_version=OPTION_LIBXML2_VERSION,
  44. libxslt_version=OPTION_LIBXSLT_VERSION)
  45. if CYTHON_INSTALLED:
  46. source_extension = ".pyx"
  47. print("Building with Cython %s." % Cython.Compiler.Version.version)
  48. else:
  49. print ("NOTE: Trying to build without Cython, pre-generated "
  50. "'%slxml.etree.c' needs to be available." % PACKAGE_PATH)
  51. source_extension = ".c"
  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. lib_versions = get_library_versions()
  58. if lib_versions[0]:
  59. print("Using build configuration of libxml2 %s and libxslt %s" %
  60. lib_versions)
  61. else:
  62. print("Using build configuration of libxslt %s" %
  63. lib_versions[1])
  64. _include_dirs = include_dirs(static_include_dirs)
  65. _library_dirs = library_dirs(static_library_dirs)
  66. _cflags = cflags(static_cflags)
  67. _define_macros = define_macros()
  68. _libraries = libraries()
  69. if _library_dirs:
  70. message = "Building against libxml2/libxslt in "
  71. if len(_library_dirs) > 1:
  72. print(message + "one of the following directories:")
  73. for dir in _library_dirs:
  74. print(" " + dir)
  75. else:
  76. print(message + "the following directory: " +
  77. _library_dirs[0])
  78. if OPTION_AUTO_RPATH:
  79. runtime_library_dirs = _library_dirs
  80. else:
  81. runtime_library_dirs = []
  82. if not OPTION_SHOW_WARNINGS:
  83. _cflags = ['-w'] + _cflags
  84. result = []
  85. for module in modules:
  86. main_module_source = PACKAGE_PATH + module + source_extension
  87. dependencies = find_dependencies(module)
  88. result.append(
  89. Extension(
  90. module,
  91. sources = [main_module_source] + dependencies,
  92. extra_compile_args = _cflags,
  93. extra_objects = static_binaries,
  94. define_macros = _define_macros,
  95. include_dirs = _include_dirs,
  96. library_dirs = _library_dirs,
  97. runtime_library_dirs = runtime_library_dirs,
  98. libraries = _libraries,
  99. ))
  100. return result
  101. def find_dependencies(module):
  102. if not CYTHON_INSTALLED:
  103. return []
  104. from Cython.Compiler.Version import version
  105. if split_version(version) < (0,9,6,13):
  106. return []
  107. package_dir = os.path.join(get_base_dir(), PACKAGE_PATH)
  108. files = os.listdir(package_dir)
  109. pxd_files = [ os.path.join(PACKAGE_PATH, filename) for filename in files
  110. if filename.endswith('.pxd') ]
  111. if 'etree' in module:
  112. pxi_files = [ os.path.join(PACKAGE_PATH, filename)
  113. for filename in files
  114. if filename.endswith('.pxi')
  115. and 'objectpath' not in filename ]
  116. pxd_files = [ filename for filename in pxd_files
  117. if 'etreepublic' not in filename ]
  118. elif 'objectify' in module:
  119. pxi_files = [ os.path.join(PACKAGE_PATH, 'objectpath.pxi') ]
  120. else:
  121. pxi_files = []
  122. return pxd_files + pxi_files
  123. def extra_setup_args():
  124. result = {}
  125. if CYTHON_INSTALLED:
  126. result['cmdclass'] = {'build_ext': build_pyx}
  127. return result
  128. def libraries():
  129. if sys.platform in ('win32',):
  130. libs = ['libxslt', 'libexslt', 'libxml2', 'iconv']
  131. if OPTION_STATIC:
  132. libs = ['%s_a' % lib for lib in libs]
  133. libs.extend(['zlib', 'WS2_32'])
  134. elif OPTION_STATIC:
  135. libs = ['z', 'm']
  136. else:
  137. libs = ['xslt', 'exslt', 'xml2', 'z', 'm']
  138. return libs
  139. def library_dirs(static_library_dirs):
  140. if OPTION_STATIC:
  141. if not static_library_dirs:
  142. static_library_dirs = env_var('LIBRARY')
  143. assert static_library_dirs, "Static build not configured, see doc/build.txt"
  144. return static_library_dirs
  145. # filter them from xslt-config --libs
  146. result = []
  147. possible_library_dirs = flags('libs')
  148. for possible_library_dir in possible_library_dirs:
  149. if possible_library_dir.startswith('-L'):
  150. result.append(possible_library_dir[2:])
  151. return result
  152. def include_dirs(static_include_dirs):
  153. if OPTION_STATIC:
  154. if not static_include_dirs:
  155. static_include_dirs = env_var('INCLUDE')
  156. return static_include_dirs
  157. # filter them from xslt-config --cflags
  158. result = []
  159. possible_include_dirs = flags('cflags')
  160. for possible_include_dir in possible_include_dirs:
  161. if possible_include_dir.startswith('-I'):
  162. result.append(possible_include_dir[2:])
  163. return result
  164. def cflags(static_cflags):
  165. result = []
  166. if OPTION_DEBUG_GCC:
  167. result.append('-g2')
  168. if OPTION_STATIC:
  169. if not static_cflags:
  170. static_cflags = env_var('CFLAGS')
  171. result.extend(static_cflags)
  172. else:
  173. # anything from xslt-config --cflags that doesn't start with -I
  174. possible_cflags = flags('cflags')
  175. for possible_cflag in possible_cflags:
  176. if not possible_cflag.startswith('-I'):
  177. result.append(possible_cflag)
  178. if sys.platform in ('darwin',):
  179. for opt in result:
  180. if 'flat_namespace' in opt:
  181. break
  182. else:
  183. result.append('-flat_namespace')
  184. return result
  185. def define_macros():
  186. macros = []
  187. if OPTION_WITHOUT_ASSERT:
  188. macros.append(('PYREX_WITHOUT_ASSERTIONS', None))
  189. if OPTION_WITHOUT_THREADING:
  190. macros.append(('WITHOUT_THREADING', None))
  191. if OPTION_WITH_REFNANNY:
  192. macros.append(('CYTHON_REFNANNY', None))
  193. return macros
  194. _ERROR_PRINTED = False
  195. def run_command(cmd, *args):
  196. if not cmd:
  197. return ''
  198. if args:
  199. cmd = ' '.join((cmd,) + args)
  200. try:
  201. import subprocess
  202. except ImportError:
  203. # Python 2.3
  204. _, rf, ef = os.popen3(cmd)
  205. else:
  206. # Python 2.4+
  207. p = subprocess.Popen(cmd, shell=True,
  208. stdout=subprocess.PIPE, stderr=subprocess.PIPE)
  209. rf, ef = p.stdout, p.stderr
  210. errors = ef.read()
  211. global _ERROR_PRINTED
  212. if errors and not _ERROR_PRINTED:
  213. _ERROR_PRINTED = True
  214. print("ERROR: %s" % errors)
  215. print("** make sure the development packages of libxml2 and libxslt are installed **\n")
  216. return decode_input(rf.read()).strip()
  217. def get_library_versions():
  218. xml2_version = run_command(find_xml2_config(), "--version")
  219. xslt_version = run_command(find_xslt_config(), "--version")
  220. return xml2_version, xslt_version
  221. def flags(option):
  222. xml2_flags = run_command(find_xml2_config(), "--%s" % option)
  223. xslt_flags = run_command(find_xslt_config(), "--%s" % option)
  224. flag_list = xml2_flags.split()
  225. for flag in xslt_flags.split():
  226. if flag not in flag_list:
  227. flag_list.append(flag)
  228. return flag_list
  229. XSLT_CONFIG = None
  230. XML2_CONFIG = None
  231. def find_xml2_config():
  232. global XML2_CONFIG
  233. if XML2_CONFIG:
  234. return XML2_CONFIG
  235. option = '--with-xml2-config='
  236. for arg in sys.argv:
  237. if arg.startswith(option):
  238. sys.argv.remove(arg)
  239. XML2_CONFIG = arg[len(option):]
  240. return XML2_CONFIG
  241. else:
  242. # default: do nothing, rely only on xslt-config
  243. XML2_CONFIG = os.getenv('XML2_CONFIG', '')
  244. return XML2_CONFIG
  245. def find_xslt_config():
  246. global XSLT_CONFIG
  247. if XSLT_CONFIG:
  248. return XSLT_CONFIG
  249. option = '--with-xslt-config='
  250. for arg in sys.argv:
  251. if arg.startswith(option):
  252. sys.argv.remove(arg)
  253. XSLT_CONFIG = arg[len(option):]
  254. return XSLT_CONFIG
  255. else:
  256. XSLT_CONFIG = os.getenv('XSLT_CONFIG', 'xslt-config')
  257. return XSLT_CONFIG
  258. ## Option handling:
  259. def has_option(name):
  260. try:
  261. sys.argv.remove('--%s' % name)
  262. return True
  263. except ValueError:
  264. pass
  265. # allow passing all cmd line options also as environment variables
  266. env_val = os.getenv(name.upper().replace('-', '_'), 'false').lower()
  267. if env_val == "true":
  268. return True
  269. return False
  270. def option_value(name):
  271. for index, option in enumerate(sys.argv):
  272. if option == '--' + name:
  273. if index+1 >= len(sys.argv):
  274. raise DistutilsOptionError(
  275. 'The option %s requires a value' % option)
  276. value = sys.argv[index+1]
  277. sys.argv[index:index+2] = []
  278. return value
  279. if option.startswith('--' + name + '='):
  280. value = option[len(name)+3:]
  281. sys.argv[index:index+1] = []
  282. return value
  283. env_val = os.getenv(name.upper().replace('-', '_'))
  284. return env_val
  285. # pick up any commandline options
  286. OPTION_WITHOUT_OBJECTIFY = has_option('without-objectify')
  287. OPTION_WITHOUT_ASSERT = has_option('without-assert')
  288. OPTION_WITHOUT_THREADING = has_option('without-threading')
  289. OPTION_WITHOUT_CYTHON = has_option('without-cython')
  290. OPTION_WITH_REFNANNY = has_option('with-refnanny')
  291. if OPTION_WITHOUT_CYTHON:
  292. CYTHON_INSTALLED = False
  293. OPTION_STATIC = has_option('static')
  294. OPTION_DEBUG_GCC = has_option('debug-gcc')
  295. OPTION_SHOW_WARNINGS = has_option('warnings')
  296. OPTION_AUTO_RPATH = has_option('auto-rpath')
  297. OPTION_BUILD_LIBXML2XSLT = has_option('static-deps')
  298. if OPTION_BUILD_LIBXML2XSLT:
  299. OPTION_STATIC = True
  300. OPTION_LIBXML2_VERSION = option_value('libxml2-version')
  301. OPTION_LIBXSLT_VERSION = option_value('libxslt-version')