##// END OF EJS Templates
setup: forcibly include cext/pure packages in py2exe (issue5625)...
Yuya Nishihara -
r33600:cfa08b06 stable
parent child Browse files
Show More
@@ -1,945 +1,950 b''
1 1 #
2 2 # This is the mercurial setup script.
3 3 #
4 4 # 'python setup.py install', or
5 5 # 'python setup.py --help' for more options
6 6
7 7 import os
8 8
9 9 supportedpy = '~= 2.7'
10 10 if os.environ.get('HGALLOWPYTHON3', ''):
11 11 # Mercurial will never work on Python 3 before 3.5 due to a lack
12 12 # of % formatting on bytestrings, and can't work on 3.6.0 or 3.6.1
13 13 # due to a bug in % formatting in bytestrings.
14 14 #
15 15 # TODO: when we actually work on Python 3, use this string as the
16 16 # actual supportedpy string.
17 17 supportedpy = ','.join([
18 18 '>=2.7',
19 19 '!=3.0.*',
20 20 '!=3.1.*',
21 21 '!=3.2.*',
22 22 '!=3.3.*',
23 23 '!=3.4.*',
24 24 '!=3.6.0',
25 25 '!=3.6.1',
26 26 ])
27 27
28 28 import sys, platform
29 29 if sys.version_info[0] >= 3:
30 30 printf = eval('print')
31 31 libdir_escape = 'unicode_escape'
32 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 if os.name == 'nt' or 'FORCE_SETUPTOOLS' in os.environ:
115 115 from setuptools import setup
116 116 else:
117 117 from distutils.core import setup
118 118 from distutils.ccompiler import new_compiler
119 119 from distutils.core import Command, Extension
120 120 from distutils.dist import Distribution
121 121 from distutils.command.build import build
122 122 from distutils.command.build_ext import build_ext
123 123 from distutils.command.build_py import build_py
124 124 from distutils.command.build_scripts import build_scripts
125 125 from distutils.command.install import install
126 126 from distutils.command.install_lib import install_lib
127 127 from distutils.command.install_scripts import install_scripts
128 128 from distutils.spawn import spawn, find_executable
129 129 from distutils import file_util
130 130 from distutils.errors import (
131 131 CCompilerError,
132 132 DistutilsError,
133 133 DistutilsExecError,
134 134 )
135 135 from distutils.sysconfig import get_python_inc, get_config_var
136 136 from distutils.version import StrictVersion
137 137
138 138 scripts = ['hg']
139 139 if os.name == 'nt':
140 140 # We remove hg.bat if we are able to build hg.exe.
141 141 scripts.append('contrib/win32/hg.bat')
142 142
143 143 def cancompile(cc, code):
144 144 tmpdir = tempfile.mkdtemp(prefix='hg-install-')
145 145 devnull = oldstderr = None
146 146 try:
147 147 fname = os.path.join(tmpdir, 'testcomp.c')
148 148 f = open(fname, 'w')
149 149 f.write(code)
150 150 f.close()
151 151 # Redirect stderr to /dev/null to hide any error messages
152 152 # from the compiler.
153 153 # This will have to be changed if we ever have to check
154 154 # for a function on Windows.
155 155 devnull = open('/dev/null', 'w')
156 156 oldstderr = os.dup(sys.stderr.fileno())
157 157 os.dup2(devnull.fileno(), sys.stderr.fileno())
158 158 objects = cc.compile([fname], output_dir=tmpdir)
159 159 cc.link_executable(objects, os.path.join(tmpdir, "a.out"))
160 160 return True
161 161 except Exception:
162 162 return False
163 163 finally:
164 164 if oldstderr is not None:
165 165 os.dup2(oldstderr, sys.stderr.fileno())
166 166 if devnull is not None:
167 167 devnull.close()
168 168 shutil.rmtree(tmpdir)
169 169
170 170 # simplified version of distutils.ccompiler.CCompiler.has_function
171 171 # that actually removes its temporary files.
172 172 def hasfunction(cc, funcname):
173 173 code = 'int main(void) { %s(); }\n' % funcname
174 174 return cancompile(cc, code)
175 175
176 176 def hasheader(cc, headername):
177 177 code = '#include <%s>\nint main(void) { return 0; }\n' % headername
178 178 return cancompile(cc, code)
179 179
180 180 # py2exe needs to be installed to work
181 181 try:
182 182 import py2exe
183 183 py2exe.Distribution # silence unused import warning
184 184 py2exeloaded = True
185 185 # import py2exe's patched Distribution class
186 186 from distutils.core import Distribution
187 187 except ImportError:
188 188 py2exeloaded = False
189 189
190 190 def runcmd(cmd, env):
191 191 p = subprocess.Popen(cmd, stdout=subprocess.PIPE,
192 192 stderr=subprocess.PIPE, env=env)
193 193 out, err = p.communicate()
194 194 return p.returncode, out, err
195 195
196 196 class hgcommand(object):
197 197 def __init__(self, cmd, env):
198 198 self.cmd = cmd
199 199 self.env = env
200 200
201 201 def run(self, args):
202 202 cmd = self.cmd + args
203 203 returncode, out, err = runcmd(cmd, self.env)
204 204 err = filterhgerr(err)
205 205 if err or returncode != 0:
206 206 printf("stderr from '%s':" % (' '.join(cmd)), file=sys.stderr)
207 207 printf(err, file=sys.stderr)
208 208 return ''
209 209 return out
210 210
211 211 def filterhgerr(err):
212 212 # If root is executing setup.py, but the repository is owned by
213 213 # another user (as in "sudo python setup.py install") we will get
214 214 # trust warnings since the .hg/hgrc file is untrusted. That is
215 215 # fine, we don't want to load it anyway. Python may warn about
216 216 # a missing __init__.py in mercurial/locale, we also ignore that.
217 217 err = [e for e in err.splitlines()
218 218 if (not e.startswith(b'not trusting file')
219 219 and not e.startswith(b'warning: Not importing')
220 220 and not e.startswith(b'obsolete feature not enabled'))]
221 221 return b'\n'.join(b' ' + e for e in err)
222 222
223 223 def findhg():
224 224 """Try to figure out how we should invoke hg for examining the local
225 225 repository contents.
226 226
227 227 Returns an hgcommand object."""
228 228 # By default, prefer the "hg" command in the user's path. This was
229 229 # presumably the hg command that the user used to create this repository.
230 230 #
231 231 # This repository may require extensions or other settings that would not
232 232 # be enabled by running the hg script directly from this local repository.
233 233 hgenv = os.environ.copy()
234 234 # Use HGPLAIN to disable hgrc settings that would change output formatting,
235 235 # and disable localization for the same reasons.
236 236 hgenv['HGPLAIN'] = '1'
237 237 hgenv['LANGUAGE'] = 'C'
238 238 hgcmd = ['hg']
239 239 # Run a simple "hg log" command just to see if using hg from the user's
240 240 # path works and can successfully interact with this repository.
241 241 check_cmd = ['log', '-r.', '-Ttest']
242 242 try:
243 243 retcode, out, err = runcmd(hgcmd + check_cmd, hgenv)
244 244 except EnvironmentError:
245 245 retcode = -1
246 246 if retcode == 0 and not filterhgerr(err):
247 247 return hgcommand(hgcmd, hgenv)
248 248
249 249 # Fall back to trying the local hg installation.
250 250 hgenv = localhgenv()
251 251 # Don't source any system hgrc files when using the local hg.
252 252 hgenv['HGRCPATH'] = ''
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
519 519 def build_extensions(self):
520 520 if os.name != 'nt':
521 521 return
522 522 if isinstance(self.compiler, HackedMingw32CCompiler):
523 523 self.compiler.compiler_so = self.compiler.compiler # no -mdll
524 524 self.compiler.dll_libraries = [] # no -lmsrvc90
525 525
526 526 # Different Python installs can have different Python library
527 527 # names. e.g. the official CPython distribution uses pythonXY.dll
528 528 # and MinGW uses libpythonX.Y.dll.
529 529 _kernel32 = ctypes.windll.kernel32
530 530 _kernel32.GetModuleFileNameA.argtypes = [ctypes.c_void_p,
531 531 ctypes.c_void_p,
532 532 ctypes.c_ulong]
533 533 _kernel32.GetModuleFileNameA.restype = ctypes.c_ulong
534 534 size = 1000
535 535 buf = ctypes.create_string_buffer(size + 1)
536 536 filelen = _kernel32.GetModuleFileNameA(sys.dllhandle, ctypes.byref(buf),
537 537 size)
538 538
539 539 if filelen > 0 and filelen != size:
540 540 dllbasename = os.path.basename(buf.value)
541 541 if not dllbasename.lower().endswith('.dll'):
542 542 raise SystemExit('Python DLL does not end with .dll: %s' %
543 543 dllbasename)
544 544 pythonlib = dllbasename[:-4]
545 545 else:
546 546 log.warn('could not determine Python DLL filename; '
547 547 'assuming pythonXY')
548 548
549 549 hv = sys.hexversion
550 550 pythonlib = 'python%d%d' % (hv >> 24, (hv >> 16) & 0xff)
551 551
552 552 log.info('using %s as Python library name' % pythonlib)
553 553 with open('mercurial/hgpythonlib.h', 'wb') as f:
554 554 f.write('/* this file is autogenerated by setup.py */\n')
555 555 f.write('#define HGPYTHONLIB "%s"\n' % pythonlib)
556 556 objects = self.compiler.compile(['mercurial/exewrapper.c'],
557 557 output_dir=self.build_temp)
558 558 dir = os.path.dirname(self.get_ext_fullpath('dummy'))
559 559 target = os.path.join(dir, 'hg')
560 560 self.compiler.link_executable(objects, target,
561 561 libraries=[],
562 562 output_dir=self.build_temp)
563 563
564 564 @property
565 565 def hgexepath(self):
566 566 dir = os.path.dirname(self.get_ext_fullpath('dummy'))
567 567 return os.path.join(self.build_temp, dir, 'hg.exe')
568 568
569 569 class hginstall(install):
570 570
571 571 user_options = install.user_options + [
572 572 ('old-and-unmanageable', None,
573 573 'noop, present for eggless setuptools compat'),
574 574 ('single-version-externally-managed', None,
575 575 'noop, present for eggless setuptools compat'),
576 576 ]
577 577
578 578 # Also helps setuptools not be sad while we refuse to create eggs.
579 579 single_version_externally_managed = True
580 580
581 581 def get_sub_commands(self):
582 582 # Screen out egg related commands to prevent egg generation. But allow
583 583 # mercurial.egg-info generation, since that is part of modern
584 584 # packaging.
585 585 excl = set(['bdist_egg'])
586 586 return filter(lambda x: x not in excl, install.get_sub_commands(self))
587 587
588 588 class hginstalllib(install_lib):
589 589 '''
590 590 This is a specialization of install_lib that replaces the copy_file used
591 591 there so that it supports setting the mode of files after copying them,
592 592 instead of just preserving the mode that the files originally had. If your
593 593 system has a umask of something like 027, preserving the permissions when
594 594 copying will lead to a broken install.
595 595
596 596 Note that just passing keep_permissions=False to copy_file would be
597 597 insufficient, as it might still be applying a umask.
598 598 '''
599 599
600 600 def run(self):
601 601 realcopyfile = file_util.copy_file
602 602 def copyfileandsetmode(*args, **kwargs):
603 603 src, dst = args[0], args[1]
604 604 dst, copied = realcopyfile(*args, **kwargs)
605 605 if copied:
606 606 st = os.stat(src)
607 607 # Persist executable bit (apply it to group and other if user
608 608 # has it)
609 609 if st[stat.ST_MODE] & stat.S_IXUSR:
610 610 setmode = int('0755', 8)
611 611 else:
612 612 setmode = int('0644', 8)
613 613 m = stat.S_IMODE(st[stat.ST_MODE])
614 614 m = (m & ~int('0777', 8)) | setmode
615 615 os.chmod(dst, m)
616 616 file_util.copy_file = copyfileandsetmode
617 617 try:
618 618 install_lib.run(self)
619 619 finally:
620 620 file_util.copy_file = realcopyfile
621 621
622 622 class hginstallscripts(install_scripts):
623 623 '''
624 624 This is a specialization of install_scripts that replaces the @LIBDIR@ with
625 625 the configured directory for modules. If possible, the path is made relative
626 626 to the directory for scripts.
627 627 '''
628 628
629 629 def initialize_options(self):
630 630 install_scripts.initialize_options(self)
631 631
632 632 self.install_lib = None
633 633
634 634 def finalize_options(self):
635 635 install_scripts.finalize_options(self)
636 636 self.set_undefined_options('install',
637 637 ('install_lib', 'install_lib'))
638 638
639 639 def run(self):
640 640 install_scripts.run(self)
641 641
642 642 # It only makes sense to replace @LIBDIR@ with the install path if
643 643 # the install path is known. For wheels, the logic below calculates
644 644 # the libdir to be "../..". This is because the internal layout of a
645 645 # wheel archive looks like:
646 646 #
647 647 # mercurial-3.6.1.data/scripts/hg
648 648 # mercurial/__init__.py
649 649 #
650 650 # When installing wheels, the subdirectories of the "<pkg>.data"
651 651 # directory are translated to system local paths and files therein
652 652 # are copied in place. The mercurial/* files are installed into the
653 653 # site-packages directory. However, the site-packages directory
654 654 # isn't known until wheel install time. This means we have no clue
655 655 # at wheel generation time what the installed site-packages directory
656 656 # will be. And, wheels don't appear to provide the ability to register
657 657 # custom code to run during wheel installation. This all means that
658 658 # we can't reliably set the libdir in wheels: the default behavior
659 659 # of looking in sys.path must do.
660 660
661 661 if (os.path.splitdrive(self.install_dir)[0] !=
662 662 os.path.splitdrive(self.install_lib)[0]):
663 663 # can't make relative paths from one drive to another, so use an
664 664 # absolute path instead
665 665 libdir = self.install_lib
666 666 else:
667 667 common = os.path.commonprefix((self.install_dir, self.install_lib))
668 668 rest = self.install_dir[len(common):]
669 669 uplevel = len([n for n in os.path.split(rest) if n])
670 670
671 671 libdir = uplevel * ('..' + os.sep) + self.install_lib[len(common):]
672 672
673 673 for outfile in self.outfiles:
674 674 with open(outfile, 'rb') as fp:
675 675 data = fp.read()
676 676
677 677 # skip binary files
678 678 if b'\0' in data:
679 679 continue
680 680
681 681 # During local installs, the shebang will be rewritten to the final
682 682 # install path. During wheel packaging, the shebang has a special
683 683 # value.
684 684 if data.startswith(b'#!python'):
685 685 log.info('not rewriting @LIBDIR@ in %s because install path '
686 686 'not known' % outfile)
687 687 continue
688 688
689 689 data = data.replace(b'@LIBDIR@', libdir.encode(libdir_escape))
690 690 with open(outfile, 'wb') as fp:
691 691 fp.write(data)
692 692
693 693 cmdclass = {'build': hgbuild,
694 694 'build_mo': hgbuildmo,
695 695 'build_ext': hgbuildext,
696 696 'build_py': hgbuildpy,
697 697 'build_scripts': hgbuildscripts,
698 698 'build_hgextindex': buildhgextindex,
699 699 'install': hginstall,
700 700 'install_lib': hginstalllib,
701 701 'install_scripts': hginstallscripts,
702 702 'build_hgexe': buildhgexe,
703 703 }
704 704
705 705 packages = ['mercurial',
706 706 'mercurial.cext',
707 707 'mercurial.cffi',
708 708 'mercurial.hgweb',
709 709 'mercurial.httpclient',
710 710 'mercurial.pure',
711 711 'hgext', 'hgext.convert', 'hgext.fsmonitor',
712 712 'hgext.fsmonitor.pywatchman', 'hgext.highlight',
713 713 'hgext.largefiles', 'hgext.zeroconf', 'hgext3rd',
714 714 'hgdemandimport']
715 715
716 716 common_depends = ['mercurial/bitmanipulation.h',
717 717 'mercurial/compat.h',
718 718 'mercurial/cext/util.h']
719 719 common_include_dirs = ['mercurial']
720 720
721 721 osutil_cflags = []
722 722 osutil_ldflags = []
723 723
724 724 # platform specific macros
725 725 for plat, func in [('bsd', 'setproctitle')]:
726 726 if re.search(plat, sys.platform) and hasfunction(new_compiler(), func):
727 727 osutil_cflags.append('-DHAVE_%s' % func.upper())
728 728
729 729 for plat, macro, code in [
730 730 ('bsd|darwin', 'BSD_STATFS', '''
731 731 #include <sys/param.h>
732 732 #include <sys/mount.h>
733 733 int main() { struct statfs s; return sizeof(s.f_fstypename); }
734 734 '''),
735 735 ('linux', 'LINUX_STATFS', '''
736 736 #include <linux/magic.h>
737 737 #include <sys/vfs.h>
738 738 int main() { struct statfs s; return sizeof(s.f_type); }
739 739 '''),
740 740 ]:
741 741 if re.search(plat, sys.platform) and cancompile(new_compiler(), code):
742 742 osutil_cflags.append('-DHAVE_%s' % macro)
743 743
744 744 if sys.platform == 'darwin':
745 745 osutil_ldflags += ['-framework', 'ApplicationServices']
746 746
747 747 extmodules = [
748 748 Extension('mercurial.cext.base85', ['mercurial/cext/base85.c'],
749 749 include_dirs=common_include_dirs,
750 750 depends=common_depends),
751 751 Extension('mercurial.cext.bdiff', ['mercurial/bdiff.c',
752 752 'mercurial/cext/bdiff.c'],
753 753 include_dirs=common_include_dirs,
754 754 depends=common_depends + ['mercurial/bdiff.h']),
755 755 Extension('mercurial.cext.diffhelpers', ['mercurial/cext/diffhelpers.c'],
756 756 include_dirs=common_include_dirs,
757 757 depends=common_depends),
758 758 Extension('mercurial.cext.mpatch', ['mercurial/mpatch.c',
759 759 'mercurial/cext/mpatch.c'],
760 760 include_dirs=common_include_dirs,
761 761 depends=common_depends),
762 762 Extension('mercurial.cext.parsers', ['mercurial/cext/dirs.c',
763 763 'mercurial/cext/manifest.c',
764 764 'mercurial/cext/parsers.c',
765 765 'mercurial/cext/pathencode.c',
766 766 'mercurial/cext/revlog.c'],
767 767 include_dirs=common_include_dirs,
768 768 depends=common_depends),
769 769 Extension('mercurial.cext.osutil', ['mercurial/cext/osutil.c'],
770 770 include_dirs=common_include_dirs,
771 771 extra_compile_args=osutil_cflags,
772 772 extra_link_args=osutil_ldflags,
773 773 depends=common_depends),
774 774 Extension('hgext.fsmonitor.pywatchman.bser',
775 775 ['hgext/fsmonitor/pywatchman/bser.c']),
776 776 ]
777 777
778 778 sys.path.insert(0, 'contrib/python-zstandard')
779 779 import setup_zstd
780 780 extmodules.append(setup_zstd.get_c_extension(name='mercurial.zstd'))
781 781
782 782 try:
783 783 from distutils import cygwinccompiler
784 784
785 785 # the -mno-cygwin option has been deprecated for years
786 786 compiler = cygwinccompiler.Mingw32CCompiler
787 787
788 788 class HackedMingw32CCompiler(cygwinccompiler.Mingw32CCompiler):
789 789 def __init__(self, *args, **kwargs):
790 790 compiler.__init__(self, *args, **kwargs)
791 791 for i in 'compiler compiler_so linker_exe linker_so'.split():
792 792 try:
793 793 getattr(self, i).remove('-mno-cygwin')
794 794 except ValueError:
795 795 pass
796 796
797 797 cygwinccompiler.Mingw32CCompiler = HackedMingw32CCompiler
798 798 except ImportError:
799 799 # the cygwinccompiler package is not available on some Python
800 800 # distributions like the ones from the optware project for Synology
801 801 # DiskStation boxes
802 802 class HackedMingw32CCompiler(object):
803 803 pass
804 804
805 805 if os.name == 'nt':
806 806 # Allow compiler/linker flags to be added to Visual Studio builds. Passing
807 807 # extra_link_args to distutils.extensions.Extension() doesn't have any
808 808 # effect.
809 809 from distutils import msvccompiler
810 810
811 811 compiler = msvccompiler.MSVCCompiler
812 812
813 813 class HackedMSVCCompiler(msvccompiler.MSVCCompiler):
814 814 def initialize(self):
815 815 compiler.initialize(self)
816 816 # "warning LNK4197: export 'func' specified multiple times"
817 817 self.ldflags_shared.append('/ignore:4197')
818 818 self.ldflags_shared_debug.append('/ignore:4197')
819 819
820 820 msvccompiler.MSVCCompiler = HackedMSVCCompiler
821 821
822 822 packagedata = {'mercurial': ['locale/*/LC_MESSAGES/hg.mo',
823 823 'help/*.txt',
824 824 'help/internals/*.txt',
825 825 'default.d/*.rc',
826 826 'dummycert.pem']}
827 827
828 828 def ordinarypath(p):
829 829 return p and p[0] != '.' and p[-1] != '~'
830 830
831 831 for root in ('templates',):
832 832 for curdir, dirs, files in os.walk(os.path.join('mercurial', root)):
833 833 curdir = curdir.split(os.sep, 1)[1]
834 834 dirs[:] = filter(ordinarypath, dirs)
835 835 for f in filter(ordinarypath, files):
836 836 f = os.path.join(curdir, f)
837 837 packagedata['mercurial'].append(f)
838 838
839 839 datafiles = []
840 840
841 841 # distutils expects version to be str/unicode. Converting it to
842 842 # unicode on Python 2 still works because it won't contain any
843 843 # non-ascii bytes and will be implicitly converted back to bytes
844 844 # when operated on.
845 845 assert isinstance(version, bytes)
846 846 setupversion = version.decode('ascii')
847 847
848 848 extra = {}
849 849
850 850 if py2exeloaded:
851 851 extra['console'] = [
852 852 {'script':'hg',
853 853 'copyright':'Copyright (C) 2005-2017 Matt Mackall and others',
854 854 'product_version':version}]
855 855 # sub command of 'build' because 'py2exe' does not handle sub_commands
856 856 build.sub_commands.insert(0, ('build_hgextindex', None))
857 857 # put dlls in sub directory so that they won't pollute PATH
858 858 extra['zipfile'] = 'lib/library.zip'
859 859
860 860 if os.name == 'nt':
861 861 # Windows binary file versions for exe/dll files must have the
862 862 # form W.X.Y.Z, where W,X,Y,Z are numbers in the range 0..65535
863 863 setupversion = version.split('+', 1)[0]
864 864
865 865 if sys.platform == 'darwin' and os.path.exists('/usr/bin/xcodebuild'):
866 866 version = runcmd(['/usr/bin/xcodebuild', '-version'], {})[1].splitlines()
867 867 if version:
868 868 version = version[0]
869 869 if sys.version_info[0] == 3:
870 870 version = version.decode('utf-8')
871 871 xcode4 = (version.startswith('Xcode') and
872 872 StrictVersion(version.split()[1]) >= StrictVersion('4.0'))
873 873 xcode51 = re.match(r'^Xcode\s+5\.1', version) is not None
874 874 else:
875 875 # xcodebuild returns empty on OS X Lion with XCode 4.3 not
876 876 # installed, but instead with only command-line tools. Assume
877 877 # that only happens on >= Lion, thus no PPC support.
878 878 xcode4 = True
879 879 xcode51 = False
880 880
881 881 # XCode 4.0 dropped support for ppc architecture, which is hardcoded in
882 882 # distutils.sysconfig
883 883 if xcode4:
884 884 os.environ['ARCHFLAGS'] = ''
885 885
886 886 # XCode 5.1 changes clang such that it now fails to compile if the
887 887 # -mno-fused-madd flag is passed, but the version of Python shipped with
888 888 # OS X 10.9 Mavericks includes this flag. This causes problems in all
889 889 # C extension modules, and a bug has been filed upstream at
890 890 # http://bugs.python.org/issue21244. We also need to patch this here
891 891 # so Mercurial can continue to compile in the meantime.
892 892 if xcode51:
893 893 cflags = get_config_var('CFLAGS')
894 894 if cflags and re.search(r'-mno-fused-madd\b', cflags) is not None:
895 895 os.environ['CFLAGS'] = (
896 896 os.environ.get('CFLAGS', '') + ' -Qunused-arguments')
897 897
898 898 setup(name='mercurial',
899 899 version=setupversion,
900 900 author='Matt Mackall and many others',
901 901 author_email='mercurial@mercurial-scm.org',
902 902 url='https://mercurial-scm.org/',
903 903 download_url='https://mercurial-scm.org/release/',
904 904 description=('Fast scalable distributed SCM (revision control, version '
905 905 'control) system'),
906 906 long_description=('Mercurial is a distributed SCM tool written in Python.'
907 907 ' It is used by a number of large projects that require'
908 908 ' fast, reliable distributed revision control, such as '
909 909 'Mozilla.'),
910 910 license='GNU GPLv2 or any later version',
911 911 classifiers=[
912 912 'Development Status :: 6 - Mature',
913 913 'Environment :: Console',
914 914 'Intended Audience :: Developers',
915 915 'Intended Audience :: System Administrators',
916 916 'License :: OSI Approved :: GNU General Public License (GPL)',
917 917 'Natural Language :: Danish',
918 918 'Natural Language :: English',
919 919 'Natural Language :: German',
920 920 'Natural Language :: Italian',
921 921 'Natural Language :: Japanese',
922 922 'Natural Language :: Portuguese (Brazilian)',
923 923 'Operating System :: Microsoft :: Windows',
924 924 'Operating System :: OS Independent',
925 925 'Operating System :: POSIX',
926 926 'Programming Language :: C',
927 927 'Programming Language :: Python',
928 928 'Topic :: Software Development :: Version Control',
929 929 ],
930 930 scripts=scripts,
931 931 packages=packages,
932 932 ext_modules=extmodules,
933 933 data_files=datafiles,
934 934 package_data=packagedata,
935 935 cmdclass=cmdclass,
936 936 distclass=hgdist,
937 options={'py2exe': {'packages': ['hgdemandimport', 'hgext', 'email']},
937 options={'py2exe': {'packages': ['hgdemandimport', 'hgext', 'email',
938 # implicitly imported per module policy
939 # (cffi wouldn't be used as a frozen exe)
940 'mercurial.cext',
941 #'mercurial.cffi',
942 'mercurial.pure']},
938 943 'bdist_mpkg': {'zipdist': False,
939 944 'license': 'COPYING',
940 945 'readme': 'contrib/macosx/Readme.html',
941 946 'welcome': 'contrib/macosx/Welcome.html',
942 947 },
943 948 },
944 949 python_requires=supportedpy,
945 950 **extra)
General Comments 0
You need to be logged in to leave comments. Login now