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