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