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