##// END OF EJS Templates
setup: when possible, build and bundle man pages...
Dan Villiom Podlaski Christiansen -
r46860:63c923fd default
parent child Browse files
Show More
@@ -1,1835 +1,1897 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 import os
7 7
8 8 # Mercurial will never work on Python 3 before 3.5 due to a lack
9 9 # of % formatting on bytestrings, and can't work on 3.6.0 or 3.6.1
10 10 # due to a bug in % formatting in bytestrings.
11 11 # We cannot support Python 3.5.0, 3.5.1, 3.5.2 because of bug in
12 12 # codecs.escape_encode() where it raises SystemError on empty bytestring
13 13 # bug link: https://bugs.python.org/issue25270
14 14 supportedpy = ','.join(
15 15 [
16 16 '>=2.7.4',
17 17 '!=3.0.*',
18 18 '!=3.1.*',
19 19 '!=3.2.*',
20 20 '!=3.3.*',
21 21 '!=3.4.*',
22 22 '!=3.5.0',
23 23 '!=3.5.1',
24 24 '!=3.5.2',
25 25 '!=3.6.0',
26 26 '!=3.6.1',
27 27 ]
28 28 )
29 29
30 30 import sys, platform
31 31 import sysconfig
32 32
33 33 if sys.version_info[0] >= 3:
34 34 printf = eval('print')
35 35 libdir_escape = 'unicode_escape'
36 36
37 37 def sysstr(s):
38 38 return s.decode('latin-1')
39 39
40 40
41 41 else:
42 42 libdir_escape = 'string_escape'
43 43
44 44 def printf(*args, **kwargs):
45 45 f = kwargs.get('file', sys.stdout)
46 46 end = kwargs.get('end', '\n')
47 47 f.write(b' '.join(args) + end)
48 48
49 49 def sysstr(s):
50 50 return s
51 51
52 52
53 53 # Attempt to guide users to a modern pip - this means that 2.6 users
54 54 # should have a chance of getting a 4.2 release, and when we ratchet
55 55 # the version requirement forward again hopefully everyone will get
56 56 # something that works for them.
57 57 if sys.version_info < (2, 7, 4, 'final'):
58 58 pip_message = (
59 59 'This may be due to an out of date pip. '
60 60 'Make sure you have pip >= 9.0.1.'
61 61 )
62 62 try:
63 63 import pip
64 64
65 65 pip_version = tuple([int(x) for x in pip.__version__.split('.')[:3]])
66 66 if pip_version < (9, 0, 1):
67 67 pip_message = (
68 68 'Your pip version is out of date, please install '
69 69 'pip >= 9.0.1. pip {} detected.'.format(pip.__version__)
70 70 )
71 71 else:
72 72 # pip is new enough - it must be something else
73 73 pip_message = ''
74 74 except Exception:
75 75 pass
76 76 error = """
77 77 Mercurial does not support Python older than 2.7.4.
78 78 Python {py} detected.
79 79 {pip}
80 80 """.format(
81 81 py=sys.version_info, pip=pip_message
82 82 )
83 83 printf(error, file=sys.stderr)
84 84 sys.exit(1)
85 85
86 86 import ssl
87 87
88 88 try:
89 89 ssl.SSLContext
90 90 except AttributeError:
91 91 error = """
92 92 The `ssl` module does not have the `SSLContext` class. This indicates an old
93 93 Python version which does not support modern security features (which were
94 94 added to Python 2.7 as part of "PEP 466"). Please make sure you have installed
95 95 at least Python 2.7.9 or a Python version with backports of these security
96 96 features.
97 97 """
98 98 printf(error, file=sys.stderr)
99 99 sys.exit(1)
100 100
101 101 # ssl.HAS_TLSv1* are preferred to check support but they were added in Python
102 102 # 3.7. Prior to CPython commit 6e8cda91d92da72800d891b2fc2073ecbc134d98
103 103 # (backported to the 3.7 branch), ssl.PROTOCOL_TLSv1_1 / ssl.PROTOCOL_TLSv1_2
104 104 # were defined only if compiled against a OpenSSL version with TLS 1.1 / 1.2
105 105 # support. At the mentioned commit, they were unconditionally defined.
106 106 _notset = object()
107 107 has_tlsv1_1 = getattr(ssl, 'HAS_TLSv1_1', _notset)
108 108 if has_tlsv1_1 is _notset:
109 109 has_tlsv1_1 = getattr(ssl, 'PROTOCOL_TLSv1_1', _notset) is not _notset
110 110 has_tlsv1_2 = getattr(ssl, 'HAS_TLSv1_2', _notset)
111 111 if has_tlsv1_2 is _notset:
112 112 has_tlsv1_2 = getattr(ssl, 'PROTOCOL_TLSv1_2', _notset) is not _notset
113 113 if not (has_tlsv1_1 or has_tlsv1_2):
114 114 error = """
115 115 The `ssl` module does not advertise support for TLS 1.1 or TLS 1.2.
116 116 Please make sure that your Python installation was compiled against an OpenSSL
117 117 version enabling these features (likely this requires the OpenSSL version to
118 118 be at least 1.0.1).
119 119 """
120 120 printf(error, file=sys.stderr)
121 121 sys.exit(1)
122 122
123 123 if sys.version_info[0] >= 3:
124 124 DYLIB_SUFFIX = sysconfig.get_config_vars()['EXT_SUFFIX']
125 125 else:
126 126 # deprecated in Python 3
127 127 DYLIB_SUFFIX = sysconfig.get_config_vars()['SO']
128 128
129 129 # Solaris Python packaging brain damage
130 130 try:
131 131 import hashlib
132 132
133 133 sha = hashlib.sha1()
134 134 except ImportError:
135 135 try:
136 136 import sha
137 137
138 138 sha.sha # silence unused import warning
139 139 except ImportError:
140 140 raise SystemExit(
141 141 "Couldn't import standard hashlib (incomplete Python install)."
142 142 )
143 143
144 144 try:
145 145 import zlib
146 146
147 147 zlib.compressobj # silence unused import warning
148 148 except ImportError:
149 149 raise SystemExit(
150 150 "Couldn't import standard zlib (incomplete Python install)."
151 151 )
152 152
153 153 # The base IronPython distribution (as of 2.7.1) doesn't support bz2
154 154 isironpython = False
155 155 try:
156 156 isironpython = (
157 157 platform.python_implementation().lower().find("ironpython") != -1
158 158 )
159 159 except AttributeError:
160 160 pass
161 161
162 162 if isironpython:
163 163 sys.stderr.write("warning: IronPython detected (no bz2 support)\n")
164 164 else:
165 165 try:
166 166 import bz2
167 167
168 168 bz2.BZ2Compressor # silence unused import warning
169 169 except ImportError:
170 170 raise SystemExit(
171 171 "Couldn't import standard bz2 (incomplete Python install)."
172 172 )
173 173
174 174 ispypy = "PyPy" in sys.version
175 175
176 176 import ctypes
177 177 import errno
178 178 import stat, subprocess, time
179 179 import re
180 180 import shutil
181 181 import tempfile
182 182
183 183 # We have issues with setuptools on some platforms and builders. Until
184 184 # those are resolved, setuptools is opt-in except for platforms where
185 185 # we don't have issues.
186 186 issetuptools = os.name == 'nt' or 'FORCE_SETUPTOOLS' in os.environ
187 187 if issetuptools:
188 188 from setuptools import setup
189 189 else:
190 190 from distutils.core import setup
191 191 from distutils.ccompiler import new_compiler
192 192 from distutils.core import Command, Extension
193 193 from distutils.dist import Distribution
194 194 from distutils.command.build import build
195 195 from distutils.command.build_ext import build_ext
196 196 from distutils.command.build_py import build_py
197 197 from distutils.command.build_scripts import build_scripts
198 198 from distutils.command.install import install
199 from distutils.command.install_data import install_data
199 200 from distutils.command.install_lib import install_lib
200 201 from distutils.command.install_scripts import install_scripts
201 202 from distutils import log
202 203 from distutils.spawn import spawn, find_executable
203 204 from distutils import file_util
204 205 from distutils.errors import (
205 206 CCompilerError,
206 207 DistutilsError,
207 208 DistutilsExecError,
208 209 )
209 210 from distutils.sysconfig import get_python_inc, get_config_var
210 211 from distutils.version import StrictVersion
211 212
212 213 # Explain to distutils.StrictVersion how our release candidates are versionned
213 214 StrictVersion.version_re = re.compile(r'^(\d+)\.(\d+)(\.(\d+))?-?(rc(\d+))?$')
214 215
216 # Can we build the documentation?
217 try:
218 import docutils
219 except ImportError:
220 docutils = None
221
215 222
216 223 def write_if_changed(path, content):
217 224 """Write content to a file iff the content hasn't changed."""
218 225 if os.path.exists(path):
219 226 with open(path, 'rb') as fh:
220 227 current = fh.read()
221 228 else:
222 229 current = b''
223 230
224 231 if current != content:
225 232 with open(path, 'wb') as fh:
226 233 fh.write(content)
227 234
228 235
229 236 scripts = ['hg']
230 237 if os.name == 'nt':
231 238 # We remove hg.bat if we are able to build hg.exe.
232 239 scripts.append('contrib/win32/hg.bat')
233 240
234 241
235 242 def cancompile(cc, code):
236 243 tmpdir = tempfile.mkdtemp(prefix='hg-install-')
237 244 devnull = oldstderr = None
238 245 try:
239 246 fname = os.path.join(tmpdir, 'testcomp.c')
240 247 f = open(fname, 'w')
241 248 f.write(code)
242 249 f.close()
243 250 # Redirect stderr to /dev/null to hide any error messages
244 251 # from the compiler.
245 252 # This will have to be changed if we ever have to check
246 253 # for a function on Windows.
247 254 devnull = open('/dev/null', 'w')
248 255 oldstderr = os.dup(sys.stderr.fileno())
249 256 os.dup2(devnull.fileno(), sys.stderr.fileno())
250 257 objects = cc.compile([fname], output_dir=tmpdir)
251 258 cc.link_executable(objects, os.path.join(tmpdir, "a.out"))
252 259 return True
253 260 except Exception:
254 261 return False
255 262 finally:
256 263 if oldstderr is not None:
257 264 os.dup2(oldstderr, sys.stderr.fileno())
258 265 if devnull is not None:
259 266 devnull.close()
260 267 shutil.rmtree(tmpdir)
261 268
262 269
263 270 # simplified version of distutils.ccompiler.CCompiler.has_function
264 271 # that actually removes its temporary files.
265 272 def hasfunction(cc, funcname):
266 273 code = 'int main(void) { %s(); }\n' % funcname
267 274 return cancompile(cc, code)
268 275
269 276
270 277 def hasheader(cc, headername):
271 278 code = '#include <%s>\nint main(void) { return 0; }\n' % headername
272 279 return cancompile(cc, code)
273 280
274 281
275 282 # py2exe needs to be installed to work
276 283 try:
277 284 import py2exe
278 285
279 286 py2exe.Distribution # silence unused import warning
280 287 py2exeloaded = True
281 288 # import py2exe's patched Distribution class
282 289 from distutils.core import Distribution
283 290 except ImportError:
284 291 py2exeloaded = False
285 292
286 293
287 294 def runcmd(cmd, env, cwd=None):
288 295 p = subprocess.Popen(
289 296 cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, env=env, cwd=cwd
290 297 )
291 298 out, err = p.communicate()
292 299 return p.returncode, out, err
293 300
294 301
295 302 class hgcommand(object):
296 303 def __init__(self, cmd, env):
297 304 self.cmd = cmd
298 305 self.env = env
299 306
300 307 def run(self, args):
301 308 cmd = self.cmd + args
302 309 returncode, out, err = runcmd(cmd, self.env)
303 310 err = filterhgerr(err)
304 311 if err or returncode != 0:
305 312 printf("stderr from '%s':" % (' '.join(cmd)), file=sys.stderr)
306 313 printf(err, file=sys.stderr)
307 314 return b''
308 315 return out
309 316
310 317
311 318 def filterhgerr(err):
312 319 # If root is executing setup.py, but the repository is owned by
313 320 # another user (as in "sudo python setup.py install") we will get
314 321 # trust warnings since the .hg/hgrc file is untrusted. That is
315 322 # fine, we don't want to load it anyway. Python may warn about
316 323 # a missing __init__.py in mercurial/locale, we also ignore that.
317 324 err = [
318 325 e
319 326 for e in err.splitlines()
320 327 if (
321 328 not e.startswith(b'not trusting file')
322 329 and not e.startswith(b'warning: Not importing')
323 330 and not e.startswith(b'obsolete feature not enabled')
324 331 and not e.startswith(b'*** failed to import extension')
325 332 and not e.startswith(b'devel-warn:')
326 333 and not (
327 334 e.startswith(b'(third party extension')
328 335 and e.endswith(b'or newer of Mercurial; disabling)')
329 336 )
330 337 )
331 338 ]
332 339 return b'\n'.join(b' ' + e for e in err)
333 340
334 341
335 342 def findhg():
336 343 """Try to figure out how we should invoke hg for examining the local
337 344 repository contents.
338 345
339 346 Returns an hgcommand object."""
340 347 # By default, prefer the "hg" command in the user's path. This was
341 348 # presumably the hg command that the user used to create this repository.
342 349 #
343 350 # This repository may require extensions or other settings that would not
344 351 # be enabled by running the hg script directly from this local repository.
345 352 hgenv = os.environ.copy()
346 353 # Use HGPLAIN to disable hgrc settings that would change output formatting,
347 354 # and disable localization for the same reasons.
348 355 hgenv['HGPLAIN'] = '1'
349 356 hgenv['LANGUAGE'] = 'C'
350 357 hgcmd = ['hg']
351 358 # Run a simple "hg log" command just to see if using hg from the user's
352 359 # path works and can successfully interact with this repository. Windows
353 360 # gives precedence to hg.exe in the current directory, so fall back to the
354 361 # python invocation of local hg, where pythonXY.dll can always be found.
355 362 check_cmd = ['log', '-r.', '-Ttest']
356 363 if os.name != 'nt' or not os.path.exists("hg.exe"):
357 364 try:
358 365 retcode, out, err = runcmd(hgcmd + check_cmd, hgenv)
359 366 except EnvironmentError:
360 367 retcode = -1
361 368 if retcode == 0 and not filterhgerr(err):
362 369 return hgcommand(hgcmd, hgenv)
363 370
364 371 # Fall back to trying the local hg installation.
365 372 hgenv = localhgenv()
366 373 hgcmd = [sys.executable, 'hg']
367 374 try:
368 375 retcode, out, err = runcmd(hgcmd + check_cmd, hgenv)
369 376 except EnvironmentError:
370 377 retcode = -1
371 378 if retcode == 0 and not filterhgerr(err):
372 379 return hgcommand(hgcmd, hgenv)
373 380
374 381 raise SystemExit(
375 382 'Unable to find a working hg binary to extract the '
376 383 'version from the repository tags'
377 384 )
378 385
379 386
380 387 def localhgenv():
381 388 """Get an environment dictionary to use for invoking or importing
382 389 mercurial from the local repository."""
383 390 # Execute hg out of this directory with a custom environment which takes
384 391 # care to not use any hgrc files and do no localization.
385 392 env = {
386 393 'HGMODULEPOLICY': 'py',
387 394 'HGRCPATH': '',
388 395 'LANGUAGE': 'C',
389 396 'PATH': '',
390 397 } # make pypi modules that use os.environ['PATH'] happy
391 398 if 'LD_LIBRARY_PATH' in os.environ:
392 399 env['LD_LIBRARY_PATH'] = os.environ['LD_LIBRARY_PATH']
393 400 if 'SystemRoot' in os.environ:
394 401 # SystemRoot is required by Windows to load various DLLs. See:
395 402 # https://bugs.python.org/issue13524#msg148850
396 403 env['SystemRoot'] = os.environ['SystemRoot']
397 404 return env
398 405
399 406
400 407 version = ''
401 408
402 409 if os.path.isdir('.hg'):
403 410 hg = findhg()
404 411 cmd = ['log', '-r', '.', '--template', '{tags}\n']
405 412 numerictags = [t for t in sysstr(hg.run(cmd)).split() if t[0:1].isdigit()]
406 413 hgid = sysstr(hg.run(['id', '-i'])).strip()
407 414 if not hgid:
408 415 # Bail out if hg is having problems interacting with this repository,
409 416 # rather than falling through and producing a bogus version number.
410 417 # Continuing with an invalid version number will break extensions
411 418 # that define minimumhgversion.
412 419 raise SystemExit('Unable to determine hg version from local repository')
413 420 if numerictags: # tag(s) found
414 421 version = numerictags[-1]
415 422 if hgid.endswith('+'): # propagate the dirty status to the tag
416 423 version += '+'
417 424 else: # no tag found
418 425 ltagcmd = ['parents', '--template', '{latesttag}']
419 426 ltag = sysstr(hg.run(ltagcmd))
420 427 changessincecmd = ['log', '-T', 'x\n', '-r', "only(.,'%s')" % ltag]
421 428 changessince = len(hg.run(changessincecmd).splitlines())
422 429 version = '%s+%s-%s' % (ltag, changessince, hgid)
423 430 if version.endswith('+'):
424 431 version += time.strftime('%Y%m%d')
425 432 elif os.path.exists('.hg_archival.txt'):
426 433 kw = dict(
427 434 [[t.strip() for t in l.split(':', 1)] for l in open('.hg_archival.txt')]
428 435 )
429 436 if 'tag' in kw:
430 437 version = kw['tag']
431 438 elif 'latesttag' in kw:
432 439 if 'changessincelatesttag' in kw:
433 440 version = '%(latesttag)s+%(changessincelatesttag)s-%(node).12s' % kw
434 441 else:
435 442 version = '%(latesttag)s+%(latesttagdistance)s-%(node).12s' % kw
436 443 else:
437 444 version = kw.get('node', '')[:12]
438 445
439 446 if version:
440 447 versionb = version
441 448 if not isinstance(versionb, bytes):
442 449 versionb = versionb.encode('ascii')
443 450
444 451 write_if_changed(
445 452 'mercurial/__version__.py',
446 453 b''.join(
447 454 [
448 455 b'# this file is autogenerated by setup.py\n'
449 456 b'version = b"%s"\n' % versionb,
450 457 ]
451 458 ),
452 459 )
453 460
454 461 try:
455 462 oldpolicy = os.environ.get('HGMODULEPOLICY', None)
456 463 os.environ['HGMODULEPOLICY'] = 'py'
457 464 from mercurial import __version__
458 465
459 466 version = __version__.version
460 467 except ImportError:
461 468 version = b'unknown'
462 469 finally:
463 470 if oldpolicy is None:
464 471 del os.environ['HGMODULEPOLICY']
465 472 else:
466 473 os.environ['HGMODULEPOLICY'] = oldpolicy
467 474
468 475
469 476 class hgbuild(build):
470 477 # Insert hgbuildmo first so that files in mercurial/locale/ are found
471 478 # when build_py is run next.
472 479 sub_commands = [('build_mo', None)] + build.sub_commands
473 480
481 def run(self):
482 if os.name == 'nt':
483 pass
484 elif docutils is None:
485 log.warn('not building optional documentation')
486 else:
487 self.run_command('build_doc')
488
474 489
475 490 class hgbuildmo(build):
476 491
477 492 description = "build translations (.mo files)"
478 493
479 494 def run(self):
480 495 if not find_executable('msgfmt'):
481 496 self.warn(
482 497 "could not find msgfmt executable, no translations "
483 498 "will be built"
484 499 )
485 500 return
486 501
487 502 podir = 'i18n'
488 503 if not os.path.isdir(podir):
489 504 self.warn("could not find %s/ directory" % podir)
490 505 return
491 506
492 507 join = os.path.join
493 508 for po in os.listdir(podir):
494 509 if not po.endswith('.po'):
495 510 continue
496 511 pofile = join(podir, po)
497 512 modir = join('locale', po[:-3], 'LC_MESSAGES')
498 513 mofile = join(modir, 'hg.mo')
499 514 mobuildfile = join('mercurial', mofile)
500 515 cmd = ['msgfmt', '-v', '-o', mobuildfile, pofile]
501 516 if sys.platform != 'sunos5':
502 517 # msgfmt on Solaris does not know about -c
503 518 cmd.append('-c')
504 519 self.mkpath(join('mercurial', modir))
505 520 self.make_file([pofile], mobuildfile, spawn, (cmd,))
506 521
507 522
508 523 class hgdist(Distribution):
509 524 pure = False
510 525 rust = False
511 526 no_rust = False
512 527 cffi = ispypy
513 528
514 529 global_options = Distribution.global_options + [
515 530 ('pure', None, "use pure (slow) Python code instead of C extensions"),
516 531 ('rust', None, "use Rust extensions additionally to C extensions"),
517 532 (
518 533 'no-rust',
519 534 None,
520 535 "do not use Rust extensions additionally to C extensions",
521 536 ),
522 537 ]
523 538
524 539 negative_opt = Distribution.negative_opt.copy()
525 540 boolean_options = ['pure', 'rust', 'no-rust']
526 541 negative_opt['no-rust'] = 'rust'
527 542
528 543 def _set_command_options(self, command_obj, option_dict=None):
529 544 # Not all distutils versions in the wild have boolean_options.
530 545 # This should be cleaned up when we're Python 3 only.
531 546 command_obj.boolean_options = (
532 547 getattr(command_obj, 'boolean_options', []) + self.boolean_options
533 548 )
534 549 return Distribution._set_command_options(
535 550 self, command_obj, option_dict=option_dict
536 551 )
537 552
538 553 def parse_command_line(self):
539 554 ret = Distribution.parse_command_line(self)
540 555 if not (self.rust or self.no_rust):
541 556 hgrustext = os.environ.get('HGWITHRUSTEXT')
542 557 # TODO record it for proper rebuild upon changes
543 558 # (see mercurial/__modulepolicy__.py)
544 559 if hgrustext != 'cpython' and hgrustext is not None:
545 560 if hgrustext:
546 561 msg = 'unkown HGWITHRUSTEXT value: %s' % hgrustext
547 562 printf(msg, file=sys.stderr)
548 563 hgrustext = None
549 564 self.rust = hgrustext is not None
550 565 self.no_rust = not self.rust
551 566 return ret
552 567
553 568 def has_ext_modules(self):
554 569 # self.ext_modules is emptied in hgbuildpy.finalize_options which is
555 570 # too late for some cases
556 571 return not self.pure and Distribution.has_ext_modules(self)
557 572
558 573
559 574 # This is ugly as a one-liner. So use a variable.
560 575 buildextnegops = dict(getattr(build_ext, 'negative_options', {}))
561 576 buildextnegops['no-zstd'] = 'zstd'
562 577 buildextnegops['no-rust'] = 'rust'
563 578
564 579
565 580 class hgbuildext(build_ext):
566 581 user_options = build_ext.user_options + [
567 582 ('zstd', None, 'compile zstd bindings [default]'),
568 583 ('no-zstd', None, 'do not compile zstd bindings'),
569 584 (
570 585 'rust',
571 586 None,
572 587 'compile Rust extensions if they are in use '
573 588 '(requires Cargo) [default]',
574 589 ),
575 590 ('no-rust', None, 'do not compile Rust extensions'),
576 591 ]
577 592
578 593 boolean_options = build_ext.boolean_options + ['zstd', 'rust']
579 594 negative_opt = buildextnegops
580 595
581 596 def initialize_options(self):
582 597 self.zstd = True
583 598 self.rust = True
584 599
585 600 return build_ext.initialize_options(self)
586 601
587 602 def finalize_options(self):
588 603 # Unless overridden by the end user, build extensions in parallel.
589 604 # Only influences behavior on Python 3.5+.
590 605 if getattr(self, 'parallel', None) is None:
591 606 self.parallel = True
592 607
593 608 return build_ext.finalize_options(self)
594 609
595 610 def build_extensions(self):
596 611 ruststandalones = [
597 612 e for e in self.extensions if isinstance(e, RustStandaloneExtension)
598 613 ]
599 614 self.extensions = [
600 615 e for e in self.extensions if e not in ruststandalones
601 616 ]
602 617 # Filter out zstd if disabled via argument.
603 618 if not self.zstd:
604 619 self.extensions = [
605 620 e for e in self.extensions if e.name != 'mercurial.zstd'
606 621 ]
607 622
608 623 # Build Rust standalon extensions if it'll be used
609 624 # and its build is not explictely disabled (for external build
610 625 # as Linux distributions would do)
611 626 if self.distribution.rust and self.rust:
612 627 for rustext in ruststandalones:
613 628 rustext.build('' if self.inplace else self.build_lib)
614 629
615 630 return build_ext.build_extensions(self)
616 631
617 632 def build_extension(self, ext):
618 633 if (
619 634 self.distribution.rust
620 635 and self.rust
621 636 and isinstance(ext, RustExtension)
622 637 ):
623 638 ext.rustbuild()
624 639 try:
625 640 build_ext.build_extension(self, ext)
626 641 except CCompilerError:
627 642 if not getattr(ext, 'optional', False):
628 643 raise
629 644 log.warn(
630 645 "Failed to build optional extension '%s' (skipping)", ext.name
631 646 )
632 647
633 648
634 649 class hgbuildscripts(build_scripts):
635 650 def run(self):
636 651 if os.name != 'nt' or self.distribution.pure:
637 652 return build_scripts.run(self)
638 653
639 654 exebuilt = False
640 655 try:
641 656 self.run_command('build_hgexe')
642 657 exebuilt = True
643 658 except (DistutilsError, CCompilerError):
644 659 log.warn('failed to build optional hg.exe')
645 660
646 661 if exebuilt:
647 662 # Copying hg.exe to the scripts build directory ensures it is
648 663 # installed by the install_scripts command.
649 664 hgexecommand = self.get_finalized_command('build_hgexe')
650 665 dest = os.path.join(self.build_dir, 'hg.exe')
651 666 self.mkpath(self.build_dir)
652 667 self.copy_file(hgexecommand.hgexepath, dest)
653 668
654 669 # Remove hg.bat because it is redundant with hg.exe.
655 670 self.scripts.remove('contrib/win32/hg.bat')
656 671
657 672 return build_scripts.run(self)
658 673
659 674
660 675 class hgbuildpy(build_py):
661 676 def finalize_options(self):
662 677 build_py.finalize_options(self)
663 678
664 679 if self.distribution.pure:
665 680 self.distribution.ext_modules = []
666 681 elif self.distribution.cffi:
667 682 from mercurial.cffi import (
668 683 bdiffbuild,
669 684 mpatchbuild,
670 685 )
671 686
672 687 exts = [
673 688 mpatchbuild.ffi.distutils_extension(),
674 689 bdiffbuild.ffi.distutils_extension(),
675 690 ]
676 691 # cffi modules go here
677 692 if sys.platform == 'darwin':
678 693 from mercurial.cffi import osutilbuild
679 694
680 695 exts.append(osutilbuild.ffi.distutils_extension())
681 696 self.distribution.ext_modules = exts
682 697 else:
683 698 h = os.path.join(get_python_inc(), 'Python.h')
684 699 if not os.path.exists(h):
685 700 raise SystemExit(
686 701 'Python headers are required to build '
687 702 'Mercurial but weren\'t found in %s' % h
688 703 )
689 704
690 705 def run(self):
691 706 basepath = os.path.join(self.build_lib, 'mercurial')
692 707 self.mkpath(basepath)
693 708
694 709 rust = self.distribution.rust
695 710 if self.distribution.pure:
696 711 modulepolicy = 'py'
697 712 elif self.build_lib == '.':
698 713 # in-place build should run without rebuilding and Rust extensions
699 714 modulepolicy = 'rust+c-allow' if rust else 'allow'
700 715 else:
701 716 modulepolicy = 'rust+c' if rust else 'c'
702 717
703 718 content = b''.join(
704 719 [
705 720 b'# this file is autogenerated by setup.py\n',
706 721 b'modulepolicy = b"%s"\n' % modulepolicy.encode('ascii'),
707 722 ]
708 723 )
709 724 write_if_changed(os.path.join(basepath, '__modulepolicy__.py'), content)
710 725
711 726 build_py.run(self)
712 727
713 728
714 729 class buildhgextindex(Command):
715 730 description = 'generate prebuilt index of hgext (for frozen package)'
716 731 user_options = []
717 732 _indexfilename = 'hgext/__index__.py'
718 733
719 734 def initialize_options(self):
720 735 pass
721 736
722 737 def finalize_options(self):
723 738 pass
724 739
725 740 def run(self):
726 741 if os.path.exists(self._indexfilename):
727 742 with open(self._indexfilename, 'w') as f:
728 743 f.write('# empty\n')
729 744
730 745 # here no extension enabled, disabled() lists up everything
731 746 code = (
732 747 'import pprint; from mercurial import extensions; '
733 748 'ext = extensions.disabled();'
734 749 'ext.pop("__index__", None);'
735 750 'pprint.pprint(ext)'
736 751 )
737 752 returncode, out, err = runcmd(
738 753 [sys.executable, '-c', code], localhgenv()
739 754 )
740 755 if err or returncode != 0:
741 756 raise DistutilsExecError(err)
742 757
743 758 with open(self._indexfilename, 'wb') as f:
744 759 f.write(b'# this file is autogenerated by setup.py\n')
745 760 f.write(b'docs = ')
746 761 f.write(out)
747 762
748 763
749 764 class buildhgexe(build_ext):
750 765 description = 'compile hg.exe from mercurial/exewrapper.c'
751 766 user_options = build_ext.user_options + [
752 767 (
753 768 'long-paths-support',
754 769 None,
755 770 'enable support for long paths on '
756 771 'Windows (off by default and '
757 772 'experimental)',
758 773 ),
759 774 ]
760 775
761 776 LONG_PATHS_MANIFEST = """
762 777 <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
763 778 <assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
764 779 <application>
765 780 <windowsSettings
766 781 xmlns:ws2="http://schemas.microsoft.com/SMI/2016/WindowsSettings">
767 782 <ws2:longPathAware>true</ws2:longPathAware>
768 783 </windowsSettings>
769 784 </application>
770 785 </assembly>"""
771 786
772 787 def initialize_options(self):
773 788 build_ext.initialize_options(self)
774 789 self.long_paths_support = False
775 790
776 791 def build_extensions(self):
777 792 if os.name != 'nt':
778 793 return
779 794 if isinstance(self.compiler, HackedMingw32CCompiler):
780 795 self.compiler.compiler_so = self.compiler.compiler # no -mdll
781 796 self.compiler.dll_libraries = [] # no -lmsrvc90
782 797
783 798 pythonlib = None
784 799
785 800 dir = os.path.dirname(self.get_ext_fullpath('dummy'))
786 801 self.hgtarget = os.path.join(dir, 'hg')
787 802
788 803 if getattr(sys, 'dllhandle', None):
789 804 # Different Python installs can have different Python library
790 805 # names. e.g. the official CPython distribution uses pythonXY.dll
791 806 # and MinGW uses libpythonX.Y.dll.
792 807 _kernel32 = ctypes.windll.kernel32
793 808 _kernel32.GetModuleFileNameA.argtypes = [
794 809 ctypes.c_void_p,
795 810 ctypes.c_void_p,
796 811 ctypes.c_ulong,
797 812 ]
798 813 _kernel32.GetModuleFileNameA.restype = ctypes.c_ulong
799 814 size = 1000
800 815 buf = ctypes.create_string_buffer(size + 1)
801 816 filelen = _kernel32.GetModuleFileNameA(
802 817 sys.dllhandle, ctypes.byref(buf), size
803 818 )
804 819
805 820 if filelen > 0 and filelen != size:
806 821 dllbasename = os.path.basename(buf.value)
807 822 if not dllbasename.lower().endswith(b'.dll'):
808 823 raise SystemExit(
809 824 'Python DLL does not end with .dll: %s' % dllbasename
810 825 )
811 826 pythonlib = dllbasename[:-4]
812 827
813 828 # Copy the pythonXY.dll next to the binary so that it runs
814 829 # without tampering with PATH.
815 830 fsdecode = lambda x: x
816 831 if sys.version_info[0] >= 3:
817 832 fsdecode = os.fsdecode
818 833 dest = os.path.join(
819 834 os.path.dirname(self.hgtarget),
820 835 fsdecode(dllbasename),
821 836 )
822 837
823 838 if not os.path.exists(dest):
824 839 shutil.copy(buf.value, dest)
825 840
826 841 if not pythonlib:
827 842 log.warn(
828 843 'could not determine Python DLL filename; assuming pythonXY'
829 844 )
830 845
831 846 hv = sys.hexversion
832 847 pythonlib = b'python%d%d' % (hv >> 24, (hv >> 16) & 0xFF)
833 848
834 849 log.info('using %s as Python library name' % pythonlib)
835 850 with open('mercurial/hgpythonlib.h', 'wb') as f:
836 851 f.write(b'/* this file is autogenerated by setup.py */\n')
837 852 f.write(b'#define HGPYTHONLIB "%s"\n' % pythonlib)
838 853
839 854 macros = None
840 855 if sys.version_info[0] >= 3:
841 856 macros = [('_UNICODE', None), ('UNICODE', None)]
842 857
843 858 objects = self.compiler.compile(
844 859 ['mercurial/exewrapper.c'],
845 860 output_dir=self.build_temp,
846 861 macros=macros,
847 862 )
848 863 self.compiler.link_executable(
849 864 objects, self.hgtarget, libraries=[], output_dir=self.build_temp
850 865 )
851 866 if self.long_paths_support:
852 867 self.addlongpathsmanifest()
853 868
854 869 def addlongpathsmanifest(self):
855 870 r"""Add manifest pieces so that hg.exe understands long paths
856 871
857 872 This is an EXPERIMENTAL feature, use with care.
858 873 To enable long paths support, one needs to do two things:
859 874 - build Mercurial with --long-paths-support option
860 875 - change HKLM\SYSTEM\CurrentControlSet\Control\FileSystem\
861 876 LongPathsEnabled to have value 1.
862 877
863 878 Please ignore 'warning 81010002: Unrecognized Element "longPathAware"';
864 879 it happens because Mercurial uses mt.exe circa 2008, which is not
865 880 yet aware of long paths support in the manifest (I think so at least).
866 881 This does not stop mt.exe from embedding/merging the XML properly.
867 882
868 883 Why resource #1 should be used for .exe manifests? I don't know and
869 884 wasn't able to find an explanation for mortals. But it seems to work.
870 885 """
871 886 exefname = self.compiler.executable_filename(self.hgtarget)
872 887 fdauto, manfname = tempfile.mkstemp(suffix='.hg.exe.manifest')
873 888 os.close(fdauto)
874 889 with open(manfname, 'w') as f:
875 890 f.write(self.LONG_PATHS_MANIFEST)
876 891 log.info("long paths manifest is written to '%s'" % manfname)
877 892 inputresource = '-inputresource:%s;#1' % exefname
878 893 outputresource = '-outputresource:%s;#1' % exefname
879 894 log.info("running mt.exe to update hg.exe's manifest in-place")
880 895 # supplying both -manifest and -inputresource to mt.exe makes
881 896 # it merge the embedded and supplied manifests in the -outputresource
882 897 self.spawn(
883 898 [
884 899 'mt.exe',
885 900 '-nologo',
886 901 '-manifest',
887 902 manfname,
888 903 inputresource,
889 904 outputresource,
890 905 ]
891 906 )
892 907 log.info("done updating hg.exe's manifest")
893 908 os.remove(manfname)
894 909
895 910 @property
896 911 def hgexepath(self):
897 912 dir = os.path.dirname(self.get_ext_fullpath('dummy'))
898 913 return os.path.join(self.build_temp, dir, 'hg.exe')
899 914
900 915
901 916 class hgbuilddoc(Command):
902 917 description = 'build documentation'
903 918 user_options = [
904 919 ('man', None, 'generate man pages'),
905 920 ('html', None, 'generate html pages'),
906 921 ]
907 922
908 923 def initialize_options(self):
909 924 self.man = None
910 925 self.html = None
911 926
912 927 def finalize_options(self):
913 928 # If --man or --html are set, only generate what we're told to.
914 929 # Otherwise generate everything.
915 930 have_subset = self.man is not None or self.html is not None
916 931
917 932 if have_subset:
918 933 self.man = True if self.man else False
919 934 self.html = True if self.html else False
920 935 else:
921 936 self.man = True
922 937 self.html = True
923 938
924 939 def run(self):
925 940 def normalizecrlf(p):
926 941 with open(p, 'rb') as fh:
927 942 orig = fh.read()
928 943
929 944 if b'\r\n' not in orig:
930 945 return
931 946
932 947 log.info('normalizing %s to LF line endings' % p)
933 948 with open(p, 'wb') as fh:
934 949 fh.write(orig.replace(b'\r\n', b'\n'))
935 950
936 951 def gentxt(root):
937 952 txt = 'doc/%s.txt' % root
938 953 log.info('generating %s' % txt)
939 954 res, out, err = runcmd(
940 955 [sys.executable, 'gendoc.py', root], os.environ, cwd='doc'
941 956 )
942 957 if res:
943 958 raise SystemExit(
944 959 'error running gendoc.py: %s'
945 960 % '\n'.join([sysstr(out), sysstr(err)])
946 961 )
947 962
948 963 with open(txt, 'wb') as fh:
949 964 fh.write(out)
950 965
951 966 def gengendoc(root):
952 967 gendoc = 'doc/%s.gendoc.txt' % root
953 968
954 969 log.info('generating %s' % gendoc)
955 970 res, out, err = runcmd(
956 971 [sys.executable, 'gendoc.py', '%s.gendoc' % root],
957 972 os.environ,
958 973 cwd='doc',
959 974 )
960 975 if res:
961 976 raise SystemExit(
962 977 'error running gendoc: %s'
963 978 % '\n'.join([sysstr(out), sysstr(err)])
964 979 )
965 980
966 981 with open(gendoc, 'wb') as fh:
967 982 fh.write(out)
968 983
969 984 def genman(root):
970 985 log.info('generating doc/%s' % root)
971 986 res, out, err = runcmd(
972 987 [
973 988 sys.executable,
974 989 'runrst',
975 990 'hgmanpage',
976 991 '--halt',
977 992 'warning',
978 993 '--strip-elements-with-class',
979 994 'htmlonly',
980 995 '%s.txt' % root,
981 996 root,
982 997 ],
983 998 os.environ,
984 999 cwd='doc',
985 1000 )
986 1001 if res:
987 1002 raise SystemExit(
988 1003 'error running runrst: %s'
989 1004 % '\n'.join([sysstr(out), sysstr(err)])
990 1005 )
991 1006
992 1007 normalizecrlf('doc/%s' % root)
993 1008
994 1009 def genhtml(root):
995 1010 log.info('generating doc/%s.html' % root)
996 1011 res, out, err = runcmd(
997 1012 [
998 1013 sys.executable,
999 1014 'runrst',
1000 1015 'html',
1001 1016 '--halt',
1002 1017 'warning',
1003 1018 '--link-stylesheet',
1004 1019 '--stylesheet-path',
1005 1020 'style.css',
1006 1021 '%s.txt' % root,
1007 1022 '%s.html' % root,
1008 1023 ],
1009 1024 os.environ,
1010 1025 cwd='doc',
1011 1026 )
1012 1027 if res:
1013 1028 raise SystemExit(
1014 1029 'error running runrst: %s'
1015 1030 % '\n'.join([sysstr(out), sysstr(err)])
1016 1031 )
1017 1032
1018 1033 normalizecrlf('doc/%s.html' % root)
1019 1034
1020 1035 # This logic is duplicated in doc/Makefile.
1021 1036 sources = {
1022 1037 f
1023 1038 for f in os.listdir('mercurial/helptext')
1024 1039 if re.search(r'[0-9]\.txt$', f)
1025 1040 }
1026 1041
1027 1042 # common.txt is a one-off.
1028 1043 gentxt('common')
1029 1044
1030 1045 for source in sorted(sources):
1031 1046 assert source[-4:] == '.txt'
1032 1047 root = source[:-4]
1033 1048
1034 1049 gentxt(root)
1035 1050 gengendoc(root)
1036 1051
1037 1052 if self.man:
1038 1053 genman(root)
1039 1054 if self.html:
1040 1055 genhtml(root)
1041 1056
1042 1057
1058 class hginstalldata(install_data):
1059 user_options = install_data.user_options + [
1060 (
1061 'install-man=',
1062 None,
1063 'installation directory for manual pages [share/man]',
1064 ),
1065 ]
1066
1067 install_man = None
1068
1069 def finalize_options(self):
1070 install_data.finalize_options(self)
1071
1072 self.set_undefined_options('install', ('install_man', 'install_man'))
1073
1074 if self.install_man is None:
1075 self.install_man = os.path.join('share', 'man')
1076
1077 if os.name == 'nt':
1078 pass
1079 elif docutils is None:
1080 log.warn('not installing manual pages')
1081 else:
1082 manpages = [
1083 f for f in os.listdir('doc') if re.search(r'\.[0-9]$', f)
1084 ]
1085
1086 self.data_files += [
1087 (
1088 os.path.join(self.install_man, 'man' + ext[1:]),
1089 ['doc/' + f for f in manpages if f.endswith(ext)],
1090 )
1091 for ext in set(os.path.splitext(f)[1] for f in manpages)
1092 ]
1093
1094
1043 1095 class hginstall(install):
1044 1096
1045 1097 user_options = install.user_options + [
1046 1098 (
1047 1099 'old-and-unmanageable',
1048 1100 None,
1049 1101 'noop, present for eggless setuptools compat',
1050 1102 ),
1051 1103 (
1052 1104 'single-version-externally-managed',
1053 1105 None,
1054 1106 'noop, present for eggless setuptools compat',
1055 1107 ),
1108 (
1109 'install-man=',
1110 None,
1111 'installation directory for manual pages [share/man]',
1112 ),
1056 1113 ]
1057 1114
1058 1115 # Also helps setuptools not be sad while we refuse to create eggs.
1059 1116 single_version_externally_managed = True
1060 1117
1118 install_man = None
1119
1061 1120 def get_sub_commands(self):
1121 subcommands = install.get_sub_commands(self)
1122 subcommands.append('install_data')
1062 1123 # Screen out egg related commands to prevent egg generation. But allow
1063 1124 # mercurial.egg-info generation, since that is part of modern
1064 1125 # packaging.
1065 1126 excl = {'bdist_egg'}
1066 return filter(lambda x: x not in excl, install.get_sub_commands(self))
1127 return filter(lambda x: x not in excl, subcommands)
1067 1128
1068 1129
1069 1130 class hginstalllib(install_lib):
1070 1131 """
1071 1132 This is a specialization of install_lib that replaces the copy_file used
1072 1133 there so that it supports setting the mode of files after copying them,
1073 1134 instead of just preserving the mode that the files originally had. If your
1074 1135 system has a umask of something like 027, preserving the permissions when
1075 1136 copying will lead to a broken install.
1076 1137
1077 1138 Note that just passing keep_permissions=False to copy_file would be
1078 1139 insufficient, as it might still be applying a umask.
1079 1140 """
1080 1141
1081 1142 def run(self):
1082 1143 realcopyfile = file_util.copy_file
1083 1144
1084 1145 def copyfileandsetmode(*args, **kwargs):
1085 1146 src, dst = args[0], args[1]
1086 1147 dst, copied = realcopyfile(*args, **kwargs)
1087 1148 if copied:
1088 1149 st = os.stat(src)
1089 1150 # Persist executable bit (apply it to group and other if user
1090 1151 # has it)
1091 1152 if st[stat.ST_MODE] & stat.S_IXUSR:
1092 1153 setmode = int('0755', 8)
1093 1154 else:
1094 1155 setmode = int('0644', 8)
1095 1156 m = stat.S_IMODE(st[stat.ST_MODE])
1096 1157 m = (m & ~int('0777', 8)) | setmode
1097 1158 os.chmod(dst, m)
1098 1159
1099 1160 file_util.copy_file = copyfileandsetmode
1100 1161 try:
1101 1162 install_lib.run(self)
1102 1163 finally:
1103 1164 file_util.copy_file = realcopyfile
1104 1165
1105 1166
1106 1167 class hginstallscripts(install_scripts):
1107 1168 """
1108 1169 This is a specialization of install_scripts that replaces the @LIBDIR@ with
1109 1170 the configured directory for modules. If possible, the path is made relative
1110 1171 to the directory for scripts.
1111 1172 """
1112 1173
1113 1174 def initialize_options(self):
1114 1175 install_scripts.initialize_options(self)
1115 1176
1116 1177 self.install_lib = None
1117 1178
1118 1179 def finalize_options(self):
1119 1180 install_scripts.finalize_options(self)
1120 1181 self.set_undefined_options('install', ('install_lib', 'install_lib'))
1121 1182
1122 1183 def run(self):
1123 1184 install_scripts.run(self)
1124 1185
1125 1186 # It only makes sense to replace @LIBDIR@ with the install path if
1126 1187 # the install path is known. For wheels, the logic below calculates
1127 1188 # the libdir to be "../..". This is because the internal layout of a
1128 1189 # wheel archive looks like:
1129 1190 #
1130 1191 # mercurial-3.6.1.data/scripts/hg
1131 1192 # mercurial/__init__.py
1132 1193 #
1133 1194 # When installing wheels, the subdirectories of the "<pkg>.data"
1134 1195 # directory are translated to system local paths and files therein
1135 1196 # are copied in place. The mercurial/* files are installed into the
1136 1197 # site-packages directory. However, the site-packages directory
1137 1198 # isn't known until wheel install time. This means we have no clue
1138 1199 # at wheel generation time what the installed site-packages directory
1139 1200 # will be. And, wheels don't appear to provide the ability to register
1140 1201 # custom code to run during wheel installation. This all means that
1141 1202 # we can't reliably set the libdir in wheels: the default behavior
1142 1203 # of looking in sys.path must do.
1143 1204
1144 1205 if (
1145 1206 os.path.splitdrive(self.install_dir)[0]
1146 1207 != os.path.splitdrive(self.install_lib)[0]
1147 1208 ):
1148 1209 # can't make relative paths from one drive to another, so use an
1149 1210 # absolute path instead
1150 1211 libdir = self.install_lib
1151 1212 else:
1152 1213 libdir = os.path.relpath(self.install_lib, self.install_dir)
1153 1214
1154 1215 for outfile in self.outfiles:
1155 1216 with open(outfile, 'rb') as fp:
1156 1217 data = fp.read()
1157 1218
1158 1219 # skip binary files
1159 1220 if b'\0' in data:
1160 1221 continue
1161 1222
1162 1223 # During local installs, the shebang will be rewritten to the final
1163 1224 # install path. During wheel packaging, the shebang has a special
1164 1225 # value.
1165 1226 if data.startswith(b'#!python'):
1166 1227 log.info(
1167 1228 'not rewriting @LIBDIR@ in %s because install path '
1168 1229 'not known' % outfile
1169 1230 )
1170 1231 continue
1171 1232
1172 1233 data = data.replace(b'@LIBDIR@', libdir.encode(libdir_escape))
1173 1234 with open(outfile, 'wb') as fp:
1174 1235 fp.write(data)
1175 1236
1176 1237
1177 1238 # virtualenv installs custom distutils/__init__.py and
1178 1239 # distutils/distutils.cfg files which essentially proxy back to the
1179 1240 # "real" distutils in the main Python install. The presence of this
1180 1241 # directory causes py2exe to pick up the "hacked" distutils package
1181 1242 # from the virtualenv and "import distutils" will fail from the py2exe
1182 1243 # build because the "real" distutils files can't be located.
1183 1244 #
1184 1245 # We work around this by monkeypatching the py2exe code finding Python
1185 1246 # modules to replace the found virtualenv distutils modules with the
1186 1247 # original versions via filesystem scanning. This is a bit hacky. But
1187 1248 # it allows us to use virtualenvs for py2exe packaging, which is more
1188 1249 # deterministic and reproducible.
1189 1250 #
1190 1251 # It's worth noting that the common StackOverflow suggestions for this
1191 1252 # problem involve copying the original distutils files into the
1192 1253 # virtualenv or into the staging directory after setup() is invoked.
1193 1254 # The former is very brittle and can easily break setup(). Our hacking
1194 1255 # of the found modules routine has a similar result as copying the files
1195 1256 # manually. But it makes fewer assumptions about how py2exe works and
1196 1257 # is less brittle.
1197 1258
1198 1259 # This only catches virtualenvs made with virtualenv (as opposed to
1199 1260 # venv, which is likely what Python 3 uses).
1200 1261 py2exehacked = py2exeloaded and getattr(sys, 'real_prefix', None) is not None
1201 1262
1202 1263 if py2exehacked:
1203 1264 from distutils.command.py2exe import py2exe as buildpy2exe
1204 1265 from py2exe.mf import Module as py2exemodule
1205 1266
1206 1267 class hgbuildpy2exe(buildpy2exe):
1207 1268 def find_needed_modules(self, mf, files, modules):
1208 1269 res = buildpy2exe.find_needed_modules(self, mf, files, modules)
1209 1270
1210 1271 # Replace virtualenv's distutils modules with the real ones.
1211 1272 modules = {}
1212 1273 for k, v in res.modules.items():
1213 1274 if k != 'distutils' and not k.startswith('distutils.'):
1214 1275 modules[k] = v
1215 1276
1216 1277 res.modules = modules
1217 1278
1218 1279 import opcode
1219 1280
1220 1281 distutilsreal = os.path.join(
1221 1282 os.path.dirname(opcode.__file__), 'distutils'
1222 1283 )
1223 1284
1224 1285 for root, dirs, files in os.walk(distutilsreal):
1225 1286 for f in sorted(files):
1226 1287 if not f.endswith('.py'):
1227 1288 continue
1228 1289
1229 1290 full = os.path.join(root, f)
1230 1291
1231 1292 parents = ['distutils']
1232 1293
1233 1294 if root != distutilsreal:
1234 1295 rel = os.path.relpath(root, distutilsreal)
1235 1296 parents.extend(p for p in rel.split(os.sep))
1236 1297
1237 1298 modname = '%s.%s' % ('.'.join(parents), f[:-3])
1238 1299
1239 1300 if modname.startswith('distutils.tests.'):
1240 1301 continue
1241 1302
1242 1303 if modname.endswith('.__init__'):
1243 1304 modname = modname[: -len('.__init__')]
1244 1305 path = os.path.dirname(full)
1245 1306 else:
1246 1307 path = None
1247 1308
1248 1309 res.modules[modname] = py2exemodule(
1249 1310 modname, full, path=path
1250 1311 )
1251 1312
1252 1313 if 'distutils' not in res.modules:
1253 1314 raise SystemExit('could not find distutils modules')
1254 1315
1255 1316 return res
1256 1317
1257 1318
1258 1319 cmdclass = {
1259 1320 'build': hgbuild,
1260 1321 'build_doc': hgbuilddoc,
1261 1322 'build_mo': hgbuildmo,
1262 1323 'build_ext': hgbuildext,
1263 1324 'build_py': hgbuildpy,
1264 1325 'build_scripts': hgbuildscripts,
1265 1326 'build_hgextindex': buildhgextindex,
1266 1327 'install': hginstall,
1267 1328 'install_lib': hginstalllib,
1329 'install_data': hginstalldata,
1268 1330 'install_scripts': hginstallscripts,
1269 1331 'build_hgexe': buildhgexe,
1270 1332 }
1271 1333
1272 1334 if py2exehacked:
1273 1335 cmdclass['py2exe'] = hgbuildpy2exe
1274 1336
1275 1337 packages = [
1276 1338 'mercurial',
1277 1339 'mercurial.cext',
1278 1340 'mercurial.cffi',
1279 1341 'mercurial.defaultrc',
1280 1342 'mercurial.helptext',
1281 1343 'mercurial.helptext.internals',
1282 1344 'mercurial.hgweb',
1283 1345 'mercurial.interfaces',
1284 1346 'mercurial.pure',
1285 1347 'mercurial.templates',
1286 1348 'mercurial.thirdparty',
1287 1349 'mercurial.thirdparty.attr',
1288 1350 'mercurial.thirdparty.zope',
1289 1351 'mercurial.thirdparty.zope.interface',
1290 1352 'mercurial.upgrade_utils',
1291 1353 'mercurial.utils',
1292 1354 'mercurial.revlogutils',
1293 1355 'mercurial.testing',
1294 1356 'hgext',
1295 1357 'hgext.convert',
1296 1358 'hgext.fsmonitor',
1297 1359 'hgext.fastannotate',
1298 1360 'hgext.fsmonitor.pywatchman',
1299 1361 'hgext.git',
1300 1362 'hgext.highlight',
1301 1363 'hgext.hooklib',
1302 1364 'hgext.infinitepush',
1303 1365 'hgext.largefiles',
1304 1366 'hgext.lfs',
1305 1367 'hgext.narrow',
1306 1368 'hgext.remotefilelog',
1307 1369 'hgext.zeroconf',
1308 1370 'hgext3rd',
1309 1371 'hgdemandimport',
1310 1372 ]
1311 1373
1312 1374 # The pygit2 dependency dropped py2 support with the 1.0 release in Dec 2019.
1313 1375 # Prior releases do not build at all on Windows, because Visual Studio 2008
1314 1376 # doesn't understand C 11. Older Linux releases are buggy.
1315 1377 if sys.version_info[0] == 2:
1316 1378 packages.remove('hgext.git')
1317 1379
1318 1380
1319 1381 for name in os.listdir(os.path.join('mercurial', 'templates')):
1320 1382 if name != '__pycache__' and os.path.isdir(
1321 1383 os.path.join('mercurial', 'templates', name)
1322 1384 ):
1323 1385 packages.append('mercurial.templates.%s' % name)
1324 1386
1325 1387 if sys.version_info[0] == 2:
1326 1388 packages.extend(
1327 1389 [
1328 1390 'mercurial.thirdparty.concurrent',
1329 1391 'mercurial.thirdparty.concurrent.futures',
1330 1392 ]
1331 1393 )
1332 1394
1333 1395 if 'HG_PY2EXE_EXTRA_INSTALL_PACKAGES' in os.environ:
1334 1396 # py2exe can't cope with namespace packages very well, so we have to
1335 1397 # install any hgext3rd.* extensions that we want in the final py2exe
1336 1398 # image here. This is gross, but you gotta do what you gotta do.
1337 1399 packages.extend(os.environ['HG_PY2EXE_EXTRA_INSTALL_PACKAGES'].split(' '))
1338 1400
1339 1401 common_depends = [
1340 1402 'mercurial/bitmanipulation.h',
1341 1403 'mercurial/compat.h',
1342 1404 'mercurial/cext/util.h',
1343 1405 ]
1344 1406 common_include_dirs = ['mercurial']
1345 1407
1346 1408 common_cflags = []
1347 1409
1348 1410 # MSVC 2008 still needs declarations at the top of the scope, but Python 3.9
1349 1411 # makes declarations not at the top of a scope in the headers.
1350 1412 if os.name != 'nt' and sys.version_info[1] < 9:
1351 1413 common_cflags = ['-Werror=declaration-after-statement']
1352 1414
1353 1415 osutil_cflags = []
1354 1416 osutil_ldflags = []
1355 1417
1356 1418 # platform specific macros
1357 1419 for plat, func in [('bsd', 'setproctitle')]:
1358 1420 if re.search(plat, sys.platform) and hasfunction(new_compiler(), func):
1359 1421 osutil_cflags.append('-DHAVE_%s' % func.upper())
1360 1422
1361 1423 for plat, macro, code in [
1362 1424 (
1363 1425 'bsd|darwin',
1364 1426 'BSD_STATFS',
1365 1427 '''
1366 1428 #include <sys/param.h>
1367 1429 #include <sys/mount.h>
1368 1430 int main() { struct statfs s; return sizeof(s.f_fstypename); }
1369 1431 ''',
1370 1432 ),
1371 1433 (
1372 1434 'linux',
1373 1435 'LINUX_STATFS',
1374 1436 '''
1375 1437 #include <linux/magic.h>
1376 1438 #include <sys/vfs.h>
1377 1439 int main() { struct statfs s; return sizeof(s.f_type); }
1378 1440 ''',
1379 1441 ),
1380 1442 ]:
1381 1443 if re.search(plat, sys.platform) and cancompile(new_compiler(), code):
1382 1444 osutil_cflags.append('-DHAVE_%s' % macro)
1383 1445
1384 1446 if sys.platform == 'darwin':
1385 1447 osutil_ldflags += ['-framework', 'ApplicationServices']
1386 1448
1387 1449 if sys.platform == 'sunos5':
1388 1450 osutil_ldflags += ['-lsocket']
1389 1451
1390 1452 xdiff_srcs = [
1391 1453 'mercurial/thirdparty/xdiff/xdiffi.c',
1392 1454 'mercurial/thirdparty/xdiff/xprepare.c',
1393 1455 'mercurial/thirdparty/xdiff/xutils.c',
1394 1456 ]
1395 1457
1396 1458 xdiff_headers = [
1397 1459 'mercurial/thirdparty/xdiff/xdiff.h',
1398 1460 'mercurial/thirdparty/xdiff/xdiffi.h',
1399 1461 'mercurial/thirdparty/xdiff/xinclude.h',
1400 1462 'mercurial/thirdparty/xdiff/xmacros.h',
1401 1463 'mercurial/thirdparty/xdiff/xprepare.h',
1402 1464 'mercurial/thirdparty/xdiff/xtypes.h',
1403 1465 'mercurial/thirdparty/xdiff/xutils.h',
1404 1466 ]
1405 1467
1406 1468
1407 1469 class RustCompilationError(CCompilerError):
1408 1470 """Exception class for Rust compilation errors."""
1409 1471
1410 1472
1411 1473 class RustExtension(Extension):
1412 1474 """Base classes for concrete Rust Extension classes."""
1413 1475
1414 1476 rusttargetdir = os.path.join('rust', 'target', 'release')
1415 1477
1416 1478 def __init__(
1417 1479 self, mpath, sources, rustlibname, subcrate, py3_features=None, **kw
1418 1480 ):
1419 1481 Extension.__init__(self, mpath, sources, **kw)
1420 1482 srcdir = self.rustsrcdir = os.path.join('rust', subcrate)
1421 1483 self.py3_features = py3_features
1422 1484
1423 1485 # adding Rust source and control files to depends so that the extension
1424 1486 # gets rebuilt if they've changed
1425 1487 self.depends.append(os.path.join(srcdir, 'Cargo.toml'))
1426 1488 cargo_lock = os.path.join(srcdir, 'Cargo.lock')
1427 1489 if os.path.exists(cargo_lock):
1428 1490 self.depends.append(cargo_lock)
1429 1491 for dirpath, subdir, fnames in os.walk(os.path.join(srcdir, 'src')):
1430 1492 self.depends.extend(
1431 1493 os.path.join(dirpath, fname)
1432 1494 for fname in fnames
1433 1495 if os.path.splitext(fname)[1] == '.rs'
1434 1496 )
1435 1497
1436 1498 @staticmethod
1437 1499 def rustdylibsuffix():
1438 1500 """Return the suffix for shared libraries produced by rustc.
1439 1501
1440 1502 See also: https://doc.rust-lang.org/reference/linkage.html
1441 1503 """
1442 1504 if sys.platform == 'darwin':
1443 1505 return '.dylib'
1444 1506 elif os.name == 'nt':
1445 1507 return '.dll'
1446 1508 else:
1447 1509 return '.so'
1448 1510
1449 1511 def rustbuild(self):
1450 1512 env = os.environ.copy()
1451 1513 if 'HGTEST_RESTOREENV' in env:
1452 1514 # Mercurial tests change HOME to a temporary directory,
1453 1515 # but, if installed with rustup, the Rust toolchain needs
1454 1516 # HOME to be correct (otherwise the 'no default toolchain'
1455 1517 # error message is issued and the build fails).
1456 1518 # This happens currently with test-hghave.t, which does
1457 1519 # invoke this build.
1458 1520
1459 1521 # Unix only fix (os.path.expanduser not really reliable if
1460 1522 # HOME is shadowed like this)
1461 1523 import pwd
1462 1524
1463 1525 env['HOME'] = pwd.getpwuid(os.getuid()).pw_dir
1464 1526
1465 1527 cargocmd = ['cargo', 'rustc', '--release']
1466 1528
1467 1529 feature_flags = []
1468 1530
1469 1531 if sys.version_info[0] == 3 and self.py3_features is not None:
1470 1532 feature_flags.append(self.py3_features)
1471 1533 cargocmd.append('--no-default-features')
1472 1534
1473 1535 rust_features = env.get("HG_RUST_FEATURES")
1474 1536 if rust_features:
1475 1537 feature_flags.append(rust_features)
1476 1538
1477 1539 cargocmd.extend(('--features', " ".join(feature_flags)))
1478 1540
1479 1541 cargocmd.append('--')
1480 1542 if sys.platform == 'darwin':
1481 1543 cargocmd.extend(
1482 1544 ("-C", "link-arg=-undefined", "-C", "link-arg=dynamic_lookup")
1483 1545 )
1484 1546 try:
1485 1547 subprocess.check_call(cargocmd, env=env, cwd=self.rustsrcdir)
1486 1548 except OSError as exc:
1487 1549 if exc.errno == errno.ENOENT:
1488 1550 raise RustCompilationError("Cargo not found")
1489 1551 elif exc.errno == errno.EACCES:
1490 1552 raise RustCompilationError(
1491 1553 "Cargo found, but permisssion to execute it is denied"
1492 1554 )
1493 1555 else:
1494 1556 raise
1495 1557 except subprocess.CalledProcessError:
1496 1558 raise RustCompilationError(
1497 1559 "Cargo failed. Working directory: %r, "
1498 1560 "command: %r, environment: %r"
1499 1561 % (self.rustsrcdir, cargocmd, env)
1500 1562 )
1501 1563
1502 1564
1503 1565 class RustStandaloneExtension(RustExtension):
1504 1566 def __init__(self, pydottedname, rustcrate, dylibname, **kw):
1505 1567 RustExtension.__init__(
1506 1568 self, pydottedname, [], dylibname, rustcrate, **kw
1507 1569 )
1508 1570 self.dylibname = dylibname
1509 1571
1510 1572 def build(self, target_dir):
1511 1573 self.rustbuild()
1512 1574 target = [target_dir]
1513 1575 target.extend(self.name.split('.'))
1514 1576 target[-1] += DYLIB_SUFFIX
1515 1577 shutil.copy2(
1516 1578 os.path.join(
1517 1579 self.rusttargetdir, self.dylibname + self.rustdylibsuffix()
1518 1580 ),
1519 1581 os.path.join(*target),
1520 1582 )
1521 1583
1522 1584
1523 1585 extmodules = [
1524 1586 Extension(
1525 1587 'mercurial.cext.base85',
1526 1588 ['mercurial/cext/base85.c'],
1527 1589 include_dirs=common_include_dirs,
1528 1590 extra_compile_args=common_cflags,
1529 1591 depends=common_depends,
1530 1592 ),
1531 1593 Extension(
1532 1594 'mercurial.cext.bdiff',
1533 1595 ['mercurial/bdiff.c', 'mercurial/cext/bdiff.c'] + xdiff_srcs,
1534 1596 include_dirs=common_include_dirs,
1535 1597 extra_compile_args=common_cflags,
1536 1598 depends=common_depends + ['mercurial/bdiff.h'] + xdiff_headers,
1537 1599 ),
1538 1600 Extension(
1539 1601 'mercurial.cext.mpatch',
1540 1602 ['mercurial/mpatch.c', 'mercurial/cext/mpatch.c'],
1541 1603 include_dirs=common_include_dirs,
1542 1604 extra_compile_args=common_cflags,
1543 1605 depends=common_depends,
1544 1606 ),
1545 1607 Extension(
1546 1608 'mercurial.cext.parsers',
1547 1609 [
1548 1610 'mercurial/cext/charencode.c',
1549 1611 'mercurial/cext/dirs.c',
1550 1612 'mercurial/cext/manifest.c',
1551 1613 'mercurial/cext/parsers.c',
1552 1614 'mercurial/cext/pathencode.c',
1553 1615 'mercurial/cext/revlog.c',
1554 1616 ],
1555 1617 include_dirs=common_include_dirs,
1556 1618 extra_compile_args=common_cflags,
1557 1619 depends=common_depends
1558 1620 + [
1559 1621 'mercurial/cext/charencode.h',
1560 1622 'mercurial/cext/revlog.h',
1561 1623 ],
1562 1624 ),
1563 1625 Extension(
1564 1626 'mercurial.cext.osutil',
1565 1627 ['mercurial/cext/osutil.c'],
1566 1628 include_dirs=common_include_dirs,
1567 1629 extra_compile_args=common_cflags + osutil_cflags,
1568 1630 extra_link_args=osutil_ldflags,
1569 1631 depends=common_depends,
1570 1632 ),
1571 1633 Extension(
1572 1634 'mercurial.thirdparty.zope.interface._zope_interface_coptimizations',
1573 1635 [
1574 1636 'mercurial/thirdparty/zope/interface/_zope_interface_coptimizations.c',
1575 1637 ],
1576 1638 extra_compile_args=common_cflags,
1577 1639 ),
1578 1640 Extension(
1579 1641 'mercurial.thirdparty.sha1dc',
1580 1642 [
1581 1643 'mercurial/thirdparty/sha1dc/cext.c',
1582 1644 'mercurial/thirdparty/sha1dc/lib/sha1.c',
1583 1645 'mercurial/thirdparty/sha1dc/lib/ubc_check.c',
1584 1646 ],
1585 1647 extra_compile_args=common_cflags,
1586 1648 ),
1587 1649 Extension(
1588 1650 'hgext.fsmonitor.pywatchman.bser',
1589 1651 ['hgext/fsmonitor/pywatchman/bser.c'],
1590 1652 extra_compile_args=common_cflags,
1591 1653 ),
1592 1654 RustStandaloneExtension(
1593 1655 'mercurial.rustext', 'hg-cpython', 'librusthg', py3_features='python3'
1594 1656 ),
1595 1657 ]
1596 1658
1597 1659
1598 1660 sys.path.insert(0, 'contrib/python-zstandard')
1599 1661 import setup_zstd
1600 1662
1601 1663 zstd = setup_zstd.get_c_extension(
1602 1664 name='mercurial.zstd', root=os.path.abspath(os.path.dirname(__file__))
1603 1665 )
1604 1666 zstd.extra_compile_args += common_cflags
1605 1667 extmodules.append(zstd)
1606 1668
1607 1669 try:
1608 1670 from distutils import cygwinccompiler
1609 1671
1610 1672 # the -mno-cygwin option has been deprecated for years
1611 1673 mingw32compilerclass = cygwinccompiler.Mingw32CCompiler
1612 1674
1613 1675 class HackedMingw32CCompiler(cygwinccompiler.Mingw32CCompiler):
1614 1676 def __init__(self, *args, **kwargs):
1615 1677 mingw32compilerclass.__init__(self, *args, **kwargs)
1616 1678 for i in 'compiler compiler_so linker_exe linker_so'.split():
1617 1679 try:
1618 1680 getattr(self, i).remove('-mno-cygwin')
1619 1681 except ValueError:
1620 1682 pass
1621 1683
1622 1684 cygwinccompiler.Mingw32CCompiler = HackedMingw32CCompiler
1623 1685 except ImportError:
1624 1686 # the cygwinccompiler package is not available on some Python
1625 1687 # distributions like the ones from the optware project for Synology
1626 1688 # DiskStation boxes
1627 1689 class HackedMingw32CCompiler(object):
1628 1690 pass
1629 1691
1630 1692
1631 1693 if os.name == 'nt':
1632 1694 # Allow compiler/linker flags to be added to Visual Studio builds. Passing
1633 1695 # extra_link_args to distutils.extensions.Extension() doesn't have any
1634 1696 # effect.
1635 1697 from distutils import msvccompiler
1636 1698
1637 1699 msvccompilerclass = msvccompiler.MSVCCompiler
1638 1700
1639 1701 class HackedMSVCCompiler(msvccompiler.MSVCCompiler):
1640 1702 def initialize(self):
1641 1703 msvccompilerclass.initialize(self)
1642 1704 # "warning LNK4197: export 'func' specified multiple times"
1643 1705 self.ldflags_shared.append('/ignore:4197')
1644 1706 self.ldflags_shared_debug.append('/ignore:4197')
1645 1707
1646 1708 msvccompiler.MSVCCompiler = HackedMSVCCompiler
1647 1709
1648 1710 packagedata = {
1649 1711 'mercurial': [
1650 1712 'locale/*/LC_MESSAGES/hg.mo',
1651 1713 'dummycert.pem',
1652 1714 ],
1653 1715 'mercurial.defaultrc': [
1654 1716 '*.rc',
1655 1717 ],
1656 1718 'mercurial.helptext': [
1657 1719 '*.txt',
1658 1720 ],
1659 1721 'mercurial.helptext.internals': [
1660 1722 '*.txt',
1661 1723 ],
1662 1724 }
1663 1725
1664 1726
1665 1727 def ordinarypath(p):
1666 1728 return p and p[0] != '.' and p[-1] != '~'
1667 1729
1668 1730
1669 1731 for root in ('templates',):
1670 1732 for curdir, dirs, files in os.walk(os.path.join('mercurial', root)):
1671 1733 packagename = curdir.replace(os.sep, '.')
1672 1734 packagedata[packagename] = list(filter(ordinarypath, files))
1673 1735
1674 1736 datafiles = []
1675 1737
1676 1738 # distutils expects version to be str/unicode. Converting it to
1677 1739 # unicode on Python 2 still works because it won't contain any
1678 1740 # non-ascii bytes and will be implicitly converted back to bytes
1679 1741 # when operated on.
1680 1742 assert isinstance(version, bytes)
1681 1743 setupversion = version.decode('ascii')
1682 1744
1683 1745 extra = {}
1684 1746
1685 1747 py2exepackages = [
1686 1748 'hgdemandimport',
1687 1749 'hgext3rd',
1688 1750 'hgext',
1689 1751 'email',
1690 1752 # implicitly imported per module policy
1691 1753 # (cffi wouldn't be used as a frozen exe)
1692 1754 'mercurial.cext',
1693 1755 #'mercurial.cffi',
1694 1756 'mercurial.pure',
1695 1757 ]
1696 1758
1697 1759 py2exeexcludes = []
1698 1760 py2exedllexcludes = ['crypt32.dll']
1699 1761
1700 1762 if issetuptools:
1701 1763 extra['python_requires'] = supportedpy
1702 1764
1703 1765 if py2exeloaded:
1704 1766 extra['console'] = [
1705 1767 {
1706 1768 'script': 'hg',
1707 1769 'copyright': 'Copyright (C) 2005-2020 Matt Mackall and others',
1708 1770 'product_version': version,
1709 1771 }
1710 1772 ]
1711 1773 # Sub command of 'build' because 'py2exe' does not handle sub_commands.
1712 1774 # Need to override hgbuild because it has a private copy of
1713 1775 # build.sub_commands.
1714 1776 hgbuild.sub_commands.insert(0, ('build_hgextindex', None))
1715 1777 # put dlls in sub directory so that they won't pollute PATH
1716 1778 extra['zipfile'] = 'lib/library.zip'
1717 1779
1718 1780 # We allow some configuration to be supplemented via environment
1719 1781 # variables. This is better than setup.cfg files because it allows
1720 1782 # supplementing configs instead of replacing them.
1721 1783 extrapackages = os.environ.get('HG_PY2EXE_EXTRA_PACKAGES')
1722 1784 if extrapackages:
1723 1785 py2exepackages.extend(extrapackages.split(' '))
1724 1786
1725 1787 excludes = os.environ.get('HG_PY2EXE_EXTRA_EXCLUDES')
1726 1788 if excludes:
1727 1789 py2exeexcludes.extend(excludes.split(' '))
1728 1790
1729 1791 dllexcludes = os.environ.get('HG_PY2EXE_EXTRA_DLL_EXCLUDES')
1730 1792 if dllexcludes:
1731 1793 py2exedllexcludes.extend(dllexcludes.split(' '))
1732 1794
1733 1795 if os.environ.get('PYOXIDIZER'):
1734 1796 hgbuild.sub_commands.insert(0, ('build_hgextindex', None))
1735 1797
1736 1798 if os.name == 'nt':
1737 1799 # Windows binary file versions for exe/dll files must have the
1738 1800 # form W.X.Y.Z, where W,X,Y,Z are numbers in the range 0..65535
1739 1801 setupversion = setupversion.split(r'+', 1)[0]
1740 1802
1741 1803 if sys.platform == 'darwin' and os.path.exists('/usr/bin/xcodebuild'):
1742 1804 version = runcmd(['/usr/bin/xcodebuild', '-version'], {})[1].splitlines()
1743 1805 if version:
1744 1806 version = version[0]
1745 1807 if sys.version_info[0] == 3:
1746 1808 version = version.decode('utf-8')
1747 1809 xcode4 = version.startswith('Xcode') and StrictVersion(
1748 1810 version.split()[1]
1749 1811 ) >= StrictVersion('4.0')
1750 1812 xcode51 = re.match(r'^Xcode\s+5\.1', version) is not None
1751 1813 else:
1752 1814 # xcodebuild returns empty on OS X Lion with XCode 4.3 not
1753 1815 # installed, but instead with only command-line tools. Assume
1754 1816 # that only happens on >= Lion, thus no PPC support.
1755 1817 xcode4 = True
1756 1818 xcode51 = False
1757 1819
1758 1820 # XCode 4.0 dropped support for ppc architecture, which is hardcoded in
1759 1821 # distutils.sysconfig
1760 1822 if xcode4:
1761 1823 os.environ['ARCHFLAGS'] = ''
1762 1824
1763 1825 # XCode 5.1 changes clang such that it now fails to compile if the
1764 1826 # -mno-fused-madd flag is passed, but the version of Python shipped with
1765 1827 # OS X 10.9 Mavericks includes this flag. This causes problems in all
1766 1828 # C extension modules, and a bug has been filed upstream at
1767 1829 # http://bugs.python.org/issue21244. We also need to patch this here
1768 1830 # so Mercurial can continue to compile in the meantime.
1769 1831 if xcode51:
1770 1832 cflags = get_config_var('CFLAGS')
1771 1833 if cflags and re.search(r'-mno-fused-madd\b', cflags) is not None:
1772 1834 os.environ['CFLAGS'] = (
1773 1835 os.environ.get('CFLAGS', '') + ' -Qunused-arguments'
1774 1836 )
1775 1837
1776 1838 setup(
1777 1839 name='mercurial',
1778 1840 version=setupversion,
1779 1841 author='Matt Mackall and many others',
1780 1842 author_email='mercurial@mercurial-scm.org',
1781 1843 url='https://mercurial-scm.org/',
1782 1844 download_url='https://mercurial-scm.org/release/',
1783 1845 description=(
1784 1846 'Fast scalable distributed SCM (revision control, version '
1785 1847 'control) system'
1786 1848 ),
1787 1849 long_description=(
1788 1850 'Mercurial is a distributed SCM tool written in Python.'
1789 1851 ' It is used by a number of large projects that require'
1790 1852 ' fast, reliable distributed revision control, such as '
1791 1853 'Mozilla.'
1792 1854 ),
1793 1855 license='GNU GPLv2 or any later version',
1794 1856 classifiers=[
1795 1857 'Development Status :: 6 - Mature',
1796 1858 'Environment :: Console',
1797 1859 'Intended Audience :: Developers',
1798 1860 'Intended Audience :: System Administrators',
1799 1861 'License :: OSI Approved :: GNU General Public License (GPL)',
1800 1862 'Natural Language :: Danish',
1801 1863 'Natural Language :: English',
1802 1864 'Natural Language :: German',
1803 1865 'Natural Language :: Italian',
1804 1866 'Natural Language :: Japanese',
1805 1867 'Natural Language :: Portuguese (Brazilian)',
1806 1868 'Operating System :: Microsoft :: Windows',
1807 1869 'Operating System :: OS Independent',
1808 1870 'Operating System :: POSIX',
1809 1871 'Programming Language :: C',
1810 1872 'Programming Language :: Python',
1811 1873 'Topic :: Software Development :: Version Control',
1812 1874 ],
1813 1875 scripts=scripts,
1814 1876 packages=packages,
1815 1877 ext_modules=extmodules,
1816 1878 data_files=datafiles,
1817 1879 package_data=packagedata,
1818 1880 cmdclass=cmdclass,
1819 1881 distclass=hgdist,
1820 1882 options={
1821 1883 'py2exe': {
1822 1884 'bundle_files': 3,
1823 1885 'dll_excludes': py2exedllexcludes,
1824 1886 'excludes': py2exeexcludes,
1825 1887 'packages': py2exepackages,
1826 1888 },
1827 1889 'bdist_mpkg': {
1828 1890 'zipdist': False,
1829 1891 'license': 'COPYING',
1830 1892 'readme': 'contrib/packaging/macosx/Readme.html',
1831 1893 'welcome': 'contrib/packaging/macosx/Welcome.html',
1832 1894 },
1833 1895 },
1834 1896 **extra
1835 1897 )
General Comments 0
You need to be logged in to leave comments. Login now