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