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