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