##// END OF EJS Templates
setup: fix installing in a mingw environment...
Mike Hommey -
r33780:7686cbb0 stable
parent child Browse files
Show More
@@ -1,952 +1,952
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 519
520 520 def build_extensions(self):
521 521 if os.name != 'nt':
522 522 return
523 523 if isinstance(self.compiler, HackedMingw32CCompiler):
524 524 self.compiler.compiler_so = self.compiler.compiler # no -mdll
525 525 self.compiler.dll_libraries = [] # no -lmsrvc90
526 526
527 527 # Different Python installs can have different Python library
528 528 # names. e.g. the official CPython distribution uses pythonXY.dll
529 529 # and MinGW uses libpythonX.Y.dll.
530 530 _kernel32 = ctypes.windll.kernel32
531 531 _kernel32.GetModuleFileNameA.argtypes = [ctypes.c_void_p,
532 532 ctypes.c_void_p,
533 533 ctypes.c_ulong]
534 534 _kernel32.GetModuleFileNameA.restype = ctypes.c_ulong
535 535 size = 1000
536 536 buf = ctypes.create_string_buffer(size + 1)
537 537 filelen = _kernel32.GetModuleFileNameA(sys.dllhandle, ctypes.byref(buf),
538 538 size)
539 539
540 540 if filelen > 0 and filelen != size:
541 541 dllbasename = os.path.basename(buf.value)
542 542 if not dllbasename.lower().endswith('.dll'):
543 543 raise SystemExit('Python DLL does not end with .dll: %s' %
544 544 dllbasename)
545 545 pythonlib = dllbasename[:-4]
546 546 else:
547 547 log.warn('could not determine Python DLL filename; '
548 548 'assuming pythonXY')
549 549
550 550 hv = sys.hexversion
551 551 pythonlib = 'python%d%d' % (hv >> 24, (hv >> 16) & 0xff)
552 552
553 553 log.info('using %s as Python library name' % pythonlib)
554 554 with open('mercurial/hgpythonlib.h', 'wb') as f:
555 555 f.write('/* this file is autogenerated by setup.py */\n')
556 556 f.write('#define HGPYTHONLIB "%s"\n' % pythonlib)
557 557 objects = self.compiler.compile(['mercurial/exewrapper.c'],
558 558 output_dir=self.build_temp)
559 559 dir = os.path.dirname(self.get_ext_fullpath('dummy'))
560 560 target = os.path.join(dir, 'hg')
561 561 self.compiler.link_executable(objects, target,
562 562 libraries=[],
563 563 output_dir=self.build_temp)
564 564
565 565 @property
566 566 def hgexepath(self):
567 567 dir = os.path.dirname(self.get_ext_fullpath('dummy'))
568 568 return os.path.join(self.build_temp, dir, 'hg.exe')
569 569
570 570 class hginstall(install):
571 571
572 572 user_options = install.user_options + [
573 573 ('old-and-unmanageable', None,
574 574 'noop, present for eggless setuptools compat'),
575 575 ('single-version-externally-managed', None,
576 576 'noop, present for eggless setuptools compat'),
577 577 ]
578 578
579 579 # Also helps setuptools not be sad while we refuse to create eggs.
580 580 single_version_externally_managed = True
581 581
582 582 def get_sub_commands(self):
583 583 # Screen out egg related commands to prevent egg generation. But allow
584 584 # mercurial.egg-info generation, since that is part of modern
585 585 # packaging.
586 586 excl = set(['bdist_egg'])
587 587 return filter(lambda x: x not in excl, install.get_sub_commands(self))
588 588
589 589 class hginstalllib(install_lib):
590 590 '''
591 591 This is a specialization of install_lib that replaces the copy_file used
592 592 there so that it supports setting the mode of files after copying them,
593 593 instead of just preserving the mode that the files originally had. If your
594 594 system has a umask of something like 027, preserving the permissions when
595 595 copying will lead to a broken install.
596 596
597 597 Note that just passing keep_permissions=False to copy_file would be
598 598 insufficient, as it might still be applying a umask.
599 599 '''
600 600
601 601 def run(self):
602 602 realcopyfile = file_util.copy_file
603 603 def copyfileandsetmode(*args, **kwargs):
604 604 src, dst = args[0], args[1]
605 605 dst, copied = realcopyfile(*args, **kwargs)
606 606 if copied:
607 607 st = os.stat(src)
608 608 # Persist executable bit (apply it to group and other if user
609 609 # has it)
610 610 if st[stat.ST_MODE] & stat.S_IXUSR:
611 611 setmode = int('0755', 8)
612 612 else:
613 613 setmode = int('0644', 8)
614 614 m = stat.S_IMODE(st[stat.ST_MODE])
615 615 m = (m & ~int('0777', 8)) | setmode
616 616 os.chmod(dst, m)
617 617 file_util.copy_file = copyfileandsetmode
618 618 try:
619 619 install_lib.run(self)
620 620 finally:
621 621 file_util.copy_file = realcopyfile
622 622
623 623 class hginstallscripts(install_scripts):
624 624 '''
625 625 This is a specialization of install_scripts that replaces the @LIBDIR@ with
626 626 the configured directory for modules. If possible, the path is made relative
627 627 to the directory for scripts.
628 628 '''
629 629
630 630 def initialize_options(self):
631 631 install_scripts.initialize_options(self)
632 632
633 633 self.install_lib = None
634 634
635 635 def finalize_options(self):
636 636 install_scripts.finalize_options(self)
637 637 self.set_undefined_options('install',
638 638 ('install_lib', 'install_lib'))
639 639
640 640 def run(self):
641 641 install_scripts.run(self)
642 642
643 643 # It only makes sense to replace @LIBDIR@ with the install path if
644 644 # the install path is known. For wheels, the logic below calculates
645 645 # the libdir to be "../..". This is because the internal layout of a
646 646 # wheel archive looks like:
647 647 #
648 648 # mercurial-3.6.1.data/scripts/hg
649 649 # mercurial/__init__.py
650 650 #
651 651 # When installing wheels, the subdirectories of the "<pkg>.data"
652 652 # directory are translated to system local paths and files therein
653 653 # are copied in place. The mercurial/* files are installed into the
654 654 # site-packages directory. However, the site-packages directory
655 655 # isn't known until wheel install time. This means we have no clue
656 656 # at wheel generation time what the installed site-packages directory
657 657 # will be. And, wheels don't appear to provide the ability to register
658 658 # custom code to run during wheel installation. This all means that
659 659 # we can't reliably set the libdir in wheels: the default behavior
660 660 # of looking in sys.path must do.
661 661
662 662 if (os.path.splitdrive(self.install_dir)[0] !=
663 663 os.path.splitdrive(self.install_lib)[0]):
664 664 # can't make relative paths from one drive to another, so use an
665 665 # absolute path instead
666 666 libdir = self.install_lib
667 667 else:
668 668 common = os.path.commonprefix((self.install_dir, self.install_lib))
669 669 rest = self.install_dir[len(common):]
670 670 uplevel = len([n for n in os.path.split(rest) if n])
671 671
672 672 libdir = uplevel * ('..' + os.sep) + self.install_lib[len(common):]
673 673
674 674 for outfile in self.outfiles:
675 675 with open(outfile, 'rb') as fp:
676 676 data = fp.read()
677 677
678 678 # skip binary files
679 679 if b'\0' in data:
680 680 continue
681 681
682 682 # During local installs, the shebang will be rewritten to the final
683 683 # install path. During wheel packaging, the shebang has a special
684 684 # value.
685 685 if data.startswith(b'#!python'):
686 686 log.info('not rewriting @LIBDIR@ in %s because install path '
687 687 'not known' % outfile)
688 688 continue
689 689
690 690 data = data.replace(b'@LIBDIR@', libdir.encode(libdir_escape))
691 691 with open(outfile, 'wb') as fp:
692 692 fp.write(data)
693 693
694 694 cmdclass = {'build': hgbuild,
695 695 'build_mo': hgbuildmo,
696 696 'build_ext': hgbuildext,
697 697 'build_py': hgbuildpy,
698 698 'build_scripts': hgbuildscripts,
699 699 'build_hgextindex': buildhgextindex,
700 700 'install': hginstall,
701 701 'install_lib': hginstalllib,
702 702 'install_scripts': hginstallscripts,
703 703 'build_hgexe': buildhgexe,
704 704 }
705 705
706 706 packages = ['mercurial',
707 707 'mercurial.cext',
708 708 'mercurial.cffi',
709 709 'mercurial.hgweb',
710 710 'mercurial.httpclient',
711 711 'mercurial.pure',
712 712 'hgext', 'hgext.convert', 'hgext.fsmonitor',
713 713 'hgext.fsmonitor.pywatchman', 'hgext.highlight',
714 714 'hgext.largefiles', 'hgext.zeroconf', 'hgext3rd',
715 715 'hgdemandimport']
716 716
717 717 common_depends = ['mercurial/bitmanipulation.h',
718 718 'mercurial/compat.h',
719 719 'mercurial/cext/util.h']
720 720 common_include_dirs = ['mercurial']
721 721
722 722 osutil_cflags = []
723 723 osutil_ldflags = []
724 724
725 725 # platform specific macros
726 726 for plat, func in [('bsd', 'setproctitle')]:
727 727 if re.search(plat, sys.platform) and hasfunction(new_compiler(), func):
728 728 osutil_cflags.append('-DHAVE_%s' % func.upper())
729 729
730 730 for plat, macro, code in [
731 731 ('bsd|darwin', 'BSD_STATFS', '''
732 732 #include <sys/param.h>
733 733 #include <sys/mount.h>
734 734 int main() { struct statfs s; return sizeof(s.f_fstypename); }
735 735 '''),
736 736 ('linux', 'LINUX_STATFS', '''
737 737 #include <linux/magic.h>
738 738 #include <sys/vfs.h>
739 739 int main() { struct statfs s; return sizeof(s.f_type); }
740 740 '''),
741 741 ]:
742 742 if re.search(plat, sys.platform) and cancompile(new_compiler(), code):
743 743 osutil_cflags.append('-DHAVE_%s' % macro)
744 744
745 745 if sys.platform == 'darwin':
746 746 osutil_ldflags += ['-framework', 'ApplicationServices']
747 747
748 748 extmodules = [
749 749 Extension('mercurial.cext.base85', ['mercurial/cext/base85.c'],
750 750 include_dirs=common_include_dirs,
751 751 depends=common_depends),
752 752 Extension('mercurial.cext.bdiff', ['mercurial/bdiff.c',
753 753 'mercurial/cext/bdiff.c'],
754 754 include_dirs=common_include_dirs,
755 755 depends=common_depends + ['mercurial/bdiff.h']),
756 756 Extension('mercurial.cext.diffhelpers', ['mercurial/cext/diffhelpers.c'],
757 757 include_dirs=common_include_dirs,
758 758 depends=common_depends),
759 759 Extension('mercurial.cext.mpatch', ['mercurial/mpatch.c',
760 760 'mercurial/cext/mpatch.c'],
761 761 include_dirs=common_include_dirs,
762 762 depends=common_depends),
763 763 Extension('mercurial.cext.parsers', ['mercurial/cext/dirs.c',
764 764 'mercurial/cext/manifest.c',
765 765 'mercurial/cext/parsers.c',
766 766 'mercurial/cext/pathencode.c',
767 767 'mercurial/cext/revlog.c'],
768 768 include_dirs=common_include_dirs,
769 769 depends=common_depends),
770 770 Extension('mercurial.cext.osutil', ['mercurial/cext/osutil.c'],
771 771 include_dirs=common_include_dirs,
772 772 extra_compile_args=osutil_cflags,
773 773 extra_link_args=osutil_ldflags,
774 774 depends=common_depends),
775 775 Extension('hgext.fsmonitor.pywatchman.bser',
776 776 ['hgext/fsmonitor/pywatchman/bser.c']),
777 777 ]
778 778
779 779 sys.path.insert(0, 'contrib/python-zstandard')
780 780 import setup_zstd
781 781 extmodules.append(setup_zstd.get_c_extension(name='mercurial.zstd'))
782 782
783 783 try:
784 784 from distutils import cygwinccompiler
785 785
786 786 # the -mno-cygwin option has been deprecated for years
787 compiler = cygwinccompiler.Mingw32CCompiler
787 mingw32compilerclass = cygwinccompiler.Mingw32CCompiler
788 788
789 789 class HackedMingw32CCompiler(cygwinccompiler.Mingw32CCompiler):
790 790 def __init__(self, *args, **kwargs):
791 compiler.__init__(self, *args, **kwargs)
791 mingw32compilerclass.__init__(self, *args, **kwargs)
792 792 for i in 'compiler compiler_so linker_exe linker_so'.split():
793 793 try:
794 794 getattr(self, i).remove('-mno-cygwin')
795 795 except ValueError:
796 796 pass
797 797
798 798 cygwinccompiler.Mingw32CCompiler = HackedMingw32CCompiler
799 799 except ImportError:
800 800 # the cygwinccompiler package is not available on some Python
801 801 # distributions like the ones from the optware project for Synology
802 802 # DiskStation boxes
803 803 class HackedMingw32CCompiler(object):
804 804 pass
805 805
806 806 if os.name == 'nt':
807 807 # Allow compiler/linker flags to be added to Visual Studio builds. Passing
808 808 # extra_link_args to distutils.extensions.Extension() doesn't have any
809 809 # effect.
810 810 from distutils import msvccompiler
811 811
812 compiler = msvccompiler.MSVCCompiler
812 msvccompilerclass = msvccompiler.MSVCCompiler
813 813
814 814 class HackedMSVCCompiler(msvccompiler.MSVCCompiler):
815 815 def initialize(self):
816 compiler.initialize(self)
816 msvccompilerclass.initialize(self)
817 817 # "warning LNK4197: export 'func' specified multiple times"
818 818 self.ldflags_shared.append('/ignore:4197')
819 819 self.ldflags_shared_debug.append('/ignore:4197')
820 820
821 821 msvccompiler.MSVCCompiler = HackedMSVCCompiler
822 822
823 823 packagedata = {'mercurial': ['locale/*/LC_MESSAGES/hg.mo',
824 824 'help/*.txt',
825 825 'help/internals/*.txt',
826 826 'default.d/*.rc',
827 827 'dummycert.pem']}
828 828
829 829 def ordinarypath(p):
830 830 return p and p[0] != '.' and p[-1] != '~'
831 831
832 832 for root in ('templates',):
833 833 for curdir, dirs, files in os.walk(os.path.join('mercurial', root)):
834 834 curdir = curdir.split(os.sep, 1)[1]
835 835 dirs[:] = filter(ordinarypath, dirs)
836 836 for f in filter(ordinarypath, files):
837 837 f = os.path.join(curdir, f)
838 838 packagedata['mercurial'].append(f)
839 839
840 840 datafiles = []
841 841
842 842 # distutils expects version to be str/unicode. Converting it to
843 843 # unicode on Python 2 still works because it won't contain any
844 844 # non-ascii bytes and will be implicitly converted back to bytes
845 845 # when operated on.
846 846 assert isinstance(version, bytes)
847 847 setupversion = version.decode('ascii')
848 848
849 849 extra = {}
850 850
851 851 if issetuptools:
852 852 extra['python_requires'] = supportedpy
853 853 if py2exeloaded:
854 854 extra['console'] = [
855 855 {'script':'hg',
856 856 'copyright':'Copyright (C) 2005-2017 Matt Mackall and others',
857 857 'product_version':version}]
858 858 # sub command of 'build' because 'py2exe' does not handle sub_commands
859 859 build.sub_commands.insert(0, ('build_hgextindex', None))
860 860 # put dlls in sub directory so that they won't pollute PATH
861 861 extra['zipfile'] = 'lib/library.zip'
862 862
863 863 if os.name == 'nt':
864 864 # Windows binary file versions for exe/dll files must have the
865 865 # form W.X.Y.Z, where W,X,Y,Z are numbers in the range 0..65535
866 866 setupversion = version.split('+', 1)[0]
867 867
868 868 if sys.platform == 'darwin' and os.path.exists('/usr/bin/xcodebuild'):
869 869 version = runcmd(['/usr/bin/xcodebuild', '-version'], {})[1].splitlines()
870 870 if version:
871 871 version = version[0]
872 872 if sys.version_info[0] == 3:
873 873 version = version.decode('utf-8')
874 874 xcode4 = (version.startswith('Xcode') and
875 875 StrictVersion(version.split()[1]) >= StrictVersion('4.0'))
876 876 xcode51 = re.match(r'^Xcode\s+5\.1', version) is not None
877 877 else:
878 878 # xcodebuild returns empty on OS X Lion with XCode 4.3 not
879 879 # installed, but instead with only command-line tools. Assume
880 880 # that only happens on >= Lion, thus no PPC support.
881 881 xcode4 = True
882 882 xcode51 = False
883 883
884 884 # XCode 4.0 dropped support for ppc architecture, which is hardcoded in
885 885 # distutils.sysconfig
886 886 if xcode4:
887 887 os.environ['ARCHFLAGS'] = ''
888 888
889 889 # XCode 5.1 changes clang such that it now fails to compile if the
890 890 # -mno-fused-madd flag is passed, but the version of Python shipped with
891 891 # OS X 10.9 Mavericks includes this flag. This causes problems in all
892 892 # C extension modules, and a bug has been filed upstream at
893 893 # http://bugs.python.org/issue21244. We also need to patch this here
894 894 # so Mercurial can continue to compile in the meantime.
895 895 if xcode51:
896 896 cflags = get_config_var('CFLAGS')
897 897 if cflags and re.search(r'-mno-fused-madd\b', cflags) is not None:
898 898 os.environ['CFLAGS'] = (
899 899 os.environ.get('CFLAGS', '') + ' -Qunused-arguments')
900 900
901 901 setup(name='mercurial',
902 902 version=setupversion,
903 903 author='Matt Mackall and many others',
904 904 author_email='mercurial@mercurial-scm.org',
905 905 url='https://mercurial-scm.org/',
906 906 download_url='https://mercurial-scm.org/release/',
907 907 description=('Fast scalable distributed SCM (revision control, version '
908 908 'control) system'),
909 909 long_description=('Mercurial is a distributed SCM tool written in Python.'
910 910 ' It is used by a number of large projects that require'
911 911 ' fast, reliable distributed revision control, such as '
912 912 'Mozilla.'),
913 913 license='GNU GPLv2 or any later version',
914 914 classifiers=[
915 915 'Development Status :: 6 - Mature',
916 916 'Environment :: Console',
917 917 'Intended Audience :: Developers',
918 918 'Intended Audience :: System Administrators',
919 919 'License :: OSI Approved :: GNU General Public License (GPL)',
920 920 'Natural Language :: Danish',
921 921 'Natural Language :: English',
922 922 'Natural Language :: German',
923 923 'Natural Language :: Italian',
924 924 'Natural Language :: Japanese',
925 925 'Natural Language :: Portuguese (Brazilian)',
926 926 'Operating System :: Microsoft :: Windows',
927 927 'Operating System :: OS Independent',
928 928 'Operating System :: POSIX',
929 929 'Programming Language :: C',
930 930 'Programming Language :: Python',
931 931 'Topic :: Software Development :: Version Control',
932 932 ],
933 933 scripts=scripts,
934 934 packages=packages,
935 935 ext_modules=extmodules,
936 936 data_files=datafiles,
937 937 package_data=packagedata,
938 938 cmdclass=cmdclass,
939 939 distclass=hgdist,
940 940 options={'py2exe': {'packages': ['hgdemandimport', 'hgext', 'email',
941 941 # implicitly imported per module policy
942 942 # (cffi wouldn't be used as a frozen exe)
943 943 'mercurial.cext',
944 944 #'mercurial.cffi',
945 945 'mercurial.pure']},
946 946 'bdist_mpkg': {'zipdist': False,
947 947 'license': 'COPYING',
948 948 'readme': 'contrib/macosx/Readme.html',
949 949 'welcome': 'contrib/macosx/Welcome.html',
950 950 },
951 951 },
952 952 **extra)
General Comments 0
You need to be logged in to leave comments. Login now