setup.py
changeset 43076 2372284d9457
parent 43044 f9d35f01b8b3
child 43349 c3e10f705a6c
equal deleted inserted replaced
43075:57875cf423c9 43076:2372284d9457
    15     # codecs.escape_encode() where it raises SystemError on empty bytestring
    15     # codecs.escape_encode() where it raises SystemError on empty bytestring
    16     # bug link: https://bugs.python.org/issue25270
    16     # bug link: https://bugs.python.org/issue25270
    17     #
    17     #
    18     # TODO: when we actually work on Python 3, use this string as the
    18     # TODO: when we actually work on Python 3, use this string as the
    19     # actual supportedpy string.
    19     # actual supportedpy string.
    20     supportedpy = ','.join([
    20     supportedpy = ','.join(
    21         '>=2.7',
    21         [
    22         '!=3.0.*',
    22             '>=2.7',
    23         '!=3.1.*',
    23             '!=3.0.*',
    24         '!=3.2.*',
    24             '!=3.1.*',
    25         '!=3.3.*',
    25             '!=3.2.*',
    26         '!=3.4.*',
    26             '!=3.3.*',
    27         '!=3.5.0',
    27             '!=3.4.*',
    28         '!=3.5.1',
    28             '!=3.5.0',
    29         '!=3.5.2',
    29             '!=3.5.1',
    30         '!=3.6.0',
    30             '!=3.5.2',
    31         '!=3.6.1',
    31             '!=3.6.0',
    32     ])
    32             '!=3.6.1',
       
    33         ]
       
    34     )
    33 
    35 
    34 import sys, platform
    36 import sys, platform
    35 import sysconfig
    37 import sysconfig
       
    38 
    36 if sys.version_info[0] >= 3:
    39 if sys.version_info[0] >= 3:
    37     printf = eval('print')
    40     printf = eval('print')
    38     libdir_escape = 'unicode_escape'
    41     libdir_escape = 'unicode_escape'
       
    42 
    39     def sysstr(s):
    43     def sysstr(s):
    40         return s.decode('latin-1')
    44         return s.decode('latin-1')
       
    45 
       
    46 
    41 else:
    47 else:
    42     libdir_escape = 'string_escape'
    48     libdir_escape = 'string_escape'
       
    49 
    43     def printf(*args, **kwargs):
    50     def printf(*args, **kwargs):
    44         f = kwargs.get('file', sys.stdout)
    51         f = kwargs.get('file', sys.stdout)
    45         end = kwargs.get('end', '\n')
    52         end = kwargs.get('end', '\n')
    46         f.write(b' '.join(args) + end)
    53         f.write(b' '.join(args) + end)
       
    54 
    47     def sysstr(s):
    55     def sysstr(s):
    48         return s
    56         return s
       
    57 
    49 
    58 
    50 # Attempt to guide users to a modern pip - this means that 2.6 users
    59 # Attempt to guide users to a modern pip - this means that 2.6 users
    51 # should have a chance of getting a 4.2 release, and when we ratchet
    60 # should have a chance of getting a 4.2 release, and when we ratchet
    52 # the version requirement forward again hopefully everyone will get
    61 # the version requirement forward again hopefully everyone will get
    53 # something that works for them.
    62 # something that works for them.
    54 if sys.version_info < (2, 7, 0, 'final'):
    63 if sys.version_info < (2, 7, 0, 'final'):
    55     pip_message = ('This may be due to an out of date pip. '
    64     pip_message = (
    56                    'Make sure you have pip >= 9.0.1.')
    65         'This may be due to an out of date pip. '
       
    66         'Make sure you have pip >= 9.0.1.'
       
    67     )
    57     try:
    68     try:
    58         import pip
    69         import pip
       
    70 
    59         pip_version = tuple([int(x) for x in pip.__version__.split('.')[:3]])
    71         pip_version = tuple([int(x) for x in pip.__version__.split('.')[:3]])
    60         if pip_version < (9, 0, 1) :
    72         if pip_version < (9, 0, 1):
    61             pip_message = (
    73             pip_message = (
    62                 'Your pip version is out of date, please install '
    74                 'Your pip version is out of date, please install '
    63                 'pip >= 9.0.1. pip {} detected.'.format(pip.__version__))
    75                 'pip >= 9.0.1. pip {} detected.'.format(pip.__version__)
       
    76             )
    64         else:
    77         else:
    65             # pip is new enough - it must be something else
    78             # pip is new enough - it must be something else
    66             pip_message = ''
    79             pip_message = ''
    67     except Exception:
    80     except Exception:
    68         pass
    81         pass
    69     error = """
    82     error = """
    70 Mercurial does not support Python older than 2.7.
    83 Mercurial does not support Python older than 2.7.
    71 Python {py} detected.
    84 Python {py} detected.
    72 {pip}
    85 {pip}
    73 """.format(py=sys.version_info, pip=pip_message)
    86 """.format(
       
    87         py=sys.version_info, pip=pip_message
       
    88     )
    74     printf(error, file=sys.stderr)
    89     printf(error, file=sys.stderr)
    75     sys.exit(1)
    90     sys.exit(1)
    76 
    91 
    77 # We don't yet officially support Python 3. But we want to allow developers to
    92 # We don't yet officially support Python 3. But we want to allow developers to
    78 # hack on. Detect and disallow running on Python 3 by default. But provide a
    93 # hack on. Detect and disallow running on Python 3 by default. But provide a
    98 this command. No special environment variables or configuration changes are
   113 this command. No special environment variables or configuration changes are
    99 necessary to run `hg` with Python 3.
   114 necessary to run `hg` with Python 3.
   100 
   115 
   101 See https://www.mercurial-scm.org/wiki/Python3 for more on Mercurial's
   116 See https://www.mercurial-scm.org/wiki/Python3 for more on Mercurial's
   102 Python 3 support.
   117 Python 3 support.
   103 """.format(py='.'.join('%d' % x for x in sys.version_info[0:2]))
   118 """.format(
       
   119             py='.'.join('%d' % x for x in sys.version_info[0:2])
       
   120         )
   104 
   121 
   105         printf(error, file=sys.stderr)
   122         printf(error, file=sys.stderr)
   106         sys.exit(1)
   123         sys.exit(1)
   107 
   124 
   108 if sys.version_info[0] >= 3:
   125 if sys.version_info[0] >= 3:
   112     DYLIB_SUFFIX = sysconfig.get_config_vars()['SO']
   129     DYLIB_SUFFIX = sysconfig.get_config_vars()['SO']
   113 
   130 
   114 # Solaris Python packaging brain damage
   131 # Solaris Python packaging brain damage
   115 try:
   132 try:
   116     import hashlib
   133     import hashlib
       
   134 
   117     sha = hashlib.sha1()
   135     sha = hashlib.sha1()
   118 except ImportError:
   136 except ImportError:
   119     try:
   137     try:
   120         import sha
   138         import sha
   121         sha.sha # silence unused import warning
   139 
       
   140         sha.sha  # silence unused import warning
   122     except ImportError:
   141     except ImportError:
   123         raise SystemExit(
   142         raise SystemExit(
   124             "Couldn't import standard hashlib (incomplete Python install).")
   143             "Couldn't import standard hashlib (incomplete Python install)."
       
   144         )
   125 
   145 
   126 try:
   146 try:
   127     import zlib
   147     import zlib
   128     zlib.compressobj # silence unused import warning
   148 
       
   149     zlib.compressobj  # silence unused import warning
   129 except ImportError:
   150 except ImportError:
   130     raise SystemExit(
   151     raise SystemExit(
   131         "Couldn't import standard zlib (incomplete Python install).")
   152         "Couldn't import standard zlib (incomplete Python install)."
       
   153     )
   132 
   154 
   133 # The base IronPython distribution (as of 2.7.1) doesn't support bz2
   155 # The base IronPython distribution (as of 2.7.1) doesn't support bz2
   134 isironpython = False
   156 isironpython = False
   135 try:
   157 try:
   136     isironpython = (platform.python_implementation()
   158     isironpython = (
   137                     .lower().find("ironpython") != -1)
   159         platform.python_implementation().lower().find("ironpython") != -1
       
   160     )
   138 except AttributeError:
   161 except AttributeError:
   139     pass
   162     pass
   140 
   163 
   141 if isironpython:
   164 if isironpython:
   142     sys.stderr.write("warning: IronPython detected (no bz2 support)\n")
   165     sys.stderr.write("warning: IronPython detected (no bz2 support)\n")
   143 else:
   166 else:
   144     try:
   167     try:
   145         import bz2
   168         import bz2
   146         bz2.BZ2Compressor # silence unused import warning
   169 
       
   170         bz2.BZ2Compressor  # silence unused import warning
   147     except ImportError:
   171     except ImportError:
   148         raise SystemExit(
   172         raise SystemExit(
   149             "Couldn't import standard bz2 (incomplete Python install).")
   173             "Couldn't import standard bz2 (incomplete Python install)."
       
   174         )
   150 
   175 
   151 ispypy = "PyPy" in sys.version
   176 ispypy = "PyPy" in sys.version
   152 
   177 
   153 hgrustext = os.environ.get('HGWITHRUSTEXT')
   178 hgrustext = os.environ.get('HGWITHRUSTEXT')
   154 # TODO record it for proper rebuild upon changes
   179 # TODO record it for proper rebuild upon changes
   161 import stat, subprocess, time
   186 import stat, subprocess, time
   162 import re
   187 import re
   163 import shutil
   188 import shutil
   164 import tempfile
   189 import tempfile
   165 from distutils import log
   190 from distutils import log
       
   191 
   166 # We have issues with setuptools on some platforms and builders. Until
   192 # We have issues with setuptools on some platforms and builders. Until
   167 # those are resolved, setuptools is opt-in except for platforms where
   193 # those are resolved, setuptools is opt-in except for platforms where
   168 # we don't have issues.
   194 # we don't have issues.
   169 issetuptools = (os.name == 'nt' or 'FORCE_SETUPTOOLS' in os.environ)
   195 issetuptools = os.name == 'nt' or 'FORCE_SETUPTOOLS' in os.environ
   170 if issetuptools:
   196 if issetuptools:
   171     from setuptools import setup
   197     from setuptools import setup
   172 else:
   198 else:
   173     from distutils.core import setup
   199     from distutils.core import setup
   174 from distutils.ccompiler import new_compiler
   200 from distutils.ccompiler import new_compiler
   192 from distutils.version import StrictVersion
   218 from distutils.version import StrictVersion
   193 
   219 
   194 # Explain to distutils.StrictVersion how our release candidates are versionned
   220 # Explain to distutils.StrictVersion how our release candidates are versionned
   195 StrictVersion.version_re = re.compile(r'^(\d+)\.(\d+)(\.(\d+))?-?(rc(\d+))?$')
   221 StrictVersion.version_re = re.compile(r'^(\d+)\.(\d+)(\.(\d+))?-?(rc(\d+))?$')
   196 
   222 
       
   223 
   197 def write_if_changed(path, content):
   224 def write_if_changed(path, content):
   198     """Write content to a file iff the content hasn't changed."""
   225     """Write content to a file iff the content hasn't changed."""
   199     if os.path.exists(path):
   226     if os.path.exists(path):
   200         with open(path, 'rb') as fh:
   227         with open(path, 'rb') as fh:
   201             current = fh.read()
   228             current = fh.read()
   204 
   231 
   205     if current != content:
   232     if current != content:
   206         with open(path, 'wb') as fh:
   233         with open(path, 'wb') as fh:
   207             fh.write(content)
   234             fh.write(content)
   208 
   235 
       
   236 
   209 scripts = ['hg']
   237 scripts = ['hg']
   210 if os.name == 'nt':
   238 if os.name == 'nt':
   211     # We remove hg.bat if we are able to build hg.exe.
   239     # We remove hg.bat if we are able to build hg.exe.
   212     scripts.append('contrib/win32/hg.bat')
   240     scripts.append('contrib/win32/hg.bat')
       
   241 
   213 
   242 
   214 def cancompile(cc, code):
   243 def cancompile(cc, code):
   215     tmpdir = tempfile.mkdtemp(prefix='hg-install-')
   244     tmpdir = tempfile.mkdtemp(prefix='hg-install-')
   216     devnull = oldstderr = None
   245     devnull = oldstderr = None
   217     try:
   246     try:
   236             os.dup2(oldstderr, sys.stderr.fileno())
   265             os.dup2(oldstderr, sys.stderr.fileno())
   237         if devnull is not None:
   266         if devnull is not None:
   238             devnull.close()
   267             devnull.close()
   239         shutil.rmtree(tmpdir)
   268         shutil.rmtree(tmpdir)
   240 
   269 
       
   270 
   241 # simplified version of distutils.ccompiler.CCompiler.has_function
   271 # simplified version of distutils.ccompiler.CCompiler.has_function
   242 # that actually removes its temporary files.
   272 # that actually removes its temporary files.
   243 def hasfunction(cc, funcname):
   273 def hasfunction(cc, funcname):
   244     code = 'int main(void) { %s(); }\n' % funcname
   274     code = 'int main(void) { %s(); }\n' % funcname
   245     return cancompile(cc, code)
   275     return cancompile(cc, code)
   246 
   276 
       
   277 
   247 def hasheader(cc, headername):
   278 def hasheader(cc, headername):
   248     code = '#include <%s>\nint main(void) { return 0; }\n' % headername
   279     code = '#include <%s>\nint main(void) { return 0; }\n' % headername
   249     return cancompile(cc, code)
   280     return cancompile(cc, code)
   250 
   281 
       
   282 
   251 # py2exe needs to be installed to work
   283 # py2exe needs to be installed to work
   252 try:
   284 try:
   253     import py2exe
   285     import py2exe
   254     py2exe.Distribution # silence unused import warning
   286 
       
   287     py2exe.Distribution  # silence unused import warning
   255     py2exeloaded = True
   288     py2exeloaded = True
   256     # import py2exe's patched Distribution class
   289     # import py2exe's patched Distribution class
   257     from distutils.core import Distribution
   290     from distutils.core import Distribution
   258 except ImportError:
   291 except ImportError:
   259     py2exeloaded = False
   292     py2exeloaded = False
   260 
   293 
       
   294 
   261 def runcmd(cmd, env, cwd=None):
   295 def runcmd(cmd, env, cwd=None):
   262     p = subprocess.Popen(cmd, stdout=subprocess.PIPE,
   296     p = subprocess.Popen(
   263                          stderr=subprocess.PIPE, env=env, cwd=cwd)
   297         cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, env=env, cwd=cwd
       
   298     )
   264     out, err = p.communicate()
   299     out, err = p.communicate()
   265     return p.returncode, out, err
   300     return p.returncode, out, err
       
   301 
   266 
   302 
   267 class hgcommand(object):
   303 class hgcommand(object):
   268     def __init__(self, cmd, env):
   304     def __init__(self, cmd, env):
   269         self.cmd = cmd
   305         self.cmd = cmd
   270         self.env = env
   306         self.env = env
   277             printf("stderr from '%s':" % (' '.join(cmd)), file=sys.stderr)
   313             printf("stderr from '%s':" % (' '.join(cmd)), file=sys.stderr)
   278             printf(err, file=sys.stderr)
   314             printf(err, file=sys.stderr)
   279             return ''
   315             return ''
   280         return out
   316         return out
   281 
   317 
       
   318 
   282 def filterhgerr(err):
   319 def filterhgerr(err):
   283     # If root is executing setup.py, but the repository is owned by
   320     # If root is executing setup.py, but the repository is owned by
   284     # another user (as in "sudo python setup.py install") we will get
   321     # another user (as in "sudo python setup.py install") we will get
   285     # trust warnings since the .hg/hgrc file is untrusted. That is
   322     # trust warnings since the .hg/hgrc file is untrusted. That is
   286     # fine, we don't want to load it anyway.  Python may warn about
   323     # fine, we don't want to load it anyway.  Python may warn about
   287     # a missing __init__.py in mercurial/locale, we also ignore that.
   324     # a missing __init__.py in mercurial/locale, we also ignore that.
   288     err = [e for e in err.splitlines()
   325     err = [
   289            if (not e.startswith(b'not trusting file')
   326         e
   290                and not e.startswith(b'warning: Not importing')
   327         for e in err.splitlines()
   291                and not e.startswith(b'obsolete feature not enabled')
   328         if (
   292                and not e.startswith(b'*** failed to import extension')
   329             not e.startswith(b'not trusting file')
   293                and not e.startswith(b'devel-warn:')
   330             and not e.startswith(b'warning: Not importing')
   294                and not (e.startswith(b'(third party extension')
   331             and not e.startswith(b'obsolete feature not enabled')
   295                         and e.endswith(b'or newer of Mercurial; disabling)')))]
   332             and not e.startswith(b'*** failed to import extension')
       
   333             and not e.startswith(b'devel-warn:')
       
   334             and not (
       
   335                 e.startswith(b'(third party extension')
       
   336                 and e.endswith(b'or newer of Mercurial; disabling)')
       
   337             )
       
   338         )
       
   339     ]
   296     return b'\n'.join(b'  ' + e for e in err)
   340     return b'\n'.join(b'  ' + e for e in err)
       
   341 
   297 
   342 
   298 def findhg():
   343 def findhg():
   299     """Try to figure out how we should invoke hg for examining the local
   344     """Try to figure out how we should invoke hg for examining the local
   300     repository contents.
   345     repository contents.
   301 
   346 
   332     except EnvironmentError:
   377     except EnvironmentError:
   333         retcode = -1
   378         retcode = -1
   334     if retcode == 0 and not filterhgerr(err):
   379     if retcode == 0 and not filterhgerr(err):
   335         return hgcommand(hgcmd, hgenv)
   380         return hgcommand(hgcmd, hgenv)
   336 
   381 
   337     raise SystemExit('Unable to find a working hg binary to extract the '
   382     raise SystemExit(
   338                      'version from the repository tags')
   383         'Unable to find a working hg binary to extract the '
       
   384         'version from the repository tags'
       
   385     )
       
   386 
   339 
   387 
   340 def localhgenv():
   388 def localhgenv():
   341     """Get an environment dictionary to use for invoking or importing
   389     """Get an environment dictionary to use for invoking or importing
   342     mercurial from the local repository."""
   390     mercurial from the local repository."""
   343     # Execute hg out of this directory with a custom environment which takes
   391     # Execute hg out of this directory with a custom environment which takes
   344     # care to not use any hgrc files and do no localization.
   392     # care to not use any hgrc files and do no localization.
   345     env = {'HGMODULEPOLICY': 'py',
   393     env = {
   346            'HGRCPATH': '',
   394         'HGMODULEPOLICY': 'py',
   347            'LANGUAGE': 'C',
   395         'HGRCPATH': '',
   348            'PATH': ''} # make pypi modules that use os.environ['PATH'] happy
   396         'LANGUAGE': 'C',
       
   397         'PATH': '',
       
   398     }  # make pypi modules that use os.environ['PATH'] happy
   349     if 'LD_LIBRARY_PATH' in os.environ:
   399     if 'LD_LIBRARY_PATH' in os.environ:
   350         env['LD_LIBRARY_PATH'] = os.environ['LD_LIBRARY_PATH']
   400         env['LD_LIBRARY_PATH'] = os.environ['LD_LIBRARY_PATH']
   351     if 'SystemRoot' in os.environ:
   401     if 'SystemRoot' in os.environ:
   352         # SystemRoot is required by Windows to load various DLLs.  See:
   402         # SystemRoot is required by Windows to load various DLLs.  See:
   353         # https://bugs.python.org/issue13524#msg148850
   403         # https://bugs.python.org/issue13524#msg148850
   354         env['SystemRoot'] = os.environ['SystemRoot']
   404         env['SystemRoot'] = os.environ['SystemRoot']
   355     return env
   405     return env
       
   406 
   356 
   407 
   357 version = ''
   408 version = ''
   358 
   409 
   359 if os.path.isdir('.hg'):
   410 if os.path.isdir('.hg'):
   360     hg = findhg()
   411     hg = findhg()
   365         # Bail out if hg is having problems interacting with this repository,
   416         # Bail out if hg is having problems interacting with this repository,
   366         # rather than falling through and producing a bogus version number.
   417         # rather than falling through and producing a bogus version number.
   367         # Continuing with an invalid version number will break extensions
   418         # Continuing with an invalid version number will break extensions
   368         # that define minimumhgversion.
   419         # that define minimumhgversion.
   369         raise SystemExit('Unable to determine hg version from local repository')
   420         raise SystemExit('Unable to determine hg version from local repository')
   370     if numerictags: # tag(s) found
   421     if numerictags:  # tag(s) found
   371         version = numerictags[-1]
   422         version = numerictags[-1]
   372         if hgid.endswith('+'): # propagate the dirty status to the tag
   423         if hgid.endswith('+'):  # propagate the dirty status to the tag
   373             version += '+'
   424             version += '+'
   374     else: # no tag found
   425     else:  # no tag found
   375         ltagcmd = ['parents', '--template', '{latesttag}']
   426         ltagcmd = ['parents', '--template', '{latesttag}']
   376         ltag = sysstr(hg.run(ltagcmd))
   427         ltag = sysstr(hg.run(ltagcmd))
   377         changessincecmd = ['log', '-T', 'x\n', '-r', "only(.,'%s')" % ltag]
   428         changessincecmd = ['log', '-T', 'x\n', '-r', "only(.,'%s')" % ltag]
   378         changessince = len(hg.run(changessincecmd).splitlines())
   429         changessince = len(hg.run(changessincecmd).splitlines())
   379         version = '%s+%s-%s' % (ltag, changessince, hgid)
   430         version = '%s+%s-%s' % (ltag, changessince, hgid)
   380     if version.endswith('+'):
   431     if version.endswith('+'):
   381         version += time.strftime('%Y%m%d')
   432         version += time.strftime('%Y%m%d')
   382 elif os.path.exists('.hg_archival.txt'):
   433 elif os.path.exists('.hg_archival.txt'):
   383     kw = dict([[t.strip() for t in l.split(':', 1)]
   434     kw = dict(
   384                for l in open('.hg_archival.txt')])
   435         [[t.strip() for t in l.split(':', 1)] for l in open('.hg_archival.txt')]
       
   436     )
   385     if 'tag' in kw:
   437     if 'tag' in kw:
   386         version = kw['tag']
   438         version = kw['tag']
   387     elif 'latesttag' in kw:
   439     elif 'latesttag' in kw:
   388         if 'changessincelatesttag' in kw:
   440         if 'changessincelatesttag' in kw:
   389             version = '%(latesttag)s+%(changessincelatesttag)s-%(node).12s' % kw
   441             version = '%(latesttag)s+%(changessincelatesttag)s-%(node).12s' % kw
   395 if version:
   447 if version:
   396     versionb = version
   448     versionb = version
   397     if not isinstance(versionb, bytes):
   449     if not isinstance(versionb, bytes):
   398         versionb = versionb.encode('ascii')
   450         versionb = versionb.encode('ascii')
   399 
   451 
   400     write_if_changed('mercurial/__version__.py', b''.join([
   452     write_if_changed(
   401         b'# this file is autogenerated by setup.py\n'
   453         'mercurial/__version__.py',
   402         b'version = b"%s"\n' % versionb,
   454         b''.join(
   403     ]))
   455             [
       
   456                 b'# this file is autogenerated by setup.py\n'
       
   457                 b'version = b"%s"\n' % versionb,
       
   458             ]
       
   459         ),
       
   460     )
   404 
   461 
   405 try:
   462 try:
   406     oldpolicy = os.environ.get('HGMODULEPOLICY', None)
   463     oldpolicy = os.environ.get('HGMODULEPOLICY', None)
   407     os.environ['HGMODULEPOLICY'] = 'py'
   464     os.environ['HGMODULEPOLICY'] = 'py'
   408     from mercurial import __version__
   465     from mercurial import __version__
       
   466 
   409     version = __version__.version
   467     version = __version__.version
   410 except ImportError:
   468 except ImportError:
   411     version = b'unknown'
   469     version = b'unknown'
   412 finally:
   470 finally:
   413     if oldpolicy is None:
   471     if oldpolicy is None:
   414         del os.environ['HGMODULEPOLICY']
   472         del os.environ['HGMODULEPOLICY']
   415     else:
   473     else:
   416         os.environ['HGMODULEPOLICY'] = oldpolicy
   474         os.environ['HGMODULEPOLICY'] = oldpolicy
   417 
   475 
       
   476 
   418 class hgbuild(build):
   477 class hgbuild(build):
   419     # Insert hgbuildmo first so that files in mercurial/locale/ are found
   478     # Insert hgbuildmo first so that files in mercurial/locale/ are found
   420     # when build_py is run next.
   479     # when build_py is run next.
   421     sub_commands = [('build_mo', None)] + build.sub_commands
   480     sub_commands = [('build_mo', None)] + build.sub_commands
   422 
   481 
       
   482 
   423 class hgbuildmo(build):
   483 class hgbuildmo(build):
   424 
   484 
   425     description = "build translations (.mo files)"
   485     description = "build translations (.mo files)"
   426 
   486 
   427     def run(self):
   487     def run(self):
   428         if not find_executable('msgfmt'):
   488         if not find_executable('msgfmt'):
   429             self.warn("could not find msgfmt executable, no translations "
   489             self.warn(
   430                      "will be built")
   490                 "could not find msgfmt executable, no translations "
       
   491                 "will be built"
       
   492             )
   431             return
   493             return
   432 
   494 
   433         podir = 'i18n'
   495         podir = 'i18n'
   434         if not os.path.isdir(podir):
   496         if not os.path.isdir(podir):
   435             self.warn("could not find %s/ directory" % podir)
   497             self.warn("could not find %s/ directory" % podir)
   464     def has_ext_modules(self):
   526     def has_ext_modules(self):
   465         # self.ext_modules is emptied in hgbuildpy.finalize_options which is
   527         # self.ext_modules is emptied in hgbuildpy.finalize_options which is
   466         # too late for some cases
   528         # too late for some cases
   467         return not self.pure and Distribution.has_ext_modules(self)
   529         return not self.pure and Distribution.has_ext_modules(self)
   468 
   530 
       
   531 
   469 # This is ugly as a one-liner. So use a variable.
   532 # This is ugly as a one-liner. So use a variable.
   470 buildextnegops = dict(getattr(build_ext, 'negative_options', {}))
   533 buildextnegops = dict(getattr(build_ext, 'negative_options', {}))
   471 buildextnegops['no-zstd'] = 'zstd'
   534 buildextnegops['no-zstd'] = 'zstd'
   472 buildextnegops['no-rust'] = 'rust'
   535 buildextnegops['no-rust'] = 'rust'
   473 
   536 
       
   537 
   474 class hgbuildext(build_ext):
   538 class hgbuildext(build_ext):
   475     user_options = build_ext.user_options + [
   539     user_options = build_ext.user_options + [
   476         ('zstd', None, 'compile zstd bindings [default]'),
   540         ('zstd', None, 'compile zstd bindings [default]'),
   477         ('no-zstd', None, 'do not compile zstd bindings'),
   541         ('no-zstd', None, 'do not compile zstd bindings'),
   478         ('rust', None,
   542         (
   479          'compile Rust extensions if they are in use '
   543             'rust',
   480          '(requires Cargo) [default]'),
   544             None,
       
   545             'compile Rust extensions if they are in use '
       
   546             '(requires Cargo) [default]',
       
   547         ),
   481         ('no-rust', None, 'do not compile Rust extensions'),
   548         ('no-rust', None, 'do not compile Rust extensions'),
   482     ]
   549     ]
   483 
   550 
   484     boolean_options = build_ext.boolean_options + ['zstd', 'rust']
   551     boolean_options = build_ext.boolean_options + ['zstd', 'rust']
   485     negative_opt = buildextnegops
   552     negative_opt = buildextnegops
   497             self.parallel = True
   564             self.parallel = True
   498 
   565 
   499         return build_ext.finalize_options(self)
   566         return build_ext.finalize_options(self)
   500 
   567 
   501     def build_extensions(self):
   568     def build_extensions(self):
   502         ruststandalones = [e for e in self.extensions
   569         ruststandalones = [
   503                            if isinstance(e, RustStandaloneExtension)]
   570             e for e in self.extensions if isinstance(e, RustStandaloneExtension)
   504         self.extensions = [e for e in self.extensions
   571         ]
   505                            if e not in ruststandalones]
   572         self.extensions = [
       
   573             e for e in self.extensions if e not in ruststandalones
       
   574         ]
   506         # Filter out zstd if disabled via argument.
   575         # Filter out zstd if disabled via argument.
   507         if not self.zstd:
   576         if not self.zstd:
   508             self.extensions = [e for e in self.extensions
   577             self.extensions = [
   509                                if e.name != 'mercurial.zstd']
   578                 e for e in self.extensions if e.name != 'mercurial.zstd'
       
   579             ]
   510 
   580 
   511         # Build Rust standalon extensions if it'll be used
   581         # Build Rust standalon extensions if it'll be used
   512         # and its build is not explictely disabled (for external build
   582         # and its build is not explictely disabled (for external build
   513         # as Linux distributions would do)
   583         # as Linux distributions would do)
   514         if self.distribution.rust and self.rust and hgrustext != 'direct-ffi':
   584         if self.distribution.rust and self.rust and hgrustext != 'direct-ffi':
   516                 rustext.build('' if self.inplace else self.build_lib)
   586                 rustext.build('' if self.inplace else self.build_lib)
   517 
   587 
   518         return build_ext.build_extensions(self)
   588         return build_ext.build_extensions(self)
   519 
   589 
   520     def build_extension(self, ext):
   590     def build_extension(self, ext):
   521         if (self.distribution.rust and self.rust
   591         if (
   522             and isinstance(ext, RustExtension)):
   592             self.distribution.rust
   523                 ext.rustbuild()
   593             and self.rust
       
   594             and isinstance(ext, RustExtension)
       
   595         ):
       
   596             ext.rustbuild()
   524         try:
   597         try:
   525             build_ext.build_extension(self, ext)
   598             build_ext.build_extension(self, ext)
   526         except CCompilerError:
   599         except CCompilerError:
   527             if not getattr(ext, 'optional', False):
   600             if not getattr(ext, 'optional', False):
   528                 raise
   601                 raise
   529             log.warn("Failed to build optional extension '%s' (skipping)",
   602             log.warn(
   530                      ext.name)
   603                 "Failed to build optional extension '%s' (skipping)", ext.name
       
   604             )
       
   605 
   531 
   606 
   532 class hgbuildscripts(build_scripts):
   607 class hgbuildscripts(build_scripts):
   533     def run(self):
   608     def run(self):
   534         if os.name != 'nt' or self.distribution.pure:
   609         if os.name != 'nt' or self.distribution.pure:
   535             return build_scripts.run(self)
   610             return build_scripts.run(self)
   552             # Remove hg.bat because it is redundant with hg.exe.
   627             # Remove hg.bat because it is redundant with hg.exe.
   553             self.scripts.remove('contrib/win32/hg.bat')
   628             self.scripts.remove('contrib/win32/hg.bat')
   554 
   629 
   555         return build_scripts.run(self)
   630         return build_scripts.run(self)
   556 
   631 
       
   632 
   557 class hgbuildpy(build_py):
   633 class hgbuildpy(build_py):
   558     def finalize_options(self):
   634     def finalize_options(self):
   559         build_py.finalize_options(self)
   635         build_py.finalize_options(self)
   560 
   636 
   561         if self.distribution.pure:
   637         if self.distribution.pure:
   563         elif self.distribution.cffi:
   639         elif self.distribution.cffi:
   564             from mercurial.cffi import (
   640             from mercurial.cffi import (
   565                 bdiffbuild,
   641                 bdiffbuild,
   566                 mpatchbuild,
   642                 mpatchbuild,
   567             )
   643             )
   568             exts = [mpatchbuild.ffi.distutils_extension(),
   644 
   569                     bdiffbuild.ffi.distutils_extension()]
   645             exts = [
       
   646                 mpatchbuild.ffi.distutils_extension(),
       
   647                 bdiffbuild.ffi.distutils_extension(),
       
   648             ]
   570             # cffi modules go here
   649             # cffi modules go here
   571             if sys.platform == 'darwin':
   650             if sys.platform == 'darwin':
   572                 from mercurial.cffi import osutilbuild
   651                 from mercurial.cffi import osutilbuild
       
   652 
   573                 exts.append(osutilbuild.ffi.distutils_extension())
   653                 exts.append(osutilbuild.ffi.distutils_extension())
   574             self.distribution.ext_modules = exts
   654             self.distribution.ext_modules = exts
   575         else:
   655         else:
   576             h = os.path.join(get_python_inc(), 'Python.h')
   656             h = os.path.join(get_python_inc(), 'Python.h')
   577             if not os.path.exists(h):
   657             if not os.path.exists(h):
   578                 raise SystemExit('Python headers are required to build '
   658                 raise SystemExit(
   579                                  'Mercurial but weren\'t found in %s' % h)
   659                     'Python headers are required to build '
       
   660                     'Mercurial but weren\'t found in %s' % h
       
   661                 )
   580 
   662 
   581     def run(self):
   663     def run(self):
   582         basepath = os.path.join(self.build_lib, 'mercurial')
   664         basepath = os.path.join(self.build_lib, 'mercurial')
   583         self.mkpath(basepath)
   665         self.mkpath(basepath)
   584 
   666 
   589             # in-place build should run without rebuilding and Rust extensions
   671             # in-place build should run without rebuilding and Rust extensions
   590             modulepolicy = 'rust+c-allow' if rust else 'allow'
   672             modulepolicy = 'rust+c-allow' if rust else 'allow'
   591         else:
   673         else:
   592             modulepolicy = 'rust+c' if rust else 'c'
   674             modulepolicy = 'rust+c' if rust else 'c'
   593 
   675 
   594         content = b''.join([
   676         content = b''.join(
   595             b'# this file is autogenerated by setup.py\n',
   677             [
   596             b'modulepolicy = b"%s"\n' % modulepolicy.encode('ascii'),
   678                 b'# this file is autogenerated by setup.py\n',
   597         ])
   679                 b'modulepolicy = b"%s"\n' % modulepolicy.encode('ascii'),
   598         write_if_changed(os.path.join(basepath, '__modulepolicy__.py'),
   680             ]
   599                          content)
   681         )
       
   682         write_if_changed(os.path.join(basepath, '__modulepolicy__.py'), content)
   600 
   683 
   601         build_py.run(self)
   684         build_py.run(self)
       
   685 
   602 
   686 
   603 class buildhgextindex(Command):
   687 class buildhgextindex(Command):
   604     description = 'generate prebuilt index of hgext (for frozen package)'
   688     description = 'generate prebuilt index of hgext (for frozen package)'
   605     user_options = []
   689     user_options = []
   606     _indexfilename = 'hgext/__index__.py'
   690     _indexfilename = 'hgext/__index__.py'
   615         if os.path.exists(self._indexfilename):
   699         if os.path.exists(self._indexfilename):
   616             with open(self._indexfilename, 'w') as f:
   700             with open(self._indexfilename, 'w') as f:
   617                 f.write('# empty\n')
   701                 f.write('# empty\n')
   618 
   702 
   619         # here no extension enabled, disabled() lists up everything
   703         # here no extension enabled, disabled() lists up everything
   620         code = ('import pprint; from mercurial import extensions; '
   704         code = (
   621                 'pprint.pprint(extensions.disabled())')
   705             'import pprint; from mercurial import extensions; '
   622         returncode, out, err = runcmd([sys.executable, '-c', code],
   706             'pprint.pprint(extensions.disabled())'
   623                                       localhgenv())
   707         )
       
   708         returncode, out, err = runcmd(
       
   709             [sys.executable, '-c', code], localhgenv()
       
   710         )
   624         if err or returncode != 0:
   711         if err or returncode != 0:
   625             raise DistutilsExecError(err)
   712             raise DistutilsExecError(err)
   626 
   713 
   627         with open(self._indexfilename, 'wb') as f:
   714         with open(self._indexfilename, 'wb') as f:
   628             f.write(b'# this file is autogenerated by setup.py\n')
   715             f.write(b'# this file is autogenerated by setup.py\n')
   629             f.write(b'docs = ')
   716             f.write(b'docs = ')
   630             f.write(out)
   717             f.write(out)
   631 
   718 
       
   719 
   632 class buildhgexe(build_ext):
   720 class buildhgexe(build_ext):
   633     description = 'compile hg.exe from mercurial/exewrapper.c'
   721     description = 'compile hg.exe from mercurial/exewrapper.c'
   634     user_options = build_ext.user_options + [
   722     user_options = build_ext.user_options + [
   635         ('long-paths-support', None, 'enable support for long paths on '
   723         (
   636                                      'Windows (off by default and '
   724             'long-paths-support',
   637                                      'experimental)'),
   725             None,
       
   726             'enable support for long paths on '
       
   727             'Windows (off by default and '
       
   728             'experimental)',
       
   729         ),
   638     ]
   730     ]
   639 
   731 
   640     LONG_PATHS_MANIFEST = """
   732     LONG_PATHS_MANIFEST = """
   641     <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
   733     <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
   642     <assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
   734     <assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
   654 
   746 
   655     def build_extensions(self):
   747     def build_extensions(self):
   656         if os.name != 'nt':
   748         if os.name != 'nt':
   657             return
   749             return
   658         if isinstance(self.compiler, HackedMingw32CCompiler):
   750         if isinstance(self.compiler, HackedMingw32CCompiler):
   659             self.compiler.compiler_so = self.compiler.compiler # no -mdll
   751             self.compiler.compiler_so = self.compiler.compiler  # no -mdll
   660             self.compiler.dll_libraries = [] # no -lmsrvc90
   752             self.compiler.dll_libraries = []  # no -lmsrvc90
   661 
   753 
   662         # Different Python installs can have different Python library
   754         # Different Python installs can have different Python library
   663         # names. e.g. the official CPython distribution uses pythonXY.dll
   755         # names. e.g. the official CPython distribution uses pythonXY.dll
   664         # and MinGW uses libpythonX.Y.dll.
   756         # and MinGW uses libpythonX.Y.dll.
   665         _kernel32 = ctypes.windll.kernel32
   757         _kernel32 = ctypes.windll.kernel32
   666         _kernel32.GetModuleFileNameA.argtypes = [ctypes.c_void_p,
   758         _kernel32.GetModuleFileNameA.argtypes = [
   667                                                  ctypes.c_void_p,
   759             ctypes.c_void_p,
   668                                                  ctypes.c_ulong]
   760             ctypes.c_void_p,
       
   761             ctypes.c_ulong,
       
   762         ]
   669         _kernel32.GetModuleFileNameA.restype = ctypes.c_ulong
   763         _kernel32.GetModuleFileNameA.restype = ctypes.c_ulong
   670         size = 1000
   764         size = 1000
   671         buf = ctypes.create_string_buffer(size + 1)
   765         buf = ctypes.create_string_buffer(size + 1)
   672         filelen = _kernel32.GetModuleFileNameA(sys.dllhandle, ctypes.byref(buf),
   766         filelen = _kernel32.GetModuleFileNameA(
   673                                                size)
   767             sys.dllhandle, ctypes.byref(buf), size
       
   768         )
   674 
   769 
   675         if filelen > 0 and filelen != size:
   770         if filelen > 0 and filelen != size:
   676             dllbasename = os.path.basename(buf.value)
   771             dllbasename = os.path.basename(buf.value)
   677             if not dllbasename.lower().endswith(b'.dll'):
   772             if not dllbasename.lower().endswith(b'.dll'):
   678                 raise SystemExit('Python DLL does not end with .dll: %s' %
   773                 raise SystemExit(
   679                                  dllbasename)
   774                     'Python DLL does not end with .dll: %s' % dllbasename
       
   775                 )
   680             pythonlib = dllbasename[:-4]
   776             pythonlib = dllbasename[:-4]
   681         else:
   777         else:
   682             log.warn('could not determine Python DLL filename; '
   778             log.warn(
   683                      'assuming pythonXY')
   779                 'could not determine Python DLL filename; ' 'assuming pythonXY'
       
   780             )
   684 
   781 
   685             hv = sys.hexversion
   782             hv = sys.hexversion
   686             pythonlib = 'python%d%d' % (hv >> 24, (hv >> 16) & 0xff)
   783             pythonlib = 'python%d%d' % (hv >> 24, (hv >> 16) & 0xFF)
   687 
   784 
   688         log.info('using %s as Python library name' % pythonlib)
   785         log.info('using %s as Python library name' % pythonlib)
   689         with open('mercurial/hgpythonlib.h', 'wb') as f:
   786         with open('mercurial/hgpythonlib.h', 'wb') as f:
   690             f.write(b'/* this file is autogenerated by setup.py */\n')
   787             f.write(b'/* this file is autogenerated by setup.py */\n')
   691             f.write(b'#define HGPYTHONLIB "%s"\n' % pythonlib)
   788             f.write(b'#define HGPYTHONLIB "%s"\n' % pythonlib)
   692 
   789 
   693         macros = None
   790         macros = None
   694         if sys.version_info[0] >= 3:
   791         if sys.version_info[0] >= 3:
   695             macros = [('_UNICODE', None), ('UNICODE', None)]
   792             macros = [('_UNICODE', None), ('UNICODE', None)]
   696 
   793 
   697         objects = self.compiler.compile(['mercurial/exewrapper.c'],
   794         objects = self.compiler.compile(
   698                                          output_dir=self.build_temp,
   795             ['mercurial/exewrapper.c'],
   699                                          macros=macros)
   796             output_dir=self.build_temp,
       
   797             macros=macros,
       
   798         )
   700         dir = os.path.dirname(self.get_ext_fullpath('dummy'))
   799         dir = os.path.dirname(self.get_ext_fullpath('dummy'))
   701         self.hgtarget = os.path.join(dir, 'hg')
   800         self.hgtarget = os.path.join(dir, 'hg')
   702         self.compiler.link_executable(objects, self.hgtarget,
   801         self.compiler.link_executable(
   703                                       libraries=[],
   802             objects, self.hgtarget, libraries=[], output_dir=self.build_temp
   704                                       output_dir=self.build_temp)
   803         )
   705         if self.long_paths_support:
   804         if self.long_paths_support:
   706             self.addlongpathsmanifest()
   805             self.addlongpathsmanifest()
   707 
   806 
   708     def addlongpathsmanifest(self):
   807     def addlongpathsmanifest(self):
   709         r"""Add manifest pieces so that hg.exe understands long paths
   808         r"""Add manifest pieces so that hg.exe understands long paths
   731         inputresource = '-inputresource:%s;#1' % exefname
   830         inputresource = '-inputresource:%s;#1' % exefname
   732         outputresource = '-outputresource:%s;#1' % exefname
   831         outputresource = '-outputresource:%s;#1' % exefname
   733         log.info("running mt.exe to update hg.exe's manifest in-place")
   832         log.info("running mt.exe to update hg.exe's manifest in-place")
   734         # supplying both -manifest and -inputresource to mt.exe makes
   833         # supplying both -manifest and -inputresource to mt.exe makes
   735         # it merge the embedded and supplied manifests in the -outputresource
   834         # it merge the embedded and supplied manifests in the -outputresource
   736         self.spawn(['mt.exe', '-nologo', '-manifest', manfname,
   835         self.spawn(
   737                     inputresource, outputresource])
   836             [
       
   837                 'mt.exe',
       
   838                 '-nologo',
       
   839                 '-manifest',
       
   840                 manfname,
       
   841                 inputresource,
       
   842                 outputresource,
       
   843             ]
       
   844         )
   738         log.info("done updating hg.exe's manifest")
   845         log.info("done updating hg.exe's manifest")
   739         os.remove(manfname)
   846         os.remove(manfname)
   740 
   847 
   741     @property
   848     @property
   742     def hgexepath(self):
   849     def hgexepath(self):
   743         dir = os.path.dirname(self.get_ext_fullpath('dummy'))
   850         dir = os.path.dirname(self.get_ext_fullpath('dummy'))
   744         return os.path.join(self.build_temp, dir, 'hg.exe')
   851         return os.path.join(self.build_temp, dir, 'hg.exe')
       
   852 
   745 
   853 
   746 class hgbuilddoc(Command):
   854 class hgbuilddoc(Command):
   747     description = 'build documentation'
   855     description = 'build documentation'
   748     user_options = [
   856     user_options = [
   749         ('man', None, 'generate man pages'),
   857         ('man', None, 'generate man pages'),
   780 
   888 
   781         def gentxt(root):
   889         def gentxt(root):
   782             txt = 'doc/%s.txt' % root
   890             txt = 'doc/%s.txt' % root
   783             log.info('generating %s' % txt)
   891             log.info('generating %s' % txt)
   784             res, out, err = runcmd(
   892             res, out, err = runcmd(
   785                 [sys.executable, 'gendoc.py', root],
   893                 [sys.executable, 'gendoc.py', root], os.environ, cwd='doc'
   786                 os.environ,
   894             )
   787                 cwd='doc')
       
   788             if res:
   895             if res:
   789                 raise SystemExit('error running gendoc.py: %s' %
   896                 raise SystemExit(
   790                                  '\n'.join([out, err]))
   897                     'error running gendoc.py: %s' % '\n'.join([out, err])
       
   898                 )
   791 
   899 
   792             with open(txt, 'wb') as fh:
   900             with open(txt, 'wb') as fh:
   793                 fh.write(out)
   901                 fh.write(out)
   794 
   902 
   795         def gengendoc(root):
   903         def gengendoc(root):
   797 
   905 
   798             log.info('generating %s' % gendoc)
   906             log.info('generating %s' % gendoc)
   799             res, out, err = runcmd(
   907             res, out, err = runcmd(
   800                 [sys.executable, 'gendoc.py', '%s.gendoc' % root],
   908                 [sys.executable, 'gendoc.py', '%s.gendoc' % root],
   801                 os.environ,
   909                 os.environ,
   802                 cwd='doc')
   910                 cwd='doc',
       
   911             )
   803             if res:
   912             if res:
   804                 raise SystemExit('error running gendoc: %s' %
   913                 raise SystemExit(
   805                                  '\n'.join([out, err]))
   914                     'error running gendoc: %s' % '\n'.join([out, err])
       
   915                 )
   806 
   916 
   807             with open(gendoc, 'wb') as fh:
   917             with open(gendoc, 'wb') as fh:
   808                 fh.write(out)
   918                 fh.write(out)
   809 
   919 
   810         def genman(root):
   920         def genman(root):
   811             log.info('generating doc/%s' % root)
   921             log.info('generating doc/%s' % root)
   812             res, out, err = runcmd(
   922             res, out, err = runcmd(
   813                 [sys.executable, 'runrst', 'hgmanpage', '--halt', 'warning',
   923                 [
   814                  '--strip-elements-with-class', 'htmlonly',
   924                     sys.executable,
   815                  '%s.txt' % root, root],
   925                     'runrst',
       
   926                     'hgmanpage',
       
   927                     '--halt',
       
   928                     'warning',
       
   929                     '--strip-elements-with-class',
       
   930                     'htmlonly',
       
   931                     '%s.txt' % root,
       
   932                     root,
       
   933                 ],
   816                 os.environ,
   934                 os.environ,
   817                 cwd='doc')
   935                 cwd='doc',
       
   936             )
   818             if res:
   937             if res:
   819                 raise SystemExit('error running runrst: %s' %
   938                 raise SystemExit(
   820                                  '\n'.join([out, err]))
   939                     'error running runrst: %s' % '\n'.join([out, err])
       
   940                 )
   821 
   941 
   822             normalizecrlf('doc/%s' % root)
   942             normalizecrlf('doc/%s' % root)
   823 
   943 
   824         def genhtml(root):
   944         def genhtml(root):
   825             log.info('generating doc/%s.html' % root)
   945             log.info('generating doc/%s.html' % root)
   826             res, out, err = runcmd(
   946             res, out, err = runcmd(
   827                 [sys.executable, 'runrst', 'html', '--halt', 'warning',
   947                 [
   828                  '--link-stylesheet', '--stylesheet-path', 'style.css',
   948                     sys.executable,
   829                  '%s.txt' % root, '%s.html' % root],
   949                     'runrst',
       
   950                     'html',
       
   951                     '--halt',
       
   952                     'warning',
       
   953                     '--link-stylesheet',
       
   954                     '--stylesheet-path',
       
   955                     'style.css',
       
   956                     '%s.txt' % root,
       
   957                     '%s.html' % root,
       
   958                 ],
   830                 os.environ,
   959                 os.environ,
   831                 cwd='doc')
   960                 cwd='doc',
       
   961             )
   832             if res:
   962             if res:
   833                 raise SystemExit('error running runrst: %s' %
   963                 raise SystemExit(
   834                                  '\n'.join([out, err]))
   964                     'error running runrst: %s' % '\n'.join([out, err])
       
   965                 )
   835 
   966 
   836             normalizecrlf('doc/%s.html' % root)
   967             normalizecrlf('doc/%s.html' % root)
   837 
   968 
   838         # This logic is duplicated in doc/Makefile.
   969         # This logic is duplicated in doc/Makefile.
   839         sources = set(f for f in os.listdir('mercurial/help')
   970         sources = set(
   840                       if re.search(r'[0-9]\.txt$', f))
   971             f
       
   972             for f in os.listdir('mercurial/help')
       
   973             if re.search(r'[0-9]\.txt$', f)
       
   974         )
   841 
   975 
   842         # common.txt is a one-off.
   976         # common.txt is a one-off.
   843         gentxt('common')
   977         gentxt('common')
   844 
   978 
   845         for source in sorted(sources):
   979         for source in sorted(sources):
   852             if self.man:
   986             if self.man:
   853                 genman(root)
   987                 genman(root)
   854             if self.html:
   988             if self.html:
   855                 genhtml(root)
   989                 genhtml(root)
   856 
   990 
       
   991 
   857 class hginstall(install):
   992 class hginstall(install):
   858 
   993 
   859     user_options = install.user_options + [
   994     user_options = install.user_options + [
   860         ('old-and-unmanageable', None,
   995         (
   861          'noop, present for eggless setuptools compat'),
   996             'old-and-unmanageable',
   862         ('single-version-externally-managed', None,
   997             None,
   863          'noop, present for eggless setuptools compat'),
   998             'noop, present for eggless setuptools compat',
       
   999         ),
       
  1000         (
       
  1001             'single-version-externally-managed',
       
  1002             None,
       
  1003             'noop, present for eggless setuptools compat',
       
  1004         ),
   864     ]
  1005     ]
   865 
  1006 
   866     # Also helps setuptools not be sad while we refuse to create eggs.
  1007     # Also helps setuptools not be sad while we refuse to create eggs.
   867     single_version_externally_managed = True
  1008     single_version_externally_managed = True
   868 
  1009 
   870         # Screen out egg related commands to prevent egg generation.  But allow
  1011         # Screen out egg related commands to prevent egg generation.  But allow
   871         # mercurial.egg-info generation, since that is part of modern
  1012         # mercurial.egg-info generation, since that is part of modern
   872         # packaging.
  1013         # packaging.
   873         excl = set(['bdist_egg'])
  1014         excl = set(['bdist_egg'])
   874         return filter(lambda x: x not in excl, install.get_sub_commands(self))
  1015         return filter(lambda x: x not in excl, install.get_sub_commands(self))
       
  1016 
   875 
  1017 
   876 class hginstalllib(install_lib):
  1018 class hginstalllib(install_lib):
   877     '''
  1019     '''
   878     This is a specialization of install_lib that replaces the copy_file used
  1020     This is a specialization of install_lib that replaces the copy_file used
   879     there so that it supports setting the mode of files after copying them,
  1021     there so that it supports setting the mode of files after copying them,
   885     insufficient, as it might still be applying a umask.
  1027     insufficient, as it might still be applying a umask.
   886     '''
  1028     '''
   887 
  1029 
   888     def run(self):
  1030     def run(self):
   889         realcopyfile = file_util.copy_file
  1031         realcopyfile = file_util.copy_file
       
  1032 
   890         def copyfileandsetmode(*args, **kwargs):
  1033         def copyfileandsetmode(*args, **kwargs):
   891             src, dst = args[0], args[1]
  1034             src, dst = args[0], args[1]
   892             dst, copied = realcopyfile(*args, **kwargs)
  1035             dst, copied = realcopyfile(*args, **kwargs)
   893             if copied:
  1036             if copied:
   894                 st = os.stat(src)
  1037                 st = os.stat(src)
   899                 else:
  1042                 else:
   900                     setmode = int('0644', 8)
  1043                     setmode = int('0644', 8)
   901                 m = stat.S_IMODE(st[stat.ST_MODE])
  1044                 m = stat.S_IMODE(st[stat.ST_MODE])
   902                 m = (m & ~int('0777', 8)) | setmode
  1045                 m = (m & ~int('0777', 8)) | setmode
   903                 os.chmod(dst, m)
  1046                 os.chmod(dst, m)
       
  1047 
   904         file_util.copy_file = copyfileandsetmode
  1048         file_util.copy_file = copyfileandsetmode
   905         try:
  1049         try:
   906             install_lib.run(self)
  1050             install_lib.run(self)
   907         finally:
  1051         finally:
   908             file_util.copy_file = realcopyfile
  1052             file_util.copy_file = realcopyfile
       
  1053 
   909 
  1054 
   910 class hginstallscripts(install_scripts):
  1055 class hginstallscripts(install_scripts):
   911     '''
  1056     '''
   912     This is a specialization of install_scripts that replaces the @LIBDIR@ with
  1057     This is a specialization of install_scripts that replaces the @LIBDIR@ with
   913     the configured directory for modules. If possible, the path is made relative
  1058     the configured directory for modules. If possible, the path is made relative
   919 
  1064 
   920         self.install_lib = None
  1065         self.install_lib = None
   921 
  1066 
   922     def finalize_options(self):
  1067     def finalize_options(self):
   923         install_scripts.finalize_options(self)
  1068         install_scripts.finalize_options(self)
   924         self.set_undefined_options('install',
  1069         self.set_undefined_options('install', ('install_lib', 'install_lib'))
   925                                    ('install_lib', 'install_lib'))
       
   926 
  1070 
   927     def run(self):
  1071     def run(self):
   928         install_scripts.run(self)
  1072         install_scripts.run(self)
   929 
  1073 
   930         # It only makes sense to replace @LIBDIR@ with the install path if
  1074         # It only makes sense to replace @LIBDIR@ with the install path if
   944         # will be. And, wheels don't appear to provide the ability to register
  1088         # will be. And, wheels don't appear to provide the ability to register
   945         # custom code to run during wheel installation. This all means that
  1089         # custom code to run during wheel installation. This all means that
   946         # we can't reliably set the libdir in wheels: the default behavior
  1090         # we can't reliably set the libdir in wheels: the default behavior
   947         # of looking in sys.path must do.
  1091         # of looking in sys.path must do.
   948 
  1092 
   949         if (os.path.splitdrive(self.install_dir)[0] !=
  1093         if (
   950             os.path.splitdrive(self.install_lib)[0]):
  1094             os.path.splitdrive(self.install_dir)[0]
       
  1095             != os.path.splitdrive(self.install_lib)[0]
       
  1096         ):
   951             # can't make relative paths from one drive to another, so use an
  1097             # can't make relative paths from one drive to another, so use an
   952             # absolute path instead
  1098             # absolute path instead
   953             libdir = self.install_lib
  1099             libdir = self.install_lib
   954         else:
  1100         else:
   955             common = os.path.commonprefix((self.install_dir, self.install_lib))
  1101             common = os.path.commonprefix((self.install_dir, self.install_lib))
   956             rest = self.install_dir[len(common):]
  1102             rest = self.install_dir[len(common) :]
   957             uplevel = len([n for n in os.path.split(rest) if n])
  1103             uplevel = len([n for n in os.path.split(rest) if n])
   958 
  1104 
   959             libdir = uplevel * ('..' + os.sep) + self.install_lib[len(common):]
  1105             libdir = uplevel * ('..' + os.sep) + self.install_lib[len(common) :]
   960 
  1106 
   961         for outfile in self.outfiles:
  1107         for outfile in self.outfiles:
   962             with open(outfile, 'rb') as fp:
  1108             with open(outfile, 'rb') as fp:
   963                 data = fp.read()
  1109                 data = fp.read()
   964 
  1110 
   968 
  1114 
   969             # During local installs, the shebang will be rewritten to the final
  1115             # During local installs, the shebang will be rewritten to the final
   970             # install path. During wheel packaging, the shebang has a special
  1116             # install path. During wheel packaging, the shebang has a special
   971             # value.
  1117             # value.
   972             if data.startswith(b'#!python'):
  1118             if data.startswith(b'#!python'):
   973                 log.info('not rewriting @LIBDIR@ in %s because install path '
  1119                 log.info(
   974                          'not known' % outfile)
  1120                     'not rewriting @LIBDIR@ in %s because install path '
       
  1121                     'not known' % outfile
       
  1122                 )
   975                 continue
  1123                 continue
   976 
  1124 
   977             data = data.replace(b'@LIBDIR@', libdir.encode(libdir_escape))
  1125             data = data.replace(b'@LIBDIR@', libdir.encode(libdir_escape))
   978             with open(outfile, 'wb') as fp:
  1126             with open(outfile, 'wb') as fp:
   979                 fp.write(data)
  1127                 fp.write(data)
       
  1128 
   980 
  1129 
   981 # virtualenv installs custom distutils/__init__.py and
  1130 # virtualenv installs custom distutils/__init__.py and
   982 # distutils/distutils.cfg files which essentially proxy back to the
  1131 # distutils/distutils.cfg files which essentially proxy back to the
   983 # "real" distutils in the main Python install. The presence of this
  1132 # "real" distutils in the main Python install. The presence of this
   984 # directory causes py2exe to pick up the "hacked" distutils package
  1133 # directory causes py2exe to pick up the "hacked" distutils package
  1018                     modules[k] = v
  1167                     modules[k] = v
  1019 
  1168 
  1020             res.modules = modules
  1169             res.modules = modules
  1021 
  1170 
  1022             import opcode
  1171             import opcode
  1023             distutilsreal = os.path.join(os.path.dirname(opcode.__file__),
  1172 
  1024                                          'distutils')
  1173             distutilsreal = os.path.join(
       
  1174                 os.path.dirname(opcode.__file__), 'distutils'
       
  1175             )
  1025 
  1176 
  1026             for root, dirs, files in os.walk(distutilsreal):
  1177             for root, dirs, files in os.walk(distutilsreal):
  1027                 for f in sorted(files):
  1178                 for f in sorted(files):
  1028                     if not f.endswith('.py'):
  1179                     if not f.endswith('.py'):
  1029                         continue
  1180                         continue
  1040 
  1191 
  1041                     if modname.startswith('distutils.tests.'):
  1192                     if modname.startswith('distutils.tests.'):
  1042                         continue
  1193                         continue
  1043 
  1194 
  1044                     if modname.endswith('.__init__'):
  1195                     if modname.endswith('.__init__'):
  1045                         modname = modname[:-len('.__init__')]
  1196                         modname = modname[: -len('.__init__')]
  1046                         path = os.path.dirname(full)
  1197                         path = os.path.dirname(full)
  1047                     else:
  1198                     else:
  1048                         path = None
  1199                         path = None
  1049 
  1200 
  1050                     res.modules[modname] = py2exemodule(modname, full,
  1201                     res.modules[modname] = py2exemodule(
  1051                                                         path=path)
  1202                         modname, full, path=path
       
  1203                     )
  1052 
  1204 
  1053             if 'distutils' not in res.modules:
  1205             if 'distutils' not in res.modules:
  1054                 raise SystemExit('could not find distutils modules')
  1206                 raise SystemExit('could not find distutils modules')
  1055 
  1207 
  1056             return res
  1208             return res
  1057 
  1209 
  1058 cmdclass = {'build': hgbuild,
  1210 
  1059             'build_doc': hgbuilddoc,
  1211 cmdclass = {
  1060             'build_mo': hgbuildmo,
  1212     'build': hgbuild,
  1061             'build_ext': hgbuildext,
  1213     'build_doc': hgbuilddoc,
  1062             'build_py': hgbuildpy,
  1214     'build_mo': hgbuildmo,
  1063             'build_scripts': hgbuildscripts,
  1215     'build_ext': hgbuildext,
  1064             'build_hgextindex': buildhgextindex,
  1216     'build_py': hgbuildpy,
  1065             'install': hginstall,
  1217     'build_scripts': hgbuildscripts,
  1066             'install_lib': hginstalllib,
  1218     'build_hgextindex': buildhgextindex,
  1067             'install_scripts': hginstallscripts,
  1219     'install': hginstall,
  1068             'build_hgexe': buildhgexe,
  1220     'install_lib': hginstalllib,
  1069             }
  1221     'install_scripts': hginstallscripts,
       
  1222     'build_hgexe': buildhgexe,
       
  1223 }
  1070 
  1224 
  1071 if py2exehacked:
  1225 if py2exehacked:
  1072     cmdclass['py2exe'] = hgbuildpy2exe
  1226     cmdclass['py2exe'] = hgbuildpy2exe
  1073 
  1227 
  1074 packages = ['mercurial',
  1228 packages = [
  1075             'mercurial.cext',
  1229     'mercurial',
  1076             'mercurial.cffi',
  1230     'mercurial.cext',
  1077             'mercurial.hgweb',
  1231     'mercurial.cffi',
  1078             'mercurial.interfaces',
  1232     'mercurial.hgweb',
  1079             'mercurial.pure',
  1233     'mercurial.interfaces',
  1080             'mercurial.thirdparty',
  1234     'mercurial.pure',
  1081             'mercurial.thirdparty.attr',
  1235     'mercurial.thirdparty',
  1082             'mercurial.thirdparty.zope',
  1236     'mercurial.thirdparty.attr',
  1083             'mercurial.thirdparty.zope.interface',
  1237     'mercurial.thirdparty.zope',
  1084             'mercurial.utils',
  1238     'mercurial.thirdparty.zope.interface',
  1085             'mercurial.revlogutils',
  1239     'mercurial.utils',
  1086             'mercurial.testing',
  1240     'mercurial.revlogutils',
  1087             'hgext', 'hgext.convert', 'hgext.fsmonitor',
  1241     'mercurial.testing',
  1088             'hgext.fastannotate',
  1242     'hgext',
  1089             'hgext.fsmonitor.pywatchman',
  1243     'hgext.convert',
  1090             'hgext.highlight',
  1244     'hgext.fsmonitor',
  1091             'hgext.infinitepush',
  1245     'hgext.fastannotate',
  1092             'hgext.largefiles', 'hgext.lfs', 'hgext.narrow',
  1246     'hgext.fsmonitor.pywatchman',
  1093             'hgext.remotefilelog',
  1247     'hgext.highlight',
  1094             'hgext.zeroconf', 'hgext3rd',
  1248     'hgext.infinitepush',
  1095             'hgdemandimport']
  1249     'hgext.largefiles',
       
  1250     'hgext.lfs',
       
  1251     'hgext.narrow',
       
  1252     'hgext.remotefilelog',
       
  1253     'hgext.zeroconf',
       
  1254     'hgext3rd',
       
  1255     'hgdemandimport',
       
  1256 ]
  1096 if sys.version_info[0] == 2:
  1257 if sys.version_info[0] == 2:
  1097     packages.extend(['mercurial.thirdparty.concurrent',
  1258     packages.extend(
  1098                      'mercurial.thirdparty.concurrent.futures'])
  1259         [
       
  1260             'mercurial.thirdparty.concurrent',
       
  1261             'mercurial.thirdparty.concurrent.futures',
       
  1262         ]
       
  1263     )
  1099 
  1264 
  1100 if 'HG_PY2EXE_EXTRA_INSTALL_PACKAGES' in os.environ:
  1265 if 'HG_PY2EXE_EXTRA_INSTALL_PACKAGES' in os.environ:
  1101     # py2exe can't cope with namespace packages very well, so we have to
  1266     # py2exe can't cope with namespace packages very well, so we have to
  1102     # install any hgext3rd.* extensions that we want in the final py2exe
  1267     # install any hgext3rd.* extensions that we want in the final py2exe
  1103     # image here. This is gross, but you gotta do what you gotta do.
  1268     # image here. This is gross, but you gotta do what you gotta do.
  1104     packages.extend(os.environ['HG_PY2EXE_EXTRA_INSTALL_PACKAGES'].split(' '))
  1269     packages.extend(os.environ['HG_PY2EXE_EXTRA_INSTALL_PACKAGES'].split(' '))
  1105 
  1270 
  1106 common_depends = ['mercurial/bitmanipulation.h',
  1271 common_depends = [
  1107                   'mercurial/compat.h',
  1272     'mercurial/bitmanipulation.h',
  1108                   'mercurial/cext/util.h']
  1273     'mercurial/compat.h',
       
  1274     'mercurial/cext/util.h',
       
  1275 ]
  1109 common_include_dirs = ['mercurial']
  1276 common_include_dirs = ['mercurial']
  1110 
  1277 
  1111 osutil_cflags = []
  1278 osutil_cflags = []
  1112 osutil_ldflags = []
  1279 osutil_ldflags = []
  1113 
  1280 
  1115 for plat, func in [('bsd', 'setproctitle')]:
  1282 for plat, func in [('bsd', 'setproctitle')]:
  1116     if re.search(plat, sys.platform) and hasfunction(new_compiler(), func):
  1283     if re.search(plat, sys.platform) and hasfunction(new_compiler(), func):
  1117         osutil_cflags.append('-DHAVE_%s' % func.upper())
  1284         osutil_cflags.append('-DHAVE_%s' % func.upper())
  1118 
  1285 
  1119 for plat, macro, code in [
  1286 for plat, macro, code in [
  1120     ('bsd|darwin', 'BSD_STATFS', '''
  1287     (
       
  1288         'bsd|darwin',
       
  1289         'BSD_STATFS',
       
  1290         '''
  1121      #include <sys/param.h>
  1291      #include <sys/param.h>
  1122      #include <sys/mount.h>
  1292      #include <sys/mount.h>
  1123      int main() { struct statfs s; return sizeof(s.f_fstypename); }
  1293      int main() { struct statfs s; return sizeof(s.f_fstypename); }
  1124      '''),
  1294      ''',
  1125     ('linux', 'LINUX_STATFS', '''
  1295     ),
       
  1296     (
       
  1297         'linux',
       
  1298         'LINUX_STATFS',
       
  1299         '''
  1126      #include <linux/magic.h>
  1300      #include <linux/magic.h>
  1127      #include <sys/vfs.h>
  1301      #include <sys/vfs.h>
  1128      int main() { struct statfs s; return sizeof(s.f_type); }
  1302      int main() { struct statfs s; return sizeof(s.f_type); }
  1129      '''),
  1303      ''',
       
  1304     ),
  1130 ]:
  1305 ]:
  1131     if re.search(plat, sys.platform) and cancompile(new_compiler(), code):
  1306     if re.search(plat, sys.platform) and cancompile(new_compiler(), code):
  1132         osutil_cflags.append('-DHAVE_%s' % macro)
  1307         osutil_cflags.append('-DHAVE_%s' % macro)
  1133 
  1308 
  1134 if sys.platform == 'darwin':
  1309 if sys.platform == 'darwin':
  1148     'mercurial/thirdparty/xdiff/xprepare.h',
  1323     'mercurial/thirdparty/xdiff/xprepare.h',
  1149     'mercurial/thirdparty/xdiff/xtypes.h',
  1324     'mercurial/thirdparty/xdiff/xtypes.h',
  1150     'mercurial/thirdparty/xdiff/xutils.h',
  1325     'mercurial/thirdparty/xdiff/xutils.h',
  1151 ]
  1326 ]
  1152 
  1327 
       
  1328 
  1153 class RustCompilationError(CCompilerError):
  1329 class RustCompilationError(CCompilerError):
  1154     """Exception class for Rust compilation errors."""
  1330     """Exception class for Rust compilation errors."""
       
  1331 
  1155 
  1332 
  1156 class RustExtension(Extension):
  1333 class RustExtension(Extension):
  1157     """Base classes for concrete Rust Extension classes.
  1334     """Base classes for concrete Rust Extension classes.
  1158     """
  1335     """
  1159 
  1336 
  1160     rusttargetdir = os.path.join('rust', 'target', 'release')
  1337     rusttargetdir = os.path.join('rust', 'target', 'release')
  1161 
  1338 
  1162     def __init__(self, mpath, sources, rustlibname, subcrate,
  1339     def __init__(
  1163                  py3_features=None, **kw):
  1340         self, mpath, sources, rustlibname, subcrate, py3_features=None, **kw
       
  1341     ):
  1164         Extension.__init__(self, mpath, sources, **kw)
  1342         Extension.__init__(self, mpath, sources, **kw)
  1165         srcdir = self.rustsrcdir = os.path.join('rust', subcrate)
  1343         srcdir = self.rustsrcdir = os.path.join('rust', subcrate)
  1166         self.py3_features = py3_features
  1344         self.py3_features = py3_features
  1167 
  1345 
  1168         # adding Rust source and control files to depends so that the extension
  1346         # adding Rust source and control files to depends so that the extension
  1170         self.depends.append(os.path.join(srcdir, 'Cargo.toml'))
  1348         self.depends.append(os.path.join(srcdir, 'Cargo.toml'))
  1171         cargo_lock = os.path.join(srcdir, 'Cargo.lock')
  1349         cargo_lock = os.path.join(srcdir, 'Cargo.lock')
  1172         if os.path.exists(cargo_lock):
  1350         if os.path.exists(cargo_lock):
  1173             self.depends.append(cargo_lock)
  1351             self.depends.append(cargo_lock)
  1174         for dirpath, subdir, fnames in os.walk(os.path.join(srcdir, 'src')):
  1352         for dirpath, subdir, fnames in os.walk(os.path.join(srcdir, 'src')):
  1175             self.depends.extend(os.path.join(dirpath, fname)
  1353             self.depends.extend(
  1176                                 for fname in fnames
  1354                 os.path.join(dirpath, fname)
  1177                                 if os.path.splitext(fname)[1] == '.rs')
  1355                 for fname in fnames
       
  1356                 if os.path.splitext(fname)[1] == '.rs'
       
  1357             )
  1178 
  1358 
  1179     @staticmethod
  1359     @staticmethod
  1180     def rustdylibsuffix():
  1360     def rustdylibsuffix():
  1181         """Return the suffix for shared libraries produced by rustc.
  1361         """Return the suffix for shared libraries produced by rustc.
  1182 
  1362 
  1200             # invoke this build.
  1380             # invoke this build.
  1201 
  1381 
  1202             # Unix only fix (os.path.expanduser not really reliable if
  1382             # Unix only fix (os.path.expanduser not really reliable if
  1203             # HOME is shadowed like this)
  1383             # HOME is shadowed like this)
  1204             import pwd
  1384             import pwd
       
  1385 
  1205             env['HOME'] = pwd.getpwuid(os.getuid()).pw_dir
  1386             env['HOME'] = pwd.getpwuid(os.getuid()).pw_dir
  1206 
  1387 
  1207         cargocmd = ['cargo', 'rustc', '-vv', '--release']
  1388         cargocmd = ['cargo', 'rustc', '-vv', '--release']
  1208         if sys.version_info[0] == 3 and self.py3_features is not None:
  1389         if sys.version_info[0] == 3 and self.py3_features is not None:
  1209             cargocmd.extend(('--features', self.py3_features,
  1390             cargocmd.extend(
  1210                              '--no-default-features'))
  1391                 ('--features', self.py3_features, '--no-default-features')
       
  1392             )
  1211         cargocmd.append('--')
  1393         cargocmd.append('--')
  1212         if sys.platform == 'darwin':
  1394         if sys.platform == 'darwin':
  1213             cargocmd.extend(("-C", "link-arg=-undefined",
  1395             cargocmd.extend(
  1214                              "-C", "link-arg=dynamic_lookup"))
  1396                 ("-C", "link-arg=-undefined", "-C", "link-arg=dynamic_lookup")
       
  1397             )
  1215         try:
  1398         try:
  1216             subprocess.check_call(cargocmd, env=env, cwd=self.rustsrcdir)
  1399             subprocess.check_call(cargocmd, env=env, cwd=self.rustsrcdir)
  1217         except OSError as exc:
  1400         except OSError as exc:
  1218             if exc.errno == errno.ENOENT:
  1401             if exc.errno == errno.ENOENT:
  1219                 raise RustCompilationError("Cargo not found")
  1402                 raise RustCompilationError("Cargo not found")
  1220             elif exc.errno == errno.EACCES:
  1403             elif exc.errno == errno.EACCES:
  1221                 raise RustCompilationError(
  1404                 raise RustCompilationError(
  1222                     "Cargo found, but permisssion to execute it is denied")
  1405                     "Cargo found, but permisssion to execute it is denied"
       
  1406                 )
  1223             else:
  1407             else:
  1224                 raise
  1408                 raise
  1225         except subprocess.CalledProcessError:
  1409         except subprocess.CalledProcessError:
  1226             raise RustCompilationError(
  1410             raise RustCompilationError(
  1227                 "Cargo failed. Working directory: %r, "
  1411                 "Cargo failed. Working directory: %r, "
  1228                 "command: %r, environment: %r"
  1412                 "command: %r, environment: %r"
  1229                 % (self.rustsrcdir, cargocmd, env))
  1413                 % (self.rustsrcdir, cargocmd, env)
       
  1414             )
       
  1415 
  1230 
  1416 
  1231 class RustEnhancedExtension(RustExtension):
  1417 class RustEnhancedExtension(RustExtension):
  1232     """A C Extension, conditionally enhanced with Rust code.
  1418     """A C Extension, conditionally enhanced with Rust code.
  1233 
  1419 
  1234     If the HGRUSTEXT environment variable is set to something else
  1420     If the HGRUSTEXT environment variable is set to something else
  1235     than 'cpython', the Rust sources get compiled and linked within the
  1421     than 'cpython', the Rust sources get compiled and linked within the
  1236     C target shared library object.
  1422     C target shared library object.
  1237     """
  1423     """
  1238 
  1424 
  1239     def __init__(self, mpath, sources, rustlibname, subcrate, **kw):
  1425     def __init__(self, mpath, sources, rustlibname, subcrate, **kw):
  1240         RustExtension.__init__(self, mpath, sources, rustlibname, subcrate,
  1426         RustExtension.__init__(
  1241                                **kw)
  1427             self, mpath, sources, rustlibname, subcrate, **kw
       
  1428         )
  1242         if hgrustext != 'direct-ffi':
  1429         if hgrustext != 'direct-ffi':
  1243             return
  1430             return
  1244         self.extra_compile_args.append('-DWITH_RUST')
  1431         self.extra_compile_args.append('-DWITH_RUST')
  1245         self.libraries.append(rustlibname)
  1432         self.libraries.append(rustlibname)
  1246         self.library_dirs.append(self.rusttargetdir)
  1433         self.library_dirs.append(self.rusttargetdir)
  1247 
  1434 
  1248     def rustbuild(self):
  1435     def rustbuild(self):
  1249         if hgrustext == 'direct-ffi':
  1436         if hgrustext == 'direct-ffi':
  1250             RustExtension.rustbuild(self)
  1437             RustExtension.rustbuild(self)
  1251 
  1438 
       
  1439 
  1252 class RustStandaloneExtension(RustExtension):
  1440 class RustStandaloneExtension(RustExtension):
  1253 
       
  1254     def __init__(self, pydottedname, rustcrate, dylibname, **kw):
  1441     def __init__(self, pydottedname, rustcrate, dylibname, **kw):
  1255         RustExtension.__init__(self, pydottedname, [], dylibname, rustcrate,
  1442         RustExtension.__init__(
  1256                                **kw)
  1443             self, pydottedname, [], dylibname, rustcrate, **kw
       
  1444         )
  1257         self.dylibname = dylibname
  1445         self.dylibname = dylibname
  1258 
  1446 
  1259     def build(self, target_dir):
  1447     def build(self, target_dir):
  1260         self.rustbuild()
  1448         self.rustbuild()
  1261         target = [target_dir]
  1449         target = [target_dir]
  1262         target.extend(self.name.split('.'))
  1450         target.extend(self.name.split('.'))
  1263         target[-1] += DYLIB_SUFFIX
  1451         target[-1] += DYLIB_SUFFIX
  1264         shutil.copy2(os.path.join(self.rusttargetdir,
  1452         shutil.copy2(
  1265                                   self.dylibname + self.rustdylibsuffix()),
  1453             os.path.join(
  1266                      os.path.join(*target))
  1454                 self.rusttargetdir, self.dylibname + self.rustdylibsuffix()
       
  1455             ),
       
  1456             os.path.join(*target),
       
  1457         )
  1267 
  1458 
  1268 
  1459 
  1269 extmodules = [
  1460 extmodules = [
  1270     Extension('mercurial.cext.base85', ['mercurial/cext/base85.c'],
  1461     Extension(
  1271               include_dirs=common_include_dirs,
  1462         'mercurial.cext.base85',
  1272               depends=common_depends),
  1463         ['mercurial/cext/base85.c'],
  1273     Extension('mercurial.cext.bdiff', ['mercurial/bdiff.c',
  1464         include_dirs=common_include_dirs,
  1274                                        'mercurial/cext/bdiff.c'] + xdiff_srcs,
  1465         depends=common_depends,
  1275               include_dirs=common_include_dirs,
  1466     ),
  1276               depends=common_depends + ['mercurial/bdiff.h'] + xdiff_headers),
  1467     Extension(
  1277     Extension('mercurial.cext.mpatch', ['mercurial/mpatch.c',
  1468         'mercurial.cext.bdiff',
  1278                                         'mercurial/cext/mpatch.c'],
  1469         ['mercurial/bdiff.c', 'mercurial/cext/bdiff.c'] + xdiff_srcs,
  1279               include_dirs=common_include_dirs,
  1470         include_dirs=common_include_dirs,
  1280               depends=common_depends),
  1471         depends=common_depends + ['mercurial/bdiff.h'] + xdiff_headers,
       
  1472     ),
       
  1473     Extension(
       
  1474         'mercurial.cext.mpatch',
       
  1475         ['mercurial/mpatch.c', 'mercurial/cext/mpatch.c'],
       
  1476         include_dirs=common_include_dirs,
       
  1477         depends=common_depends,
       
  1478     ),
  1281     RustEnhancedExtension(
  1479     RustEnhancedExtension(
  1282         'mercurial.cext.parsers', ['mercurial/cext/charencode.c',
  1480         'mercurial.cext.parsers',
  1283                                    'mercurial/cext/dirs.c',
  1481         [
  1284                                    'mercurial/cext/manifest.c',
  1482             'mercurial/cext/charencode.c',
  1285                                    'mercurial/cext/parsers.c',
  1483             'mercurial/cext/dirs.c',
  1286                                    'mercurial/cext/pathencode.c',
  1484             'mercurial/cext/manifest.c',
  1287                                    'mercurial/cext/revlog.c'],
  1485             'mercurial/cext/parsers.c',
       
  1486             'mercurial/cext/pathencode.c',
       
  1487             'mercurial/cext/revlog.c',
       
  1488         ],
  1288         'hgdirectffi',
  1489         'hgdirectffi',
  1289         'hg-direct-ffi',
  1490         'hg-direct-ffi',
  1290         include_dirs=common_include_dirs,
  1491         include_dirs=common_include_dirs,
  1291         depends=common_depends + ['mercurial/cext/charencode.h',
  1492         depends=common_depends
  1292                                   'mercurial/cext/revlog.h',
  1493         + [
  1293                                   'rust/hg-core/src/ancestors.rs',
  1494             'mercurial/cext/charencode.h',
  1294                                   'rust/hg-core/src/lib.rs']),
  1495             'mercurial/cext/revlog.h',
  1295     Extension('mercurial.cext.osutil', ['mercurial/cext/osutil.c'],
  1496             'rust/hg-core/src/ancestors.rs',
  1296               include_dirs=common_include_dirs,
  1497             'rust/hg-core/src/lib.rs',
  1297               extra_compile_args=osutil_cflags,
  1498         ],
  1298               extra_link_args=osutil_ldflags,
  1499     ),
  1299               depends=common_depends),
       
  1300     Extension(
  1500     Extension(
  1301         'mercurial.thirdparty.zope.interface._zope_interface_coptimizations', [
  1501         'mercurial.cext.osutil',
  1302         'mercurial/thirdparty/zope/interface/_zope_interface_coptimizations.c',
  1502         ['mercurial/cext/osutil.c'],
  1303         ]),
  1503         include_dirs=common_include_dirs,
  1304     Extension('hgext.fsmonitor.pywatchman.bser',
  1504         extra_compile_args=osutil_cflags,
  1305               ['hgext/fsmonitor/pywatchman/bser.c']),
  1505         extra_link_args=osutil_ldflags,
  1306     RustStandaloneExtension('mercurial.rustext', 'hg-cpython', 'librusthg',
  1506         depends=common_depends,
  1307                             py3_features='python3'),
  1507     ),
  1308     ]
  1508     Extension(
       
  1509         'mercurial.thirdparty.zope.interface._zope_interface_coptimizations',
       
  1510         [
       
  1511             'mercurial/thirdparty/zope/interface/_zope_interface_coptimizations.c',
       
  1512         ],
       
  1513     ),
       
  1514     Extension(
       
  1515         'hgext.fsmonitor.pywatchman.bser', ['hgext/fsmonitor/pywatchman/bser.c']
       
  1516     ),
       
  1517     RustStandaloneExtension(
       
  1518         'mercurial.rustext', 'hg-cpython', 'librusthg', py3_features='python3'
       
  1519     ),
       
  1520 ]
  1309 
  1521 
  1310 
  1522 
  1311 sys.path.insert(0, 'contrib/python-zstandard')
  1523 sys.path.insert(0, 'contrib/python-zstandard')
  1312 import setup_zstd
  1524 import setup_zstd
  1313 extmodules.append(setup_zstd.get_c_extension(
  1525 
  1314     name='mercurial.zstd',
  1526 extmodules.append(
  1315     root=os.path.abspath(os.path.dirname(__file__))))
  1527     setup_zstd.get_c_extension(
       
  1528         name='mercurial.zstd', root=os.path.abspath(os.path.dirname(__file__))
       
  1529     )
       
  1530 )
  1316 
  1531 
  1317 try:
  1532 try:
  1318     from distutils import cygwinccompiler
  1533     from distutils import cygwinccompiler
  1319 
  1534 
  1320     # the -mno-cygwin option has been deprecated for years
  1535     # the -mno-cygwin option has been deprecated for years
  1335     # distributions like the ones from the optware project for Synology
  1550     # distributions like the ones from the optware project for Synology
  1336     # DiskStation boxes
  1551     # DiskStation boxes
  1337     class HackedMingw32CCompiler(object):
  1552     class HackedMingw32CCompiler(object):
  1338         pass
  1553         pass
  1339 
  1554 
       
  1555 
  1340 if os.name == 'nt':
  1556 if os.name == 'nt':
  1341     # Allow compiler/linker flags to be added to Visual Studio builds.  Passing
  1557     # Allow compiler/linker flags to be added to Visual Studio builds.  Passing
  1342     # extra_link_args to distutils.extensions.Extension() doesn't have any
  1558     # extra_link_args to distutils.extensions.Extension() doesn't have any
  1343     # effect.
  1559     # effect.
  1344     from distutils import msvccompiler
  1560     from distutils import msvccompiler
  1352             self.ldflags_shared.append('/ignore:4197')
  1568             self.ldflags_shared.append('/ignore:4197')
  1353             self.ldflags_shared_debug.append('/ignore:4197')
  1569             self.ldflags_shared_debug.append('/ignore:4197')
  1354 
  1570 
  1355     msvccompiler.MSVCCompiler = HackedMSVCCompiler
  1571     msvccompiler.MSVCCompiler = HackedMSVCCompiler
  1356 
  1572 
  1357 packagedata = {'mercurial': ['locale/*/LC_MESSAGES/hg.mo',
  1573 packagedata = {
  1358                              'help/*.txt',
  1574     'mercurial': [
  1359                              'help/internals/*.txt',
  1575         'locale/*/LC_MESSAGES/hg.mo',
  1360                              'default.d/*.rc',
  1576         'help/*.txt',
  1361                              'dummycert.pem']}
  1577         'help/internals/*.txt',
       
  1578         'default.d/*.rc',
       
  1579         'dummycert.pem',
       
  1580     ]
       
  1581 }
       
  1582 
  1362 
  1583 
  1363 def ordinarypath(p):
  1584 def ordinarypath(p):
  1364     return p and p[0] != '.' and p[-1] != '~'
  1585     return p and p[0] != '.' and p[-1] != '~'
       
  1586 
  1365 
  1587 
  1366 for root in ('templates',):
  1588 for root in ('templates',):
  1367     for curdir, dirs, files in os.walk(os.path.join('mercurial', root)):
  1589     for curdir, dirs, files in os.walk(os.path.join('mercurial', root)):
  1368         curdir = curdir.split(os.sep, 1)[1]
  1590         curdir = curdir.split(os.sep, 1)[1]
  1369         dirs[:] = filter(ordinarypath, dirs)
  1591         dirs[:] = filter(ordinarypath, dirs)
  1400 if issetuptools:
  1622 if issetuptools:
  1401     extra['python_requires'] = supportedpy
  1623     extra['python_requires'] = supportedpy
  1402 
  1624 
  1403 if py2exeloaded:
  1625 if py2exeloaded:
  1404     extra['console'] = [
  1626     extra['console'] = [
  1405         {'script':'hg',
  1627         {
  1406          'copyright':'Copyright (C) 2005-2019 Matt Mackall and others',
  1628             'script': 'hg',
  1407          'product_version':version}]
  1629             'copyright': 'Copyright (C) 2005-2019 Matt Mackall and others',
       
  1630             'product_version': version,
       
  1631         }
       
  1632     ]
  1408     # Sub command of 'build' because 'py2exe' does not handle sub_commands.
  1633     # Sub command of 'build' because 'py2exe' does not handle sub_commands.
  1409     # Need to override hgbuild because it has a private copy of
  1634     # Need to override hgbuild because it has a private copy of
  1410     # build.sub_commands.
  1635     # build.sub_commands.
  1411     hgbuild.sub_commands.insert(0, ('build_hgextindex', None))
  1636     hgbuild.sub_commands.insert(0, ('build_hgextindex', None))
  1412     # put dlls in sub directory so that they won't pollute PATH
  1637     # put dlls in sub directory so that they won't pollute PATH
  1436     version = runcmd(['/usr/bin/xcodebuild', '-version'], {})[1].splitlines()
  1661     version = runcmd(['/usr/bin/xcodebuild', '-version'], {})[1].splitlines()
  1437     if version:
  1662     if version:
  1438         version = version[0]
  1663         version = version[0]
  1439         if sys.version_info[0] == 3:
  1664         if sys.version_info[0] == 3:
  1440             version = version.decode('utf-8')
  1665             version = version.decode('utf-8')
  1441         xcode4 = (version.startswith('Xcode') and
  1666         xcode4 = version.startswith('Xcode') and StrictVersion(
  1442                   StrictVersion(version.split()[1]) >= StrictVersion('4.0'))
  1667             version.split()[1]
       
  1668         ) >= StrictVersion('4.0')
  1443         xcode51 = re.match(r'^Xcode\s+5\.1', version) is not None
  1669         xcode51 = re.match(r'^Xcode\s+5\.1', version) is not None
  1444     else:
  1670     else:
  1445         # xcodebuild returns empty on OS X Lion with XCode 4.3 not
  1671         # xcodebuild returns empty on OS X Lion with XCode 4.3 not
  1446         # installed, but instead with only command-line tools. Assume
  1672         # installed, but instead with only command-line tools. Assume
  1447         # that only happens on >= Lion, thus no PPC support.
  1673         # that only happens on >= Lion, thus no PPC support.
  1461     # so Mercurial can continue to compile in the meantime.
  1687     # so Mercurial can continue to compile in the meantime.
  1462     if xcode51:
  1688     if xcode51:
  1463         cflags = get_config_var('CFLAGS')
  1689         cflags = get_config_var('CFLAGS')
  1464         if cflags and re.search(r'-mno-fused-madd\b', cflags) is not None:
  1690         if cflags and re.search(r'-mno-fused-madd\b', cflags) is not None:
  1465             os.environ['CFLAGS'] = (
  1691             os.environ['CFLAGS'] = (
  1466                 os.environ.get('CFLAGS', '') + ' -Qunused-arguments')
  1692                 os.environ.get('CFLAGS', '') + ' -Qunused-arguments'
  1467 
  1693             )
  1468 setup(name='mercurial',
  1694 
  1469       version=setupversion,
  1695 setup(
  1470       author='Matt Mackall and many others',
  1696     name='mercurial',
  1471       author_email='mercurial@mercurial-scm.org',
  1697     version=setupversion,
  1472       url='https://mercurial-scm.org/',
  1698     author='Matt Mackall and many others',
  1473       download_url='https://mercurial-scm.org/release/',
  1699     author_email='mercurial@mercurial-scm.org',
  1474       description=('Fast scalable distributed SCM (revision control, version '
  1700     url='https://mercurial-scm.org/',
  1475                    'control) system'),
  1701     download_url='https://mercurial-scm.org/release/',
  1476       long_description=('Mercurial is a distributed SCM tool written in Python.'
  1702     description=(
  1477                         ' It is used by a number of large projects that require'
  1703         'Fast scalable distributed SCM (revision control, version '
  1478                         ' fast, reliable distributed revision control, such as '
  1704         'control) system'
  1479                         'Mozilla.'),
  1705     ),
  1480       license='GNU GPLv2 or any later version',
  1706     long_description=(
  1481       classifiers=[
  1707         'Mercurial is a distributed SCM tool written in Python.'
  1482           'Development Status :: 6 - Mature',
  1708         ' It is used by a number of large projects that require'
  1483           'Environment :: Console',
  1709         ' fast, reliable distributed revision control, such as '
  1484           'Intended Audience :: Developers',
  1710         'Mozilla.'
  1485           'Intended Audience :: System Administrators',
  1711     ),
  1486           'License :: OSI Approved :: GNU General Public License (GPL)',
  1712     license='GNU GPLv2 or any later version',
  1487           'Natural Language :: Danish',
  1713     classifiers=[
  1488           'Natural Language :: English',
  1714         'Development Status :: 6 - Mature',
  1489           'Natural Language :: German',
  1715         'Environment :: Console',
  1490           'Natural Language :: Italian',
  1716         'Intended Audience :: Developers',
  1491           'Natural Language :: Japanese',
  1717         'Intended Audience :: System Administrators',
  1492           'Natural Language :: Portuguese (Brazilian)',
  1718         'License :: OSI Approved :: GNU General Public License (GPL)',
  1493           'Operating System :: Microsoft :: Windows',
  1719         'Natural Language :: Danish',
  1494           'Operating System :: OS Independent',
  1720         'Natural Language :: English',
  1495           'Operating System :: POSIX',
  1721         'Natural Language :: German',
  1496           'Programming Language :: C',
  1722         'Natural Language :: Italian',
  1497           'Programming Language :: Python',
  1723         'Natural Language :: Japanese',
  1498           'Topic :: Software Development :: Version Control',
  1724         'Natural Language :: Portuguese (Brazilian)',
  1499       ],
  1725         'Operating System :: Microsoft :: Windows',
  1500       scripts=scripts,
  1726         'Operating System :: OS Independent',
  1501       packages=packages,
  1727         'Operating System :: POSIX',
  1502       ext_modules=extmodules,
  1728         'Programming Language :: C',
  1503       data_files=datafiles,
  1729         'Programming Language :: Python',
  1504       package_data=packagedata,
  1730         'Topic :: Software Development :: Version Control',
  1505       cmdclass=cmdclass,
  1731     ],
  1506       distclass=hgdist,
  1732     scripts=scripts,
  1507       options={
  1733     packages=packages,
  1508           'py2exe': {
  1734     ext_modules=extmodules,
  1509               'bundle_files': 3,
  1735     data_files=datafiles,
  1510               'dll_excludes': py2exedllexcludes,
  1736     package_data=packagedata,
  1511               'excludes': py2exeexcludes,
  1737     cmdclass=cmdclass,
  1512               'packages': py2exepackages,
  1738     distclass=hgdist,
  1513           },
  1739     options={
  1514           'bdist_mpkg': {
  1740         'py2exe': {
  1515               'zipdist': False,
  1741             'bundle_files': 3,
  1516               'license': 'COPYING',
  1742             'dll_excludes': py2exedllexcludes,
  1517               'readme': 'contrib/packaging/macosx/Readme.html',
  1743             'excludes': py2exeexcludes,
  1518               'welcome': 'contrib/packaging/macosx/Welcome.html',
  1744             'packages': py2exepackages,
  1519           },
  1745         },
  1520       },
  1746         'bdist_mpkg': {
  1521       **extra)
  1747             'zipdist': False,
       
  1748             'license': 'COPYING',
       
  1749             'readme': 'contrib/packaging/macosx/Readme.html',
       
  1750             'welcome': 'contrib/packaging/macosx/Welcome.html',
       
  1751         },
       
  1752     },
       
  1753     **extra
       
  1754 )