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