PageRenderTime 137ms CodeModel.GetById 30ms RepoModel.GetById 2ms app.codeStats 0ms

/setup.py

https://bitbucket.org/jen2000/mercurial-pycon
Python | 389 lines | 297 code | 50 blank | 42 comment | 62 complexity | 3a584173a6e0bcc568ab66ad1057db3d MD5 | raw file
Possible License(s): GPL-2.0
  1. #
  2. # This is the mercurial setup script.
  3. #
  4. # 'python setup.py install', or
  5. # 'python setup.py --help' for more options
  6. import sys
  7. if not hasattr(sys, 'version_info') or sys.version_info < (2, 4, 0, 'final'):
  8. raise SystemExit("Mercurial requires Python 2.4 or later.")
  9. if sys.version_info[0] >= 3:
  10. def b(s):
  11. '''A helper function to emulate 2.6+ bytes literals using string
  12. literals.'''
  13. return s.encode('latin1')
  14. else:
  15. def b(s):
  16. '''A helper function to emulate 2.6+ bytes literals using string
  17. literals.'''
  18. return s
  19. # Solaris Python packaging brain damage
  20. try:
  21. import hashlib
  22. sha = hashlib.sha1()
  23. except:
  24. try:
  25. import sha
  26. except:
  27. raise SystemExit(
  28. "Couldn't import standard hashlib (incomplete Python install).")
  29. try:
  30. import zlib
  31. except:
  32. raise SystemExit(
  33. "Couldn't import standard zlib (incomplete Python install).")
  34. try:
  35. import bz2
  36. except:
  37. raise SystemExit(
  38. "Couldn't import standard bz2 (incomplete Python install).")
  39. import os, subprocess, time
  40. import shutil
  41. import tempfile
  42. from distutils import log
  43. from distutils.core import setup, Extension
  44. from distutils.dist import Distribution
  45. from distutils.command.build import build
  46. from distutils.command.build_ext import build_ext
  47. from distutils.command.build_py import build_py
  48. from distutils.command.install_scripts import install_scripts
  49. from distutils.spawn import spawn, find_executable
  50. from distutils.ccompiler import new_compiler
  51. from distutils.errors import CCompilerError
  52. from distutils.sysconfig import get_python_inc
  53. from distutils.version import StrictVersion
  54. scripts = ['hg']
  55. if os.name == 'nt':
  56. scripts.append('contrib/win32/hg.bat')
  57. # simplified version of distutils.ccompiler.CCompiler.has_function
  58. # that actually removes its temporary files.
  59. def hasfunction(cc, funcname):
  60. tmpdir = tempfile.mkdtemp(prefix='hg-install-')
  61. devnull = oldstderr = None
  62. try:
  63. try:
  64. fname = os.path.join(tmpdir, 'funcname.c')
  65. f = open(fname, 'w')
  66. f.write('int main(void) {\n')
  67. f.write(' %s();\n' % funcname)
  68. f.write('}\n')
  69. f.close()
  70. # Redirect stderr to /dev/null to hide any error messages
  71. # from the compiler.
  72. # This will have to be changed if we ever have to check
  73. # for a function on Windows.
  74. devnull = open('/dev/null', 'w')
  75. oldstderr = os.dup(sys.stderr.fileno())
  76. os.dup2(devnull.fileno(), sys.stderr.fileno())
  77. objects = cc.compile([fname], output_dir=tmpdir)
  78. cc.link_executable(objects, os.path.join(tmpdir, "a.out"))
  79. except:
  80. return False
  81. return True
  82. finally:
  83. if oldstderr is not None:
  84. os.dup2(oldstderr, sys.stderr.fileno())
  85. if devnull is not None:
  86. devnull.close()
  87. shutil.rmtree(tmpdir)
  88. # py2exe needs to be installed to work
  89. try:
  90. import py2exe
  91. py2exeloaded = True
  92. except ImportError:
  93. py2exeloaded = False
  94. def runcmd(cmd, env):
  95. p = subprocess.Popen(cmd, stdout=subprocess.PIPE,
  96. stderr=subprocess.PIPE, env=env)
  97. out, err = p.communicate()
  98. # If root is executing setup.py, but the repository is owned by
  99. # another user (as in "sudo python setup.py install") we will get
  100. # trust warnings since the .hg/hgrc file is untrusted. That is
  101. # fine, we don't want to load it anyway. Python may warn about
  102. # a missing __init__.py in mercurial/locale, we also ignore that.
  103. err = [e for e in err.splitlines()
  104. if not e.startswith(b('Not trusting file')) \
  105. and not e.startswith(b('warning: Not importing'))]
  106. if err:
  107. return ''
  108. return out
  109. version = ''
  110. if os.path.isdir('.hg'):
  111. # Execute hg out of this directory with a custom environment which
  112. # includes the pure Python modules in mercurial/pure. We also take
  113. # care to not use any hgrc files and do no localization.
  114. pypath = ['mercurial', os.path.join('mercurial', 'pure')]
  115. env = {'PYTHONPATH': os.pathsep.join(pypath),
  116. 'HGRCPATH': '',
  117. 'LANGUAGE': 'C'}
  118. if 'LD_LIBRARY_PATH' in os.environ:
  119. env['LD_LIBRARY_PATH'] = os.environ['LD_LIBRARY_PATH']
  120. if 'SystemRoot' in os.environ:
  121. # Copy SystemRoot into the custom environment for Python 2.6
  122. # under Windows. Otherwise, the subprocess will fail with
  123. # error 0xc0150004. See: http://bugs.python.org/issue3440
  124. env['SystemRoot'] = os.environ['SystemRoot']
  125. cmd = [sys.executable, 'hg', 'id', '-i', '-t']
  126. l = runcmd(cmd, env).split()
  127. while len(l) > 1 and l[-1][0].isalpha(): # remove non-numbered tags
  128. l.pop()
  129. if len(l) > 1: # tag found
  130. version = l[-1]
  131. if l[0].endswith('+'): # propagate the dirty status to the tag
  132. version += '+'
  133. elif len(l) == 1: # no tag found
  134. cmd = [sys.executable, 'hg', 'parents', '--template',
  135. '{latesttag}+{latesttagdistance}-']
  136. version = runcmd(cmd, env) + l[0]
  137. if version.endswith('+'):
  138. version += time.strftime('%Y%m%d')
  139. elif os.path.exists('.hg_archival.txt'):
  140. kw = dict([[t.strip() for t in l.split(':', 1)]
  141. for l in open('.hg_archival.txt')])
  142. if 'tag' in kw:
  143. version = kw['tag']
  144. elif 'latesttag' in kw:
  145. version = '%(latesttag)s+%(latesttagdistance)s-%(node).12s' % kw
  146. else:
  147. version = kw.get('node', '')[:12]
  148. if version:
  149. f = open("mercurial/__version__.py", "w")
  150. f.write('# this file is autogenerated by setup.py\n')
  151. f.write('version = "%s"\n' % version)
  152. f.close()
  153. try:
  154. from mercurial import __version__
  155. version = __version__.version
  156. except ImportError:
  157. version = 'unknown'
  158. class hgbuildmo(build):
  159. description = "build translations (.mo files)"
  160. def run(self):
  161. if not find_executable('msgfmt'):
  162. self.warn("could not find msgfmt executable, no translations "
  163. "will be built")
  164. return
  165. podir = 'i18n'
  166. if not os.path.isdir(podir):
  167. self.warn("could not find %s/ directory" % podir)
  168. return
  169. join = os.path.join
  170. for po in os.listdir(podir):
  171. if not po.endswith('.po'):
  172. continue
  173. pofile = join(podir, po)
  174. modir = join('locale', po[:-3], 'LC_MESSAGES')
  175. mofile = join(modir, 'hg.mo')
  176. mobuildfile = join('mercurial', mofile)
  177. cmd = ['msgfmt', '-v', '-o', mobuildfile, pofile]
  178. if sys.platform != 'sunos5':
  179. # msgfmt on Solaris does not know about -c
  180. cmd.append('-c')
  181. self.mkpath(join('mercurial', modir))
  182. self.make_file([pofile], mobuildfile, spawn, (cmd,))
  183. # Insert hgbuildmo first so that files in mercurial/locale/ are found
  184. # when build_py is run next.
  185. build.sub_commands.insert(0, ('build_mo', None))
  186. Distribution.pure = 0
  187. Distribution.global_options.append(('pure', None, "use pure (slow) Python "
  188. "code instead of C extensions"))
  189. class hgbuildext(build_ext):
  190. def build_extension(self, ext):
  191. try:
  192. build_ext.build_extension(self, ext)
  193. except CCompilerError:
  194. if not getattr(ext, 'optional', False):
  195. raise
  196. log.warn("Failed to build optional extension '%s' (skipping)",
  197. ext.name)
  198. class hgbuildpy(build_py):
  199. def finalize_options(self):
  200. build_py.finalize_options(self)
  201. if self.distribution.pure:
  202. if self.py_modules is None:
  203. self.py_modules = []
  204. for ext in self.distribution.ext_modules:
  205. if ext.name.startswith("mercurial."):
  206. self.py_modules.append("mercurial.pure.%s" % ext.name[10:])
  207. self.distribution.ext_modules = []
  208. else:
  209. if not os.path.exists(os.path.join(get_python_inc(), 'Python.h')):
  210. raise SystemExit("Python headers are required to build Mercurial")
  211. def find_modules(self):
  212. modules = build_py.find_modules(self)
  213. for module in modules:
  214. if module[0] == "mercurial.pure":
  215. if module[1] != "__init__":
  216. yield ("mercurial", module[1], module[2])
  217. else:
  218. yield module
  219. class hginstallscripts(install_scripts):
  220. '''
  221. This is a specialization of install_scripts that replaces the @LIBDIR@ with
  222. the configured directory for modules. If possible, the path is made relative
  223. to the directory for scripts.
  224. '''
  225. def initialize_options(self):
  226. install_scripts.initialize_options(self)
  227. self.install_lib = None
  228. def finalize_options(self):
  229. install_scripts.finalize_options(self)
  230. self.set_undefined_options('install',
  231. ('install_lib', 'install_lib'))
  232. def run(self):
  233. install_scripts.run(self)
  234. if (os.path.splitdrive(self.install_dir)[0] !=
  235. os.path.splitdrive(self.install_lib)[0]):
  236. # can't make relative paths from one drive to another, so use an
  237. # absolute path instead
  238. libdir = self.install_lib
  239. else:
  240. common = os.path.commonprefix((self.install_dir, self.install_lib))
  241. rest = self.install_dir[len(common):]
  242. uplevel = len([n for n in os.path.split(rest) if n])
  243. libdir = uplevel * ('..' + os.sep) + self.install_lib[len(common):]
  244. for outfile in self.outfiles:
  245. fp = open(outfile, 'rb')
  246. data = fp.read()
  247. fp.close()
  248. # skip binary files
  249. if '\0' in data:
  250. continue
  251. data = data.replace('@LIBDIR@', libdir.encode('string_escape'))
  252. fp = open(outfile, 'wb')
  253. fp.write(data)
  254. fp.close()
  255. cmdclass = {'build_mo': hgbuildmo,
  256. 'build_ext': hgbuildext,
  257. 'build_py': hgbuildpy,
  258. 'install_scripts': hginstallscripts}
  259. packages = ['mercurial', 'mercurial.hgweb', 'hgext', 'hgext.convert',
  260. 'hgext.highlight', 'hgext.zeroconf']
  261. pymodules = []
  262. extmodules = [
  263. Extension('mercurial.base85', ['mercurial/base85.c']),
  264. Extension('mercurial.bdiff', ['mercurial/bdiff.c']),
  265. Extension('mercurial.diffhelpers', ['mercurial/diffhelpers.c']),
  266. Extension('mercurial.mpatch', ['mercurial/mpatch.c']),
  267. Extension('mercurial.parsers', ['mercurial/parsers.c']),
  268. ]
  269. # disable osutil.c under windows + python 2.4 (issue1364)
  270. if sys.platform == 'win32' and sys.version_info < (2, 5, 0, 'final'):
  271. pymodules.append('mercurial.pure.osutil')
  272. else:
  273. extmodules.append(Extension('mercurial.osutil', ['mercurial/osutil.c']))
  274. if sys.platform == 'linux2' and os.uname()[2] > '2.6':
  275. # The inotify extension is only usable with Linux 2.6 kernels.
  276. # You also need a reasonably recent C library.
  277. # In any case, if it fails to build the error will be skipped ('optional').
  278. cc = new_compiler()
  279. if hasfunction(cc, 'inotify_add_watch'):
  280. inotify = Extension('hgext.inotify.linux._inotify',
  281. ['hgext/inotify/linux/_inotify.c'],
  282. ['mercurial'])
  283. inotify.optional = True
  284. extmodules.append(inotify)
  285. packages.extend(['hgext.inotify', 'hgext.inotify.linux'])
  286. packagedata = {'mercurial': ['locale/*/LC_MESSAGES/hg.mo',
  287. 'help/*.txt']}
  288. def ordinarypath(p):
  289. return p and p[0] != '.' and p[-1] != '~'
  290. for root in ('templates',):
  291. for curdir, dirs, files in os.walk(os.path.join('mercurial', root)):
  292. curdir = curdir.split(os.sep, 1)[1]
  293. dirs[:] = filter(ordinarypath, dirs)
  294. for f in filter(ordinarypath, files):
  295. f = os.path.join(curdir, f)
  296. packagedata['mercurial'].append(f)
  297. datafiles = []
  298. setupversion = version
  299. extra = {}
  300. if py2exeloaded:
  301. extra['console'] = [
  302. {'script':'hg',
  303. 'copyright':'Copyright (C) 2005-2010 Matt Mackall and others',
  304. 'product_version':version}]
  305. if os.name == 'nt':
  306. # Windows binary file versions for exe/dll files must have the
  307. # form W.X.Y.Z, where W,X,Y,Z are numbers in the range 0..65535
  308. setupversion = version.split('+', 1)[0]
  309. if sys.platform == 'darwin' and os.path.exists('/usr/bin/xcodebuild'):
  310. # XCode 4.0 dropped support for ppc architecture, which is hardcoded in
  311. # distutils.sysconfig
  312. version = runcmd(['/usr/bin/xcodebuild', '-version'], {}).splitlines()[0]
  313. # Also parse only first digit, because 3.2.1 can't be parsed nicely
  314. if (version.startswith('Xcode') and
  315. StrictVersion(version.split()[1]) >= StrictVersion('4.0')):
  316. os.environ['ARCHFLAGS'] = '-arch i386 -arch x86_64'
  317. setup(name='mercurial',
  318. version=setupversion,
  319. author='Matt Mackall',
  320. author_email='mpm@selenic.com',
  321. url='http://mercurial.selenic.com/',
  322. description='Scalable distributed SCM',
  323. license='GNU GPLv2+',
  324. scripts=scripts,
  325. packages=packages,
  326. py_modules=pymodules,
  327. ext_modules=extmodules,
  328. data_files=datafiles,
  329. package_data=packagedata,
  330. cmdclass=cmdclass,
  331. options=dict(py2exe=dict(packages=['hgext', 'email']),
  332. bdist_mpkg=dict(zipdist=True,
  333. license='COPYING',
  334. readme='contrib/macosx/Readme.html',
  335. welcome='contrib/macosx/Welcome.html')),
  336. **extra)