##// END OF EJS Templates
setup: compile hg.exe...
Adrian Buehlmann -
r17061:f20e4d76 default
parent child Browse files
Show More
@@ -1,498 +1,513
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.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 if err:
153 153 return ''
154 154 return out
155 155
156 156 version = ''
157 157
158 158 # Execute hg out of this directory with a custom environment which
159 159 # includes the pure Python modules in mercurial/pure. We also take
160 160 # care to not use any hgrc files and do no localization.
161 161 pypath = ['mercurial', os.path.join('mercurial', 'pure')]
162 162 env = {'PYTHONPATH': os.pathsep.join(pypath),
163 163 'HGRCPATH': '',
164 164 'LANGUAGE': 'C'}
165 165 if 'LD_LIBRARY_PATH' in os.environ:
166 166 env['LD_LIBRARY_PATH'] = os.environ['LD_LIBRARY_PATH']
167 167 if 'SystemRoot' in os.environ:
168 168 # Copy SystemRoot into the custom environment for Python 2.6
169 169 # under Windows. Otherwise, the subprocess will fail with
170 170 # error 0xc0150004. See: http://bugs.python.org/issue3440
171 171 env['SystemRoot'] = os.environ['SystemRoot']
172 172
173 173 if os.path.isdir('.hg'):
174 174 cmd = [sys.executable, 'hg', 'id', '-i', '-t']
175 175 l = runhg(cmd, env).split()
176 176 while len(l) > 1 and l[-1][0].isalpha(): # remove non-numbered tags
177 177 l.pop()
178 178 if len(l) > 1: # tag found
179 179 version = l[-1]
180 180 if l[0].endswith('+'): # propagate the dirty status to the tag
181 181 version += '+'
182 182 elif len(l) == 1: # no tag found
183 183 cmd = [sys.executable, 'hg', 'parents', '--template',
184 184 '{latesttag}+{latesttagdistance}-']
185 185 version = runhg(cmd, env) + l[0]
186 186 if version.endswith('+'):
187 187 version += time.strftime('%Y%m%d')
188 188 elif os.path.exists('.hg_archival.txt'):
189 189 kw = dict([[t.strip() for t in l.split(':', 1)]
190 190 for l in open('.hg_archival.txt')])
191 191 if 'tag' in kw:
192 192 version = kw['tag']
193 193 elif 'latesttag' in kw:
194 194 version = '%(latesttag)s+%(latesttagdistance)s-%(node).12s' % kw
195 195 else:
196 196 version = kw.get('node', '')[:12]
197 197
198 198 if version:
199 199 f = open("mercurial/__version__.py", "w")
200 200 f.write('# this file is autogenerated by setup.py\n')
201 201 f.write('version = "%s"\n' % version)
202 202 f.close()
203 203
204 204
205 205 try:
206 206 from mercurial import __version__
207 207 version = __version__.version
208 208 except ImportError:
209 209 version = 'unknown'
210 210
211 211 class hgbuild(build):
212 212 # Insert hgbuildmo first so that files in mercurial/locale/ are found
213 213 # when build_py is run next.
214 214 sub_commands = [('build_mo', None),
215 215
216 216 # We also need build_ext before build_py. Otherwise, when 2to3 is
217 217 # called (in build_py), it will not find osutil & friends,
218 218 # thinking that those modules are global and, consequently, making
219 219 # a mess, now that all module imports are global.
220 220
221 221 ('build_ext', build.has_ext_modules),
222 222 ] + build.sub_commands
223 223
224 224 class hgbuildmo(build):
225 225
226 226 description = "build translations (.mo files)"
227 227
228 228 def run(self):
229 229 if not find_executable('msgfmt'):
230 230 self.warn("could not find msgfmt executable, no translations "
231 231 "will be built")
232 232 return
233 233
234 234 podir = 'i18n'
235 235 if not os.path.isdir(podir):
236 236 self.warn("could not find %s/ directory" % podir)
237 237 return
238 238
239 239 join = os.path.join
240 240 for po in os.listdir(podir):
241 241 if not po.endswith('.po'):
242 242 continue
243 243 pofile = join(podir, po)
244 244 modir = join('locale', po[:-3], 'LC_MESSAGES')
245 245 mofile = join(modir, 'hg.mo')
246 246 mobuildfile = join('mercurial', mofile)
247 247 cmd = ['msgfmt', '-v', '-o', mobuildfile, pofile]
248 248 if sys.platform != 'sunos5':
249 249 # msgfmt on Solaris does not know about -c
250 250 cmd.append('-c')
251 251 self.mkpath(join('mercurial', modir))
252 252 self.make_file([pofile], mobuildfile, spawn, (cmd,))
253 253
254 254
255 255 class hgdist(Distribution):
256 256 pure = 0
257 257
258 258 global_options = Distribution.global_options + \
259 259 [('pure', None, "use pure (slow) Python "
260 260 "code instead of C extensions"),
261 261 ('c2to3', None, "(experimental!) convert "
262 262 "code with 2to3"),
263 263 ]
264 264
265 265 def has_ext_modules(self):
266 266 # self.ext_modules is emptied in hgbuildpy.finalize_options which is
267 267 # too late for some cases
268 268 return not self.pure and Distribution.has_ext_modules(self)
269 269
270 270 class hgbuildext(build_ext):
271 271
272 272 def build_extension(self, ext):
273 273 try:
274 274 build_ext.build_extension(self, ext)
275 275 except CCompilerError:
276 276 if not getattr(ext, 'optional', False):
277 277 raise
278 278 log.warn("Failed to build optional extension '%s' (skipping)",
279 279 ext.name)
280 280
281 281 class hgbuildpy(build_py):
282 282 if convert2to3:
283 283 fixer_names = sorted(set(getfixers("lib2to3.fixes") +
284 284 getfixers("hgfixes")))
285 285
286 286 def finalize_options(self):
287 287 build_py.finalize_options(self)
288 288
289 289 if self.distribution.pure:
290 290 if self.py_modules is None:
291 291 self.py_modules = []
292 292 for ext in self.distribution.ext_modules:
293 293 if ext.name.startswith("mercurial."):
294 294 self.py_modules.append("mercurial.pure.%s" % ext.name[10:])
295 295 self.distribution.ext_modules = []
296 296 else:
297 297 if not os.path.exists(os.path.join(get_python_inc(), 'Python.h')):
298 298 raise SystemExit('Python headers are required to build '
299 299 'Mercurial')
300 300
301 301 def find_modules(self):
302 302 modules = build_py.find_modules(self)
303 303 for module in modules:
304 304 if module[0] == "mercurial.pure":
305 305 if module[1] != "__init__":
306 306 yield ("mercurial", module[1], module[2])
307 307 else:
308 308 yield module
309 309
310 310 class buildhgextindex(Command):
311 311 description = 'generate prebuilt index of hgext (for frozen package)'
312 312 user_options = []
313 313 _indexfilename = 'hgext/__index__.py'
314 314
315 315 def initialize_options(self):
316 316 pass
317 317
318 318 def finalize_options(self):
319 319 pass
320 320
321 321 def run(self):
322 322 if os.path.exists(self._indexfilename):
323 323 os.unlink(self._indexfilename)
324 324
325 325 # here no extension enabled, disabled() lists up everything
326 326 code = ('import pprint; from mercurial import extensions; '
327 327 'pprint.pprint(extensions.disabled())')
328 328 out, err = runcmd([sys.executable, '-c', code], env)
329 329 if err:
330 330 raise DistutilsExecError(err)
331 331
332 332 f = open(self._indexfilename, 'w')
333 333 f.write('# this file is autogenerated by setup.py\n')
334 334 f.write('docs = ')
335 335 f.write(out)
336 336 f.close()
337 337
338 class buildhgexe(build_ext):
339 description = 'compile hg.exe from mercurial/exewrapper.c'
340
341 def build_extensions(self):
342 if os.name != 'nt':
343 return
344 objects = self.compiler.compile(['mercurial/exewrapper.c'],
345 output_dir=self.build_temp)
346 dir = os.path.dirname(self.get_ext_fullpath('dummy'))
347 target = os.path.join(dir, 'hg')
348 self.compiler.link_executable(objects, target,
349 output_dir=self.build_temp)
350
338 351 class hginstallscripts(install_scripts):
339 352 '''
340 353 This is a specialization of install_scripts that replaces the @LIBDIR@ with
341 354 the configured directory for modules. If possible, the path is made relative
342 355 to the directory for scripts.
343 356 '''
344 357
345 358 def initialize_options(self):
346 359 install_scripts.initialize_options(self)
347 360
348 361 self.install_lib = None
349 362
350 363 def finalize_options(self):
351 364 install_scripts.finalize_options(self)
352 365 self.set_undefined_options('install',
353 366 ('install_lib', 'install_lib'))
354 367
355 368 def run(self):
356 369 install_scripts.run(self)
357 370
358 371 if (os.path.splitdrive(self.install_dir)[0] !=
359 372 os.path.splitdrive(self.install_lib)[0]):
360 373 # can't make relative paths from one drive to another, so use an
361 374 # absolute path instead
362 375 libdir = self.install_lib
363 376 else:
364 377 common = os.path.commonprefix((self.install_dir, self.install_lib))
365 378 rest = self.install_dir[len(common):]
366 379 uplevel = len([n for n in os.path.split(rest) if n])
367 380
368 381 libdir = uplevel * ('..' + os.sep) + self.install_lib[len(common):]
369 382
370 383 for outfile in self.outfiles:
371 384 fp = open(outfile, 'rb')
372 385 data = fp.read()
373 386 fp.close()
374 387
375 388 # skip binary files
376 389 if b('\0') in data:
377 390 continue
378 391
379 392 data = data.replace('@LIBDIR@', libdir.encode('string_escape'))
380 393 fp = open(outfile, 'wb')
381 394 fp.write(data)
382 395 fp.close()
383 396
384 397 cmdclass = {'build': hgbuild,
385 398 'build_mo': hgbuildmo,
386 399 'build_ext': hgbuildext,
387 400 'build_py': hgbuildpy,
388 401 'build_hgextindex': buildhgextindex,
389 'install_scripts': hginstallscripts}
402 'install_scripts': hginstallscripts,
403 'build_hgexe': buildhgexe,
404 }
390 405
391 406 packages = ['mercurial', 'mercurial.hgweb', 'mercurial.httpclient',
392 407 'hgext', 'hgext.convert', 'hgext.highlight', 'hgext.zeroconf',
393 408 'hgext.largefiles']
394 409
395 410 pymodules = []
396 411
397 412 extmodules = [
398 413 Extension('mercurial.base85', ['mercurial/base85.c']),
399 414 Extension('mercurial.bdiff', ['mercurial/bdiff.c']),
400 415 Extension('mercurial.diffhelpers', ['mercurial/diffhelpers.c']),
401 416 Extension('mercurial.mpatch', ['mercurial/mpatch.c']),
402 417 Extension('mercurial.parsers', ['mercurial/parsers.c']),
403 418 ]
404 419
405 420 osutil_ldflags = []
406 421
407 422 if sys.platform == 'darwin':
408 423 osutil_ldflags += ['-framework', 'ApplicationServices']
409 424
410 425 # disable osutil.c under windows + python 2.4 (issue1364)
411 426 if sys.platform == 'win32' and sys.version_info < (2, 5, 0, 'final'):
412 427 pymodules.append('mercurial.pure.osutil')
413 428 else:
414 429 extmodules.append(Extension('mercurial.osutil', ['mercurial/osutil.c'],
415 430 extra_link_args=osutil_ldflags))
416 431
417 432 if sys.platform.startswith('linux') and os.uname()[2] > '2.6':
418 433 # The inotify extension is only usable with Linux 2.6 kernels.
419 434 # You also need a reasonably recent C library.
420 435 # In any case, if it fails to build the error will be skipped ('optional').
421 436 cc = new_compiler()
422 437 if hasfunction(cc, 'inotify_add_watch'):
423 438 inotify = Extension('hgext.inotify.linux._inotify',
424 439 ['hgext/inotify/linux/_inotify.c'],
425 440 ['mercurial'])
426 441 inotify.optional = True
427 442 extmodules.append(inotify)
428 443 packages.extend(['hgext.inotify', 'hgext.inotify.linux'])
429 444
430 445 packagedata = {'mercurial': ['locale/*/LC_MESSAGES/hg.mo',
431 446 'help/*.txt']}
432 447
433 448 def ordinarypath(p):
434 449 return p and p[0] != '.' and p[-1] != '~'
435 450
436 451 for root in ('templates',):
437 452 for curdir, dirs, files in os.walk(os.path.join('mercurial', root)):
438 453 curdir = curdir.split(os.sep, 1)[1]
439 454 dirs[:] = filter(ordinarypath, dirs)
440 455 for f in filter(ordinarypath, files):
441 456 f = os.path.join(curdir, f)
442 457 packagedata['mercurial'].append(f)
443 458
444 459 datafiles = []
445 460 setupversion = version
446 461 extra = {}
447 462
448 463 if py2exeloaded:
449 464 extra['console'] = [
450 465 {'script':'hg',
451 466 'copyright':'Copyright (C) 2005-2010 Matt Mackall and others',
452 467 'product_version':version}]
453 468 # sub command of 'build' because 'py2exe' does not handle sub_commands
454 469 build.sub_commands.insert(0, ('build_hgextindex', None))
455 470
456 471 if os.name == 'nt':
457 472 # Windows binary file versions for exe/dll files must have the
458 473 # form W.X.Y.Z, where W,X,Y,Z are numbers in the range 0..65535
459 474 setupversion = version.split('+', 1)[0]
460 475
461 476 if sys.platform == 'darwin' and os.path.exists('/usr/bin/xcodebuild'):
462 477 # XCode 4.0 dropped support for ppc architecture, which is hardcoded in
463 478 # distutils.sysconfig
464 479 version = runcmd(['/usr/bin/xcodebuild', '-version'], {})[0].splitlines()
465 480 if version:
466 481 version = version[0]
467 482 xcode4 = (version.startswith('Xcode') and
468 483 StrictVersion(version.split()[1]) >= StrictVersion('4.0'))
469 484 else:
470 485 # xcodebuild returns empty on OS X Lion with XCode 4.3 not
471 486 # installed, but instead with only command-line tools. Assume
472 487 # that only happens on >= Lion, thus no PPC support.
473 488 xcode4 = True
474 489
475 490 if xcode4:
476 491 os.environ['ARCHFLAGS'] = ''
477 492
478 493 setup(name='mercurial',
479 494 version=setupversion,
480 495 author='Matt Mackall',
481 496 author_email='mpm@selenic.com',
482 497 url='http://mercurial.selenic.com/',
483 498 description='Scalable distributed SCM',
484 499 license='GNU GPLv2+',
485 500 scripts=scripts,
486 501 packages=packages,
487 502 py_modules=pymodules,
488 503 ext_modules=extmodules,
489 504 data_files=datafiles,
490 505 package_data=packagedata,
491 506 cmdclass=cmdclass,
492 507 distclass=hgdist,
493 508 options=dict(py2exe=dict(packages=['hgext', 'email']),
494 509 bdist_mpkg=dict(zipdist=True,
495 510 license='COPYING',
496 511 readme='contrib/macosx/Readme.html',
497 512 welcome='contrib/macosx/Welcome.html')),
498 513 **extra)
General Comments 0
You need to be logged in to leave comments. Login now