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