PageRenderTime 69ms CodeModel.GetById 35ms RepoModel.GetById 0ms app.codeStats 1ms

/setup.py

https://bitbucket.org/mirror/mercurial/
Python | 588 lines | 464 code | 70 blank | 54 comment | 90 complexity | bf211913a5c5f2f3759283765b70ab31 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, platform
  7. if getattr(sys, 'version_info', (0, 0, 0)) < (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. printf = eval('print')
  15. libdir_escape = 'unicode_escape'
  16. else:
  17. libdir_escape = 'string_escape'
  18. def b(s):
  19. '''A helper function to emulate 2.6+ bytes literals using string
  20. literals.'''
  21. return s
  22. def printf(*args, **kwargs):
  23. f = kwargs.get('file', sys.stdout)
  24. end = kwargs.get('end', '\n')
  25. f.write(b(' ').join(args) + end)
  26. # Solaris Python packaging brain damage
  27. try:
  28. import hashlib
  29. sha = hashlib.sha1()
  30. except ImportError:
  31. try:
  32. import sha
  33. except ImportError:
  34. raise SystemExit(
  35. "Couldn't import standard hashlib (incomplete Python install).")
  36. try:
  37. import zlib
  38. except ImportError:
  39. raise SystemExit(
  40. "Couldn't import standard zlib (incomplete Python install).")
  41. # The base IronPython distribution (as of 2.7.1) doesn't support bz2
  42. isironpython = False
  43. try:
  44. isironpython = (platform.python_implementation()
  45. .lower().find("ironpython") != -1)
  46. except AttributeError:
  47. pass
  48. if isironpython:
  49. sys.stderr.write("warning: IronPython detected (no bz2 support)\n")
  50. else:
  51. try:
  52. import bz2
  53. except ImportError:
  54. raise SystemExit(
  55. "Couldn't import standard bz2 (incomplete Python install).")
  56. import os, subprocess, time
  57. import re
  58. import shutil
  59. import tempfile
  60. from distutils import log
  61. from distutils.core import setup, Command, Extension
  62. from distutils.dist import Distribution
  63. from distutils.command.build import build
  64. from distutils.command.build_ext import build_ext
  65. from distutils.command.build_py import build_py
  66. from distutils.command.install_scripts import install_scripts
  67. from distutils.spawn import spawn, find_executable
  68. from distutils import cygwinccompiler
  69. from distutils.errors import CCompilerError, DistutilsExecError
  70. from distutils.sysconfig import get_python_inc, get_config_var
  71. from distutils.version import StrictVersion
  72. convert2to3 = '--c2to3' in sys.argv
  73. if convert2to3:
  74. try:
  75. from distutils.command.build_py import build_py_2to3 as build_py
  76. from lib2to3.refactor import get_fixers_from_package as getfixers
  77. except ImportError:
  78. if sys.version_info[0] < 3:
  79. raise SystemExit("--c2to3 is only compatible with python3.")
  80. raise
  81. sys.path.append('contrib')
  82. elif sys.version_info[0] >= 3:
  83. raise SystemExit("setup.py with python3 needs --c2to3 (experimental)")
  84. scripts = ['hg']
  85. if os.name == 'nt':
  86. scripts.append('contrib/win32/hg.bat')
  87. # simplified version of distutils.ccompiler.CCompiler.has_function
  88. # that actually removes its temporary files.
  89. def hasfunction(cc, funcname):
  90. tmpdir = tempfile.mkdtemp(prefix='hg-install-')
  91. devnull = oldstderr = None
  92. try:
  93. try:
  94. fname = os.path.join(tmpdir, 'funcname.c')
  95. f = open(fname, 'w')
  96. f.write('int main(void) {\n')
  97. f.write(' %s();\n' % funcname)
  98. f.write('}\n')
  99. f.close()
  100. # Redirect stderr to /dev/null to hide any error messages
  101. # from the compiler.
  102. # This will have to be changed if we ever have to check
  103. # for a function on Windows.
  104. devnull = open('/dev/null', 'w')
  105. oldstderr = os.dup(sys.stderr.fileno())
  106. os.dup2(devnull.fileno(), sys.stderr.fileno())
  107. objects = cc.compile([fname], output_dir=tmpdir)
  108. cc.link_executable(objects, os.path.join(tmpdir, "a.out"))
  109. except Exception:
  110. return False
  111. return True
  112. finally:
  113. if oldstderr is not None:
  114. os.dup2(oldstderr, sys.stderr.fileno())
  115. if devnull is not None:
  116. devnull.close()
  117. shutil.rmtree(tmpdir)
  118. # py2exe needs to be installed to work
  119. try:
  120. import py2exe
  121. py2exeloaded = True
  122. # import py2exe's patched Distribution class
  123. from distutils.core import Distribution
  124. except ImportError:
  125. py2exeloaded = False
  126. def runcmd(cmd, env):
  127. if sys.platform == 'plan9':
  128. # subprocess kludge to work around issues in half-baked Python
  129. # ports, notably bichued/python:
  130. _, out, err = os.popen3(cmd)
  131. return str(out), str(err)
  132. else:
  133. p = subprocess.Popen(cmd, stdout=subprocess.PIPE,
  134. stderr=subprocess.PIPE, env=env)
  135. out, err = p.communicate()
  136. return out, err
  137. def runhg(cmd, env):
  138. out, err = runcmd(cmd, env)
  139. # If root is executing setup.py, but the repository is owned by
  140. # another user (as in "sudo python setup.py install") we will get
  141. # trust warnings since the .hg/hgrc file is untrusted. That is
  142. # fine, we don't want to load it anyway. Python may warn about
  143. # a missing __init__.py in mercurial/locale, we also ignore that.
  144. err = [e for e in err.splitlines()
  145. if not e.startswith(b('not trusting file')) \
  146. and not e.startswith(b('warning: Not importing')) \
  147. and not e.startswith(b('obsolete feature not enabled'))]
  148. if err:
  149. printf("stderr from '%s':" % (' '.join(cmd)), file=sys.stderr)
  150. printf(b('\n').join([b(' ') + e for e in err]), file=sys.stderr)
  151. return ''
  152. return out
  153. version = ''
  154. # Execute hg out of this directory with a custom environment which
  155. # includes the pure Python modules in mercurial/pure. We also take
  156. # care to not use any hgrc files and do no localization.
  157. pypath = ['mercurial', os.path.join('mercurial', 'pure')]
  158. env = {'PYTHONPATH': os.pathsep.join(pypath),
  159. 'HGRCPATH': '',
  160. 'LANGUAGE': 'C'}
  161. if 'LD_LIBRARY_PATH' in os.environ:
  162. env['LD_LIBRARY_PATH'] = os.environ['LD_LIBRARY_PATH']
  163. if 'SystemRoot' in os.environ:
  164. # Copy SystemRoot into the custom environment for Python 2.6
  165. # under Windows. Otherwise, the subprocess will fail with
  166. # error 0xc0150004. See: http://bugs.python.org/issue3440
  167. env['SystemRoot'] = os.environ['SystemRoot']
  168. if os.path.isdir('.hg'):
  169. cmd = [sys.executable, 'hg', 'log', '-r', '.', '--template', '{tags}\n']
  170. numerictags = [t for t in runhg(cmd, env).split() if t[0].isdigit()]
  171. hgid = runhg([sys.executable, 'hg', 'id', '-i'], env).strip()
  172. if numerictags: # tag(s) found
  173. version = numerictags[-1]
  174. if hgid.endswith('+'): # propagate the dirty status to the tag
  175. version += '+'
  176. else: # no tag found
  177. cmd = [sys.executable, 'hg', 'parents', '--template',
  178. '{latesttag}+{latesttagdistance}-']
  179. version = runhg(cmd, env) + hgid
  180. if version.endswith('+'):
  181. version += time.strftime('%Y%m%d')
  182. elif os.path.exists('.hg_archival.txt'):
  183. kw = dict([[t.strip() for t in l.split(':', 1)]
  184. for l in open('.hg_archival.txt')])
  185. if 'tag' in kw:
  186. version = kw['tag']
  187. elif 'latesttag' in kw:
  188. version = '%(latesttag)s+%(latesttagdistance)s-%(node).12s' % kw
  189. else:
  190. version = kw.get('node', '')[:12]
  191. if version:
  192. f = open("mercurial/__version__.py", "w")
  193. f.write('# this file is autogenerated by setup.py\n')
  194. f.write('version = "%s"\n' % version)
  195. f.close()
  196. try:
  197. from mercurial import __version__
  198. version = __version__.version
  199. except ImportError:
  200. version = 'unknown'
  201. class hgbuild(build):
  202. # Insert hgbuildmo first so that files in mercurial/locale/ are found
  203. # when build_py is run next.
  204. sub_commands = [('build_mo', None),
  205. # We also need build_ext before build_py. Otherwise, when 2to3 is
  206. # called (in build_py), it will not find osutil & friends,
  207. # thinking that those modules are global and, consequently, making
  208. # a mess, now that all module imports are global.
  209. ('build_ext', build.has_ext_modules),
  210. ] + build.sub_commands
  211. class hgbuildmo(build):
  212. description = "build translations (.mo files)"
  213. def run(self):
  214. if not find_executable('msgfmt'):
  215. self.warn("could not find msgfmt executable, no translations "
  216. "will be built")
  217. return
  218. podir = 'i18n'
  219. if not os.path.isdir(podir):
  220. self.warn("could not find %s/ directory" % podir)
  221. return
  222. join = os.path.join
  223. for po in os.listdir(podir):
  224. if not po.endswith('.po'):
  225. continue
  226. pofile = join(podir, po)
  227. modir = join('locale', po[:-3], 'LC_MESSAGES')
  228. mofile = join(modir, 'hg.mo')
  229. mobuildfile = join('mercurial', mofile)
  230. cmd = ['msgfmt', '-v', '-o', mobuildfile, pofile]
  231. if sys.platform != 'sunos5':
  232. # msgfmt on Solaris does not know about -c
  233. cmd.append('-c')
  234. self.mkpath(join('mercurial', modir))
  235. self.make_file([pofile], mobuildfile, spawn, (cmd,))
  236. class hgdist(Distribution):
  237. pure = 0
  238. global_options = Distribution.global_options + \
  239. [('pure', None, "use pure (slow) Python "
  240. "code instead of C extensions"),
  241. ('c2to3', None, "(experimental!) convert "
  242. "code with 2to3"),
  243. ]
  244. def has_ext_modules(self):
  245. # self.ext_modules is emptied in hgbuildpy.finalize_options which is
  246. # too late for some cases
  247. return not self.pure and Distribution.has_ext_modules(self)
  248. class hgbuildext(build_ext):
  249. def build_extension(self, ext):
  250. try:
  251. build_ext.build_extension(self, ext)
  252. except CCompilerError:
  253. if not getattr(ext, 'optional', False):
  254. raise
  255. log.warn("Failed to build optional extension '%s' (skipping)",
  256. ext.name)
  257. class hgbuildpy(build_py):
  258. if convert2to3:
  259. fixer_names = sorted(set(getfixers("lib2to3.fixes") +
  260. getfixers("hgfixes")))
  261. def finalize_options(self):
  262. build_py.finalize_options(self)
  263. if self.distribution.pure:
  264. if self.py_modules is None:
  265. self.py_modules = []
  266. for ext in self.distribution.ext_modules:
  267. if ext.name.startswith("mercurial."):
  268. self.py_modules.append("mercurial.pure.%s" % ext.name[10:])
  269. self.distribution.ext_modules = []
  270. else:
  271. h = os.path.join(get_python_inc(), 'Python.h')
  272. if not os.path.exists(h):
  273. raise SystemExit('Python headers are required to build '
  274. 'Mercurial but weren\'t found in %s' % h)
  275. def find_modules(self):
  276. modules = build_py.find_modules(self)
  277. for module in modules:
  278. if module[0] == "mercurial.pure":
  279. if module[1] != "__init__":
  280. yield ("mercurial", module[1], module[2])
  281. else:
  282. yield module
  283. class buildhgextindex(Command):
  284. description = 'generate prebuilt index of hgext (for frozen package)'
  285. user_options = []
  286. _indexfilename = 'hgext/__index__.py'
  287. def initialize_options(self):
  288. pass
  289. def finalize_options(self):
  290. pass
  291. def run(self):
  292. if os.path.exists(self._indexfilename):
  293. f = open(self._indexfilename, 'w')
  294. f.write('# empty\n')
  295. f.close()
  296. # here no extension enabled, disabled() lists up everything
  297. code = ('import pprint; from mercurial import extensions; '
  298. 'pprint.pprint(extensions.disabled())')
  299. out, err = runcmd([sys.executable, '-c', code], env)
  300. if err:
  301. raise DistutilsExecError(err)
  302. f = open(self._indexfilename, 'w')
  303. f.write('# this file is autogenerated by setup.py\n')
  304. f.write('docs = ')
  305. f.write(out)
  306. f.close()
  307. class buildhgexe(build_ext):
  308. description = 'compile hg.exe from mercurial/exewrapper.c'
  309. def build_extensions(self):
  310. if os.name != 'nt':
  311. return
  312. if isinstance(self.compiler, HackedMingw32CCompiler):
  313. self.compiler.compiler_so = self.compiler.compiler # no -mdll
  314. self.compiler.dll_libraries = [] # no -lmsrvc90
  315. hv = sys.hexversion
  316. pythonlib = 'python%d%d' % (hv >> 24, (hv >> 16) & 0xff)
  317. f = open('mercurial/hgpythonlib.h', 'wb')
  318. f.write('/* this file is autogenerated by setup.py */\n')
  319. f.write('#define HGPYTHONLIB "%s"\n' % pythonlib)
  320. f.close()
  321. objects = self.compiler.compile(['mercurial/exewrapper.c'],
  322. output_dir=self.build_temp)
  323. dir = os.path.dirname(self.get_ext_fullpath('dummy'))
  324. target = os.path.join(dir, 'hg')
  325. self.compiler.link_executable(objects, target,
  326. libraries=[],
  327. output_dir=self.build_temp)
  328. class hginstallscripts(install_scripts):
  329. '''
  330. This is a specialization of install_scripts that replaces the @LIBDIR@ with
  331. the configured directory for modules. If possible, the path is made relative
  332. to the directory for scripts.
  333. '''
  334. def initialize_options(self):
  335. install_scripts.initialize_options(self)
  336. self.install_lib = None
  337. def finalize_options(self):
  338. install_scripts.finalize_options(self)
  339. self.set_undefined_options('install',
  340. ('install_lib', 'install_lib'))
  341. def run(self):
  342. install_scripts.run(self)
  343. if (os.path.splitdrive(self.install_dir)[0] !=
  344. os.path.splitdrive(self.install_lib)[0]):
  345. # can't make relative paths from one drive to another, so use an
  346. # absolute path instead
  347. libdir = self.install_lib
  348. else:
  349. common = os.path.commonprefix((self.install_dir, self.install_lib))
  350. rest = self.install_dir[len(common):]
  351. uplevel = len([n for n in os.path.split(rest) if n])
  352. libdir = uplevel * ('..' + os.sep) + self.install_lib[len(common):]
  353. for outfile in self.outfiles:
  354. fp = open(outfile, 'rb')
  355. data = fp.read()
  356. fp.close()
  357. # skip binary files
  358. if b('\0') in data:
  359. continue
  360. data = data.replace(b('@LIBDIR@'), libdir.encode(libdir_escape))
  361. fp = open(outfile, 'wb')
  362. fp.write(data)
  363. fp.close()
  364. cmdclass = {'build': hgbuild,
  365. 'build_mo': hgbuildmo,
  366. 'build_ext': hgbuildext,
  367. 'build_py': hgbuildpy,
  368. 'build_hgextindex': buildhgextindex,
  369. 'install_scripts': hginstallscripts,
  370. 'build_hgexe': buildhgexe,
  371. }
  372. packages = ['mercurial', 'mercurial.hgweb', 'mercurial.httpclient',
  373. 'hgext', 'hgext.convert', 'hgext.highlight', 'hgext.zeroconf',
  374. 'hgext.largefiles']
  375. pymodules = []
  376. common_depends = ['mercurial/util.h']
  377. extmodules = [
  378. Extension('mercurial.base85', ['mercurial/base85.c'],
  379. depends=common_depends),
  380. Extension('mercurial.bdiff', ['mercurial/bdiff.c'],
  381. depends=common_depends),
  382. Extension('mercurial.diffhelpers', ['mercurial/diffhelpers.c'],
  383. depends=common_depends),
  384. Extension('mercurial.mpatch', ['mercurial/mpatch.c'],
  385. depends=common_depends),
  386. Extension('mercurial.parsers', ['mercurial/dirs.c',
  387. 'mercurial/parsers.c',
  388. 'mercurial/pathencode.c'],
  389. depends=common_depends),
  390. ]
  391. osutil_ldflags = []
  392. if sys.platform == 'darwin':
  393. osutil_ldflags += ['-framework', 'ApplicationServices']
  394. # disable osutil.c under windows + python 2.4 (issue1364)
  395. if sys.platform == 'win32' and sys.version_info < (2, 5, 0, 'final'):
  396. pymodules.append('mercurial.pure.osutil')
  397. else:
  398. extmodules.append(Extension('mercurial.osutil', ['mercurial/osutil.c'],
  399. extra_link_args=osutil_ldflags,
  400. depends=common_depends))
  401. # the -mno-cygwin option has been deprecated for years
  402. Mingw32CCompiler = cygwinccompiler.Mingw32CCompiler
  403. class HackedMingw32CCompiler(cygwinccompiler.Mingw32CCompiler):
  404. def __init__(self, *args, **kwargs):
  405. Mingw32CCompiler.__init__(self, *args, **kwargs)
  406. for i in 'compiler compiler_so linker_exe linker_so'.split():
  407. try:
  408. getattr(self, i).remove('-mno-cygwin')
  409. except ValueError:
  410. pass
  411. cygwinccompiler.Mingw32CCompiler = HackedMingw32CCompiler
  412. packagedata = {'mercurial': ['locale/*/LC_MESSAGES/hg.mo',
  413. 'help/*.txt']}
  414. def ordinarypath(p):
  415. return p and p[0] != '.' and p[-1] != '~'
  416. for root in ('templates',):
  417. for curdir, dirs, files in os.walk(os.path.join('mercurial', root)):
  418. curdir = curdir.split(os.sep, 1)[1]
  419. dirs[:] = filter(ordinarypath, dirs)
  420. for f in filter(ordinarypath, files):
  421. f = os.path.join(curdir, f)
  422. packagedata['mercurial'].append(f)
  423. datafiles = []
  424. setupversion = version
  425. extra = {}
  426. if py2exeloaded:
  427. extra['console'] = [
  428. {'script':'hg',
  429. 'copyright':'Copyright (C) 2005-2010 Matt Mackall and others',
  430. 'product_version':version}]
  431. # sub command of 'build' because 'py2exe' does not handle sub_commands
  432. build.sub_commands.insert(0, ('build_hgextindex', None))
  433. if os.name == 'nt':
  434. # Windows binary file versions for exe/dll files must have the
  435. # form W.X.Y.Z, where W,X,Y,Z are numbers in the range 0..65535
  436. setupversion = version.split('+', 1)[0]
  437. if sys.platform == 'darwin' and os.path.exists('/usr/bin/xcodebuild'):
  438. version = runcmd(['/usr/bin/xcodebuild', '-version'], {})[0].splitlines()
  439. if version:
  440. version = version[0]
  441. xcode4 = (version.startswith('Xcode') and
  442. StrictVersion(version.split()[1]) >= StrictVersion('4.0'))
  443. xcode51 = re.match(r'^Xcode\s+5\.1', version) is not None
  444. else:
  445. # xcodebuild returns empty on OS X Lion with XCode 4.3 not
  446. # installed, but instead with only command-line tools. Assume
  447. # that only happens on >= Lion, thus no PPC support.
  448. xcode4 = True
  449. xcode51 = False
  450. # XCode 4.0 dropped support for ppc architecture, which is hardcoded in
  451. # distutils.sysconfig
  452. if xcode4:
  453. os.environ['ARCHFLAGS'] = ''
  454. # XCode 5.1 changes clang such that it now fails to compile if the
  455. # -mno-fused-madd flag is passed, but the version of Python shipped with
  456. # OS X 10.9 Mavericks includes this flag. This causes problems in all
  457. # C extension modules, and a bug has been filed upstream at
  458. # http://bugs.python.org/issue21244. We also need to patch this here
  459. # so Mercurial can continue to compile in the meantime.
  460. if xcode51:
  461. cflags = get_config_var('CFLAGS')
  462. if cflags and re.search(r'-mno-fused-madd\b', cflags) is not None:
  463. os.environ['CFLAGS'] = (
  464. os.environ.get('CFLAGS', '') + ' -Qunused-arguments')
  465. setup(name='mercurial',
  466. version=setupversion,
  467. author='Matt Mackall and many others',
  468. author_email='mercurial@selenic.com',
  469. url='http://mercurial.selenic.com/',
  470. download_url='http://mercurial.selenic.com/release/',
  471. description=('Fast scalable distributed SCM (revision control, version '
  472. 'control) system'),
  473. long_description=('Mercurial is a distributed SCM tool written in Python.'
  474. ' It is used by a number of large projects that require'
  475. ' fast, reliable distributed revision control, such as '
  476. 'Mozilla.'),
  477. license='GNU GPLv2 or any later version',
  478. classifiers=[
  479. 'Development Status :: 6 - Mature',
  480. 'Environment :: Console',
  481. 'Intended Audience :: Developers',
  482. 'Intended Audience :: System Administrators',
  483. 'License :: OSI Approved :: GNU General Public License (GPL)',
  484. 'Natural Language :: Danish',
  485. 'Natural Language :: English',
  486. 'Natural Language :: German',
  487. 'Natural Language :: Italian',
  488. 'Natural Language :: Japanese',
  489. 'Natural Language :: Portuguese (Brazilian)',
  490. 'Operating System :: Microsoft :: Windows',
  491. 'Operating System :: OS Independent',
  492. 'Operating System :: POSIX',
  493. 'Programming Language :: C',
  494. 'Programming Language :: Python',
  495. 'Topic :: Software Development :: Version Control',
  496. ],
  497. scripts=scripts,
  498. packages=packages,
  499. py_modules=pymodules,
  500. ext_modules=extmodules,
  501. data_files=datafiles,
  502. package_data=packagedata,
  503. cmdclass=cmdclass,
  504. distclass=hgdist,
  505. options={'py2exe': {'packages': ['hgext', 'email']},
  506. 'bdist_mpkg': {'zipdist': True,
  507. 'license': 'COPYING',
  508. 'readme': 'contrib/macosx/Readme.html',
  509. 'welcome': 'contrib/macosx/Welcome.html',
  510. },
  511. },
  512. **extra)