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