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