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