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