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