##// END OF EJS Templates
setup: explicitly declare supported Python versions...
Augie Fackler -
r33588:e0bbe32d stable
parent child Browse files
Show More
@@ -1,895 +1,915 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 supportedpy = '~= 2.7'
8 if 'HGALLOWPYTHON3':
9 # Mercurial will never work on Python 3 before 3.5 due to a lack
10 # of % formatting on bytestrings, and can't work on 3.6.0 or 3.6.1
11 # due to a bug in % formatting in bytestrings.
12 #
13 # TODO: when we actually work on Python 3, use this string as the
14 # actual supportedpy string.
15 supportedpy = ','.join([
16 '>=2.7',
17 '!=3.0.*',
18 '!=3.1.*',
19 '!=3.2.*',
20 '!=3.3.*',
21 '!=3.4.*',
22 '!=3.6.0',
23 '!=3.6.1',
24 ])
25
7 26 import sys, platform
8 27 if sys.version_info < (2, 7, 0, 'final'):
9 28 raise SystemExit('Mercurial requires Python 2.7 or later.')
10 29
11 30 if sys.version_info[0] >= 3:
12 31 printf = eval('print')
13 32 libdir_escape = 'unicode_escape'
14 33 else:
15 34 libdir_escape = 'string_escape'
16 35 def printf(*args, **kwargs):
17 36 f = kwargs.get('file', sys.stdout)
18 37 end = kwargs.get('end', '\n')
19 38 f.write(b' '.join(args) + end)
20 39
21 40 # Solaris Python packaging brain damage
22 41 try:
23 42 import hashlib
24 43 sha = hashlib.sha1()
25 44 except ImportError:
26 45 try:
27 46 import sha
28 47 sha.sha # silence unused import warning
29 48 except ImportError:
30 49 raise SystemExit(
31 50 "Couldn't import standard hashlib (incomplete Python install).")
32 51
33 52 try:
34 53 import zlib
35 54 zlib.compressobj # silence unused import warning
36 55 except ImportError:
37 56 raise SystemExit(
38 57 "Couldn't import standard zlib (incomplete Python install).")
39 58
40 59 # The base IronPython distribution (as of 2.7.1) doesn't support bz2
41 60 isironpython = False
42 61 try:
43 62 isironpython = (platform.python_implementation()
44 63 .lower().find("ironpython") != -1)
45 64 except AttributeError:
46 65 pass
47 66
48 67 if isironpython:
49 68 sys.stderr.write("warning: IronPython detected (no bz2 support)\n")
50 69 else:
51 70 try:
52 71 import bz2
53 72 bz2.BZ2Compressor # silence unused import warning
54 73 except ImportError:
55 74 raise SystemExit(
56 75 "Couldn't import standard bz2 (incomplete Python install).")
57 76
58 77 ispypy = "PyPy" in sys.version
59 78
60 79 import ctypes
61 80 import os, stat, subprocess, time
62 81 import re
63 82 import shutil
64 83 import tempfile
65 84 from distutils import log
66 85 # We have issues with setuptools on some platforms and builders. Until
67 86 # those are resolved, setuptools is opt-in except for platforms where
68 87 # we don't have issues.
69 88 if os.name == 'nt' or 'FORCE_SETUPTOOLS' in os.environ:
70 89 from setuptools import setup
71 90 else:
72 91 from distutils.core import setup
73 92 from distutils.ccompiler import new_compiler
74 93 from distutils.core import Command, Extension
75 94 from distutils.dist import Distribution
76 95 from distutils.command.build import build
77 96 from distutils.command.build_ext import build_ext
78 97 from distutils.command.build_py import build_py
79 98 from distutils.command.build_scripts import build_scripts
80 99 from distutils.command.install import install
81 100 from distutils.command.install_lib import install_lib
82 101 from distutils.command.install_scripts import install_scripts
83 102 from distutils.spawn import spawn, find_executable
84 103 from distutils import file_util
85 104 from distutils.errors import (
86 105 CCompilerError,
87 106 DistutilsError,
88 107 DistutilsExecError,
89 108 )
90 109 from distutils.sysconfig import get_python_inc, get_config_var
91 110 from distutils.version import StrictVersion
92 111
93 112 scripts = ['hg']
94 113 if os.name == 'nt':
95 114 # We remove hg.bat if we are able to build hg.exe.
96 115 scripts.append('contrib/win32/hg.bat')
97 116
98 117 def cancompile(cc, code):
99 118 tmpdir = tempfile.mkdtemp(prefix='hg-install-')
100 119 devnull = oldstderr = None
101 120 try:
102 121 fname = os.path.join(tmpdir, 'testcomp.c')
103 122 f = open(fname, 'w')
104 123 f.write(code)
105 124 f.close()
106 125 # Redirect stderr to /dev/null to hide any error messages
107 126 # from the compiler.
108 127 # This will have to be changed if we ever have to check
109 128 # for a function on Windows.
110 129 devnull = open('/dev/null', 'w')
111 130 oldstderr = os.dup(sys.stderr.fileno())
112 131 os.dup2(devnull.fileno(), sys.stderr.fileno())
113 132 objects = cc.compile([fname], output_dir=tmpdir)
114 133 cc.link_executable(objects, os.path.join(tmpdir, "a.out"))
115 134 return True
116 135 except Exception:
117 136 return False
118 137 finally:
119 138 if oldstderr is not None:
120 139 os.dup2(oldstderr, sys.stderr.fileno())
121 140 if devnull is not None:
122 141 devnull.close()
123 142 shutil.rmtree(tmpdir)
124 143
125 144 # simplified version of distutils.ccompiler.CCompiler.has_function
126 145 # that actually removes its temporary files.
127 146 def hasfunction(cc, funcname):
128 147 code = 'int main(void) { %s(); }\n' % funcname
129 148 return cancompile(cc, code)
130 149
131 150 def hasheader(cc, headername):
132 151 code = '#include <%s>\nint main(void) { return 0; }\n' % headername
133 152 return cancompile(cc, code)
134 153
135 154 # py2exe needs to be installed to work
136 155 try:
137 156 import py2exe
138 157 py2exe.Distribution # silence unused import warning
139 158 py2exeloaded = True
140 159 # import py2exe's patched Distribution class
141 160 from distutils.core import Distribution
142 161 except ImportError:
143 162 py2exeloaded = False
144 163
145 164 def runcmd(cmd, env):
146 165 p = subprocess.Popen(cmd, stdout=subprocess.PIPE,
147 166 stderr=subprocess.PIPE, env=env)
148 167 out, err = p.communicate()
149 168 return p.returncode, out, err
150 169
151 170 class hgcommand(object):
152 171 def __init__(self, cmd, env):
153 172 self.cmd = cmd
154 173 self.env = env
155 174
156 175 def run(self, args):
157 176 cmd = self.cmd + args
158 177 returncode, out, err = runcmd(cmd, self.env)
159 178 # If root is executing setup.py, but the repository is owned by
160 179 # another user (as in "sudo python setup.py install") we will get
161 180 # trust warnings since the .hg/hgrc file is untrusted. That is
162 181 # fine, we don't want to load it anyway. Python may warn about
163 182 # a missing __init__.py in mercurial/locale, we also ignore that.
164 183 err = [e for e in err.splitlines()
165 184 if not e.startswith(b'not trusting file') \
166 185 and not e.startswith(b'warning: Not importing') \
167 186 and not e.startswith(b'obsolete feature not enabled')]
168 187 if err or returncode != 0:
169 188 printf("stderr from '%s':" % (' '.join(cmd)), file=sys.stderr)
170 189 printf(b'\n'.join([b' ' + e for e in err]), file=sys.stderr)
171 190 return ''
172 191 return out
173 192
174 193 def findhg():
175 194 """Try to figure out how we should invoke hg for examining the local
176 195 repository contents.
177 196
178 197 Returns an hgcommand object."""
179 198 # By default, prefer the "hg" command in the user's path. This was
180 199 # presumably the hg command that the user used to create this repository.
181 200 #
182 201 # This repository may require extensions or other settings that would not
183 202 # be enabled by running the hg script directly from this local repository.
184 203 hgenv = os.environ.copy()
185 204 # Use HGPLAIN to disable hgrc settings that would change output formatting,
186 205 # and disable localization for the same reasons.
187 206 hgenv['HGPLAIN'] = '1'
188 207 hgenv['LANGUAGE'] = 'C'
189 208 hgcmd = ['hg']
190 209 # Run a simple "hg log" command just to see if using hg from the user's
191 210 # path works and can successfully interact with this repository.
192 211 check_cmd = ['log', '-r.', '-Ttest']
193 212 try:
194 213 retcode, out, err = runcmd(hgcmd + check_cmd, hgenv)
195 214 except EnvironmentError:
196 215 retcode = -1
197 216 if retcode == 0:
198 217 return hgcommand(hgcmd, hgenv)
199 218
200 219 # Fall back to trying the local hg installation.
201 220 hgenv = localhgenv()
202 221 # Don't source any system hgrc files when using the local hg.
203 222 hgenv['HGRCPATH'] = ''
204 223 hgcmd = [sys.executable, 'hg']
205 224 try:
206 225 retcode, out, err = runcmd(hgcmd + check_cmd, hgenv)
207 226 except EnvironmentError:
208 227 retcode = -1
209 228 if retcode == 0:
210 229 return hgcommand(hgcmd, hgenv)
211 230
212 231 raise SystemExit('Unable to find a working hg binary to extract the '
213 232 'version from the repository tags')
214 233
215 234 def localhgenv():
216 235 """Get an environment dictionary to use for invoking or importing
217 236 mercurial from the local repository."""
218 237 # Execute hg out of this directory with a custom environment which takes
219 238 # care to not use any hgrc files and do no localization.
220 239 env = {'HGMODULEPOLICY': 'py',
221 240 'HGRCPATH': '',
222 241 'LANGUAGE': 'C',
223 242 'PATH': ''} # make pypi modules that use os.environ['PATH'] happy
224 243 if 'LD_LIBRARY_PATH' in os.environ:
225 244 env['LD_LIBRARY_PATH'] = os.environ['LD_LIBRARY_PATH']
226 245 if 'SystemRoot' in os.environ:
227 246 # SystemRoot is required by Windows to load various DLLs. See:
228 247 # https://bugs.python.org/issue13524#msg148850
229 248 env['SystemRoot'] = os.environ['SystemRoot']
230 249 return env
231 250
232 251 version = ''
233 252
234 253 if os.path.isdir('.hg'):
235 254 hg = findhg()
236 255 cmd = ['log', '-r', '.', '--template', '{tags}\n']
237 256 numerictags = [t for t in hg.run(cmd).split() if t[0:1].isdigit()]
238 257 hgid = hg.run(['id', '-i']).strip()
239 258 if not hgid:
240 259 # Bail out if hg is having problems interacting with this repository,
241 260 # rather than falling through and producing a bogus version number.
242 261 # Continuing with an invalid version number will break extensions
243 262 # that define minimumhgversion.
244 263 raise SystemExit('Unable to determine hg version from local repository')
245 264 if numerictags: # tag(s) found
246 265 version = numerictags[-1]
247 266 if hgid.endswith('+'): # propagate the dirty status to the tag
248 267 version += '+'
249 268 else: # no tag found
250 269 ltagcmd = ['parents', '--template', '{latesttag}']
251 270 ltag = hg.run(ltagcmd)
252 271 changessincecmd = ['log', '-T', 'x\n', '-r', "only(.,'%s')" % ltag]
253 272 changessince = len(hg.run(changessincecmd).splitlines())
254 273 version = '%s+%s-%s' % (ltag, changessince, hgid)
255 274 if version.endswith('+'):
256 275 version += time.strftime('%Y%m%d')
257 276 elif os.path.exists('.hg_archival.txt'):
258 277 kw = dict([[t.strip() for t in l.split(':', 1)]
259 278 for l in open('.hg_archival.txt')])
260 279 if 'tag' in kw:
261 280 version = kw['tag']
262 281 elif 'latesttag' in kw:
263 282 if 'changessincelatesttag' in kw:
264 283 version = '%(latesttag)s+%(changessincelatesttag)s-%(node).12s' % kw
265 284 else:
266 285 version = '%(latesttag)s+%(latesttagdistance)s-%(node).12s' % kw
267 286 else:
268 287 version = kw.get('node', '')[:12]
269 288
270 289 if version:
271 290 with open("mercurial/__version__.py", "w") as f:
272 291 f.write('# this file is autogenerated by setup.py\n')
273 292 f.write('version = "%s"\n' % version)
274 293
275 294 try:
276 295 oldpolicy = os.environ.get('HGMODULEPOLICY', None)
277 296 os.environ['HGMODULEPOLICY'] = 'py'
278 297 from mercurial import __version__
279 298 version = __version__.version
280 299 except ImportError:
281 300 version = 'unknown'
282 301 finally:
283 302 if oldpolicy is None:
284 303 del os.environ['HGMODULEPOLICY']
285 304 else:
286 305 os.environ['HGMODULEPOLICY'] = oldpolicy
287 306
288 307 class hgbuild(build):
289 308 # Insert hgbuildmo first so that files in mercurial/locale/ are found
290 309 # when build_py is run next.
291 310 sub_commands = [('build_mo', None)] + build.sub_commands
292 311
293 312 class hgbuildmo(build):
294 313
295 314 description = "build translations (.mo files)"
296 315
297 316 def run(self):
298 317 if not find_executable('msgfmt'):
299 318 self.warn("could not find msgfmt executable, no translations "
300 319 "will be built")
301 320 return
302 321
303 322 podir = 'i18n'
304 323 if not os.path.isdir(podir):
305 324 self.warn("could not find %s/ directory" % podir)
306 325 return
307 326
308 327 join = os.path.join
309 328 for po in os.listdir(podir):
310 329 if not po.endswith('.po'):
311 330 continue
312 331 pofile = join(podir, po)
313 332 modir = join('locale', po[:-3], 'LC_MESSAGES')
314 333 mofile = join(modir, 'hg.mo')
315 334 mobuildfile = join('mercurial', mofile)
316 335 cmd = ['msgfmt', '-v', '-o', mobuildfile, pofile]
317 336 if sys.platform != 'sunos5':
318 337 # msgfmt on Solaris does not know about -c
319 338 cmd.append('-c')
320 339 self.mkpath(join('mercurial', modir))
321 340 self.make_file([pofile], mobuildfile, spawn, (cmd,))
322 341
323 342
324 343 class hgdist(Distribution):
325 344 pure = False
326 345 cffi = ispypy
327 346
328 347 global_options = Distribution.global_options + \
329 348 [('pure', None, "use pure (slow) Python "
330 349 "code instead of C extensions"),
331 350 ]
332 351
333 352 def has_ext_modules(self):
334 353 # self.ext_modules is emptied in hgbuildpy.finalize_options which is
335 354 # too late for some cases
336 355 return not self.pure and Distribution.has_ext_modules(self)
337 356
338 357 # This is ugly as a one-liner. So use a variable.
339 358 buildextnegops = dict(getattr(build_ext, 'negative_options', {}))
340 359 buildextnegops['no-zstd'] = 'zstd'
341 360
342 361 class hgbuildext(build_ext):
343 362 user_options = build_ext.user_options + [
344 363 ('zstd', None, 'compile zstd bindings [default]'),
345 364 ('no-zstd', None, 'do not compile zstd bindings'),
346 365 ]
347 366
348 367 boolean_options = build_ext.boolean_options + ['zstd']
349 368 negative_opt = buildextnegops
350 369
351 370 def initialize_options(self):
352 371 self.zstd = True
353 372 return build_ext.initialize_options(self)
354 373
355 374 def build_extensions(self):
356 375 # Filter out zstd if disabled via argument.
357 376 if not self.zstd:
358 377 self.extensions = [e for e in self.extensions
359 378 if e.name != 'mercurial.zstd']
360 379
361 380 return build_ext.build_extensions(self)
362 381
363 382 def build_extension(self, ext):
364 383 try:
365 384 build_ext.build_extension(self, ext)
366 385 except CCompilerError:
367 386 if not getattr(ext, 'optional', False):
368 387 raise
369 388 log.warn("Failed to build optional extension '%s' (skipping)",
370 389 ext.name)
371 390
372 391 class hgbuildscripts(build_scripts):
373 392 def run(self):
374 393 if os.name != 'nt' or self.distribution.pure:
375 394 return build_scripts.run(self)
376 395
377 396 exebuilt = False
378 397 try:
379 398 self.run_command('build_hgexe')
380 399 exebuilt = True
381 400 except (DistutilsError, CCompilerError):
382 401 log.warn('failed to build optional hg.exe')
383 402
384 403 if exebuilt:
385 404 # Copying hg.exe to the scripts build directory ensures it is
386 405 # installed by the install_scripts command.
387 406 hgexecommand = self.get_finalized_command('build_hgexe')
388 407 dest = os.path.join(self.build_dir, 'hg.exe')
389 408 self.mkpath(self.build_dir)
390 409 self.copy_file(hgexecommand.hgexepath, dest)
391 410
392 411 # Remove hg.bat because it is redundant with hg.exe.
393 412 self.scripts.remove('contrib/win32/hg.bat')
394 413
395 414 return build_scripts.run(self)
396 415
397 416 class hgbuildpy(build_py):
398 417 def finalize_options(self):
399 418 build_py.finalize_options(self)
400 419
401 420 if self.distribution.pure:
402 421 self.distribution.ext_modules = []
403 422 elif self.distribution.cffi:
404 423 from mercurial.cffi import (
405 424 bdiffbuild,
406 425 mpatchbuild,
407 426 )
408 427 exts = [mpatchbuild.ffi.distutils_extension(),
409 428 bdiffbuild.ffi.distutils_extension()]
410 429 # cffi modules go here
411 430 if sys.platform == 'darwin':
412 431 from mercurial.cffi import osutilbuild
413 432 exts.append(osutilbuild.ffi.distutils_extension())
414 433 self.distribution.ext_modules = exts
415 434 else:
416 435 h = os.path.join(get_python_inc(), 'Python.h')
417 436 if not os.path.exists(h):
418 437 raise SystemExit('Python headers are required to build '
419 438 'Mercurial but weren\'t found in %s' % h)
420 439
421 440 def run(self):
422 441 basepath = os.path.join(self.build_lib, 'mercurial')
423 442 self.mkpath(basepath)
424 443
425 444 if self.distribution.pure:
426 445 modulepolicy = 'py'
427 446 elif self.build_lib == '.':
428 447 # in-place build should run without rebuilding C extensions
429 448 modulepolicy = 'allow'
430 449 else:
431 450 modulepolicy = 'c'
432 451 with open(os.path.join(basepath, '__modulepolicy__.py'), "w") as f:
433 452 f.write('# this file is autogenerated by setup.py\n')
434 453 f.write('modulepolicy = b"%s"\n' % modulepolicy)
435 454
436 455 build_py.run(self)
437 456
438 457 class buildhgextindex(Command):
439 458 description = 'generate prebuilt index of hgext (for frozen package)'
440 459 user_options = []
441 460 _indexfilename = 'hgext/__index__.py'
442 461
443 462 def initialize_options(self):
444 463 pass
445 464
446 465 def finalize_options(self):
447 466 pass
448 467
449 468 def run(self):
450 469 if os.path.exists(self._indexfilename):
451 470 with open(self._indexfilename, 'w') as f:
452 471 f.write('# empty\n')
453 472
454 473 # here no extension enabled, disabled() lists up everything
455 474 code = ('import pprint; from mercurial import extensions; '
456 475 'pprint.pprint(extensions.disabled())')
457 476 returncode, out, err = runcmd([sys.executable, '-c', code],
458 477 localhgenv())
459 478 if err or returncode != 0:
460 479 raise DistutilsExecError(err)
461 480
462 481 with open(self._indexfilename, 'w') as f:
463 482 f.write('# this file is autogenerated by setup.py\n')
464 483 f.write('docs = ')
465 484 f.write(out)
466 485
467 486 class buildhgexe(build_ext):
468 487 description = 'compile hg.exe from mercurial/exewrapper.c'
469 488
470 489 def build_extensions(self):
471 490 if os.name != 'nt':
472 491 return
473 492 if isinstance(self.compiler, HackedMingw32CCompiler):
474 493 self.compiler.compiler_so = self.compiler.compiler # no -mdll
475 494 self.compiler.dll_libraries = [] # no -lmsrvc90
476 495
477 496 # Different Python installs can have different Python library
478 497 # names. e.g. the official CPython distribution uses pythonXY.dll
479 498 # and MinGW uses libpythonX.Y.dll.
480 499 _kernel32 = ctypes.windll.kernel32
481 500 _kernel32.GetModuleFileNameA.argtypes = [ctypes.c_void_p,
482 501 ctypes.c_void_p,
483 502 ctypes.c_ulong]
484 503 _kernel32.GetModuleFileNameA.restype = ctypes.c_ulong
485 504 size = 1000
486 505 buf = ctypes.create_string_buffer(size + 1)
487 506 filelen = _kernel32.GetModuleFileNameA(sys.dllhandle, ctypes.byref(buf),
488 507 size)
489 508
490 509 if filelen > 0 and filelen != size:
491 510 dllbasename = os.path.basename(buf.value)
492 511 if not dllbasename.lower().endswith('.dll'):
493 512 raise SystemExit('Python DLL does not end with .dll: %s' %
494 513 dllbasename)
495 514 pythonlib = dllbasename[:-4]
496 515 else:
497 516 log.warn('could not determine Python DLL filename; '
498 517 'assuming pythonXY')
499 518
500 519 hv = sys.hexversion
501 520 pythonlib = 'python%d%d' % (hv >> 24, (hv >> 16) & 0xff)
502 521
503 522 log.info('using %s as Python library name' % pythonlib)
504 523 with open('mercurial/hgpythonlib.h', 'wb') as f:
505 524 f.write('/* this file is autogenerated by setup.py */\n')
506 525 f.write('#define HGPYTHONLIB "%s"\n' % pythonlib)
507 526 objects = self.compiler.compile(['mercurial/exewrapper.c'],
508 527 output_dir=self.build_temp)
509 528 dir = os.path.dirname(self.get_ext_fullpath('dummy'))
510 529 target = os.path.join(dir, 'hg')
511 530 self.compiler.link_executable(objects, target,
512 531 libraries=[],
513 532 output_dir=self.build_temp)
514 533
515 534 @property
516 535 def hgexepath(self):
517 536 dir = os.path.dirname(self.get_ext_fullpath('dummy'))
518 537 return os.path.join(self.build_temp, dir, 'hg.exe')
519 538
520 539 class hginstall(install):
521 540
522 541 user_options = install.user_options + [
523 542 ('old-and-unmanageable', None,
524 543 'noop, present for eggless setuptools compat'),
525 544 ('single-version-externally-managed', None,
526 545 'noop, present for eggless setuptools compat'),
527 546 ]
528 547
529 548 # Also helps setuptools not be sad while we refuse to create eggs.
530 549 single_version_externally_managed = True
531 550
532 551 def get_sub_commands(self):
533 552 # Screen out egg related commands to prevent egg generation. But allow
534 553 # mercurial.egg-info generation, since that is part of modern
535 554 # packaging.
536 555 excl = {'bdist_egg'}
537 556 return filter(lambda x: x not in excl, install.get_sub_commands(self))
538 557
539 558 class hginstalllib(install_lib):
540 559 '''
541 560 This is a specialization of install_lib that replaces the copy_file used
542 561 there so that it supports setting the mode of files after copying them,
543 562 instead of just preserving the mode that the files originally had. If your
544 563 system has a umask of something like 027, preserving the permissions when
545 564 copying will lead to a broken install.
546 565
547 566 Note that just passing keep_permissions=False to copy_file would be
548 567 insufficient, as it might still be applying a umask.
549 568 '''
550 569
551 570 def run(self):
552 571 realcopyfile = file_util.copy_file
553 572 def copyfileandsetmode(*args, **kwargs):
554 573 src, dst = args[0], args[1]
555 574 dst, copied = realcopyfile(*args, **kwargs)
556 575 if copied:
557 576 st = os.stat(src)
558 577 # Persist executable bit (apply it to group and other if user
559 578 # has it)
560 579 if st[stat.ST_MODE] & stat.S_IXUSR:
561 580 setmode = int('0755', 8)
562 581 else:
563 582 setmode = int('0644', 8)
564 583 m = stat.S_IMODE(st[stat.ST_MODE])
565 584 m = (m & ~int('0777', 8)) | setmode
566 585 os.chmod(dst, m)
567 586 file_util.copy_file = copyfileandsetmode
568 587 try:
569 588 install_lib.run(self)
570 589 finally:
571 590 file_util.copy_file = realcopyfile
572 591
573 592 class hginstallscripts(install_scripts):
574 593 '''
575 594 This is a specialization of install_scripts that replaces the @LIBDIR@ with
576 595 the configured directory for modules. If possible, the path is made relative
577 596 to the directory for scripts.
578 597 '''
579 598
580 599 def initialize_options(self):
581 600 install_scripts.initialize_options(self)
582 601
583 602 self.install_lib = None
584 603
585 604 def finalize_options(self):
586 605 install_scripts.finalize_options(self)
587 606 self.set_undefined_options('install',
588 607 ('install_lib', 'install_lib'))
589 608
590 609 def run(self):
591 610 install_scripts.run(self)
592 611
593 612 # It only makes sense to replace @LIBDIR@ with the install path if
594 613 # the install path is known. For wheels, the logic below calculates
595 614 # the libdir to be "../..". This is because the internal layout of a
596 615 # wheel archive looks like:
597 616 #
598 617 # mercurial-3.6.1.data/scripts/hg
599 618 # mercurial/__init__.py
600 619 #
601 620 # When installing wheels, the subdirectories of the "<pkg>.data"
602 621 # directory are translated to system local paths and files therein
603 622 # are copied in place. The mercurial/* files are installed into the
604 623 # site-packages directory. However, the site-packages directory
605 624 # isn't known until wheel install time. This means we have no clue
606 625 # at wheel generation time what the installed site-packages directory
607 626 # will be. And, wheels don't appear to provide the ability to register
608 627 # custom code to run during wheel installation. This all means that
609 628 # we can't reliably set the libdir in wheels: the default behavior
610 629 # of looking in sys.path must do.
611 630
612 631 if (os.path.splitdrive(self.install_dir)[0] !=
613 632 os.path.splitdrive(self.install_lib)[0]):
614 633 # can't make relative paths from one drive to another, so use an
615 634 # absolute path instead
616 635 libdir = self.install_lib
617 636 else:
618 637 common = os.path.commonprefix((self.install_dir, self.install_lib))
619 638 rest = self.install_dir[len(common):]
620 639 uplevel = len([n for n in os.path.split(rest) if n])
621 640
622 641 libdir = uplevel * ('..' + os.sep) + self.install_lib[len(common):]
623 642
624 643 for outfile in self.outfiles:
625 644 with open(outfile, 'rb') as fp:
626 645 data = fp.read()
627 646
628 647 # skip binary files
629 648 if b'\0' in data:
630 649 continue
631 650
632 651 # During local installs, the shebang will be rewritten to the final
633 652 # install path. During wheel packaging, the shebang has a special
634 653 # value.
635 654 if data.startswith(b'#!python'):
636 655 log.info('not rewriting @LIBDIR@ in %s because install path '
637 656 'not known' % outfile)
638 657 continue
639 658
640 659 data = data.replace(b'@LIBDIR@', libdir.encode(libdir_escape))
641 660 with open(outfile, 'wb') as fp:
642 661 fp.write(data)
643 662
644 663 cmdclass = {'build': hgbuild,
645 664 'build_mo': hgbuildmo,
646 665 'build_ext': hgbuildext,
647 666 'build_py': hgbuildpy,
648 667 'build_scripts': hgbuildscripts,
649 668 'build_hgextindex': buildhgextindex,
650 669 'install': hginstall,
651 670 'install_lib': hginstalllib,
652 671 'install_scripts': hginstallscripts,
653 672 'build_hgexe': buildhgexe,
654 673 }
655 674
656 675 packages = ['mercurial',
657 676 'mercurial.cext',
658 677 'mercurial.cffi',
659 678 'mercurial.hgweb',
660 679 'mercurial.httpclient',
661 680 'mercurial.pure',
662 681 'hgext', 'hgext.convert', 'hgext.fsmonitor',
663 682 'hgext.fsmonitor.pywatchman', 'hgext.highlight',
664 683 'hgext.largefiles', 'hgext.zeroconf', 'hgext3rd',
665 684 'hgdemandimport']
666 685
667 686 common_depends = ['mercurial/bitmanipulation.h',
668 687 'mercurial/compat.h',
669 688 'mercurial/cext/util.h']
670 689 common_include_dirs = ['mercurial']
671 690
672 691 osutil_cflags = []
673 692 osutil_ldflags = []
674 693
675 694 # platform specific macros
676 695 for plat, func in [('bsd', 'setproctitle')]:
677 696 if re.search(plat, sys.platform) and hasfunction(new_compiler(), func):
678 697 osutil_cflags.append('-DHAVE_%s' % func.upper())
679 698
680 699 for plat, macro, code in [
681 700 ('bsd|darwin', 'BSD_STATFS', '''
682 701 #include <sys/param.h>
683 702 #include <sys/mount.h>
684 703 int main() { struct statfs s; return sizeof(s.f_fstypename); }
685 704 '''),
686 705 ('linux', 'LINUX_STATFS', '''
687 706 #include <linux/magic.h>
688 707 #include <sys/vfs.h>
689 708 int main() { struct statfs s; return sizeof(s.f_type); }
690 709 '''),
691 710 ]:
692 711 if re.search(plat, sys.platform) and cancompile(new_compiler(), code):
693 712 osutil_cflags.append('-DHAVE_%s' % macro)
694 713
695 714 if sys.platform == 'darwin':
696 715 osutil_ldflags += ['-framework', 'ApplicationServices']
697 716
698 717 extmodules = [
699 718 Extension('mercurial.cext.base85', ['mercurial/cext/base85.c'],
700 719 include_dirs=common_include_dirs,
701 720 depends=common_depends),
702 721 Extension('mercurial.cext.bdiff', ['mercurial/bdiff.c',
703 722 'mercurial/cext/bdiff.c'],
704 723 include_dirs=common_include_dirs,
705 724 depends=common_depends + ['mercurial/bdiff.h']),
706 725 Extension('mercurial.cext.diffhelpers', ['mercurial/cext/diffhelpers.c'],
707 726 include_dirs=common_include_dirs,
708 727 depends=common_depends),
709 728 Extension('mercurial.cext.mpatch', ['mercurial/mpatch.c',
710 729 'mercurial/cext/mpatch.c'],
711 730 include_dirs=common_include_dirs,
712 731 depends=common_depends),
713 732 Extension('mercurial.cext.parsers', ['mercurial/cext/dirs.c',
714 733 'mercurial/cext/manifest.c',
715 734 'mercurial/cext/parsers.c',
716 735 'mercurial/cext/pathencode.c',
717 736 'mercurial/cext/revlog.c'],
718 737 include_dirs=common_include_dirs,
719 738 depends=common_depends),
720 739 Extension('mercurial.cext.osutil', ['mercurial/cext/osutil.c'],
721 740 include_dirs=common_include_dirs,
722 741 extra_compile_args=osutil_cflags,
723 742 extra_link_args=osutil_ldflags,
724 743 depends=common_depends),
725 744 Extension('hgext.fsmonitor.pywatchman.bser',
726 745 ['hgext/fsmonitor/pywatchman/bser.c']),
727 746 ]
728 747
729 748 sys.path.insert(0, 'contrib/python-zstandard')
730 749 import setup_zstd
731 750 extmodules.append(setup_zstd.get_c_extension(name='mercurial.zstd'))
732 751
733 752 try:
734 753 from distutils import cygwinccompiler
735 754
736 755 # the -mno-cygwin option has been deprecated for years
737 756 compiler = cygwinccompiler.Mingw32CCompiler
738 757
739 758 class HackedMingw32CCompiler(cygwinccompiler.Mingw32CCompiler):
740 759 def __init__(self, *args, **kwargs):
741 760 compiler.__init__(self, *args, **kwargs)
742 761 for i in 'compiler compiler_so linker_exe linker_so'.split():
743 762 try:
744 763 getattr(self, i).remove('-mno-cygwin')
745 764 except ValueError:
746 765 pass
747 766
748 767 cygwinccompiler.Mingw32CCompiler = HackedMingw32CCompiler
749 768 except ImportError:
750 769 # the cygwinccompiler package is not available on some Python
751 770 # distributions like the ones from the optware project for Synology
752 771 # DiskStation boxes
753 772 class HackedMingw32CCompiler(object):
754 773 pass
755 774
756 775 if os.name == 'nt':
757 776 # Allow compiler/linker flags to be added to Visual Studio builds. Passing
758 777 # extra_link_args to distutils.extensions.Extension() doesn't have any
759 778 # effect.
760 779 from distutils import msvccompiler
761 780
762 781 compiler = msvccompiler.MSVCCompiler
763 782
764 783 class HackedMSVCCompiler(msvccompiler.MSVCCompiler):
765 784 def initialize(self):
766 785 compiler.initialize(self)
767 786 # "warning LNK4197: export 'func' specified multiple times"
768 787 self.ldflags_shared.append('/ignore:4197')
769 788 self.ldflags_shared_debug.append('/ignore:4197')
770 789
771 790 msvccompiler.MSVCCompiler = HackedMSVCCompiler
772 791
773 792 packagedata = {'mercurial': ['locale/*/LC_MESSAGES/hg.mo',
774 793 'help/*.txt',
775 794 'help/internals/*.txt',
776 795 'default.d/*.rc',
777 796 'dummycert.pem']}
778 797
779 798 def ordinarypath(p):
780 799 return p and p[0] != '.' and p[-1] != '~'
781 800
782 801 for root in ('templates',):
783 802 for curdir, dirs, files in os.walk(os.path.join('mercurial', root)):
784 803 curdir = curdir.split(os.sep, 1)[1]
785 804 dirs[:] = filter(ordinarypath, dirs)
786 805 for f in filter(ordinarypath, files):
787 806 f = os.path.join(curdir, f)
788 807 packagedata['mercurial'].append(f)
789 808
790 809 datafiles = []
791 810
792 811 # distutils expects version to be str/unicode. Converting it to
793 812 # unicode on Python 2 still works because it won't contain any
794 813 # non-ascii bytes and will be implicitly converted back to bytes
795 814 # when operated on.
796 815 assert isinstance(version, bytes)
797 816 setupversion = version.decode('ascii')
798 817
799 818 extra = {}
800 819
801 820 if py2exeloaded:
802 821 extra['console'] = [
803 822 {'script':'hg',
804 823 'copyright':'Copyright (C) 2005-2017 Matt Mackall and others',
805 824 'product_version':version}]
806 825 # sub command of 'build' because 'py2exe' does not handle sub_commands
807 826 build.sub_commands.insert(0, ('build_hgextindex', None))
808 827 # put dlls in sub directory so that they won't pollute PATH
809 828 extra['zipfile'] = 'lib/library.zip'
810 829
811 830 if os.name == 'nt':
812 831 # Windows binary file versions for exe/dll files must have the
813 832 # form W.X.Y.Z, where W,X,Y,Z are numbers in the range 0..65535
814 833 setupversion = version.split('+', 1)[0]
815 834
816 835 if sys.platform == 'darwin' and os.path.exists('/usr/bin/xcodebuild'):
817 836 version = runcmd(['/usr/bin/xcodebuild', '-version'], {})[1].splitlines()
818 837 if version:
819 838 version = version[0]
820 839 if sys.version_info[0] == 3:
821 840 version = version.decode('utf-8')
822 841 xcode4 = (version.startswith('Xcode') and
823 842 StrictVersion(version.split()[1]) >= StrictVersion('4.0'))
824 843 xcode51 = re.match(r'^Xcode\s+5\.1', version) is not None
825 844 else:
826 845 # xcodebuild returns empty on OS X Lion with XCode 4.3 not
827 846 # installed, but instead with only command-line tools. Assume
828 847 # that only happens on >= Lion, thus no PPC support.
829 848 xcode4 = True
830 849 xcode51 = False
831 850
832 851 # XCode 4.0 dropped support for ppc architecture, which is hardcoded in
833 852 # distutils.sysconfig
834 853 if xcode4:
835 854 os.environ['ARCHFLAGS'] = ''
836 855
837 856 # XCode 5.1 changes clang such that it now fails to compile if the
838 857 # -mno-fused-madd flag is passed, but the version of Python shipped with
839 858 # OS X 10.9 Mavericks includes this flag. This causes problems in all
840 859 # C extension modules, and a bug has been filed upstream at
841 860 # http://bugs.python.org/issue21244. We also need to patch this here
842 861 # so Mercurial can continue to compile in the meantime.
843 862 if xcode51:
844 863 cflags = get_config_var('CFLAGS')
845 864 if cflags and re.search(r'-mno-fused-madd\b', cflags) is not None:
846 865 os.environ['CFLAGS'] = (
847 866 os.environ.get('CFLAGS', '') + ' -Qunused-arguments')
848 867
849 868 setup(name='mercurial',
850 869 version=setupversion,
851 870 author='Matt Mackall and many others',
852 871 author_email='mercurial@mercurial-scm.org',
853 872 url='https://mercurial-scm.org/',
854 873 download_url='https://mercurial-scm.org/release/',
855 874 description=('Fast scalable distributed SCM (revision control, version '
856 875 'control) system'),
857 876 long_description=('Mercurial is a distributed SCM tool written in Python.'
858 877 ' It is used by a number of large projects that require'
859 878 ' fast, reliable distributed revision control, such as '
860 879 'Mozilla.'),
861 880 license='GNU GPLv2 or any later version',
862 881 classifiers=[
863 882 'Development Status :: 6 - Mature',
864 883 'Environment :: Console',
865 884 'Intended Audience :: Developers',
866 885 'Intended Audience :: System Administrators',
867 886 'License :: OSI Approved :: GNU General Public License (GPL)',
868 887 'Natural Language :: Danish',
869 888 'Natural Language :: English',
870 889 'Natural Language :: German',
871 890 'Natural Language :: Italian',
872 891 'Natural Language :: Japanese',
873 892 'Natural Language :: Portuguese (Brazilian)',
874 893 'Operating System :: Microsoft :: Windows',
875 894 'Operating System :: OS Independent',
876 895 'Operating System :: POSIX',
877 896 'Programming Language :: C',
878 897 'Programming Language :: Python',
879 898 'Topic :: Software Development :: Version Control',
880 899 ],
881 900 scripts=scripts,
882 901 packages=packages,
883 902 ext_modules=extmodules,
884 903 data_files=datafiles,
885 904 package_data=packagedata,
886 905 cmdclass=cmdclass,
887 906 distclass=hgdist,
888 907 options={'py2exe': {'packages': ['hgdemandimport', 'hgext', 'email']},
889 908 'bdist_mpkg': {'zipdist': False,
890 909 'license': 'COPYING',
891 910 'readme': 'contrib/macosx/Readme.html',
892 911 'welcome': 'contrib/macosx/Welcome.html',
893 912 },
894 913 },
914 python_requires=supportedpy,
895 915 **extra)
General Comments 0
You need to be logged in to leave comments. Login now