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