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