##// END OF EJS Templates
setup: support executing with python3 including 2to3...
Simon Heimberg -
r15494:7a7a1c59 default
parent child Browse files
Show More
@@ -1,462 +1,484 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:
27 27 try:
28 28 import sha
29 29 except:
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:
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().lower().find("ironpython") != -1
43 43 except:
44 44 pass
45 45
46 46 if isironpython:
47 47 sys.stderr.write("warning: IronPython detected (no bz2 support)\n")
48 48 else:
49 49 try:
50 50 import bz2
51 51 except:
52 52 raise SystemExit(
53 53 "Couldn't import standard bz2 (incomplete Python install).")
54 54
55 55 import os, subprocess, time
56 56 import shutil
57 57 import tempfile
58 58 from distutils import log
59 59 from distutils.core import setup, Command, Extension
60 60 from distutils.dist import Distribution
61 61 from distutils.command.build import build
62 62 from distutils.command.build_ext import build_ext
63 63 from distutils.command.build_py import build_py
64 64 from distutils.command.install_scripts import install_scripts
65 65 from distutils.spawn import spawn, find_executable
66 66 from distutils.ccompiler import new_compiler
67 67 from distutils.errors import CCompilerError, DistutilsExecError
68 68 from distutils.sysconfig import get_python_inc
69 69 from distutils.version import StrictVersion
70 70
71 convert2to3 = '--c2to3' in sys.argv
72 if convert2to3:
73 try:
74 from distutils.command.build_py import build_py_2to3 as build_py
75 from lib2to3.refactor import get_fixers_from_package as getfixers
76 except ImportError:
77 if sys.version_info[0] < 3:
78 raise SystemExit("--c2to3 is only compatible with python3.")
79 raise
80 sys.path.append('contrib')
81
82
71 83 scripts = ['hg']
72 84 if os.name == 'nt':
73 85 scripts.append('contrib/win32/hg.bat')
74 86
75 87 # simplified version of distutils.ccompiler.CCompiler.has_function
76 88 # that actually removes its temporary files.
77 89 def hasfunction(cc, funcname):
78 90 tmpdir = tempfile.mkdtemp(prefix='hg-install-')
79 91 devnull = oldstderr = None
80 92 try:
81 93 try:
82 94 fname = os.path.join(tmpdir, 'funcname.c')
83 95 f = open(fname, 'w')
84 96 f.write('int main(void) {\n')
85 97 f.write(' %s();\n' % funcname)
86 98 f.write('}\n')
87 99 f.close()
88 100 # Redirect stderr to /dev/null to hide any error messages
89 101 # from the compiler.
90 102 # This will have to be changed if we ever have to check
91 103 # for a function on Windows.
92 104 devnull = open('/dev/null', 'w')
93 105 oldstderr = os.dup(sys.stderr.fileno())
94 106 os.dup2(devnull.fileno(), sys.stderr.fileno())
95 107 objects = cc.compile([fname], output_dir=tmpdir)
96 108 cc.link_executable(objects, os.path.join(tmpdir, "a.out"))
97 109 except:
98 110 return False
99 111 return True
100 112 finally:
101 113 if oldstderr is not None:
102 114 os.dup2(oldstderr, sys.stderr.fileno())
103 115 if devnull is not None:
104 116 devnull.close()
105 117 shutil.rmtree(tmpdir)
106 118
107 119 # py2exe needs to be installed to work
108 120 try:
109 121 import py2exe
110 122 py2exeloaded = True
111 123 except ImportError:
112 124 py2exeloaded = False
113 125
114 126 def runcmd(cmd, env):
115 127 p = subprocess.Popen(cmd, stdout=subprocess.PIPE,
116 128 stderr=subprocess.PIPE, env=env)
117 129 out, err = p.communicate()
118 130 return out, err
119 131
120 132 def runhg(cmd, env):
121 133 out, err = runcmd(cmd, env)
122 134 # If root is executing setup.py, but the repository is owned by
123 135 # another user (as in "sudo python setup.py install") we will get
124 136 # trust warnings since the .hg/hgrc file is untrusted. That is
125 137 # fine, we don't want to load it anyway. Python may warn about
126 138 # a missing __init__.py in mercurial/locale, we also ignore that.
127 139 err = [e for e in err.splitlines()
128 140 if not e.startswith(b('Not trusting file')) \
129 141 and not e.startswith(b('warning: Not importing'))]
130 142 if err:
131 143 return ''
132 144 return out
133 145
134 146 version = ''
135 147
136 148 # Execute hg out of this directory with a custom environment which
137 149 # includes the pure Python modules in mercurial/pure. We also take
138 150 # care to not use any hgrc files and do no localization.
139 151 pypath = ['mercurial', os.path.join('mercurial', 'pure')]
140 152 env = {'PYTHONPATH': os.pathsep.join(pypath),
141 153 'HGRCPATH': '',
142 154 'LANGUAGE': 'C'}
143 155 if 'LD_LIBRARY_PATH' in os.environ:
144 156 env['LD_LIBRARY_PATH'] = os.environ['LD_LIBRARY_PATH']
145 157 if 'SystemRoot' in os.environ:
146 158 # Copy SystemRoot into the custom environment for Python 2.6
147 159 # under Windows. Otherwise, the subprocess will fail with
148 160 # error 0xc0150004. See: http://bugs.python.org/issue3440
149 161 env['SystemRoot'] = os.environ['SystemRoot']
150 162
151 163 if os.path.isdir('.hg'):
152 164 cmd = [sys.executable, 'hg', 'id', '-i', '-t']
153 165 l = runhg(cmd, env).split()
154 166 while len(l) > 1 and l[-1][0].isalpha(): # remove non-numbered tags
155 167 l.pop()
156 168 if len(l) > 1: # tag found
157 169 version = l[-1]
158 170 if l[0].endswith('+'): # propagate the dirty status to the tag
159 171 version += '+'
160 172 elif len(l) == 1: # no tag found
161 173 cmd = [sys.executable, 'hg', 'parents', '--template',
162 174 '{latesttag}+{latesttagdistance}-']
163 175 version = runhg(cmd, env) + l[0]
164 176 if version.endswith('+'):
165 177 version += time.strftime('%Y%m%d')
166 178 elif os.path.exists('.hg_archival.txt'):
167 179 kw = dict([[t.strip() for t in l.split(':', 1)]
168 180 for l in open('.hg_archival.txt')])
169 181 if 'tag' in kw:
170 182 version = kw['tag']
171 183 elif 'latesttag' in kw:
172 184 version = '%(latesttag)s+%(latesttagdistance)s-%(node).12s' % kw
173 185 else:
174 186 version = kw.get('node', '')[:12]
175 187
176 188 if version:
177 189 f = open("mercurial/__version__.py", "w")
178 190 f.write('# this file is autogenerated by setup.py\n')
179 191 f.write('version = "%s"\n' % version)
180 192 f.close()
181 193
182 194
183 195 try:
184 196 from mercurial import __version__
185 197 version = __version__.version
186 198 except ImportError:
187 199 version = 'unknown'
188 200
189 201 class hgbuild(build):
190 202 # Insert hgbuildmo first so that files in mercurial/locale/ are found
191 203 # when build_py is run next.
192 204 sub_commands = [('build_mo', None),
205 # We also need build_ext before build_py. Otherwise, when 2to3 is called (in
206 # build_py), it will not find osutil & friends, thinking that those modules are
207 # global and, consequently, making a mess, now that all module imports are
208 # global.
209 ('build_ext', build.has_ext_modules),
193 210 ] + build.sub_commands
194 211
195 212 class hgbuildmo(Command):
196 213
197 214 description = "build translations (.mo files)"
198 215
199 216 def run(self):
200 217 if not find_executable('msgfmt'):
201 218 self.warn("could not find msgfmt executable, no translations "
202 219 "will be built")
203 220 return
204 221
205 222 podir = 'i18n'
206 223 if not os.path.isdir(podir):
207 224 self.warn("could not find %s/ directory" % podir)
208 225 return
209 226
210 227 join = os.path.join
211 228 for po in os.listdir(podir):
212 229 if not po.endswith('.po'):
213 230 continue
214 231 pofile = join(podir, po)
215 232 modir = join('locale', po[:-3], 'LC_MESSAGES')
216 233 mofile = join(modir, 'hg.mo')
217 234 mobuildfile = join('mercurial', mofile)
218 235 cmd = ['msgfmt', '-v', '-o', mobuildfile, pofile]
219 236 if sys.platform != 'sunos5':
220 237 # msgfmt on Solaris does not know about -c
221 238 cmd.append('-c')
222 239 self.mkpath(join('mercurial', modir))
223 240 self.make_file([pofile], mobuildfile, spawn, (cmd,))
224 241
225 242 def initialize_options(self):
226 243 pass
227 244
228 245 def finalize_options(self):
229 246 pass
230 247
231 248
232 249 class hgdist(Distribution):
233 250 pure = 0
234 251
235 252 global_options = Distribution.global_options + \
236 253 [('pure', None, "use pure (slow) Python "
237 254 "code instead of C extensions"),
255 ('c2to3', None, "(experimental!) convert "
256 "code with 2to3"),
238 257 ]
239 258
240 259 def has_ext_modules(self):
241 260 # self.ext_modules is emptied in hgbuildpy.finalize_options which is
242 261 # too late for some cases
243 262 return not self.pure and Distribution.has_ext_modules(self)
244 263
245 264 class hgbuildext(build_ext):
246 265
247 266 def build_extension(self, ext):
248 267 try:
249 268 build_ext.build_extension(self, ext)
250 269 except CCompilerError:
251 270 if not getattr(ext, 'optional', False):
252 271 raise
253 272 log.warn("Failed to build optional extension '%s' (skipping)",
254 273 ext.name)
255 274
256 275 class hgbuildpy(build_py):
276 if convert2to3:
277 fixer_names = sorted(set(getfixers("lib2to3.fixes") +
278 getfixers("hgfixes")))
257 279
258 280 def finalize_options(self):
259 281 build_py.finalize_options(self)
260 282
261 283 if self.distribution.pure:
262 284 if self.py_modules is None:
263 285 self.py_modules = []
264 286 for ext in self.distribution.ext_modules:
265 287 if ext.name.startswith("mercurial."):
266 288 self.py_modules.append("mercurial.pure.%s" % ext.name[10:])
267 289 self.distribution.ext_modules = []
268 290 else:
269 291 if not os.path.exists(os.path.join(get_python_inc(), 'Python.h')):
270 292 raise SystemExit("Python headers are required to build Mercurial")
271 293
272 294 def find_modules(self):
273 295 modules = build_py.find_modules(self)
274 296 for module in modules:
275 297 if module[0] == "mercurial.pure":
276 298 if module[1] != "__init__":
277 299 yield ("mercurial", module[1], module[2])
278 300 else:
279 301 yield module
280 302
281 303 class buildhgextindex(Command):
282 304 description = 'generate prebuilt index of hgext (for frozen package)'
283 305 user_options = []
284 306 _indexfilename = 'hgext/__index__.py'
285 307
286 308 def initialize_options(self):
287 309 pass
288 310
289 311 def finalize_options(self):
290 312 pass
291 313
292 314 def run(self):
293 315 if os.path.exists(self._indexfilename):
294 316 os.unlink(self._indexfilename)
295 317
296 318 # here no extension enabled, disabled() lists up everything
297 319 code = ('import pprint; from mercurial import extensions; '
298 320 'pprint.pprint(extensions.disabled())')
299 321 out, err = runcmd([sys.executable, '-c', code], env)
300 322 if err:
301 323 raise DistutilsExecError(err)
302 324
303 325 f = open(self._indexfilename, 'w')
304 326 f.write('# this file is autogenerated by setup.py\n')
305 327 f.write('docs = ')
306 328 f.write(out)
307 329 f.close()
308 330
309 331 class hginstallscripts(install_scripts):
310 332 '''
311 333 This is a specialization of install_scripts that replaces the @LIBDIR@ with
312 334 the configured directory for modules. If possible, the path is made relative
313 335 to the directory for scripts.
314 336 '''
315 337
316 338 def initialize_options(self):
317 339 install_scripts.initialize_options(self)
318 340
319 341 self.install_lib = None
320 342
321 343 def finalize_options(self):
322 344 install_scripts.finalize_options(self)
323 345 self.set_undefined_options('install',
324 346 ('install_lib', 'install_lib'))
325 347
326 348 def run(self):
327 349 install_scripts.run(self)
328 350
329 351 if (os.path.splitdrive(self.install_dir)[0] !=
330 352 os.path.splitdrive(self.install_lib)[0]):
331 353 # can't make relative paths from one drive to another, so use an
332 354 # absolute path instead
333 355 libdir = self.install_lib
334 356 else:
335 357 common = os.path.commonprefix((self.install_dir, self.install_lib))
336 358 rest = self.install_dir[len(common):]
337 359 uplevel = len([n for n in os.path.split(rest) if n])
338 360
339 361 libdir = uplevel * ('..' + os.sep) + self.install_lib[len(common):]
340 362
341 363 for outfile in self.outfiles:
342 364 fp = open(outfile, 'rb')
343 365 data = fp.read()
344 366 fp.close()
345 367
346 368 # skip binary files
347 if '\0' in data:
369 if b('\0') in data:
348 370 continue
349 371
350 372 data = data.replace('@LIBDIR@', libdir.encode('string_escape'))
351 373 fp = open(outfile, 'wb')
352 374 fp.write(data)
353 375 fp.close()
354 376
355 377 cmdclass = {'build': hgbuild,
356 378 'build_mo': hgbuildmo,
357 379 'build_ext': hgbuildext,
358 380 'build_py': hgbuildpy,
359 381 'build_hgextindex': buildhgextindex,
360 382 'install_scripts': hginstallscripts}
361 383
362 384 packages = ['mercurial', 'mercurial.hgweb',
363 385 'mercurial.httpclient', 'mercurial.httpclient.tests',
364 386 'hgext', 'hgext.convert', 'hgext.highlight', 'hgext.zeroconf',
365 387 'hgext.largefiles']
366 388
367 389 pymodules = []
368 390
369 391 extmodules = [
370 392 Extension('mercurial.base85', ['mercurial/base85.c']),
371 393 Extension('mercurial.bdiff', ['mercurial/bdiff.c']),
372 394 Extension('mercurial.diffhelpers', ['mercurial/diffhelpers.c']),
373 395 Extension('mercurial.mpatch', ['mercurial/mpatch.c']),
374 396 Extension('mercurial.parsers', ['mercurial/parsers.c']),
375 397 ]
376 398
377 399 osutil_ldflags = []
378 400
379 401 if sys.platform == 'darwin':
380 402 osutil_ldflags += ['-framework', 'ApplicationServices']
381 403
382 404 # disable osutil.c under windows + python 2.4 (issue1364)
383 405 if sys.platform == 'win32' and sys.version_info < (2, 5, 0, 'final'):
384 406 pymodules.append('mercurial.pure.osutil')
385 407 else:
386 408 extmodules.append(Extension('mercurial.osutil', ['mercurial/osutil.c'],
387 409 extra_link_args=osutil_ldflags))
388 410
389 411 if sys.platform.startswith('linux') and os.uname()[2] > '2.6':
390 412 # The inotify extension is only usable with Linux 2.6 kernels.
391 413 # You also need a reasonably recent C library.
392 414 # In any case, if it fails to build the error will be skipped ('optional').
393 415 cc = new_compiler()
394 416 if hasfunction(cc, 'inotify_add_watch'):
395 417 inotify = Extension('hgext.inotify.linux._inotify',
396 418 ['hgext/inotify/linux/_inotify.c'],
397 419 ['mercurial'])
398 420 inotify.optional = True
399 421 extmodules.append(inotify)
400 422 packages.extend(['hgext.inotify', 'hgext.inotify.linux'])
401 423
402 424 packagedata = {'mercurial': ['locale/*/LC_MESSAGES/hg.mo',
403 425 'help/*.txt']}
404 426
405 427 def ordinarypath(p):
406 428 return p and p[0] != '.' and p[-1] != '~'
407 429
408 430 for root in ('templates',):
409 431 for curdir, dirs, files in os.walk(os.path.join('mercurial', root)):
410 432 curdir = curdir.split(os.sep, 1)[1]
411 433 dirs[:] = filter(ordinarypath, dirs)
412 434 for f in filter(ordinarypath, files):
413 435 f = os.path.join(curdir, f)
414 436 packagedata['mercurial'].append(f)
415 437
416 438 datafiles = []
417 439 setupversion = version
418 440 extra = {}
419 441
420 442 if py2exeloaded:
421 443 extra['console'] = [
422 444 {'script':'hg',
423 445 'copyright':'Copyright (C) 2005-2010 Matt Mackall and others',
424 446 'product_version':version}]
425 447 # sub command of 'build' because 'py2exe' does not handle sub_commands
426 448 build.sub_commands.insert(0, ('build_hgextindex', None))
427 449
428 450 if os.name == 'nt':
429 451 # Windows binary file versions for exe/dll files must have the
430 452 # form W.X.Y.Z, where W,X,Y,Z are numbers in the range 0..65535
431 453 setupversion = version.split('+', 1)[0]
432 454
433 455 if sys.platform == 'darwin' and os.path.exists('/usr/bin/xcodebuild'):
434 456 # XCode 4.0 dropped support for ppc architecture, which is hardcoded in
435 457 # distutils.sysconfig
436 458 version = runcmd(['/usr/bin/xcodebuild', '-version'], {})[0].splitlines()[0]
437 459 # Also parse only first digit, because 3.2.1 can't be parsed nicely
438 460 if (version.startswith('Xcode') and
439 461 StrictVersion(version.split()[1]) >= StrictVersion('4.0')):
440 462 os.environ['ARCHFLAGS'] = ''
441 463
442 464 setup(name='mercurial',
443 465 version=setupversion,
444 466 author='Matt Mackall',
445 467 author_email='mpm@selenic.com',
446 468 url='http://mercurial.selenic.com/',
447 469 description='Scalable distributed SCM',
448 470 license='GNU GPLv2+',
449 471 scripts=scripts,
450 472 packages=packages,
451 473 py_modules=pymodules,
452 474 ext_modules=extmodules,
453 475 data_files=datafiles,
454 476 package_data=packagedata,
455 477 cmdclass=cmdclass,
456 478 distclass=hgdist,
457 479 options=dict(py2exe=dict(packages=['hgext', 'email']),
458 480 bdist_mpkg=dict(zipdist=True,
459 481 license='COPYING',
460 482 readme='contrib/macosx/Readme.html',
461 483 welcome='contrib/macosx/Welcome.html')),
462 484 **extra)
General Comments 0
You need to be logged in to leave comments. Login now