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