buildlibxml.py 8.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234
  1. import os, re, sys
  2. from distutils import log, sysconfig
  3. try:
  4. from urlparse import urlsplit, urljoin
  5. from urllib import urlretrieve
  6. except ImportError:
  7. from urllib.parse import urlsplit
  8. from urllib.request import urlretrieve
  9. ## Routines to download and build libxml2/xslt:
  10. LIBXML2_LOCATION = 'ftp://xmlsoft.org/libxml2/'
  11. match_libfile_version = re.compile('^[^-]*-([.0-9-]+)[.].*').match
  12. def ftp_listdir(url):
  13. import ftplib, posixpath
  14. scheme, netloc, path, qs, fragment = urlsplit(url)
  15. assert scheme.lower() == 'ftp'
  16. server = ftplib.FTP(netloc)
  17. server.login()
  18. files = [posixpath.basename(fn) for fn in server.nlst(path)]
  19. return files
  20. def tryint(s):
  21. try:
  22. return int(s)
  23. except ValueError:
  24. return s
  25. def download_libxml2(dest_dir, version=None):
  26. """Downloads libxml2, returning the filename where the library was downloaded"""
  27. version_re = re.compile(r'^LATEST_LIBXML2_IS_(.*)$')
  28. filename = 'libxml2-%s.tar.gz'
  29. return download_library(dest_dir, LIBXML2_LOCATION, 'libxml2',
  30. version_re, filename, version=version)
  31. def download_libxslt(dest_dir, version=None):
  32. """Downloads libxslt, returning the filename where the library was downloaded"""
  33. version_re = re.compile(r'^LATEST_LIBXSLT_IS_(.*)$')
  34. filename = 'libxslt-%s.tar.gz'
  35. return download_library(dest_dir, LIBXML2_LOCATION, 'libxslt',
  36. version_re, filename, version=version)
  37. def download_library(dest_dir, location, name, version_re, filename,
  38. version=None):
  39. if version is None:
  40. try:
  41. fns = ftp_listdir(location)
  42. for fn in fns:
  43. match = version_re.search(fn)
  44. if match:
  45. version = match.group(1)
  46. print('Latest version of %s is %s' % (name, version))
  47. break
  48. else:
  49. raise Exception(
  50. "Could not find the most current version of the %s from the files: %s"
  51. % (name, fns))
  52. except IOError:
  53. # network failure - maybe we have the files already?
  54. latest = (0,0,0)
  55. fns = os.listdir(dest_dir)
  56. for fn in fns:
  57. if fn.startswith(name+'-'):
  58. match = match_libfile_version(fn)
  59. if match:
  60. version = tuple(map(tryint, match.group(1).split('.')))
  61. if version > latest:
  62. latest = version
  63. filename = fn
  64. break
  65. else:
  66. raise
  67. filename = filename % version
  68. full_url = urljoin(location, filename)
  69. dest_filename = os.path.join(dest_dir, filename)
  70. if os.path.exists(dest_filename):
  71. print('Using existing %s downloaded into %s (delete this file if you want to re-download the package)'
  72. % (name, dest_filename))
  73. else:
  74. print('Downloading %s into %s' % (name, dest_filename))
  75. urlretrieve(full_url, dest_filename)
  76. return dest_filename
  77. ## Backported method of tarfile.TarFile.extractall (doesn't exist in 2.4):
  78. def _extractall(self, path=".", members=None):
  79. """Extract all members from the archive to the current working
  80. directory and set owner, modification time and permissions on
  81. directories afterwards. `path' specifies a different directory
  82. to extract to. `members' is optional and must be a subset of the
  83. list returned by getmembers().
  84. """
  85. import copy
  86. is_ignored_file = re.compile(
  87. r'''[\\/]((test|results?)[\\/]
  88. |doc[\\/].*(Log|[.](out|imp|err|png|ent|gif|tif|pdf))$
  89. |tests[\\/](.*[\\/])?(?!Makefile)[^\\/]*$
  90. |python[\\/].*[.]py$
  91. )
  92. ''', re.X).search
  93. directories = []
  94. if members is None:
  95. members = self
  96. for tarinfo in members:
  97. if is_ignored_file(tarinfo.name):
  98. continue
  99. if tarinfo.isdir():
  100. # Extract directories with a safe mode.
  101. directories.append((tarinfo.name, tarinfo))
  102. tarinfo = copy.copy(tarinfo)
  103. tarinfo.mode = 448 # 0700
  104. self.extract(tarinfo, path)
  105. # Reverse sort directories.
  106. directories.sort()
  107. directories.reverse()
  108. # Set correct owner, mtime and filemode on directories.
  109. for name, tarinfo in directories:
  110. dirpath = os.path.join(path, name)
  111. try:
  112. self.chown(tarinfo, dirpath)
  113. self.utime(tarinfo, dirpath)
  114. self.chmod(tarinfo, dirpath)
  115. except tarfile.ExtractError:
  116. if self.errorlevel > 1:
  117. raise
  118. else:
  119. self._dbg(1, "tarfile: %s" % sys.exc_info()[1])
  120. def unpack_tarball(tar_filename, dest):
  121. import tarfile
  122. print('Unpacking %s into %s' % (os.path.basename(tar_filename), dest))
  123. tar = tarfile.open(tar_filename)
  124. base_dir = None
  125. for member in tar:
  126. base_name = member.name.split('/')[0]
  127. if base_dir is None:
  128. base_dir = base_name
  129. else:
  130. if base_dir != base_name:
  131. print('Unexpected path in %s: %s' % (tar_filename, base_name))
  132. _extractall(tar, dest)
  133. tar.close()
  134. return os.path.join(dest, base_dir)
  135. def call_subprocess(cmd, **kw):
  136. import subprocess
  137. cwd = kw.get('cwd', '.')
  138. cmd_desc = ' '.join(cmd)
  139. log.info('Running "%s" in %s' % (cmd_desc, cwd))
  140. returncode = subprocess.call(cmd, **kw)
  141. if returncode:
  142. raise Exception('Command "%s" returned code %s' % (cmd_desc, returncode))
  143. def safe_mkdir(dir):
  144. if not os.path.exists(dir):
  145. os.makedirs(dir)
  146. def build_libxml2xslt(download_dir, build_dir,
  147. static_include_dirs, static_library_dirs,
  148. static_cflags, static_binaries,
  149. libxml2_version=None, libxslt_version=None):
  150. safe_mkdir(download_dir)
  151. safe_mkdir(build_dir)
  152. libxml2_dir = unpack_tarball(download_libxml2(download_dir, libxml2_version), build_dir)
  153. libxslt_dir = unpack_tarball(download_libxslt(download_dir, libxslt_version), build_dir)
  154. prefix = os.path.join(os.path.abspath(build_dir), 'libxml2')
  155. safe_mkdir(prefix)
  156. call_setup = {}
  157. env_setup = None
  158. if sys.platform in ('darwin',):
  159. # We compile Universal if we are on a machine > 10.3
  160. major_version = int(os.uname()[2].split('.')[0])
  161. if major_version > 7:
  162. env = os.environ.copy()
  163. env.update({
  164. 'CFLAGS' : "-arch ppc -arch i386 -isysroot /Developer/SDKs/MacOSX10.4u.sdk -O2",
  165. 'LDFLAGS' : "-arch ppc -arch i386 -isysroot /Developer/SDKs/MacOSX10.4u.sdk",
  166. 'MACOSX_DEPLOYMENT_TARGET' : "10.3"
  167. })
  168. call_setup['env'] = env
  169. # We may loose the link to iconv, so make sure it's there
  170. static_binaries.append('-liconv')
  171. configure_cmd = ['./configure',
  172. '--without-python',
  173. '--disable-dependency-tracking',
  174. '--disable-shared',
  175. '--prefix=%s' % prefix,
  176. ]
  177. call_subprocess(configure_cmd, cwd=libxml2_dir, **call_setup)
  178. call_subprocess(
  179. ['make'], cwd=libxml2_dir, **call_setup)
  180. call_subprocess(
  181. ['make', 'install'], cwd=libxml2_dir, **call_setup)
  182. libxslt_configure_cmd = configure_cmd + [
  183. '--with-libxml-prefix=%s' % prefix,
  184. ]
  185. if sys.platform in ('darwin',):
  186. libxslt_configure_cmd += [
  187. '--without-crypto',
  188. ]
  189. call_subprocess(libxslt_configure_cmd, cwd=libxslt_dir, **call_setup)
  190. call_subprocess(
  191. ['make'], cwd=libxslt_dir, **call_setup)
  192. call_subprocess(
  193. ['make', 'install'], cwd=libxslt_dir, **call_setup)
  194. xslt_config = os.path.join(prefix, 'bin', 'xslt-config')
  195. xml2_config = os.path.join(prefix, 'bin', 'xml2-config')
  196. lib_dir = os.path.join(prefix, 'lib')
  197. static_include_dirs.extend([
  198. os.path.join(prefix, 'include'),
  199. os.path.join(prefix, 'include', 'libxml2'),
  200. os.path.join(prefix, 'include', 'libxslt'),
  201. os.path.join(prefix, 'include', 'libexslt')])
  202. static_library_dirs.append(lib_dir)
  203. for filename in os.listdir(lib_dir):
  204. if [l for l in ['libxml2', 'libxslt', 'libexslt'] if l in filename]:
  205. if [ext for ext in ['.a'] if filename.endswith(ext)]:
  206. static_binaries.append(os.path.join(lib_dir,filename))
  207. return (xml2_config, xslt_config)