##// END OF EJS Templates
rewrite: simplify the `retained_extras` extra logic...
rewrite: simplify the `retained_extras` extra logic First, we move the definition of value outside of the rebase extensions, as this apply to all rebase-like operation and some live in other place (like evolve). Second we make it a simple set, so that it is easy for an extension to add a new value in it. Third, we move the associated logic in core too. That make it easily available to other extensions. Fourth we simplify it usage, as the verbose version of the filtering is just a handful on line long, we are just going to test all the value for updates, so the Projection overlay is not bringing much here. Note that, we make it a module level set, is a key is worth preserving it is probably worth preserving in all cases. This was already the behavior prior to this change.

File last commit:

r51536:cbcbf63b default
r51536:cbcbf63b default
Show More
setup.py
1804 lines | 57.9 KiB | text/x-python | PythonLexer
mpm@selenic.com
More whitespace cleanups...
r575 #
# This is the mercurial setup script.
mpm@selenic.com
Add back links from file revisions to changeset revisions...
r0 #
Christian Ebert
setup.py not executable: change instructions at beginning of file
r4816 # 'python setup.py install', or
# 'python setup.py --help' for more options
Augie Fackler
setup: fix mistake that prevented Python 3 from being excluded...
r33592 import os
Gregory Szorc
setup: drop support for Python 3.5...
r49705 # Mercurial can't work on 3.6.0 or 3.6.1 due to a bug in % formatting
# in bytestrings.
Ian Moody
setup: allow py3 install without env vars...
r43666 supportedpy = ','.join(
[
Gregory Szorc
setup: drop support for Python 3.5...
r49705 '>=3.6.2',
Ian Moody
setup: allow py3 install without env vars...
r43666 ]
)
Augie Fackler
setup: explicitly declare supported Python versions...
r33588
Zachary Gramana
setup.py: workaround for missing bz2 module in IronPython...
r14295 import sys, platform
Georges Racinet
rust-cpython: management of shared libray suffix...
r42657 import sysconfig
Augie Fackler
formatting: blacken the codebase...
r43346
Augie Fackler
setup: remove more Python 2 support code...
r49681 def sysstr(s):
return s.decode('latin-1')
Renato Cunha
setup.py: Adjustments to make setup.py run in py3k....
r11532
Augie Fackler
formatting: blacken the codebase...
r43346
setup: further improve the error path for version retrieval...
r50988 def eprint(*args, **kwargs):
kwargs['file'] = sys.stderr
print(*args, **kwargs)
Manuel Jacob
setup: require a Python version with modern SSL features...
r45410 import ssl
Manuel Jacob
setup: require that Python has TLS 1.1 or TLS 1.2...
r45429 # ssl.HAS_TLSv1* are preferred to check support but they were added in Python
# 3.7. Prior to CPython commit 6e8cda91d92da72800d891b2fc2073ecbc134d98
# (backported to the 3.7 branch), ssl.PROTOCOL_TLSv1_1 / ssl.PROTOCOL_TLSv1_2
# were defined only if compiled against a OpenSSL version with TLS 1.1 / 1.2
# support. At the mentioned commit, they were unconditionally defined.
_notset = object()
has_tlsv1_1 = getattr(ssl, 'HAS_TLSv1_1', _notset)
if has_tlsv1_1 is _notset:
has_tlsv1_1 = getattr(ssl, 'PROTOCOL_TLSv1_1', _notset) is not _notset
has_tlsv1_2 = getattr(ssl, 'HAS_TLSv1_2', _notset)
if has_tlsv1_2 is _notset:
has_tlsv1_2 = getattr(ssl, 'PROTOCOL_TLSv1_2', _notset) is not _notset
if not (has_tlsv1_1 or has_tlsv1_2):
error = """
The `ssl` module does not advertise support for TLS 1.1 or TLS 1.2.
Please make sure that your Python installation was compiled against an OpenSSL
version enabling these features (likely this requires the OpenSSL version to
be at least 1.0.1).
"""
Augie Fackler
setup: remove printf trampoline...
r49682 print(error, file=sys.stderr)
Manuel Jacob
setup: require that Python has TLS 1.1 or TLS 1.2...
r45429 sys.exit(1)
Augie Fackler
setup: remove Python 2 support code for determining dylib suffix...
r49685 DYLIB_SUFFIX = sysconfig.get_config_vars()['EXT_SUFFIX']
Georges Racinet
rust-cpython: management of shared libray suffix...
r42657
Matt Mackall
setup: warn about missing standard Python components
r7558 # Solaris Python packaging brain damage
try:
import hashlib
Augie Fackler
formatting: blacken the codebase...
r43346
Matt Mackall
setup: warn about missing standard Python components
r7558 sha = hashlib.sha1()
Brodie Rao
cleanup: replace naked excepts with more specific ones
r16688 except ImportError:
Matt Mackall
setup: warn about missing standard Python components
r7558 try:
import sha
Augie Fackler
formatting: blacken the codebase...
r43346
sha.sha # silence unused import warning
Brodie Rao
cleanup: replace naked excepts with more specific ones
r16688 except ImportError:
Matt Mackall
setup: warn about missing standard Python components
r7558 raise SystemExit(
Augie Fackler
formatting: blacken the codebase...
r43346 "Couldn't import standard hashlib (incomplete Python install)."
)
Matt Mackall
setup: warn about missing standard Python components
r7558
try:
import zlib
Augie Fackler
formatting: blacken the codebase...
r43346
zlib.compressobj # silence unused import warning
Brodie Rao
cleanup: replace naked excepts with more specific ones
r16688 except ImportError:
Matt Mackall
setup: warn about missing standard Python components
r7558 raise SystemExit(
Augie Fackler
formatting: blacken the codebase...
r43346 "Couldn't import standard zlib (incomplete Python install)."
)
Matt Mackall
setup: warn about missing standard Python components
r7558
Zachary Gramana
setup.py: workaround for missing bz2 module in IronPython...
r14295 # The base IronPython distribution (as of 2.7.1) doesn't support bz2
isironpython = False
Dirkjan Ochtman
setup: fail if bz2 is not available
r10761 try:
Augie Fackler
formatting: blacken the codebase...
r43346 isironpython = (
platform.python_implementation().lower().find("ironpython") != -1
)
Brodie Rao
cleanup: replace naked excepts with more specific ones
r16688 except AttributeError:
Zachary Gramana
setup.py: workaround for missing bz2 module in IronPython...
r14295 pass
if isironpython:
Simon Heimberg
setup: make script executable with python3...
r15492 sys.stderr.write("warning: IronPython detected (no bz2 support)\n")
Zachary Gramana
setup.py: workaround for missing bz2 module in IronPython...
r14295 else:
try:
import bz2
Augie Fackler
formatting: blacken the codebase...
r43346
bz2.BZ2Compressor # silence unused import warning
Brodie Rao
cleanup: replace naked excepts with more specific ones
r16688 except ImportError:
Zachary Gramana
setup.py: workaround for missing bz2 module in IronPython...
r14295 raise SystemExit(
Augie Fackler
formatting: blacken the codebase...
r43346 "Couldn't import standard bz2 (incomplete Python install)."
)
Dirkjan Ochtman
setup: fail if bz2 is not available
r10761
Joan Massich
setup.py: do not install c extensions on pypy...
r24192 ispypy = "PyPy" in sys.version
Gregory Szorc
setup: detect Python DLL filename from loaded DLL...
r29020 import ctypes
Augie Fackler
setup: fix mistake that prevented Python 3 from being excluded...
r33592 import stat, subprocess, time
Kent Frazier
setup.py: fix C extension compilation issue with OS X 10.9 and Xcode 5.1
r21038 import re
Alexis S. L. Carvalho
setup.py: use a simplified custom version of CCompiler.has_function...
r6251 import shutil
import tempfile
Augie Fackler
formatting: blacken the codebase...
r43346
Gregory Szorc
setup: use setuptools on Windows (issue5400)...
r31289 # We have issues with setuptools on some platforms and builders. Until
# those are resolved, setuptools is opt-in except for platforms where
# we don't have issues.
Augie Fackler
formatting: blacken the codebase...
r43346 issetuptools = os.name == 'nt' or 'FORCE_SETUPTOOLS' in os.environ
Yuya Nishihara
setup: silence warning of unknown option python_requires on distutils...
r33601 if issetuptools:
Nathan Goldbaum
setup: import setup from setuptools if FORCE_SETUPTOOLS is set...
r26600 from setuptools import setup
else:
from distutils.core import setup
Jun Wu
setup: test setproctitle before building osutil...
r30408 from distutils.ccompiler import new_compiler
Nathan Goldbaum
setup: import setup from setuptools if FORCE_SETUPTOOLS is set...
r26600 from distutils.core import Command, Extension
Martin Geisler
add --pure flag to setup.py...
r7722 from distutils.dist import Distribution
Martin Geisler
i18n: new build_mo command for setup.py...
r7649 from distutils.command.build import build
Christian Boos
setup: ignore failures to build optional inotify extension
r11468 from distutils.command.build_ext import build_ext
Martin Geisler
add --pure flag to setup.py...
r7722 from distutils.command.build_py import build_py
Gregory Szorc
setup.py: attempt to build and install hg.exe on Windows...
r27268 from distutils.command.build_scripts import build_scripts
Matt Harbison
setup: prevent setuptools from laying an egg...
r32647 from distutils.command.install import install
Kyle Lippincott
setup: set mode 644 or 755 on installed files
r22640 from distutils.command.install_lib import install_lib
Dan Villiom Podlaski Christiansen
setup/hg: always load Mercurial from where it was installed....
r12661 from distutils.command.install_scripts import install_scripts
Dan Villiom Podlaski Christiansen
setup: don't import distutils prior to checking FORCE_SETUPTOOLS...
r46850 from distutils import log
Martin Geisler
i18n: new build_mo command for setup.py...
r7649 from distutils.spawn import spawn, find_executable
Ludovic Chabant
setup: don't fail when Python doesn't have the cygwinccompiler package...
r23677 from distutils import file_util
Gregory Szorc
setup.py: attempt to build and install hg.exe on Windows...
r27268 from distutils.errors import (
CCompilerError,
DistutilsError,
DistutilsExecError,
)
Matt Harbison
setup: drop legacy osx compiler tuning to enable universal builds...
r50774 from distutils.sysconfig import get_python_inc
"Paul Morelle "
setup: explain to distutils how we write rc versions...
r40485
Augie Fackler
formatting: blacken the codebase...
r43346
Gregory Szorc
setup: only write some autogenerated files if they change...
r35229 def write_if_changed(path, content):
"""Write content to a file iff the content hasn't changed."""
if os.path.exists(path):
with open(path, 'rb') as fh:
current = fh.read()
else:
current = b''
if current != content:
with open(path, 'wb') as fh:
fh.write(content)
Augie Fackler
formatting: blacken the codebase...
r43346
Paul Moore
Add a batch file driver for Windows
r6513 scripts = ['hg']
if os.name == 'nt':
Gregory Szorc
setup.py: attempt to build and install hg.exe on Windows...
r27268 # We remove hg.bat if we are able to build hg.exe.
Paul Moore
Add a batch file driver for Windows
r6513 scripts.append('contrib/win32/hg.bat')
Matt Mackall
Fix setup.py warning
r3893
Augie Fackler
formatting: blacken the codebase...
r43346
Jun Wu
setup: split "hasfunction" to test arbitrary code...
r31559 def cancompile(cc, code):
Alexis S. L. Carvalho
setup.py: use a simplified custom version of CCompiler.has_function...
r6251 tmpdir = tempfile.mkdtemp(prefix='hg-install-')
Alexis S. L. Carvalho
setup.py: hide compiler error messages while searching for inotify
r6373 devnull = oldstderr = None
Alexis S. L. Carvalho
setup.py: use a simplified custom version of CCompiler.has_function...
r6251 try:
Jun Wu
setup: split "hasfunction" to test arbitrary code...
r31559 fname = os.path.join(tmpdir, 'testcomp.c')
Matt Mackall
setup: use try/except/finally...
r25089 f = open(fname, 'w')
Jun Wu
setup: split "hasfunction" to test arbitrary code...
r31559 f.write(code)
Matt Mackall
setup: use try/except/finally...
r25089 f.close()
# Redirect stderr to /dev/null to hide any error messages
# from the compiler.
# This will have to be changed if we ever have to check
# for a function on Windows.
devnull = open('/dev/null', 'w')
oldstderr = os.dup(sys.stderr.fileno())
os.dup2(devnull.fileno(), sys.stderr.fileno())
objects = cc.compile([fname], output_dir=tmpdir)
cc.link_executable(objects, os.path.join(tmpdir, "a.out"))
Alexis S. L. Carvalho
setup.py: use a simplified custom version of CCompiler.has_function...
r6251 return True
Matt Mackall
setup: use try/except/finally...
r25089 except Exception:
return False
Alexis S. L. Carvalho
setup.py: use a simplified custom version of CCompiler.has_function...
r6251 finally:
Alexis S. L. Carvalho
setup.py: hide compiler error messages while searching for inotify
r6373 if oldstderr is not None:
os.dup2(oldstderr, sys.stderr.fileno())
if devnull is not None:
devnull.close()
Alexis S. L. Carvalho
setup.py: use a simplified custom version of CCompiler.has_function...
r6251 shutil.rmtree(tmpdir)
Augie Fackler
formatting: blacken the codebase...
r43346
Jun Wu
setup: split "hasfunction" to test arbitrary code...
r31559 # simplified version of distutils.ccompiler.CCompiler.has_function
# that actually removes its temporary files.
def hasfunction(cc, funcname):
code = 'int main(void) { %s(); }\n' % funcname
return cancompile(cc, code)
Augie Fackler
formatting: blacken the codebase...
r43346
Jun Wu
setup: add a function to test header files
r31560 def hasheader(cc, headername):
code = '#include <%s>\nint main(void) { return 0; }\n' % headername
return cancompile(cc, code)
Augie Fackler
formatting: blacken the codebase...
r43346
Volker.Kleinfeld@gmx.de
Support for the distutils extention 'py2exe' added....
r1283 # py2exe needs to be installed to work
try:
Bryan O'Sullivan
Clean up whitespace damage.
r1294 import py2exe
Augie Fackler
formatting: blacken the codebase...
r43346
Matt Harbison
setup: fix the py2exe logic to work with py3...
r49964 py2exe.patch_distutils()
Adrian Buehlmann
setup.py: fixing version info for Windows hg.exe (py2exe)...
r10400 py2exeloaded = True
Pascal Quantin
setup: fix py2exe generation broken by c3a6ec304055 (issue3116)...
r15527 # import py2exe's patched Distribution class
from distutils.core import Distribution
Bryan O'Sullivan
Fix Volker's modifications to setup.py for non-Windows systems.
r1284 except ImportError:
Adrian Buehlmann
setup.py: fixing version info for Windows hg.exe (py2exe)...
r10400 py2exeloaded = False
Volker.Kleinfeld@gmx.de
Support for the distutils extention 'py2exe' added....
r1283
Augie Fackler
formatting: blacken the codebase...
r43346
Gregory Szorc
setup: define build_doc command...
r42016 def runcmd(cmd, env, cwd=None):
Augie Fackler
formatting: blacken the codebase...
r43346 p = subprocess.Popen(
cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, env=env, cwd=cwd
)
Matt Harbison
plan9: drop py26 hacks
r32886 out, err = p.communicate()
Adam Simpkins
setup: update runcmd() to also return the exit status...
r33111 return p.returncode, out, err
Jon M. Dugan
setup: fix mac build broken by e42d18538e1d...
r13636
Augie Fackler
formatting: blacken the codebase...
r43346
Gregory Szorc
py3: use class X: instead of class X(object):...
r49801 class hgcommand:
Adam Simpkins
setup: prefer using the system hg to interact with the local repository...
r33114 def __init__(self, cmd, env):
self.cmd = cmd
self.env = env
Gilles Moris
setup: refactor the version string to a subset of tag+tagdist-hash+date...
r9615
Adam Simpkins
setup: replace runhg() with an hgcommand helper class...
r33113 def run(self, args):
cmd = self.cmd + args
returncode, out, err = runcmd(cmd, self.env)
Yuya Nishihara
setup: do not select hg executable that prints unexpected warnings...
r33599 err = filterhgerr(err)
setup: treat error output and non-zero return code differently...
r50866 if err:
Augie Fackler
setup: remove printf trampoline...
r49682 print("stderr from '%s':" % (' '.join(cmd)), file=sys.stderr)
print(err, file=sys.stderr)
setup: treat error output and non-zero return code differently...
r50866 if returncode != 0:
Martin von Zweigbergk
py3: make setup.py's hgcommand() consistently return bytes...
r45094 return b''
Adam Simpkins
setup: replace runhg() with an hgcommand helper class...
r33113 return out
Jeremy Whitlock
setup: read .hg_archival.txt for version info (issue1670)...
r8548
Augie Fackler
formatting: blacken the codebase...
r43346
Yuya Nishihara
setup: do not select hg executable that prints unexpected warnings...
r33599 def filterhgerr(err):
# If root is executing setup.py, but the repository is owned by
# another user (as in "sudo python setup.py install") we will get
# trust warnings since the .hg/hgrc file is untrusted. That is
# fine, we don't want to load it anyway. Python may warn about
# a missing __init__.py in mercurial/locale, we also ignore that.
Augie Fackler
formatting: blacken the codebase...
r43346 err = [
e
for e in err.splitlines()
if (
not e.startswith(b'not trusting file')
and not e.startswith(b'warning: Not importing')
and not e.startswith(b'obsolete feature not enabled')
and not e.startswith(b'*** failed to import extension')
and not e.startswith(b'devel-warn:')
and not (
e.startswith(b'(third party extension')
and e.endswith(b'or newer of Mercurial; disabling)')
)
)
]
Yuya Nishihara
setup: do not select hg executable that prints unexpected warnings...
r33599 return b'\n'.join(b' ' + e for e in err)
Augie Fackler
formatting: blacken the codebase...
r43346
Adam Simpkins
setup: prefer using the system hg to interact with the local repository...
r33114 def findhg():
"""Try to figure out how we should invoke hg for examining the local
repository contents.
Returns an hgcommand object."""
# By default, prefer the "hg" command in the user's path. This was
# presumably the hg command that the user used to create this repository.
#
# This repository may require extensions or other settings that would not
# be enabled by running the hg script directly from this local repository.
hgenv = os.environ.copy()
# Use HGPLAIN to disable hgrc settings that would change output formatting,
# and disable localization for the same reasons.
hgenv['HGPLAIN'] = '1'
hgenv['LANGUAGE'] = 'C'
hgcmd = ['hg']
# Run a simple "hg log" command just to see if using hg from the user's
Matt Harbison
setup: avoid attempting to invoke the system-wide hg.exe on Windows...
r41016 # path works and can successfully interact with this repository. Windows
# gives precedence to hg.exe in the current directory, so fall back to the
# python invocation of local hg, where pythonXY.dll can always be found.
Adam Simpkins
setup: prefer using the system hg to interact with the local repository...
r33114 check_cmd = ['log', '-r.', '-Ttest']
Matt Harbison
setup: don't skip the search for global hg.exe if there is no local instance...
r44684 if os.name != 'nt' or not os.path.exists("hg.exe"):
Matt Harbison
setup: avoid attempting to invoke the system-wide hg.exe on Windows...
r41016 try:
retcode, out, err = runcmd(hgcmd + check_cmd, hgenv)
except EnvironmentError:
retcode = -1
if retcode == 0 and not filterhgerr(err):
return hgcommand(hgcmd, hgenv)
Adam Simpkins
setup: prefer using the system hg to interact with the local repository...
r33114
# Fall back to trying the local hg installation.
hgenv = localhgenv()
hgcmd = [sys.executable, 'hg']
try:
retcode, out, err = runcmd(hgcmd + check_cmd, hgenv)
except EnvironmentError:
retcode = -1
Yuya Nishihara
setup: do not select hg executable that prints unexpected warnings...
r33599 if retcode == 0 and not filterhgerr(err):
Adam Simpkins
setup: prefer using the system hg to interact with the local repository...
r33114 return hgcommand(hgcmd, hgenv)
setup: further improve the error path for version retrieval...
r50988 eprint("/!\\")
eprint(r"/!\ Unable to find a working hg binary")
eprint(r"/!\ Version cannot be extract from the repository")
eprint(r"/!\ Re-run the setup once a first version is built")
return None
Augie Fackler
formatting: blacken the codebase...
r43346
Adam Simpkins
setup: prefer using the system hg to interact with the local repository...
r33114
def localhgenv():
"""Get an environment dictionary to use for invoking or importing
mercurial from the local repository."""
Adam Simpkins
setup: move environment computation into a helper function...
r33112 # Execute hg out of this directory with a custom environment which takes
# care to not use any hgrc files and do no localization.
Augie Fackler
formatting: blacken the codebase...
r43346 env = {
'HGMODULEPOLICY': 'py',
'HGRCPATH': '',
'LANGUAGE': 'C',
'PATH': '',
} # make pypi modules that use os.environ['PATH'] happy
Adam Simpkins
setup: move environment computation into a helper function...
r33112 if 'LD_LIBRARY_PATH' in os.environ:
env['LD_LIBRARY_PATH'] = os.environ['LD_LIBRARY_PATH']
if 'SystemRoot' in os.environ:
# SystemRoot is required by Windows to load various DLLs. See:
# https://bugs.python.org/issue13524#msg148850
env['SystemRoot'] = os.environ['SystemRoot']
Jun Wu
setup: fix localhgenv...
r33117 return env
Adam Simpkins
setup: move environment computation into a helper function...
r33112
Augie Fackler
formatting: blacken the codebase...
r43346
Adam Simpkins
setup: move environment computation into a helper function...
r33112 version = ''
Matt Mackall
setup: set env global earlier (3073)
r15367
setup: further improve the error path for version retrieval...
r50988
def _try_get_version():
Adam Simpkins
setup: prefer using the system hg to interact with the local repository...
r33114 hg = findhg()
setup: further improve the error path for version retrieval...
r50988 if hg is None:
return ''
hgid = None
numerictags = []
Adam Simpkins
setup: replace runhg() with an hgcommand helper class...
r33113 cmd = ['log', '-r', '.', '--template', '{tags}\n']
setup: further improve the error path for version retrieval...
r50988 pieces = sysstr(hg.run(cmd)).split()
numerictags = [t for t in pieces if t[0:1].isdigit()]
Yuya Nishihara
setup: convert version strings to unicode on Python 3...
r35246 hgid = sysstr(hg.run(['id', '-i'])).strip()
setup: support building from an ongoing merge...
r51063 if hgid.count('+') == 2:
hgid = hgid.replace("+", ".", 1)
Adam Simpkins
setup: fail if we cannot determine the version number...
r33110 if not hgid:
setup: further improve the error path for version retrieval...
r50988 eprint("/!\\")
eprint(r"/!\ Unable to determine hg version from local repository")
eprint(r"/!\ Failed to retrieve current revision tags")
return ''
Augie Fackler
formatting: blacken the codebase...
r43346 if numerictags: # tag(s) found
Bryan O'Sullivan
setup: calculate version more correctly...
r17709 version = numerictags[-1]
Augie Fackler
formatting: blacken the codebase...
r43346 if hgid.endswith('+'): # propagate the dirty status to the tag
Gilles Moris
setup: refactor the version string to a subset of tag+tagdist-hash+date...
r9615 version += '+'
setup: further improve the error path for version retrieval...
r50988 else: # no tag found on the checked out revision
setup: support building from an ongoing merge...
r51063 ltagcmd = ['log', '--rev', 'wdir()', '--template', '{latesttag}']
Yuya Nishihara
setup: convert version strings to unicode on Python 3...
r35246 ltag = sysstr(hg.run(ltagcmd))
setup: make the version computation process more resistant...
r50865 if not ltag:
setup: further improve the error path for version retrieval...
r50988 eprint("/!\\")
eprint(r"/!\ Unable to determine hg version from local repository")
eprint(
r"/!\ Failed to retrieve current revision distance to lated tag"
)
return ''
setup: support building from an ongoing merge...
r51063 changessincecmd = [
'log',
'-T',
'x\n',
'-r',
"only(parents(),'%s')" % ltag,
]
Adam Simpkins
setup: replace runhg() with an hgcommand helper class...
r33113 changessince = len(hg.run(changessincecmd).splitlines())
Joerg Sonnenberger
build: fake PEP440 versions...
r47262 version = '%s+hg%s.%s' % (ltag, changessince, hgid)
Gilles Moris
setup: refactor the version string to a subset of tag+tagdist-hash+date...
r9615 if version.endswith('+'):
Joerg Sonnenberger
build: fake PEP440 versions...
r47262 version = version[:-1] + 'local' + time.strftime('%Y%m%d')
setup: further improve the error path for version retrieval...
r50988 return version
if os.path.isdir('.hg'):
version = _try_get_version()
Gilles Moris
setup: refactor the version string to a subset of tag+tagdist-hash+date...
r9615 elif os.path.exists('.hg_archival.txt'):
Augie Fackler
formatting: blacken the codebase...
r43346 kw = dict(
[[t.strip() for t in l.split(':', 1)] for l in open('.hg_archival.txt')]
)
Gilles Moris
setup: refactor the version string to a subset of tag+tagdist-hash+date...
r9615 if 'tag' in kw:
timeless
cleanup: remove superfluous space after space after equals (python)
r27637 version = kw['tag']
Gilles Moris
setup: refactor the version string to a subset of tag+tagdist-hash+date...
r9615 elif 'latesttag' in kw:
Siddharth Agarwal
setup: use changessincelatesttag from archive if present...
r23646 if 'changessincelatesttag' in kw:
Joerg Sonnenberger
build: fake PEP440 versions...
r47262 version = (
Martin von Zweigbergk
build: make version from .hg_archival.txt consistent with that from .hg/...
r47273 '%(latesttag)s+hg%(changessincelatesttag)s.%(node).12s' % kw
Joerg Sonnenberger
build: fake PEP440 versions...
r47262 )
Siddharth Agarwal
setup: use changessincelatesttag from archive if present...
r23646 else:
Martin von Zweigbergk
build: make version from .hg_archival.txt consistent with that from .hg/...
r47273 version = '%(latesttag)s+hg%(latesttagdistance)s.%(node).12s' % kw
Christian Ebert
setup.py: subprocess instead of os.popen, sys.stderr.write instead of print...
r8547 else:
Martin von Zweigbergk
build: make version from .hg_archival.txt consistent with that from .hg/...
r47273 version = '0+hg' + kw.get('node', '')[:12]
Joerg Sonnenberger
packaging: extract pre-computed version when running from plain tarball...
r47759 elif os.path.exists('mercurial/__version__.py'):
Raphaël Gomès
lint: fix failing code check...
r47772 with open('mercurial/__version__.py') as f:
data = f.read()
Joerg Sonnenberger
packaging: extract pre-computed version when running from plain tarball...
r47759 version = re.search('version = b"(.*)"', data).group(1)
setup: further improve the error path for version retrieval...
r50988 if not version:
if os.environ.get("MERCURIAL_SETUP_MAKE_LOCAL") == "1":
version = "0.0+0"
eprint("/!\\")
eprint(r"/!\ Using '0.0+0' as the default version")
eprint(r"/!\ Re-run make local once that first version is built")
eprint("/!\\")
else:
eprint("/!\\")
eprint(r"/!\ Could not determine the Mercurial version")
eprint(r"/!\ You need to build a local version first")
eprint(r"/!\ Run `make local` and try again")
eprint("/!\\")
msg = "Run `make local` first to get a working local version"
raise SystemExit(msg)
Gregory Szorc
setup: only write some autogenerated files if they change...
r35229
setup: further improve the error path for version retrieval...
r50988 versionb = version
if not isinstance(versionb, bytes):
versionb = versionb.encode('ascii')
write_if_changed(
'mercurial/__version__.py',
b''.join(
[
b'# this file is autogenerated by setup.py\n'
b'version = b"%s"\n' % versionb,
]
),
)
Jeremy Whitlock
Fix how setup.py identifies the Mercurial version....
r8493
Augie Fackler
formatting: blacken the codebase...
r43346
Simon Heimberg
setup: subclass build command
r15460 class hgbuild(build):
# Insert hgbuildmo first so that files in mercurial/locale/ are found
# when build_py is run next.
Gregory Szorc
setup: remove support for 2to3...
r28398 sub_commands = [('build_mo', None)] + build.sub_commands
Simon Heimberg
setup: subclass build command
r15460
Augie Fackler
formatting: blacken the codebase...
r43346
Matt Mackall
setup: backout 8504699d1aa6...
r15523 class hgbuildmo(build):
Martin Geisler
i18n: new build_mo command for setup.py...
r7649
description = "build translations (.mo files)"
def run(self):
if not find_executable('msgfmt'):
Augie Fackler
formatting: blacken the codebase...
r43346 self.warn(
"could not find msgfmt executable, no translations "
"will be built"
)
Martin Geisler
i18n: new build_mo command for setup.py...
r7649 return
podir = 'i18n'
if not os.path.isdir(podir):
self.warn("could not find %s/ directory" % podir)
return
join = os.path.join
for po in os.listdir(podir):
if not po.endswith('.po'):
continue
pofile = join(podir, po)
modir = join('locale', po[:-3], 'LC_MESSAGES')
mofile = join(modir, 'hg.mo')
Dan Villiom Podlaski Christiansen
setup: install translation files as package data...
r9999 mobuildfile = join('mercurial', mofile)
cmd = ['msgfmt', '-v', '-o', mobuildfile, pofile]
Martin Geisler
setup: do not use -c with msgfmt on Solaris (issue1489)
r7720 if sys.platform != 'sunos5':
# msgfmt on Solaris does not know about -c
cmd.append('-c')
Dan Villiom Podlaski Christiansen
setup: install translation files as package data...
r9999 self.mkpath(join('mercurial', modir))
self.make_file([pofile], mobuildfile, spawn, (cmd,))
Martin Geisler
i18n: new build_mo command for setup.py...
r7649
Dan Villiom Podlaski Christiansen
setup/hg: always load Mercurial from where it was installed....
r12661
Simon Heimberg
setup: subclass distribution instead of overwriting original
r15458 class hgdist(Distribution):
Maciej Fijalkowski
setup: prepare for future cffi modules by adding placeholder in setup
r29505 pure = False
setup-rust: add a --no-rust flag...
r44971 rust = False
no_rust = False
Maciej Fijalkowski
setup: prepare for future cffi modules by adding placeholder in setup
r29505 cffi = ispypy
Simon Heimberg
setup: subclass distribution instead of overwriting original
r15458
Augie Fackler
cleanup: use () to wrap long lines instead of \...
r41925 global_options = Distribution.global_options + [
('pure', None, "use pure (slow) Python code instead of C extensions"),
Georges Racinet
rust: new rust options in setup.py...
r42653 ('rust', None, "use Rust extensions additionally to C extensions"),
setup-rust: add a --no-rust flag...
r44971 (
'no-rust',
None,
"do not use Rust extensions additionally to C extensions",
),
Augie Fackler
cleanup: use () to wrap long lines instead of \...
r41925 ]
Martin Geisler
add --pure flag to setup.py...
r7722
setup-rust: add a --no-rust flag...
r44971 negative_opt = Distribution.negative_opt.copy()
boolean_options = ['pure', 'rust', 'no-rust']
negative_opt['no-rust'] = 'rust'
def _set_command_options(self, command_obj, option_dict=None):
Augie Fackler
setup: work around old versions of distutils breaking setup.py...
r45120 # Not all distutils versions in the wild have boolean_options.
# This should be cleaned up when we're Python 3 only.
command_obj.boolean_options = (
getattr(command_obj, 'boolean_options', []) + self.boolean_options
)
setup-rust: add a --no-rust flag...
r44971 return Distribution._set_command_options(
self, command_obj, option_dict=option_dict
)
def parse_command_line(self):
ret = Distribution.parse_command_line(self)
if not (self.rust or self.no_rust):
hgrustext = os.environ.get('HGWITHRUSTEXT')
# TODO record it for proper rebuild upon changes
# (see mercurial/__modulepolicy__.py)
if hgrustext != 'cpython' and hgrustext is not None:
if hgrustext:
Raphaël Gomès
typo: s/unkown/unknown across the codebase...
r49252 msg = 'unknown HGWITHRUSTEXT value: %s' % hgrustext
Augie Fackler
setup: remove printf trampoline...
r49682 print(msg, file=sys.stderr)
setup-rust: add a --no-rust flag...
r44971 hgrustext = None
self.rust = hgrustext is not None
self.no_rust = not self.rust
return ret
Simon Heimberg
setup: has_ext_modules always returns false when pure is specified...
r15459 def has_ext_modules(self):
# self.ext_modules is emptied in hgbuildpy.finalize_options which is
# too late for some cases
return not self.pure and Distribution.has_ext_modules(self)
Augie Fackler
formatting: blacken the codebase...
r43346
Gregory Szorc
setup: add flag to build_ext to control building zstd...
r30450 # This is ugly as a one-liner. So use a variable.
buildextnegops = dict(getattr(build_ext, 'negative_options', {}))
buildextnegops['no-zstd'] = 'zstd'
Georges Racinet
rust: new rust options in setup.py...
r42653 buildextnegops['no-rust'] = 'rust'
Gregory Szorc
setup: add flag to build_ext to control building zstd...
r30450
Augie Fackler
formatting: blacken the codebase...
r43346
Christian Boos
setup: ignore failures to build optional inotify extension
r11468 class hgbuildext(build_ext):
Gregory Szorc
setup: add flag to build_ext to control building zstd...
r30450 user_options = build_ext.user_options + [
('zstd', None, 'compile zstd bindings [default]'),
('no-zstd', None, 'do not compile zstd bindings'),
Augie Fackler
formatting: blacken the codebase...
r43346 (
'rust',
None,
'compile Rust extensions if they are in use '
'(requires Cargo) [default]',
),
Georges Racinet
rust: new rust options in setup.py...
r42653 ('no-rust', None, 'do not compile Rust extensions'),
Gregory Szorc
setup: add flag to build_ext to control building zstd...
r30450 ]
Georges Racinet
rust: new rust options in setup.py...
r42653 boolean_options = build_ext.boolean_options + ['zstd', 'rust']
Gregory Szorc
setup: add flag to build_ext to control building zstd...
r30450 negative_opt = buildextnegops
def initialize_options(self):
self.zstd = True
Georges Racinet
rust: new rust options in setup.py...
r42653 self.rust = True
Gregory Szorc
setup: add flag to build_ext to control building zstd...
r30450 return build_ext.initialize_options(self)
Gregory Szorc
setup: build extensions in parallel by default...
r43314 def finalize_options(self):
# Unless overridden by the end user, build extensions in parallel.
# Only influences behavior on Python 3.5+.
if getattr(self, 'parallel', None) is None:
self.parallel = True
return build_ext.finalize_options(self)
Gregory Szorc
setup: add flag to build_ext to control building zstd...
r30450 def build_extensions(self):
Augie Fackler
formatting: blacken the codebase...
r43346 ruststandalones = [
e for e in self.extensions if isinstance(e, RustStandaloneExtension)
]
self.extensions = [
e for e in self.extensions if e not in ruststandalones
]
Gregory Szorc
setup: add flag to build_ext to control building zstd...
r30450 # Filter out zstd if disabled via argument.
if not self.zstd:
Augie Fackler
formatting: blacken the codebase...
r43346 self.extensions = [
e for e in self.extensions if e.name != 'mercurial.zstd'
]
Gregory Szorc
setup: add flag to build_ext to control building zstd...
r30450
Matt Harbison
setup.py: fix some documentation typos...
r49333 # Build Rust standalone extensions if it'll be used
# and its build is not explicitly disabled (for external build
Georges Racinet
rust: new rust options in setup.py...
r42653 # as Linux distributions would do)
setup-rust: remove the legacy 'direct-ffi' variant...
r44956 if self.distribution.rust and self.rust:
Dan Villiom Podlaski Christiansen
rust: lower compile error on non-linux platforms to a warning...
r47081 if not sys.platform.startswith('linux'):
self.warn(
"rust extensions have only been tested on Linux "
"and may not behave correctly on other platforms"
)
Georges Racinet
rust: new rust options in setup.py...
r42653 for rustext in ruststandalones:
rustext.build('' if self.inplace else self.build_lib)
Georges Racinet
rust-cpython: build via HGWITHRUSTEXT=cpython...
r41003
Gregory Szorc
setup: add flag to build_ext to control building zstd...
r30450 return build_ext.build_extensions(self)
Christian Boos
setup: ignore failures to build optional inotify extension
r11468
def build_extension(self, ext):
Augie Fackler
formatting: blacken the codebase...
r43346 if (
self.distribution.rust
and self.rust
and isinstance(ext, RustExtension)
):
ext.rustbuild()
Christian Boos
setup: ignore failures to build optional inotify extension
r11468 try:
build_ext.build_extension(self, ext)
except CCompilerError:
Martin Geisler
setup: slight simplification
r12501 if not getattr(ext, 'optional', False):
Christian Boos
setup: ignore failures to build optional inotify extension
r11468 raise
Augie Fackler
formatting: blacken the codebase...
r43346 log.warn(
"Failed to build optional extension '%s' (skipping)", ext.name
)
Christian Boos
setup: ignore failures to build optional inotify extension
r11468
Gregory Szorc
setup.py: attempt to build and install hg.exe on Windows...
r27268 class hgbuildscripts(build_scripts):
def run(self):
FUJIWARA Katsunori
setup: avoid procedure related to hg.exe at setup.py --pure...
r28041 if os.name != 'nt' or self.distribution.pure:
Gregory Szorc
setup.py: attempt to build and install hg.exe on Windows...
r27268 return build_scripts.run(self)
exebuilt = False
try:
self.run_command('build_hgexe')
exebuilt = True
except (DistutilsError, CCompilerError):
log.warn('failed to build optional hg.exe')
if exebuilt:
# Copying hg.exe to the scripts build directory ensures it is
# installed by the install_scripts command.
hgexecommand = self.get_finalized_command('build_hgexe')
dest = os.path.join(self.build_dir, 'hg.exe')
self.mkpath(self.build_dir)
self.copy_file(hgexecommand.hgexepath, dest)
# Remove hg.bat because it is redundant with hg.exe.
self.scripts.remove('contrib/win32/hg.bat')
return build_scripts.run(self)
Augie Fackler
formatting: blacken the codebase...
r43346
Martin Geisler
setup: cleanup coding style
r10000 class hgbuildpy(build_py):
Martin Geisler
add --pure flag to setup.py...
r7722 def finalize_options(self):
build_py.finalize_options(self)
if self.distribution.pure:
self.distribution.ext_modules = []
Maciej Fijalkowski
setup: prepare for future cffi modules by adding placeholder in setup
r29505 elif self.distribution.cffi:
Jun Wu
setup: move cffi stuff to mercurial/cffi...
r30346 from mercurial.cffi import (
Yuya Nishihara
cffi: rename build scripts...
r32505 bdiffbuild,
mpatchbuild,
Jun Wu
setup: move cffi stuff to mercurial/cffi...
r30346 )
Augie Fackler
formatting: blacken the codebase...
r43346
exts = [
mpatchbuild.ffi.distutils_extension(),
bdiffbuild.ffi.distutils_extension(),
]
Maciej Fijalkowski
setup: prepare for future cffi modules by adding placeholder in setup
r29505 # cffi modules go here
Maciej Fijalkowski
osutil: add darwin-only version of os.listdir using cffi
r29600 if sys.platform == 'darwin':
Yuya Nishihara
cffi: rename build scripts...
r32505 from mercurial.cffi import osutilbuild
Augie Fackler
formatting: blacken the codebase...
r43346
Yuya Nishihara
cffi: rename build scripts...
r32505 exts.append(osutilbuild.ffi.distutils_extension())
Maciej Fijalkowski
setup: prepare for future cffi modules by adding placeholder in setup
r29505 self.distribution.ext_modules = exts
Nicolas Dumazet
setup: user-friendly error message if Python headers are missing
r12649 else:
Mads Kiilerich
setup: make error message for missing Python headers more helpful
r18905 h = os.path.join(get_python_inc(), 'Python.h')
if not os.path.exists(h):
Augie Fackler
formatting: blacken the codebase...
r43346 raise SystemExit(
'Python headers are required to build '
'Mercurial but weren\'t found in %s' % h
)
Martin Geisler
add --pure flag to setup.py...
r7722
timeless
setup: create a module for the modulepolicy...
r28430 def run(self):
Yuya Nishihara
setup: do not overwrite local __modulepolicy__.py on out-of-source build...
r32653 basepath = os.path.join(self.build_lib, 'mercurial')
self.mkpath(basepath)
Georges Racinet
rust: new rust options in setup.py...
r42653 rust = self.distribution.rust
timeless
setup: create a module for the modulepolicy...
r28430 if self.distribution.pure:
modulepolicy = 'py'
Yuya Nishihara
policy: relax the default for in-place build...
r32251 elif self.build_lib == '.':
Georges Racinet
rust: new rust options in setup.py...
r42653 # in-place build should run without rebuilding and Rust extensions
modulepolicy = 'rust+c-allow' if rust else 'allow'
timeless
setup: create a module for the modulepolicy...
r28430 else:
Georges Racinet
rust: new rust options in setup.py...
r42653 modulepolicy = 'rust+c' if rust else 'c'
Gregory Szorc
setup: only write some autogenerated files if they change...
r35229
Augie Fackler
formatting: blacken the codebase...
r43346 content = b''.join(
[
b'# this file is autogenerated by setup.py\n',
b'modulepolicy = b"%s"\n' % modulepolicy.encode('ascii'),
]
)
write_if_changed(os.path.join(basepath, '__modulepolicy__.py'), content)
Gregory Szorc
setup: refactor handling of modules with C/Python implementations...
r27222
timeless
setup: create a module for the modulepolicy...
r28430 build_py.run(self)
Martin Geisler
add --pure flag to setup.py...
r7722
Augie Fackler
formatting: blacken the codebase...
r43346
Yuya Nishihara
setup: add command to generate index of extensions...
r14538 class buildhgextindex(Command):
description = 'generate prebuilt index of hgext (for frozen package)'
user_options = []
_indexfilename = 'hgext/__index__.py'
def initialize_options(self):
pass
def finalize_options(self):
pass
def run(self):
if os.path.exists(self._indexfilename):
timeless
setup: switch to with open as...
r28418 with open(self._indexfilename, 'w') as f:
f.write('# empty\n')
Yuya Nishihara
setup: add command to generate index of extensions...
r14538
# here no extension enabled, disabled() lists up everything
Augie Fackler
formatting: blacken the codebase...
r43346 code = (
'import pprint; from mercurial import extensions; '
Matt Harbison
setup: exclude the __index__ module from itself when generating...
r44862 'ext = extensions.disabled();'
'ext.pop("__index__", None);'
'pprint.pprint(ext)'
Augie Fackler
formatting: blacken the codebase...
r43346 )
returncode, out, err = runcmd(
[sys.executable, '-c', code], localhgenv()
)
Adam Simpkins
setup: update runcmd() to also return the exit status...
r33111 if err or returncode != 0:
Yuya Nishihara
setup: add command to generate index of extensions...
r14538 raise DistutilsExecError(err)
Matt Harbison
py3: write out hgextindex as bytes in setup.py...
r42244 with open(self._indexfilename, 'wb') as f:
f.write(b'# this file is autogenerated by setup.py\n')
f.write(b'docs = ')
timeless
setup: switch to with open as...
r28418 f.write(out)
Yuya Nishihara
setup: add command to generate index of extensions...
r14538
Augie Fackler
formatting: blacken the codebase...
r43346
Adrian Buehlmann
setup: compile hg.exe...
r17061 class buildhgexe(build_ext):
description = 'compile hg.exe from mercurial/exewrapper.c'
Kostia Balytskyi
windows: add an experimental option for long paths support...
r34531
Matt Harbison
setup: use the full executable manifest from `python.exe`...
r50291 LONG_PATHS_MANIFEST = """\
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
<trustInfo xmlns="urn:schemas-microsoft-com:asm.v3">
<security>
<requestedPrivileges>
<requestedExecutionLevel
level="asInvoker"
uiAccess="false"
/>
</requestedPrivileges>
</security>
</trustInfo>
<compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
<application>
<!-- Windows Vista -->
<supportedOS Id="{e2011457-1546-43c5-a5fe-008deee3d3f0}"/>
<!-- Windows 7 -->
<supportedOS Id="{35138b9a-5d96-4fbd-8e2d-a2440225f93a}"/>
<!-- Windows 8 -->
<supportedOS Id="{4a2f28e3-53b9-4441-ba9c-d69d4a4a6e38}"/>
<!-- Windows 8.1 -->
<supportedOS Id="{1f676c76-80e1-4239-95bb-83d0f6d0da78}"/>
<!-- Windows 10 and Windows 11 -->
<supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}"/>
</application>
</compatibility>
<application xmlns="urn:schemas-microsoft-com:asm.v3">
<windowsSettings
xmlns:ws2="http://schemas.microsoft.com/SMI/2016/WindowsSettings">
<ws2:longPathAware>true</ws2:longPathAware>
</windowsSettings>
</application>
<dependency>
<dependentAssembly>
<assemblyIdentity type="win32"
name="Microsoft.Windows.Common-Controls"
version="6.0.0.0"
processorArchitecture="*"
publicKeyToken="6595b64144ccf1df"
language="*" />
</dependentAssembly>
</dependency>
</assembly>
"""
Kostia Balytskyi
windows: add an experimental option for long paths support...
r34531
def initialize_options(self):
build_ext.initialize_options(self)
Adrian Buehlmann
setup: compile hg.exe...
r17061
def build_extensions(self):
if os.name != 'nt':
return
Adrian Buehlmann
setup: fix build_hgexe for mingw32 compiler...
r17246 if isinstance(self.compiler, HackedMingw32CCompiler):
Augie Fackler
formatting: blacken the codebase...
r43346 self.compiler.compiler_so = self.compiler.compiler # no -mdll
self.compiler.dll_libraries = [] # no -lmsrvc90
Gregory Szorc
setup: detect Python DLL filename from loaded DLL...
r29020
Matt Harbison
setup: conditionalize access to `sys.dllhandle` when building extensions...
r44073 pythonlib = None
Gregory Szorc
setup: detect Python DLL filename from loaded DLL...
r29020
Matt Harbison
setup: stop shadowing the builtin `dir` symbol...
r50289 dirname = os.path.dirname(self.get_ext_fullpath('dummy'))
self.hgtarget = os.path.join(dirname, 'hg')
Raphaël Gomès
backout: backed out changeset f78d8b8c46d7...
r49124
Matt Harbison
setup: conditionalize access to `sys.dllhandle` when building extensions...
r44073 if getattr(sys, 'dllhandle', None):
# Different Python installs can have different Python library
# names. e.g. the official CPython distribution uses pythonXY.dll
# and MinGW uses libpythonX.Y.dll.
_kernel32 = ctypes.windll.kernel32
_kernel32.GetModuleFileNameA.argtypes = [
ctypes.c_void_p,
ctypes.c_void_p,
ctypes.c_ulong,
]
_kernel32.GetModuleFileNameA.restype = ctypes.c_ulong
size = 1000
buf = ctypes.create_string_buffer(size + 1)
filelen = _kernel32.GetModuleFileNameA(
sys.dllhandle, ctypes.byref(buf), size
)
if filelen > 0 and filelen != size:
dllbasename = os.path.basename(buf.value)
if not dllbasename.lower().endswith(b'.dll'):
raise SystemExit(
'Python DLL does not end with .dll: %s' % dllbasename
)
pythonlib = dllbasename[:-4]
Raphaël Gomès
backout: backed out changeset f78d8b8c46d7...
r49124 # Copy the pythonXY.dll next to the binary so that it runs
# without tampering with PATH.
dest = os.path.join(
os.path.dirname(self.hgtarget),
Augie Fackler
setup: inline os.fsdecode now that we're done with Python 2...
r49684 os.fsdecode(dllbasename),
Raphaël Gomès
backout: backed out changeset f78d8b8c46d7...
r49124 )
if not os.path.exists(dest):
shutil.copy(buf.value, dest)
# Also overwrite python3.dll so that hgext.git is usable.
# TODO: also handle the MSYS flavor
Augie Fackler
setup: unconditionally do this python 3 step...
r49686 python_x = os.path.join(
os.path.dirname(os.fsdecode(buf.value)),
"python3.dll",
)
if os.path.exists(python_x):
dest = os.path.join(
os.path.dirname(self.hgtarget),
os.path.basename(python_x),
Raphaël Gomès
backout: backed out changeset f78d8b8c46d7...
r49124 )
Augie Fackler
setup: unconditionally do this python 3 step...
r49686 shutil.copy(python_x, dest)
Raphaël Gomès
backout: backed out changeset f78d8b8c46d7...
r49124
Matt Harbison
setup: conditionalize access to `sys.dllhandle` when building extensions...
r44073 if not pythonlib:
Augie Fackler
formatting: blacken the codebase...
r43346 log.warn(
Matt Harbison
setup: combine two contiguous string literals...
r44071 'could not determine Python DLL filename; assuming pythonXY'
Augie Fackler
formatting: blacken the codebase...
r43346 )
Gregory Szorc
setup: detect Python DLL filename from loaded DLL...
r29020
hv = sys.hexversion
Matt Harbison
setup: use bytes for assumed python version...
r44072 pythonlib = b'python%d%d' % (hv >> 24, (hv >> 16) & 0xFF)
Gregory Szorc
setup: detect Python DLL filename from loaded DLL...
r29020
log.info('using %s as Python library name' % pythonlib)
timeless
setup: switch to with open as...
r28418 with open('mercurial/hgpythonlib.h', 'wb') as f:
Matt Harbison
py3: add b'' to some setup.py strings for Windows...
r39644 f.write(b'/* this file is autogenerated by setup.py */\n')
f.write(b'#define HGPYTHONLIB "%s"\n' % pythonlib)
Matt Harbison
setup: build exewrapper with Unicode support on py3...
r40433
Augie Fackler
formatting: blacken the codebase...
r43346 objects = self.compiler.compile(
['mercurial/exewrapper.c'],
output_dir=self.build_temp,
Augie Fackler
setup: inline now-constant list...
r49687 macros=[('_UNICODE', None), ('UNICODE', None)],
Augie Fackler
formatting: blacken the codebase...
r43346 )
self.compiler.link_executable(
objects, self.hgtarget, libraries=[], output_dir=self.build_temp
)
Matt Harbison
setup: unconditionally enable the `long-paths-support` option on Windows...
r50290
self.addlongpathsmanifest()
Kostia Balytskyi
windows: add an experimental option for long paths support...
r34531
def addlongpathsmanifest(self):
Matt Harbison
setup: unconditionally enable the `long-paths-support` option on Windows...
r50290 """Add manifest pieces so that hg.exe understands long paths
Kostia Balytskyi
windows: add an experimental option for long paths support...
r34531
Why resource #1 should be used for .exe manifests? I don't know and
wasn't able to find an explanation for mortals. But it seems to work.
"""
exefname = self.compiler.executable_filename(self.hgtarget)
fdauto, manfname = tempfile.mkstemp(suffix='.hg.exe.manifest')
os.close(fdauto)
Matt Harbison
setup: use the full executable manifest from `python.exe`...
r50291 with open(manfname, 'w', encoding="UTF-8") as f:
Kostia Balytskyi
windows: add an experimental option for long paths support...
r34531 f.write(self.LONG_PATHS_MANIFEST)
log.info("long paths manifest is written to '%s'" % manfname)
outputresource = '-outputresource:%s;#1' % exefname
log.info("running mt.exe to update hg.exe's manifest in-place")
Matt Harbison
setup: use the full executable manifest from `python.exe`...
r50291
Augie Fackler
formatting: blacken the codebase...
r43346 self.spawn(
[
Matt Harbison
setup: unconditionally enable the `long-paths-support` option on Windows...
r50290 self.compiler.mt,
Augie Fackler
formatting: blacken the codebase...
r43346 '-nologo',
'-manifest',
manfname,
outputresource,
]
)
Kostia Balytskyi
windows: add an experimental option for long paths support...
r34531 log.info("done updating hg.exe's manifest")
os.remove(manfname)
Adrian Buehlmann
setup: compile hg.exe...
r17061
Gregory Szorc
setup.py: attempt to build and install hg.exe on Windows...
r27268 @property
def hgexepath(self):
dir = os.path.dirname(self.get_ext_fullpath('dummy'))
return os.path.join(self.build_temp, dir, 'hg.exe')
Augie Fackler
formatting: blacken the codebase...
r43346
Gregory Szorc
setup: define build_doc command...
r42016 class hgbuilddoc(Command):
description = 'build documentation'
user_options = [
('man', None, 'generate man pages'),
('html', None, 'generate html pages'),
]
def initialize_options(self):
self.man = None
self.html = None
def finalize_options(self):
# If --man or --html are set, only generate what we're told to.
# Otherwise generate everything.
have_subset = self.man is not None or self.html is not None
if have_subset:
self.man = True if self.man else False
self.html = True if self.html else False
else:
self.man = True
self.html = True
def run(self):
def normalizecrlf(p):
with open(p, 'rb') as fh:
orig = fh.read()
if b'\r\n' not in orig:
return
log.info('normalizing %s to LF line endings' % p)
with open(p, 'wb') as fh:
fh.write(orig.replace(b'\r\n', b'\n'))
def gentxt(root):
txt = 'doc/%s.txt' % root
log.info('generating %s' % txt)
res, out, err = runcmd(
Augie Fackler
formatting: blacken the codebase...
r43346 [sys.executable, 'gendoc.py', root], os.environ, cwd='doc'
)
Gregory Szorc
setup: define build_doc command...
r42016 if res:
Augie Fackler
formatting: blacken the codebase...
r43346 raise SystemExit(
Gregory Szorc
setup: use sysstr() on process output...
r45244 'error running gendoc.py: %s'
% '\n'.join([sysstr(out), sysstr(err)])
Augie Fackler
formatting: blacken the codebase...
r43346 )
Gregory Szorc
setup: define build_doc command...
r42016
with open(txt, 'wb') as fh:
fh.write(out)
def gengendoc(root):
gendoc = 'doc/%s.gendoc.txt' % root
log.info('generating %s' % gendoc)
res, out, err = runcmd(
[sys.executable, 'gendoc.py', '%s.gendoc' % root],
os.environ,
Augie Fackler
formatting: blacken the codebase...
r43346 cwd='doc',
)
Gregory Szorc
setup: define build_doc command...
r42016 if res:
Augie Fackler
formatting: blacken the codebase...
r43346 raise SystemExit(
Gregory Szorc
setup: use sysstr() on process output...
r45244 'error running gendoc: %s'
% '\n'.join([sysstr(out), sysstr(err)])
Augie Fackler
formatting: blacken the codebase...
r43346 )
Gregory Szorc
setup: define build_doc command...
r42016
with open(gendoc, 'wb') as fh:
fh.write(out)
def genman(root):
log.info('generating doc/%s' % root)
res, out, err = runcmd(
Augie Fackler
formatting: blacken the codebase...
r43346 [
sys.executable,
'runrst',
'hgmanpage',
'--halt',
'warning',
'--strip-elements-with-class',
'htmlonly',
'%s.txt' % root,
root,
],
Gregory Szorc
setup: define build_doc command...
r42016 os.environ,
Augie Fackler
formatting: blacken the codebase...
r43346 cwd='doc',
)
Gregory Szorc
setup: define build_doc command...
r42016 if res:
Augie Fackler
formatting: blacken the codebase...
r43346 raise SystemExit(
Gregory Szorc
setup: use sysstr() on process output...
r45244 'error running runrst: %s'
% '\n'.join([sysstr(out), sysstr(err)])
Augie Fackler
formatting: blacken the codebase...
r43346 )
Gregory Szorc
setup: define build_doc command...
r42016
normalizecrlf('doc/%s' % root)
def genhtml(root):
log.info('generating doc/%s.html' % root)
res, out, err = runcmd(
Augie Fackler
formatting: blacken the codebase...
r43346 [
sys.executable,
'runrst',
'html',
'--halt',
'warning',
'--link-stylesheet',
'--stylesheet-path',
'style.css',
'%s.txt' % root,
'%s.html' % root,
],
Gregory Szorc
setup: define build_doc command...
r42016 os.environ,
Augie Fackler
formatting: blacken the codebase...
r43346 cwd='doc',
)
Gregory Szorc
setup: define build_doc command...
r42016 if res:
Augie Fackler
formatting: blacken the codebase...
r43346 raise SystemExit(
Gregory Szorc
setup: use sysstr() on process output...
r45244 'error running runrst: %s'
% '\n'.join([sysstr(out), sysstr(err)])
Augie Fackler
formatting: blacken the codebase...
r43346 )
Gregory Szorc
setup: define build_doc command...
r42016
normalizecrlf('doc/%s.html' % root)
# This logic is duplicated in doc/Makefile.
Augie Fackler
cleanup: run pyupgrade on our source tree to clean up varying things...
r44937 sources = {
Augie Fackler
formatting: blacken the codebase...
r43346 f
Augie Fackler
windows: further build fixes for the WiX installer...
r44188 for f in os.listdir('mercurial/helptext')
Augie Fackler
formatting: blacken the codebase...
r43346 if re.search(r'[0-9]\.txt$', f)
Augie Fackler
cleanup: run pyupgrade on our source tree to clean up varying things...
r44937 }
Gregory Szorc
setup: define build_doc command...
r42016
# common.txt is a one-off.
gentxt('common')
for source in sorted(sources):
assert source[-4:] == '.txt'
root = source[:-4]
gentxt(root)
gengendoc(root)
if self.man:
genman(root)
if self.html:
genhtml(root)
Augie Fackler
formatting: blacken the codebase...
r43346
Matt Harbison
setup: prevent setuptools from laying an egg...
r32647 class hginstall(install):
Augie Fackler
setup: introduce dummy copies of setuptools flags...
r32725
user_options = install.user_options + [
Augie Fackler
formatting: blacken the codebase...
r43346 (
'old-and-unmanageable',
None,
'noop, present for eggless setuptools compat',
),
(
'single-version-externally-managed',
None,
'noop, present for eggless setuptools compat',
),
Augie Fackler
setup: introduce dummy copies of setuptools flags...
r32725 ]
Matthew Martin
completion: install completers to conventional locations...
r49845 sub_commands = install.sub_commands + [
('install_completion', lambda self: True)
]
Augie Fackler
setup: introduce dummy copies of setuptools flags...
r32725 # Also helps setuptools not be sad while we refuse to create eggs.
single_version_externally_managed = True
Matt Harbison
setup: prevent setuptools from laying an egg...
r32647 def get_sub_commands(self):
# Screen out egg related commands to prevent egg generation. But allow
# mercurial.egg-info generation, since that is part of modern
# packaging.
Augie Fackler
cleanup: run pyupgrade on our source tree to clean up varying things...
r44937 excl = {'bdist_egg'}
test-regression: backing out D9640 (63c923fd7fa8)...
r47033 return filter(lambda x: x not in excl, install.get_sub_commands(self))
Matt Harbison
setup: prevent setuptools from laying an egg...
r32647
Augie Fackler
formatting: blacken the codebase...
r43346
Kyle Lippincott
setup: set mode 644 or 755 on installed files
r22640 class hginstalllib(install_lib):
Augie Fackler
formating: upgrade to black 20.8b1...
r46554 """
Kyle Lippincott
setup: set mode 644 or 755 on installed files
r22640 This is a specialization of install_lib that replaces the copy_file used
there so that it supports setting the mode of files after copying them,
instead of just preserving the mode that the files originally had. If your
system has a umask of something like 027, preserving the permissions when
copying will lead to a broken install.
Note that just passing keep_permissions=False to copy_file would be
insufficient, as it might still be applying a umask.
Augie Fackler
formating: upgrade to black 20.8b1...
r46554 """
Kyle Lippincott
setup: set mode 644 or 755 on installed files
r22640
def run(self):
realcopyfile = file_util.copy_file
Augie Fackler
formatting: blacken the codebase...
r43346
Kyle Lippincott
setup: set mode 644 or 755 on installed files
r22640 def copyfileandsetmode(*args, **kwargs):
src, dst = args[0], args[1]
dst, copied = realcopyfile(*args, **kwargs)
if copied:
st = os.stat(src)
# Persist executable bit (apply it to group and other if user
# has it)
if st[stat.ST_MODE] & stat.S_IXUSR:
Augie Fackler
setup: hide octal literals inside strings so they're portable (issue4554)
r24941 setmode = int('0755', 8)
Kyle Lippincott
setup: set mode 644 or 755 on installed files
r22640 else:
Augie Fackler
setup: hide octal literals inside strings so they're portable (issue4554)
r24941 setmode = int('0644', 8)
m = stat.S_IMODE(st[stat.ST_MODE])
m = (m & ~int('0777', 8)) | setmode
os.chmod(dst, m)
Augie Fackler
formatting: blacken the codebase...
r43346
Kyle Lippincott
setup: set mode 644 or 755 on installed files
r22640 file_util.copy_file = copyfileandsetmode
try:
install_lib.run(self)
finally:
file_util.copy_file = realcopyfile
Augie Fackler
formatting: blacken the codebase...
r43346
Dan Villiom Podlaski Christiansen
setup/hg: always load Mercurial from where it was installed....
r12661 class hginstallscripts(install_scripts):
Augie Fackler
formating: upgrade to black 20.8b1...
r46554 """
Dan Villiom Podlaski Christiansen
setup/hg: always load Mercurial from where it was installed....
r12661 This is a specialization of install_scripts that replaces the @LIBDIR@ with
the configured directory for modules. If possible, the path is made relative
to the directory for scripts.
Augie Fackler
formating: upgrade to black 20.8b1...
r46554 """
Dan Villiom Podlaski Christiansen
setup/hg: always load Mercurial from where it was installed....
r12661
def initialize_options(self):
install_scripts.initialize_options(self)
self.install_lib = None
def finalize_options(self):
install_scripts.finalize_options(self)
Augie Fackler
formatting: blacken the codebase...
r43346 self.set_undefined_options('install', ('install_lib', 'install_lib'))
Dan Villiom Podlaski Christiansen
setup/hg: always load Mercurial from where it was installed....
r12661
def run(self):
install_scripts.run(self)
Gregory Szorc
setup.py: don't rewrite @LIBDIR@ when creating wheels...
r27269 # It only makes sense to replace @LIBDIR@ with the install path if
# the install path is known. For wheels, the logic below calculates
# the libdir to be "../..". This is because the internal layout of a
# wheel archive looks like:
#
# mercurial-3.6.1.data/scripts/hg
# mercurial/__init__.py
#
# When installing wheels, the subdirectories of the "<pkg>.data"
# directory are translated to system local paths and files therein
# are copied in place. The mercurial/* files are installed into the
# site-packages directory. However, the site-packages directory
# isn't known until wheel install time. This means we have no clue
# at wheel generation time what the installed site-packages directory
# will be. And, wheels don't appear to provide the ability to register
# custom code to run during wheel installation. This all means that
# we can't reliably set the libdir in wheels: the default behavior
# of looking in sys.path must do.
Augie Fackler
formatting: blacken the codebase...
r43346 if (
os.path.splitdrive(self.install_dir)[0]
!= os.path.splitdrive(self.install_lib)[0]
):
Dan Villiom Podlaski Christiansen
setup/hg: always load Mercurial from where it was installed....
r12661 # can't make relative paths from one drive to another, so use an
# absolute path instead
libdir = self.install_lib
else:
Martin von Zweigbergk
packaging: leverage os.path.relpath() in setup.py...
r44539 libdir = os.path.relpath(self.install_lib, self.install_dir)
Dan Villiom Podlaski Christiansen
setup/hg: always load Mercurial from where it was installed....
r12661
for outfile in self.outfiles:
timeless
setup: switch to with open as...
r28418 with open(outfile, 'rb') as fp:
data = fp.read()
Dan Villiom Podlaski Christiansen
setup/hg: always load Mercurial from where it was installed....
r12661
# skip binary files
Gregory Szorc
setup.py: use bytes literals...
r27348 if b'\0' in data:
Dan Villiom Podlaski Christiansen
setup/hg: always load Mercurial from where it was installed....
r12661 continue
Gregory Szorc
setup.py: don't rewrite @LIBDIR@ when creating wheels...
r27269 # During local installs, the shebang will be rewritten to the final
# install path. During wheel packaging, the shebang has a special
# value.
if data.startswith(b'#!python'):
Augie Fackler
formatting: blacken the codebase...
r43346 log.info(
'not rewriting @LIBDIR@ in %s because install path '
'not known' % outfile
)
Gregory Szorc
setup.py: don't rewrite @LIBDIR@ when creating wheels...
r27269 continue
Augie Fackler
setup: inline encoding constant that is only used once...
r49683 data = data.replace(b'@LIBDIR@', libdir.encode('unicode_escape'))
timeless
setup: switch to with open as...
r28418 with open(outfile, 'wb') as fp:
fp.write(data)
Dan Villiom Podlaski Christiansen
setup/hg: always load Mercurial from where it was installed....
r12661
Augie Fackler
formatting: blacken the codebase...
r43346
Matthew Martin
completion: install completers to conventional locations...
r49845 class hginstallcompletion(Command):
description = 'Install shell completion'
def initialize_options(self):
self.install_dir = None
Raphaël Gomès
setup: fix incomplete implementation of Command...
r49914 self.outputs = []
Matthew Martin
completion: install completers to conventional locations...
r49845
def finalize_options(self):
self.set_undefined_options(
'install_data', ('install_dir', 'install_dir')
)
Raphaël Gomès
setup: fix incomplete implementation of Command...
r49914 def get_outputs(self):
return self.outputs
Matthew Martin
completion: install completers to conventional locations...
r49845 def run(self):
for src, dir_path, dest in (
(
'bash_completion',
('share', 'bash-completion', 'completions'),
'hg',
),
('zsh_completion', ('share', 'zsh', 'site-functions'), '_hg'),
):
dir = os.path.join(self.install_dir, *dir_path)
self.mkpath(dir)
Raphaël Gomès
setup: fix incomplete implementation of Command...
r49914
dest = os.path.join(dir, dest)
self.outputs.append(dest)
self.copy_file(os.path.join('contrib', src), dest)
Matthew Martin
completion: install completers to conventional locations...
r49845
Gregory Szorc
setup: properly package distutils in py2exe virtualenv builds...
r42360 # virtualenv installs custom distutils/__init__.py and
# distutils/distutils.cfg files which essentially proxy back to the
# "real" distutils in the main Python install. The presence of this
# directory causes py2exe to pick up the "hacked" distutils package
# from the virtualenv and "import distutils" will fail from the py2exe
# build because the "real" distutils files can't be located.
#
# We work around this by monkeypatching the py2exe code finding Python
# modules to replace the found virtualenv distutils modules with the
# original versions via filesystem scanning. This is a bit hacky. But
# it allows us to use virtualenvs for py2exe packaging, which is more
# deterministic and reproducible.
#
# It's worth noting that the common StackOverflow suggestions for this
# problem involve copying the original distutils files into the
# virtualenv or into the staging directory after setup() is invoked.
# The former is very brittle and can easily break setup(). Our hacking
# of the found modules routine has a similar result as copying the files
# manually. But it makes fewer assumptions about how py2exe works and
# is less brittle.
# This only catches virtualenvs made with virtualenv (as opposed to
# venv, which is likely what Python 3 uses).
py2exehacked = py2exeloaded and getattr(sys, 'real_prefix', None) is not None
if py2exehacked:
from distutils.command.py2exe import py2exe as buildpy2exe
from py2exe.mf import Module as py2exemodule
class hgbuildpy2exe(buildpy2exe):
def find_needed_modules(self, mf, files, modules):
res = buildpy2exe.find_needed_modules(self, mf, files, modules)
# Replace virtualenv's distutils modules with the real ones.
Gregory Szorc
setup: remove set and dict comprehensions...
r42365 modules = {}
for k, v in res.modules.items():
if k != 'distutils' and not k.startswith('distutils.'):
modules[k] = v
res.modules = modules
Gregory Szorc
setup: properly package distutils in py2exe virtualenv builds...
r42360
import opcode
Augie Fackler
formatting: blacken the codebase...
r43346
distutilsreal = os.path.join(
os.path.dirname(opcode.__file__), 'distutils'
)
Gregory Szorc
setup: properly package distutils in py2exe virtualenv builds...
r42360
for root, dirs, files in os.walk(distutilsreal):
for f in sorted(files):
if not f.endswith('.py'):
continue
full = os.path.join(root, f)
parents = ['distutils']
if root != distutilsreal:
rel = os.path.relpath(root, distutilsreal)
parents.extend(p for p in rel.split(os.sep))
modname = '%s.%s' % ('.'.join(parents), f[:-3])
if modname.startswith('distutils.tests.'):
continue
if modname.endswith('.__init__'):
Augie Fackler
formatting: blacken the codebase...
r43346 modname = modname[: -len('.__init__')]
Gregory Szorc
setup: properly package distutils in py2exe virtualenv builds...
r42360 path = os.path.dirname(full)
else:
path = None
Augie Fackler
formatting: blacken the codebase...
r43346 res.modules[modname] = py2exemodule(
modname, full, path=path
)
Gregory Szorc
setup: properly package distutils in py2exe virtualenv builds...
r42360
if 'distutils' not in res.modules:
raise SystemExit('could not find distutils modules')
return res
Augie Fackler
formatting: blacken the codebase...
r43346
cmdclass = {
'build': hgbuild,
'build_doc': hgbuilddoc,
'build_mo': hgbuildmo,
'build_ext': hgbuildext,
'build_py': hgbuildpy,
'build_scripts': hgbuildscripts,
'build_hgextindex': buildhgextindex,
'install': hginstall,
Matthew Martin
completion: install completers to conventional locations...
r49845 'install_completion': hginstallcompletion,
Augie Fackler
formatting: blacken the codebase...
r43346 'install_lib': hginstalllib,
'install_scripts': hginstallscripts,
'build_hgexe': buildhgexe,
}
Thomas Arendsen Hein
Applied coding style to setup.py
r3238
Gregory Szorc
setup: properly package distutils in py2exe virtualenv builds...
r42360 if py2exehacked:
cmdclass['py2exe'] = hgbuildpy2exe
Augie Fackler
formatting: blacken the codebase...
r43346 packages = [
'mercurial',
'mercurial.cext',
'mercurial.cffi',
Matt Harbison
setup: include `defaultrc` in the package list...
r44478 'mercurial.defaultrc',
Simon Sapin
dirstate-v2: Introduce a docket file...
r48474 'mercurial.dirstateutils',
Matt Harbison
help: create packages for the help text...
r44031 'mercurial.helptext',
'mercurial.helptext.internals',
Augie Fackler
formatting: blacken the codebase...
r43346 'mercurial.hgweb',
'mercurial.interfaces',
'mercurial.pure',
pacien
stabletailgraph: implement stable-tail sort...
r51288 'mercurial.stabletailgraph',
Martin von Zweigbergk
packaging: mark mercurial.templates and subdirs as packages...
r45864 'mercurial.templates',
Augie Fackler
formatting: blacken the codebase...
r43346 'mercurial.thirdparty',
'mercurial.thirdparty.attr',
'mercurial.thirdparty.zope',
'mercurial.thirdparty.zope.interface',
upgrade: split actual upgrade code away from the main module...
r46661 'mercurial.upgrade_utils',
Augie Fackler
formatting: blacken the codebase...
r43346 'mercurial.utils',
'mercurial.revlogutils',
'mercurial.testing',
'hgext',
'hgext.convert',
'hgext.fsmonitor',
'hgext.fastannotate',
'hgext.fsmonitor.pywatchman',
Augie Fackler
git: skeleton of a new extension to _directly_ operate on git repos...
r44961 'hgext.git',
Augie Fackler
formatting: blacken the codebase...
r43346 'hgext.highlight',
Joerg Sonnenberger
hgext: start building a library for simple hooks...
r44897 'hgext.hooklib',
Augie Fackler
formatting: blacken the codebase...
r43346 'hgext.infinitepush',
'hgext.largefiles',
'hgext.lfs',
'hgext.narrow',
'hgext.remotefilelog',
'hgext.zeroconf',
'hgext3rd',
'hgdemandimport',
]
Martin von Zweigbergk
packaging: mark mercurial.templates and subdirs as packages...
r45864
for name in os.listdir(os.path.join('mercurial', 'templates')):
if name != '__pycache__' and os.path.isdir(
os.path.join('mercurial', 'templates', name)
):
packages.append('mercurial.templates.%s' % name)
Augie Fackler
py2exe: add workaround to allow bundling of hgext3rd.* extensions...
r42221 if 'HG_PY2EXE_EXTRA_INSTALL_PACKAGES' in os.environ:
# py2exe can't cope with namespace packages very well, so we have to
# install any hgext3rd.* extensions that we want in the final py2exe
# image here. This is gross, but you gotta do what you gotta do.
packages.extend(os.environ['HG_PY2EXE_EXTRA_INSTALL_PACKAGES'].split(' '))
Augie Fackler
formatting: blacken the codebase...
r43346 common_depends = [
'mercurial/bitmanipulation.h',
'mercurial/compat.h',
'mercurial/cext/util.h',
]
Yuya Nishihara
policy: add "cext" package which will host CPython extension modules...
r32206 common_include_dirs = ['mercurial']
Wei, Elson
setup: check if mercurial/util.h has been modified...
r19724
Matt Harbison
setup: build C extensions with -Werror=declaration-after-statement...
r45079 common_cflags = []
Augie Fackler
setup: relax -Werror for declaration-after-statement on Python 3.9...
r45096 # MSVC 2008 still needs declarations at the top of the scope, but Python 3.9
# makes declarations not at the top of a scope in the headers.
if os.name != 'nt' and sys.version_info[1] < 9:
Matt Harbison
setup: build C extensions with -Werror=declaration-after-statement...
r45079 common_cflags = ['-Werror=declaration-after-statement']
Jun Wu
setup: test setproctitle before building osutil...
r30408 osutil_cflags = []
Adrian Buehlmann
setup: move osutil_ldflags logic to before extmodules definition
r25073 osutil_ldflags = []
Jun Wu
setup: detect statfs...
r31561 # platform specific macros
Jun Wu
statfs: change Linux feature detection...
r31622 for plat, func in [('bsd', 'setproctitle')]:
Jun Wu
setup: detect statfs...
r31561 if re.search(plat, sys.platform) and hasfunction(new_compiler(), func):
Jun Wu
setup: test setproctitle before building osutil...
r30408 osutil_cflags.append('-DHAVE_%s' % func.upper())
Jun Wu
setup: use a more strict way to test BSD or OSX's statfs...
r31596 for plat, macro, code in [
Augie Fackler
formatting: blacken the codebase...
r43346 (
'bsd|darwin',
'BSD_STATFS',
'''
Jun Wu
setup: use a more strict way to test BSD or OSX's statfs...
r31596 #include <sys/param.h>
#include <sys/mount.h>
int main() { struct statfs s; return sizeof(s.f_fstypename); }
Augie Fackler
formatting: blacken the codebase...
r43346 ''',
),
(
'linux',
'LINUX_STATFS',
'''
Jun Wu
statfs: change Linux feature detection...
r31622 #include <linux/magic.h>
#include <sys/vfs.h>
int main() { struct statfs s; return sizeof(s.f_type); }
Augie Fackler
formatting: blacken the codebase...
r43346 ''',
),
Jun Wu
setup: use a more strict way to test BSD or OSX's statfs...
r31596 ]:
if re.search(plat, sys.platform) and cancompile(new_compiler(), code):
osutil_cflags.append('-DHAVE_%s' % macro)
Adrian Buehlmann
setup: move osutil_ldflags logic to before extmodules definition
r25073 if sys.platform == 'darwin':
osutil_ldflags += ['-framework', 'ApplicationServices']
Alexander Pyhalov
setup: link osutil.so to libsocket on Solaris/illumos (issue6299)
r45199 if sys.platform == 'sunos5':
osutil_ldflags += ['-lsocket']
Jun Wu
bdiff: add a xdiffblocks method...
r36693 xdiff_srcs = [
'mercurial/thirdparty/xdiff/xdiffi.c',
'mercurial/thirdparty/xdiff/xprepare.c',
'mercurial/thirdparty/xdiff/xutils.c',
]
xdiff_headers = [
'mercurial/thirdparty/xdiff/xdiff.h',
'mercurial/thirdparty/xdiff/xdiffi.h',
'mercurial/thirdparty/xdiff/xinclude.h',
'mercurial/thirdparty/xdiff/xmacros.h',
'mercurial/thirdparty/xdiff/xprepare.h',
'mercurial/thirdparty/xdiff/xtypes.h',
'mercurial/thirdparty/xdiff/xutils.h',
]
Augie Fackler
formatting: blacken the codebase...
r43346
Georges Racinet
rust: better treatment of cargo/rustc errors...
r41002 class RustCompilationError(CCompilerError):
"""Exception class for Rust compilation errors."""
Augie Fackler
formatting: blacken the codebase...
r43346
Georges Racinet
rust: exposing in parsers module...
r40309 class RustExtension(Extension):
Augie Fackler
formating: upgrade to black 20.8b1...
r46554 """Base classes for concrete Rust Extension classes."""
Georges Racinet
rust: exposing in parsers module...
r40309
rusttargetdir = os.path.join('rust', 'target', 'release')
Simon Sapin
rust: Make the hg-cpython crate default to Python 3...
r48996 def __init__(self, mpath, sources, rustlibname, subcrate, **kw):
Georges Racinet
rust: exposing in parsers module...
r40309 Extension.__init__(self, mpath, sources, **kw)
srcdir = self.rustsrcdir = os.path.join('rust', subcrate)
# adding Rust source and control files to depends so that the extension
# gets rebuilt if they've changed
self.depends.append(os.path.join(srcdir, 'Cargo.toml'))
cargo_lock = os.path.join(srcdir, 'Cargo.lock')
if os.path.exists(cargo_lock):
self.depends.append(cargo_lock)
for dirpath, subdir, fnames in os.walk(os.path.join(srcdir, 'src')):
Augie Fackler
formatting: blacken the codebase...
r43346 self.depends.extend(
os.path.join(dirpath, fname)
for fname in fnames
if os.path.splitext(fname)[1] == '.rs'
)
Georges Racinet
rust: exposing in parsers module...
r40309
Georges Racinet
rust-cpython: management of shared libray suffix...
r42657 @staticmethod
def rustdylibsuffix():
"""Return the suffix for shared libraries produced by rustc.
See also: https://doc.rust-lang.org/reference/linkage.html
"""
if sys.platform == 'darwin':
return '.dylib'
elif os.name == 'nt':
return '.dll'
else:
return '.so'
Georges Racinet
rust: exposing in parsers module...
r40309 def rustbuild(self):
env = os.environ.copy()
if 'HGTEST_RESTOREENV' in env:
# Mercurial tests change HOME to a temporary directory,
# but, if installed with rustup, the Rust toolchain needs
# HOME to be correct (otherwise the 'no default toolchain'
# error message is issued and the build fails).
# This happens currently with test-hghave.t, which does
# invoke this build.
# Unix only fix (os.path.expanduser not really reliable if
# HOME is shadowed like this)
import pwd
Augie Fackler
formatting: blacken the codebase...
r43346
Georges Racinet
rust: exposing in parsers module...
r40309 env['HOME'] = pwd.getpwuid(os.getuid()).pw_dir
Valentin Gatien-Baron
setup: stop asking cargo to spam...
r45365 cargocmd = ['cargo', 'rustc', '--release']
Raphaël Gomès
rust-re2: add wrapper for calling Re2 from Rust...
r44786
rust_features = env.get("HG_RUST_FEATURES")
if rust_features:
Simon Sapin
rust: enable Python 3 support unconditionally...
r49697 cargocmd.extend(('--features', rust_features))
Raphaël Gomès
rust-re2: add wrapper for calling Re2 from Rust...
r44786
Georges Racinet
rust: switched to 'cargo rustc' in setup.py...
r42659 cargocmd.append('--')
Georges Racinet
rust-cpython: fix build for MacOSX...
r42658 if sys.platform == 'darwin':
Augie Fackler
formatting: blacken the codebase...
r43346 cargocmd.extend(
("-C", "link-arg=-undefined", "-C", "link-arg=dynamic_lookup")
)
Georges Racinet
rust: better treatment of cargo/rustc errors...
r41002 try:
subprocess.check_call(cargocmd, env=env, cwd=self.rustsrcdir)
Manuel Jacob
py3: catch specific OSError subclasses instead of checking errno...
r50205 except FileNotFoundError:
raise RustCompilationError("Cargo not found")
except PermissionError:
raise RustCompilationError(
"Cargo found, but permission to execute it is denied"
)
Georges Racinet
rust: better treatment of cargo/rustc errors...
r41002 except subprocess.CalledProcessError:
raise RustCompilationError(
"Cargo failed. Working directory: %r, "
Philippe Pepiot
setup: fix a possible NameError on rust build...
r42243 "command: %r, environment: %r"
Augie Fackler
formatting: blacken the codebase...
r43346 % (self.rustsrcdir, cargocmd, env)
)
Georges Racinet
rust: exposing in parsers module...
r40309
Georges Racinet
rust-cpython: build via HGWITHRUSTEXT=cpython...
r41003 class RustStandaloneExtension(RustExtension):
def __init__(self, pydottedname, rustcrate, dylibname, **kw):
Augie Fackler
formatting: blacken the codebase...
r43346 RustExtension.__init__(
self, pydottedname, [], dylibname, rustcrate, **kw
)
Georges Racinet
rust-cpython: build via HGWITHRUSTEXT=cpython...
r41003 self.dylibname = dylibname
def build(self, target_dir):
self.rustbuild()
target = [target_dir]
target.extend(self.name.split('.'))
Georges Racinet
rust-cpython: management of shared libray suffix...
r42657 target[-1] += DYLIB_SUFFIX
Cédric Krier
setup: Ensure target directory exists with building rust extension...
r50851 target = os.path.join(*target)
os.makedirs(os.path.dirname(target), exist_ok=True)
Augie Fackler
formatting: blacken the codebase...
r43346 shutil.copy2(
os.path.join(
self.rusttargetdir, self.dylibname + self.rustdylibsuffix()
),
Cédric Krier
setup: Ensure target directory exists with building rust extension...
r50851 target,
Augie Fackler
formatting: blacken the codebase...
r43346 )
Georges Racinet
rust-cpython: build via HGWITHRUSTEXT=cpython...
r41003
Martin Geisler
setup: cleanup coding style
r10000 extmodules = [
Augie Fackler
formatting: blacken the codebase...
r43346 Extension(
'mercurial.cext.base85',
['mercurial/cext/base85.c'],
include_dirs=common_include_dirs,
Matt Harbison
setup: build C extensions with -Werror=declaration-after-statement...
r45079 extra_compile_args=common_cflags,
Augie Fackler
formatting: blacken the codebase...
r43346 depends=common_depends,
),
Extension(
'mercurial.cext.bdiff',
['mercurial/bdiff.c', 'mercurial/cext/bdiff.c'] + xdiff_srcs,
include_dirs=common_include_dirs,
Matt Harbison
setup: build C extensions with -Werror=declaration-after-statement...
r45079 extra_compile_args=common_cflags,
Augie Fackler
formatting: blacken the codebase...
r43346 depends=common_depends + ['mercurial/bdiff.h'] + xdiff_headers,
),
Extension(
'mercurial.cext.mpatch',
['mercurial/mpatch.c', 'mercurial/cext/mpatch.c'],
include_dirs=common_include_dirs,
Matt Harbison
setup: build C extensions with -Werror=declaration-after-statement...
r45079 extra_compile_args=common_cflags,
Augie Fackler
formatting: blacken the codebase...
r43346 depends=common_depends,
),
setup-rust: remove the legacy 'direct-ffi' variant...
r44956 Extension(
Augie Fackler
formatting: blacken the codebase...
r43346 'mercurial.cext.parsers',
[
'mercurial/cext/charencode.c',
'mercurial/cext/dirs.c',
'mercurial/cext/manifest.c',
'mercurial/cext/parsers.c',
'mercurial/cext/pathencode.c',
'mercurial/cext/revlog.c',
],
Georges Racinet
rust-cpython: build via HGWITHRUSTEXT=cpython...
r41003 include_dirs=common_include_dirs,
Matt Harbison
setup: build C extensions with -Werror=declaration-after-statement...
r45079 extra_compile_args=common_cflags,
Augie Fackler
formatting: blacken the codebase...
r43346 depends=common_depends
Augie Fackler
formating: upgrade to black 20.8b1...
r46554 + [
'mercurial/cext/charencode.h',
'mercurial/cext/revlog.h',
],
Augie Fackler
formatting: blacken the codebase...
r43346 ),
Gregory Szorc
setup: register zope.interface packages and compile C extension...
r37197 Extension(
Augie Fackler
formatting: blacken the codebase...
r43346 'mercurial.cext.osutil',
['mercurial/cext/osutil.c'],
include_dirs=common_include_dirs,
Matt Harbison
setup: build C extensions with -Werror=declaration-after-statement...
r45079 extra_compile_args=common_cflags + osutil_cflags,
Augie Fackler
formatting: blacken the codebase...
r43346 extra_link_args=osutil_ldflags,
depends=common_depends,
),
Extension(
'mercurial.thirdparty.zope.interface._zope_interface_coptimizations',
[
'mercurial/thirdparty/zope/interface/_zope_interface_coptimizations.c',
],
Matt Harbison
setup: build C extensions with -Werror=declaration-after-statement...
r45079 extra_compile_args=common_cflags,
Augie Fackler
formatting: blacken the codebase...
r43346 ),
Extension(
Augie Fackler
sha1dc: initial implementation of Python extension...
r44510 'mercurial.thirdparty.sha1dc',
[
'mercurial/thirdparty/sha1dc/cext.c',
'mercurial/thirdparty/sha1dc/lib/sha1.c',
'mercurial/thirdparty/sha1dc/lib/ubc_check.c',
],
Matt Harbison
setup: build C extensions with -Werror=declaration-after-statement...
r45079 extra_compile_args=common_cflags,
Augie Fackler
sha1dc: initial implementation of Python extension...
r44510 ),
Extension(
Matt Harbison
setup: build C extensions with -Werror=declaration-after-statement...
r45079 'hgext.fsmonitor.pywatchman.bser',
['hgext/fsmonitor/pywatchman/bser.c'],
extra_compile_args=common_cflags,
Augie Fackler
formatting: blacken the codebase...
r43346 ),
RustStandaloneExtension(
Simon Sapin
rust: Make the hg-cpython crate default to Python 3...
r48996 'mercurial.rustext',
'hg-cpython',
'librusthg',
Augie Fackler
formatting: blacken the codebase...
r43346 ),
]
Bryan O'Sullivan
Add osutil module, containing a listdir function....
r5396
Georges Racinet
rust-cpython: build via HGWITHRUSTEXT=cpython...
r41003
Gregory Szorc
setup: compile zstd C extension...
r30436 sys.path.insert(0, 'contrib/python-zstandard')
import setup_zstd
Augie Fackler
formatting: blacken the codebase...
r43346
Matt Harbison
setup: build C extensions with -Werror=declaration-after-statement...
r45079 zstd = setup_zstd.get_c_extension(
name='mercurial.zstd', root=os.path.abspath(os.path.dirname(__file__))
Augie Fackler
formatting: blacken the codebase...
r43346 )
Matt Harbison
setup: build C extensions with -Werror=declaration-after-statement...
r45079 zstd.extra_compile_args += common_cflags
extmodules.append(zstd)
Gregory Szorc
setup: compile zstd C extension...
r30436
Ludovic Chabant
setup: don't fail when Python doesn't have the cygwinccompiler package...
r23677 try:
from distutils import cygwinccompiler
# the -mno-cygwin option has been deprecated for years
Mike Hommey
setup: fix installing in a mingw environment...
r33780 mingw32compilerclass = cygwinccompiler.Mingw32CCompiler
Bryan O'Sullivan
setup: disable -mno-cygwin if building under mingw32...
r17121
Ludovic Chabant
setup: don't fail when Python doesn't have the cygwinccompiler package...
r23677 class HackedMingw32CCompiler(cygwinccompiler.Mingw32CCompiler):
def __init__(self, *args, **kwargs):
Mike Hommey
setup: fix installing in a mingw environment...
r33780 mingw32compilerclass.__init__(self, *args, **kwargs)
Ludovic Chabant
setup: don't fail when Python doesn't have the cygwinccompiler package...
r23677 for i in 'compiler compiler_so linker_exe linker_so'.split():
try:
getattr(self, i).remove('-mno-cygwin')
except ValueError:
pass
Bryan O'Sullivan
setup: disable -mno-cygwin if building under mingw32...
r17121
Ludovic Chabant
setup: don't fail when Python doesn't have the cygwinccompiler package...
r23677 cygwinccompiler.Mingw32CCompiler = HackedMingw32CCompiler
except ImportError:
# the cygwinccompiler package is not available on some Python
# distributions like the ones from the optware project for Synology
# DiskStation boxes
Gregory Szorc
py3: use class X: instead of class X(object):...
r49801 class HackedMingw32CCompiler:
Ludovic Chabant
setup: don't fail when Python doesn't have the cygwinccompiler package...
r23677 pass
Bryan O'Sullivan
setup: disable -mno-cygwin if building under mingw32...
r17121
Augie Fackler
formatting: blacken the codebase...
r43346
Matt Harbison
setup: avoid linker warnings on Windows about multiple export specifications...
r32782 if os.name == 'nt':
# Allow compiler/linker flags to be added to Visual Studio builds. Passing
# extra_link_args to distutils.extensions.Extension() doesn't have any
# effect.
from distutils import msvccompiler
Mike Hommey
setup: fix installing in a mingw environment...
r33780 msvccompilerclass = msvccompiler.MSVCCompiler
Matt Harbison
setup: avoid linker warnings on Windows about multiple export specifications...
r32782
class HackedMSVCCompiler(msvccompiler.MSVCCompiler):
def initialize(self):
Mike Hommey
setup: fix installing in a mingw environment...
r33780 msvccompilerclass.initialize(self)
Matt Harbison
setup: avoid linker warnings on Windows about multiple export specifications...
r32782 # "warning LNK4197: export 'func' specified multiple times"
self.ldflags_shared.append('/ignore:4197')
self.ldflags_shared_debug.append('/ignore:4197')
msvccompiler.MSVCCompiler = HackedMSVCCompiler
Augie Fackler
formatting: blacken the codebase...
r43346 packagedata = {
Augie Fackler
formating: upgrade to black 20.8b1...
r46554 'mercurial': [
'locale/*/LC_MESSAGES/hg.mo',
'dummycert.pem',
],
'mercurial.defaultrc': [
'*.rc',
],
'mercurial.helptext': [
'*.txt',
],
'mercurial.helptext.internals': [
'*.txt',
],
Matt Harbison
setup: include vendored 3rd party type stubs...
r50550 'mercurial.thirdparty.attr': [
'*.pyi',
'py.typed',
],
Augie Fackler
formatting: blacken the codebase...
r43346 }
Dan Villiom Podlaski Christiansen
setup: install translation files as package data...
r9999
def ordinarypath(p):
return p and p[0] != '.' and p[-1] != '~'
Augie Fackler
formatting: blacken the codebase...
r43346
Matt Mackall
many, many trivial check-code fixups
r10282 for root in ('templates',):
Dan Villiom Podlaski Christiansen
setup: install translation files as package data...
r9999 for curdir, dirs, files in os.walk(os.path.join('mercurial', root)):
Martin von Zweigbergk
packaging: include templates with their package as key in package_data...
r45865 packagename = curdir.replace(os.sep, '.')
packagedata[packagename] = list(filter(ordinarypath, files))
Dan Villiom Podlaski Christiansen
setup: install translation files as package data...
r9999
Martin Geisler
i18n: let Makefile generate i18n/hg.pot...
r7648 datafiles = []
Gregory Szorc
setup: convert setupversion to unicode...
r31316
# distutils expects version to be str/unicode. Converting it to
# unicode on Python 2 still works because it won't contain any
# non-ascii bytes and will be implicitly converted back to bytes
# when operated on.
Joerg Sonnenberger
build: fake PEP440 versions...
r47262 assert isinstance(version, str)
setupversion = version
Gregory Szorc
setup: convert setupversion to unicode...
r31316
Adrian Buehlmann
setup.py: fixing version info for Windows hg.exe (py2exe)...
r10400 extra = {}
Gregory Szorc
setup: include additional packages in py2exe distribution...
r42017 py2exepackages = [
'hgdemandimport',
Gregory Szorc
setup: include hgext3rd package in py2exe builds...
r42084 'hgext3rd',
Gregory Szorc
setup: include additional packages in py2exe distribution...
r42017 'hgext',
'email',
# implicitly imported per module policy
# (cffi wouldn't be used as a frozen exe)
'mercurial.cext',
#'mercurial.cffi',
'mercurial.pure',
]
Matt Harbison
packaging: allow specifying modules to include with py2exe...
r47109 py2exe_includes = []
Gregory Szorc
setup: configure py2exe config via environment variables...
r42082 py2exeexcludes = []
Gregory Szorc
setup: exclude crypt32.dll in py2exe builds...
r42120 py2exedllexcludes = ['crypt32.dll']
Gregory Szorc
setup: configure py2exe config via environment variables...
r42082
Yuya Nishihara
setup: silence warning of unknown option python_requires on distutils...
r33601 if issetuptools:
extra['python_requires'] = supportedpy
Gregory Szorc
setup: include additional packages in py2exe distribution...
r42017
Adrian Buehlmann
setup.py: fixing version info for Windows hg.exe (py2exe)...
r10400 if py2exeloaded:
extra['console'] = [
Augie Fackler
formatting: blacken the codebase...
r43346 {
'script': 'hg',
Matt Harbison
copyright: update to 2023
r50725 'copyright': 'Copyright (C) 2005-2023 Olivia Mackall and others',
Augie Fackler
formatting: blacken the codebase...
r43346 'product_version': version,
}
]
Gregory Szorc
setup: properly install build_hgextindex for py2exe builds...
r42083 # Sub command of 'build' because 'py2exe' does not handle sub_commands.
# Need to override hgbuild because it has a private copy of
# build.sub_commands.
hgbuild.sub_commands.insert(0, ('build_hgextindex', None))
Steve Borho
wix: move library.zip and all *.pyd into a lib/ folder...
r25409 # put dlls in sub directory so that they won't pollute PATH
extra['zipfile'] = 'lib/library.zip'
Adrian Buehlmann
setup.py: fixing version info for Windows hg.exe (py2exe)...
r10400
Gregory Szorc
setup: configure py2exe config via environment variables...
r42082 # We allow some configuration to be supplemented via environment
# variables. This is better than setup.cfg files because it allows
# supplementing configs instead of replacing them.
extrapackages = os.environ.get('HG_PY2EXE_EXTRA_PACKAGES')
if extrapackages:
py2exepackages.extend(extrapackages.split(' '))
Gregory Szorc
setup: include additional packages in py2exe distribution...
r42017
Matt Harbison
packaging: allow specifying modules to include with py2exe...
r47109 extra_includes = os.environ.get('HG_PY2EXE_EXTRA_INCLUDES')
if extra_includes:
py2exe_includes.extend(extra_includes.split(' '))
Gregory Szorc
setup: configure py2exe config via environment variables...
r42082 excludes = os.environ.get('HG_PY2EXE_EXTRA_EXCLUDES')
if excludes:
py2exeexcludes.extend(excludes.split(' '))
Gregory Szorc
setup: include additional packages in py2exe distribution...
r42017
Gregory Szorc
setup: configure py2exe config via environment variables...
r42082 dllexcludes = os.environ.get('HG_PY2EXE_EXTRA_DLL_EXCLUDES')
if dllexcludes:
py2exedllexcludes.extend(dllexcludes.split(' '))
Gregory Szorc
setup: include additional packages in py2exe distribution...
r42017
Martin von Zweigbergk
extensions: make `hg help extensions` list disabled extensions with PyOxidizer...
r45692 if os.environ.get('PYOXIDIZER'):
hgbuild.sub_commands.insert(0, ('build_hgextindex', None))
Adrian Buehlmann
setup.py: fixing version info for Windows hg.exe (py2exe)...
r10400 if os.name == 'nt':
# Windows binary file versions for exe/dll files must have the
# form W.X.Y.Z, where W,X,Y,Z are numbers in the range 0..65535
Matt Harbison
py3: stringify setupversion on Windows...
r40419 setupversion = setupversion.split(r'+', 1)[0]
Martin Geisler
i18n: let Makefile generate i18n/hg.pot...
r7648
Augie Fackler
formatting: blacken the codebase...
r43346 setup(
name='mercurial',
version=setupversion,
Raphaël Gomès
contributor: change mentions of mpm to olivia...
r47575 author='Olivia Mackall and many others',
Augie Fackler
formatting: blacken the codebase...
r43346 author_email='mercurial@mercurial-scm.org',
url='https://mercurial-scm.org/',
download_url='https://mercurial-scm.org/release/',
description=(
'Fast scalable distributed SCM (revision control, version '
'control) system'
),
long_description=(
'Mercurial is a distributed SCM tool written in Python.'
' It is used by a number of large projects that require'
' fast, reliable distributed revision control, such as '
'Mozilla.'
),
license='GNU GPLv2 or any later version',
classifiers=[
'Development Status :: 6 - Mature',
'Environment :: Console',
'Intended Audience :: Developers',
'Intended Audience :: System Administrators',
'License :: OSI Approved :: GNU General Public License (GPL)',
'Natural Language :: Danish',
'Natural Language :: English',
'Natural Language :: German',
'Natural Language :: Italian',
'Natural Language :: Japanese',
'Natural Language :: Portuguese (Brazilian)',
'Operating System :: Microsoft :: Windows',
'Operating System :: OS Independent',
'Operating System :: POSIX',
'Programming Language :: C',
'Programming Language :: Python',
'Topic :: Software Development :: Version Control',
],
scripts=scripts,
packages=packages,
ext_modules=extmodules,
data_files=datafiles,
package_data=packagedata,
cmdclass=cmdclass,
distclass=hgdist,
options={
'py2exe': {
'bundle_files': 3,
'dll_excludes': py2exedllexcludes,
Matt Harbison
packaging: allow specifying modules to include with py2exe...
r47109 'includes': py2exe_includes,
Augie Fackler
formatting: blacken the codebase...
r43346 'excludes': py2exeexcludes,
'packages': py2exepackages,
},
'bdist_mpkg': {
'zipdist': False,
'license': 'COPYING',
'readme': 'contrib/packaging/macosx/Readme.html',
'welcome': 'contrib/packaging/macosx/Welcome.html',
},
},
**extra
)