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