setup.py
1839 lines
| 59.1 KiB
| text/x-python
|
PythonLexer
mpm@selenic.com
|
r575 | # | ||
# This is the mercurial setup script. | ||||
mpm@selenic.com
|
r0 | # | ||
Christian Ebert
|
r4816 | # 'python setup.py install', or | ||
# 'python setup.py --help' for more options | ||||
Augie Fackler
|
r33592 | import os | ||
Gregory Szorc
|
r49705 | # Mercurial can't work on 3.6.0 or 3.6.1 due to a bug in % formatting | ||
# in bytestrings. | ||||
Ian Moody
|
r43666 | supportedpy = ','.join( | ||
[ | ||||
Gregory Szorc
|
r49705 | '>=3.6.2', | ||
Ian Moody
|
r43666 | ] | ||
) | ||||
Augie Fackler
|
r33588 | |||
Zachary Gramana
|
r14295 | import sys, platform | ||
Georges Racinet
|
r42657 | import sysconfig | ||
Augie Fackler
|
r43346 | |||
Augie Fackler
|
r49681 | def sysstr(s): | ||
return s.decode('latin-1') | ||||
Renato Cunha
|
r11532 | |||
Augie Fackler
|
r43346 | |||
r50988 | def eprint(*args, **kwargs): | |||
kwargs['file'] = sys.stderr | ||||
print(*args, **kwargs) | ||||
Manuel Jacob
|
r45410 | import ssl | ||
Manuel Jacob
|
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
|
r49682 | print(error, file=sys.stderr) | ||
Manuel Jacob
|
r45429 | sys.exit(1) | ||
Augie Fackler
|
r49685 | DYLIB_SUFFIX = sysconfig.get_config_vars()['EXT_SUFFIX'] | ||
Georges Racinet
|
r42657 | |||
Matt Mackall
|
r7558 | # Solaris Python packaging brain damage | ||
try: | ||||
import hashlib | ||||
Augie Fackler
|
r43346 | |||
Matt Mackall
|
r7558 | sha = hashlib.sha1() | ||
Brodie Rao
|
r16688 | except ImportError: | ||
Matt Mackall
|
r7558 | try: | ||
import sha | ||||
Augie Fackler
|
r43346 | |||
sha.sha # silence unused import warning | ||||
Brodie Rao
|
r16688 | except ImportError: | ||
Matt Mackall
|
r7558 | raise SystemExit( | ||
Augie Fackler
|
r43346 | "Couldn't import standard hashlib (incomplete Python install)." | ||
) | ||||
Matt Mackall
|
r7558 | |||
try: | ||||
import zlib | ||||
Augie Fackler
|
r43346 | |||
zlib.compressobj # silence unused import warning | ||||
Brodie Rao
|
r16688 | except ImportError: | ||
Matt Mackall
|
r7558 | raise SystemExit( | ||
Augie Fackler
|
r43346 | "Couldn't import standard zlib (incomplete Python install)." | ||
) | ||||
Matt Mackall
|
r7558 | |||
Zachary Gramana
|
r14295 | # The base IronPython distribution (as of 2.7.1) doesn't support bz2 | ||
isironpython = False | ||||
Dirkjan Ochtman
|
r10761 | try: | ||
Augie Fackler
|
r43346 | isironpython = ( | ||
platform.python_implementation().lower().find("ironpython") != -1 | ||||
) | ||||
Brodie Rao
|
r16688 | except AttributeError: | ||
Zachary Gramana
|
r14295 | pass | ||
if isironpython: | ||||
Simon Heimberg
|
r15492 | sys.stderr.write("warning: IronPython detected (no bz2 support)\n") | ||
Zachary Gramana
|
r14295 | else: | ||
try: | ||||
import bz2 | ||||
Augie Fackler
|
r43346 | |||
bz2.BZ2Compressor # silence unused import warning | ||||
Brodie Rao
|
r16688 | except ImportError: | ||
Zachary Gramana
|
r14295 | raise SystemExit( | ||
Augie Fackler
|
r43346 | "Couldn't import standard bz2 (incomplete Python install)." | ||
) | ||||
Dirkjan Ochtman
|
r10761 | |||
Joan Massich
|
r24192 | ispypy = "PyPy" in sys.version | ||
Gregory Szorc
|
r29020 | import ctypes | ||
Augie Fackler
|
r33592 | import stat, subprocess, time | ||
Kent Frazier
|
r21038 | import re | ||
Alexis S. L. Carvalho
|
r6251 | import shutil | ||
import tempfile | ||||
Augie Fackler
|
r43346 | |||
Gregory Szorc
|
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
|
r43346 | issetuptools = os.name == 'nt' or 'FORCE_SETUPTOOLS' in os.environ | ||
Yuya Nishihara
|
r33601 | if issetuptools: | ||
Nathan Goldbaum
|
r26600 | from setuptools import setup | ||
else: | ||||
Mads Kiilerich
|
r51626 | try: | ||
from distutils.core import setup | ||||
except ModuleNotFoundError: | ||||
from setuptools import setup | ||||
Jun Wu
|
r30408 | from distutils.ccompiler import new_compiler | ||
Nathan Goldbaum
|
r26600 | from distutils.core import Command, Extension | ||
Martin Geisler
|
r7722 | from distutils.dist import Distribution | ||
Martin Geisler
|
r7649 | from distutils.command.build import build | ||
Christian Boos
|
r11468 | from distutils.command.build_ext import build_ext | ||
Martin Geisler
|
r7722 | from distutils.command.build_py import build_py | ||
Gregory Szorc
|
r27268 | from distutils.command.build_scripts import build_scripts | ||
Matt Harbison
|
r32647 | from distutils.command.install import install | ||
Kyle Lippincott
|
r22640 | from distutils.command.install_lib import install_lib | ||
Dan Villiom Podlaski Christiansen
|
r12661 | from distutils.command.install_scripts import install_scripts | ||
Dan Villiom Podlaski Christiansen
|
r46850 | from distutils import log | ||
Martin Geisler
|
r7649 | from distutils.spawn import spawn, find_executable | ||
Ludovic Chabant
|
r23677 | from distutils import file_util | ||
Gregory Szorc
|
r27268 | from distutils.errors import ( | ||
CCompilerError, | ||||
DistutilsError, | ||||
DistutilsExecError, | ||||
) | ||||
Matt Harbison
|
r50774 | from distutils.sysconfig import get_python_inc | ||
"Paul Morelle "
|
r40485 | |||
Augie Fackler
|
r43346 | |||
Gregory Szorc
|
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
|
r43346 | |||
Paul Moore
|
r6513 | scripts = ['hg'] | ||
if os.name == 'nt': | ||||
Gregory Szorc
|
r27268 | # We remove hg.bat if we are able to build hg.exe. | ||
Paul Moore
|
r6513 | scripts.append('contrib/win32/hg.bat') | ||
Matt Mackall
|
r3893 | |||
Augie Fackler
|
r43346 | |||
Jun Wu
|
r31559 | def cancompile(cc, code): | ||
Alexis S. L. Carvalho
|
r6251 | tmpdir = tempfile.mkdtemp(prefix='hg-install-') | ||
Alexis S. L. Carvalho
|
r6373 | devnull = oldstderr = None | ||
Alexis S. L. Carvalho
|
r6251 | try: | ||
Jun Wu
|
r31559 | fname = os.path.join(tmpdir, 'testcomp.c') | ||
Matt Mackall
|
r25089 | f = open(fname, 'w') | ||
Jun Wu
|
r31559 | f.write(code) | ||
Matt Mackall
|
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
|
r6251 | return True | ||
Matt Mackall
|
r25089 | except Exception: | ||
return False | ||||
Alexis S. L. Carvalho
|
r6251 | finally: | ||
Alexis S. L. Carvalho
|
r6373 | if oldstderr is not None: | ||
os.dup2(oldstderr, sys.stderr.fileno()) | ||||
if devnull is not None: | ||||
devnull.close() | ||||
Alexis S. L. Carvalho
|
r6251 | shutil.rmtree(tmpdir) | ||
Augie Fackler
|
r43346 | |||
Jun Wu
|
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
|
r43346 | |||
Jun Wu
|
r31560 | def hasheader(cc, headername): | ||
code = '#include <%s>\nint main(void) { return 0; }\n' % headername | ||||
return cancompile(cc, code) | ||||
Augie Fackler
|
r43346 | |||
Volker.Kleinfeld@gmx.de
|
r1283 | # py2exe needs to be installed to work | ||
try: | ||||
Bryan O'Sullivan
|
r1294 | import py2exe | ||
Augie Fackler
|
r43346 | |||
Matt Harbison
|
r49964 | py2exe.patch_distutils() | ||
Adrian Buehlmann
|
r10400 | py2exeloaded = True | ||
Pascal Quantin
|
r15527 | # import py2exe's patched Distribution class | ||
from distutils.core import Distribution | ||||
Bryan O'Sullivan
|
r1284 | except ImportError: | ||
Adrian Buehlmann
|
r10400 | py2exeloaded = False | ||
Volker.Kleinfeld@gmx.de
|
r1283 | |||
Augie Fackler
|
r43346 | |||
Gregory Szorc
|
r42016 | def runcmd(cmd, env, cwd=None): | ||
Augie Fackler
|
r43346 | p = subprocess.Popen( | ||
cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, env=env, cwd=cwd | ||||
) | ||||
Matt Harbison
|
r32886 | out, err = p.communicate() | ||
Adam Simpkins
|
r33111 | return p.returncode, out, err | ||
Jon M. Dugan
|
r13636 | |||
Augie Fackler
|
r43346 | |||
Gregory Szorc
|
r49801 | class hgcommand: | ||
Adam Simpkins
|
r33114 | def __init__(self, cmd, env): | ||
self.cmd = cmd | ||||
self.env = env | ||||
Gilles Moris
|
r9615 | |||
r52053 | def __repr__(self): | |||
return f"<hgcommand cmd={self.cmd} env={self.env}>" | ||||
Adam Simpkins
|
r33113 | def run(self, args): | ||
cmd = self.cmd + args | ||||
returncode, out, err = runcmd(cmd, self.env) | ||||
Yuya Nishihara
|
r33599 | err = filterhgerr(err) | ||
r50866 | if err: | |||
Augie Fackler
|
r49682 | print("stderr from '%s':" % (' '.join(cmd)), file=sys.stderr) | ||
print(err, file=sys.stderr) | ||||
r50866 | if returncode != 0: | |||
r52452 | print( | |||
"non zero-return '%s': %d" % (' '.join(cmd), returncode), | ||||
file=sys.stderr, | ||||
) | ||||
Martin von Zweigbergk
|
r45094 | return b'' | ||
Adam Simpkins
|
r33113 | return out | ||
Jeremy Whitlock
|
r8548 | |||
Augie Fackler
|
r43346 | |||
Yuya Nishihara
|
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
|
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
|
r33599 | return b'\n'.join(b' ' + e for e in err) | ||
Augie Fackler
|
r43346 | |||
Adam Simpkins
|
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
|
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
|
r33114 | check_cmd = ['log', '-r.', '-Ttest'] | ||
Arseniy Alekseyev
|
r51823 | attempts = [] | ||
def attempt(cmd, env): | ||||
Matt Harbison
|
r41016 | try: | ||
retcode, out, err = runcmd(hgcmd + check_cmd, hgenv) | ||||
Arseniy Alekseyev
|
r51823 | res = (True, retcode, out, err) | ||
if retcode == 0 and not filterhgerr(err): | ||||
return True | ||||
except EnvironmentError as e: | ||||
res = (False, e) | ||||
attempts.append((cmd, res)) | ||||
return False | ||||
if os.name != 'nt' or not os.path.exists("hg.exe"): | ||||
if attempt(hgcmd + check_cmd, hgenv): | ||||
Matt Harbison
|
r41016 | return hgcommand(hgcmd, hgenv) | ||
Adam Simpkins
|
r33114 | |||
r52054 | # Fall back to trying the local hg installation (pure python) | |||
repo_hg = os.path.join(os.path.dirname(__file__), 'hg') | ||||
Adam Simpkins
|
r33114 | hgenv = localhgenv() | ||
r52054 | hgcmd = [sys.executable, repo_hg] | |||
if attempt(hgcmd + check_cmd, hgenv): | ||||
return hgcommand(hgcmd, hgenv) | ||||
# Fall back to trying the local hg installation (whatever we can) | ||||
hgenv = localhgenv(pure_python=False) | ||||
hgcmd = [sys.executable, repo_hg] | ||||
Arseniy Alekseyev
|
r51823 | if attempt(hgcmd + check_cmd, hgenv): | ||
Adam Simpkins
|
r33114 | return hgcommand(hgcmd, hgenv) | ||
r50988 | eprint("/!\\") | |||
eprint(r"/!\ Unable to find a working hg binary") | ||||
Arseniy Alekseyev
|
r51823 | eprint(r"/!\ Version cannot be extracted from the repository") | ||
r50988 | eprint(r"/!\ Re-run the setup once a first version is built") | |||
Arseniy Alekseyev
|
r51823 | eprint(r"/!\ Attempts:") | ||
for i, e in enumerate(attempts): | ||||
eprint(r"/!\ attempt #%d:" % (i)) | ||||
eprint(r"/!\ cmd: ", e[0]) | ||||
res = e[1] | ||||
if res[0]: | ||||
eprint(r"/!\ return code:", res[1]) | ||||
eprint("/!\\ std output:\n%s" % (res[2].decode()), end="") | ||||
eprint("/!\\ std error:\n%s" % (res[3].decode()), end="") | ||||
else: | ||||
eprint(r"/!\ exception: ", res[1]) | ||||
r50988 | return None | |||
Augie Fackler
|
r43346 | |||
Adam Simpkins
|
r33114 | |||
r52054 | def localhgenv(pure_python=True): | |||
Adam Simpkins
|
r33114 | """Get an environment dictionary to use for invoking or importing | ||
mercurial from the local repository.""" | ||||
Adam Simpkins
|
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
|
r43346 | env = { | ||
'HGRCPATH': '', | ||||
'LANGUAGE': 'C', | ||||
'PATH': '', | ||||
} # make pypi modules that use os.environ['PATH'] happy | ||||
r52054 | if pure_python: | |||
env['HGMODULEPOLICY'] = 'py' | ||||
Adam Simpkins
|
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
|
r33117 | return env | ||
Adam Simpkins
|
r33112 | |||
Augie Fackler
|
r43346 | |||
Adam Simpkins
|
r33112 | version = '' | ||
Matt Mackall
|
r15367 | |||
r50988 | ||||
def _try_get_version(): | ||||
Adam Simpkins
|
r33114 | hg = findhg() | ||
r50988 | if hg is None: | |||
return '' | ||||
hgid = None | ||||
numerictags = [] | ||||
Adam Simpkins
|
r33113 | cmd = ['log', '-r', '.', '--template', '{tags}\n'] | ||
r50988 | pieces = sysstr(hg.run(cmd)).split() | |||
numerictags = [t for t in pieces if t[0:1].isdigit()] | ||||
Yuya Nishihara
|
r35246 | hgid = sysstr(hg.run(['id', '-i'])).strip() | ||
r51063 | if hgid.count('+') == 2: | |||
hgid = hgid.replace("+", ".", 1) | ||||
Adam Simpkins
|
r33110 | if not hgid: | ||
r50988 | eprint("/!\\") | |||
eprint(r"/!\ Unable to determine hg version from local repository") | ||||
eprint(r"/!\ Failed to retrieve current revision tags") | ||||
return '' | ||||
Augie Fackler
|
r43346 | if numerictags: # tag(s) found | ||
Bryan O'Sullivan
|
r17709 | version = numerictags[-1] | ||
Augie Fackler
|
r43346 | if hgid.endswith('+'): # propagate the dirty status to the tag | ||
Gilles Moris
|
r9615 | version += '+' | ||
r50988 | else: # no tag found on the checked out revision | |||
r51063 | ltagcmd = ['log', '--rev', 'wdir()', '--template', '{latesttag}'] | |||
Yuya Nishihara
|
r35246 | ltag = sysstr(hg.run(ltagcmd)) | ||
r50865 | if not ltag: | |||
r50988 | eprint("/!\\") | |||
eprint(r"/!\ Unable to determine hg version from local repository") | ||||
eprint( | ||||
r"/!\ Failed to retrieve current revision distance to lated tag" | ||||
) | ||||
return '' | ||||
r51063 | changessincecmd = [ | |||
'log', | ||||
'-T', | ||||
'x\n', | ||||
'-r', | ||||
"only(parents(),'%s')" % ltag, | ||||
] | ||||
Adam Simpkins
|
r33113 | changessince = len(hg.run(changessincecmd).splitlines()) | ||
Joerg Sonnenberger
|
r47262 | version = '%s+hg%s.%s' % (ltag, changessince, hgid) | ||
Gilles Moris
|
r9615 | if version.endswith('+'): | ||
Joerg Sonnenberger
|
r47262 | version = version[:-1] + 'local' + time.strftime('%Y%m%d') | ||
r50988 | return version | |||
if os.path.isdir('.hg'): | ||||
version = _try_get_version() | ||||
Gilles Moris
|
r9615 | elif os.path.exists('.hg_archival.txt'): | ||
Augie Fackler
|
r43346 | kw = dict( | ||
[[t.strip() for t in l.split(':', 1)] for l in open('.hg_archival.txt')] | ||||
) | ||||
Gilles Moris
|
r9615 | if 'tag' in kw: | ||
timeless
|
r27637 | version = kw['tag'] | ||
Gilles Moris
|
r9615 | elif 'latesttag' in kw: | ||
Siddharth Agarwal
|
r23646 | if 'changessincelatesttag' in kw: | ||
Joerg Sonnenberger
|
r47262 | version = ( | ||
Martin von Zweigbergk
|
r47273 | '%(latesttag)s+hg%(changessincelatesttag)s.%(node).12s' % kw | ||
Joerg Sonnenberger
|
r47262 | ) | ||
Siddharth Agarwal
|
r23646 | else: | ||
Martin von Zweigbergk
|
r47273 | version = '%(latesttag)s+hg%(latesttagdistance)s.%(node).12s' % kw | ||
Christian Ebert
|
r8547 | else: | ||
Martin von Zweigbergk
|
r47273 | version = '0+hg' + kw.get('node', '')[:12] | ||
Joerg Sonnenberger
|
r47759 | elif os.path.exists('mercurial/__version__.py'): | ||
Raphaël Gomès
|
r47772 | with open('mercurial/__version__.py') as f: | ||
data = f.read() | ||||
Joerg Sonnenberger
|
r47759 | version = re.search('version = b"(.*)"', data).group(1) | ||
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
|
r35229 | |||
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
|
r8493 | |||
Augie Fackler
|
r43346 | |||
Simon Heimberg
|
r15460 | class hgbuild(build): | ||
# Insert hgbuildmo first so that files in mercurial/locale/ are found | ||||
# when build_py is run next. | ||||
Gregory Szorc
|
r28398 | sub_commands = [('build_mo', None)] + build.sub_commands | ||
Simon Heimberg
|
r15460 | |||
Augie Fackler
|
r43346 | |||
Matt Mackall
|
r15523 | class hgbuildmo(build): | ||
Martin Geisler
|
r7649 | |||
description = "build translations (.mo files)" | ||||
def run(self): | ||||
if not find_executable('msgfmt'): | ||||
Augie Fackler
|
r43346 | self.warn( | ||
"could not find msgfmt executable, no translations " | ||||
"will be built" | ||||
) | ||||
Martin Geisler
|
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
|
r9999 | mobuildfile = join('mercurial', mofile) | ||
cmd = ['msgfmt', '-v', '-o', mobuildfile, pofile] | ||||
Martin Geisler
|
r7720 | if sys.platform != 'sunos5': | ||
# msgfmt on Solaris does not know about -c | ||||
cmd.append('-c') | ||||
Dan Villiom Podlaski Christiansen
|
r9999 | self.mkpath(join('mercurial', modir)) | ||
self.make_file([pofile], mobuildfile, spawn, (cmd,)) | ||||
Martin Geisler
|
r7649 | |||
Dan Villiom Podlaski Christiansen
|
r12661 | |||
Simon Heimberg
|
r15458 | class hgdist(Distribution): | ||
Maciej Fijalkowski
|
r29505 | pure = False | ||
r44971 | rust = False | |||
no_rust = False | ||||
Maciej Fijalkowski
|
r29505 | cffi = ispypy | ||
Simon Heimberg
|
r15458 | |||
Augie Fackler
|
r41925 | global_options = Distribution.global_options + [ | ||
('pure', None, "use pure (slow) Python code instead of C extensions"), | ||||
Georges Racinet
|
r42653 | ('rust', None, "use Rust extensions additionally to C extensions"), | ||
r44971 | ( | |||
'no-rust', | ||||
None, | ||||
"do not use Rust extensions additionally to C extensions", | ||||
), | ||||
Augie Fackler
|
r41925 | ] | ||
Martin Geisler
|
r7722 | |||
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
|
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 | ||||
) | ||||
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
|
r49252 | msg = 'unknown HGWITHRUSTEXT value: %s' % hgrustext | ||
Augie Fackler
|
r49682 | print(msg, file=sys.stderr) | ||
r44971 | hgrustext = None | |||
self.rust = hgrustext is not None | ||||
self.no_rust = not self.rust | ||||
return ret | ||||
Simon Heimberg
|
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
|
r43346 | |||
Gregory Szorc
|
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
|
r42653 | buildextnegops['no-rust'] = 'rust' | ||
Gregory Szorc
|
r30450 | |||
Augie Fackler
|
r43346 | |||
Christian Boos
|
r11468 | class hgbuildext(build_ext): | ||
Gregory Szorc
|
r30450 | user_options = build_ext.user_options + [ | ||
('zstd', None, 'compile zstd bindings [default]'), | ||||
('no-zstd', None, 'do not compile zstd bindings'), | ||||
Augie Fackler
|
r43346 | ( | ||
'rust', | ||||
None, | ||||
'compile Rust extensions if they are in use ' | ||||
'(requires Cargo) [default]', | ||||
), | ||||
Georges Racinet
|
r42653 | ('no-rust', None, 'do not compile Rust extensions'), | ||
Gregory Szorc
|
r30450 | ] | ||
Georges Racinet
|
r42653 | boolean_options = build_ext.boolean_options + ['zstd', 'rust'] | ||
Gregory Szorc
|
r30450 | negative_opt = buildextnegops | ||
def initialize_options(self): | ||||
self.zstd = True | ||||
Georges Racinet
|
r42653 | self.rust = True | ||
Gregory Szorc
|
r30450 | return build_ext.initialize_options(self) | ||
Gregory Szorc
|
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
|
r30450 | def build_extensions(self): | ||
Augie Fackler
|
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
|
r30450 | # Filter out zstd if disabled via argument. | ||
if not self.zstd: | ||||
Augie Fackler
|
r43346 | self.extensions = [ | ||
e for e in self.extensions if e.name != 'mercurial.zstd' | ||||
] | ||||
Gregory Szorc
|
r30450 | |||
Matt Harbison
|
r49333 | # Build Rust standalone extensions if it'll be used | ||
# and its build is not explicitly disabled (for external build | ||||
Georges Racinet
|
r42653 | # as Linux distributions would do) | ||
r44956 | if self.distribution.rust and self.rust: | |||
Dan Villiom Podlaski Christiansen
|
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
|
r42653 | for rustext in ruststandalones: | ||
rustext.build('' if self.inplace else self.build_lib) | ||||
Georges Racinet
|
r41003 | |||
Gregory Szorc
|
r30450 | return build_ext.build_extensions(self) | ||
Christian Boos
|
r11468 | |||
def build_extension(self, ext): | ||||
Augie Fackler
|
r43346 | if ( | ||
self.distribution.rust | ||||
and self.rust | ||||
and isinstance(ext, RustExtension) | ||||
): | ||||
ext.rustbuild() | ||||
Christian Boos
|
r11468 | try: | ||
build_ext.build_extension(self, ext) | ||||
except CCompilerError: | ||||
Martin Geisler
|
r12501 | if not getattr(ext, 'optional', False): | ||
Christian Boos
|
r11468 | raise | ||
Augie Fackler
|
r43346 | log.warn( | ||
"Failed to build optional extension '%s' (skipping)", ext.name | ||||
) | ||||
Christian Boos
|
r11468 | |||
Gregory Szorc
|
r27268 | class hgbuildscripts(build_scripts): | ||
def run(self): | ||||
FUJIWARA Katsunori
|
r28041 | if os.name != 'nt' or self.distribution.pure: | ||
Gregory Szorc
|
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
|
r43346 | |||
Martin Geisler
|
r10000 | class hgbuildpy(build_py): | ||
Martin Geisler
|
r7722 | def finalize_options(self): | ||
build_py.finalize_options(self) | ||||
if self.distribution.pure: | ||||
self.distribution.ext_modules = [] | ||||
Maciej Fijalkowski
|
r29505 | elif self.distribution.cffi: | ||
Jun Wu
|
r30346 | from mercurial.cffi import ( | ||
Yuya Nishihara
|
r32505 | bdiffbuild, | ||
mpatchbuild, | ||||
Jun Wu
|
r30346 | ) | ||
Augie Fackler
|
r43346 | |||
exts = [ | ||||
mpatchbuild.ffi.distutils_extension(), | ||||
bdiffbuild.ffi.distutils_extension(), | ||||
] | ||||
Maciej Fijalkowski
|
r29505 | # cffi modules go here | ||
Maciej Fijalkowski
|
r29600 | if sys.platform == 'darwin': | ||
Yuya Nishihara
|
r32505 | from mercurial.cffi import osutilbuild | ||
Augie Fackler
|
r43346 | |||
Yuya Nishihara
|
r32505 | exts.append(osutilbuild.ffi.distutils_extension()) | ||
Maciej Fijalkowski
|
r29505 | self.distribution.ext_modules = exts | ||
Nicolas Dumazet
|
r12649 | else: | ||
Mads Kiilerich
|
r18905 | h = os.path.join(get_python_inc(), 'Python.h') | ||
if not os.path.exists(h): | ||||
Augie Fackler
|
r43346 | raise SystemExit( | ||
'Python headers are required to build ' | ||||
'Mercurial but weren\'t found in %s' % h | ||||
) | ||||
Martin Geisler
|
r7722 | |||
timeless
|
r28430 | def run(self): | ||
Yuya Nishihara
|
r32653 | basepath = os.path.join(self.build_lib, 'mercurial') | ||
self.mkpath(basepath) | ||||
Georges Racinet
|
r42653 | rust = self.distribution.rust | ||
timeless
|
r28430 | if self.distribution.pure: | ||
modulepolicy = 'py' | ||||
Yuya Nishihara
|
r32251 | elif self.build_lib == '.': | ||
Georges Racinet
|
r42653 | # in-place build should run without rebuilding and Rust extensions | ||
modulepolicy = 'rust+c-allow' if rust else 'allow' | ||||
timeless
|
r28430 | else: | ||
Georges Racinet
|
r42653 | modulepolicy = 'rust+c' if rust else 'c' | ||
Gregory Szorc
|
r35229 | |||
Augie Fackler
|
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
|
r27222 | |||
timeless
|
r28430 | build_py.run(self) | ||
Martin Geisler
|
r7722 | |||
Augie Fackler
|
r43346 | |||
Yuya Nishihara
|
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
|
r28418 | with open(self._indexfilename, 'w') as f: | ||
f.write('# empty\n') | ||||
Yuya Nishihara
|
r14538 | |||
# here no extension enabled, disabled() lists up everything | ||||
Augie Fackler
|
r43346 | code = ( | ||
'import pprint; from mercurial import extensions; ' | ||||
Matt Harbison
|
r44862 | 'ext = extensions.disabled();' | ||
'ext.pop("__index__", None);' | ||||
'pprint.pprint(ext)' | ||||
Augie Fackler
|
r43346 | ) | ||
returncode, out, err = runcmd( | ||||
[sys.executable, '-c', code], localhgenv() | ||||
) | ||||
Adam Simpkins
|
r33111 | if err or returncode != 0: | ||
Yuya Nishihara
|
r14538 | raise DistutilsExecError(err) | ||
Matt Harbison
|
r42244 | with open(self._indexfilename, 'wb') as f: | ||
f.write(b'# this file is autogenerated by setup.py\n') | ||||
f.write(b'docs = ') | ||||
timeless
|
r28418 | f.write(out) | ||
Yuya Nishihara
|
r14538 | |||
Augie Fackler
|
r43346 | |||
Adrian Buehlmann
|
r17061 | class buildhgexe(build_ext): | ||
description = 'compile hg.exe from mercurial/exewrapper.c' | ||||
Kostia Balytskyi
|
r34531 | |||
Matt Harbison
|
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
|
r34531 | |||
def initialize_options(self): | ||||
build_ext.initialize_options(self) | ||||
Adrian Buehlmann
|
r17061 | |||
def build_extensions(self): | ||||
if os.name != 'nt': | ||||
return | ||||
Adrian Buehlmann
|
r17246 | if isinstance(self.compiler, HackedMingw32CCompiler): | ||
Augie Fackler
|
r43346 | self.compiler.compiler_so = self.compiler.compiler # no -mdll | ||
self.compiler.dll_libraries = [] # no -lmsrvc90 | ||||
Gregory Szorc
|
r29020 | |||
Matt Harbison
|
r44073 | pythonlib = None | ||
Gregory Szorc
|
r29020 | |||
Matt Harbison
|
r50289 | dirname = os.path.dirname(self.get_ext_fullpath('dummy')) | ||
self.hgtarget = os.path.join(dirname, 'hg') | ||||
Raphaël Gomès
|
r49124 | |||
Matt Harbison
|
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
|
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
|
r49684 | os.fsdecode(dllbasename), | ||
Raphaël Gomès
|
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
|
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
|
r49124 | ) | ||
Augie Fackler
|
r49686 | shutil.copy(python_x, dest) | ||
Raphaël Gomès
|
r49124 | |||
Matt Harbison
|
r44073 | if not pythonlib: | ||
Augie Fackler
|
r43346 | log.warn( | ||
Matt Harbison
|
r44071 | 'could not determine Python DLL filename; assuming pythonXY' | ||
Augie Fackler
|
r43346 | ) | ||
Gregory Szorc
|
r29020 | |||
hv = sys.hexversion | ||||
Matt Harbison
|
r44072 | pythonlib = b'python%d%d' % (hv >> 24, (hv >> 16) & 0xFF) | ||
Gregory Szorc
|
r29020 | |||
log.info('using %s as Python library name' % pythonlib) | ||||
timeless
|
r28418 | with open('mercurial/hgpythonlib.h', 'wb') as f: | ||
Matt Harbison
|
r39644 | f.write(b'/* this file is autogenerated by setup.py */\n') | ||
f.write(b'#define HGPYTHONLIB "%s"\n' % pythonlib) | ||||
Matt Harbison
|
r40433 | |||
Augie Fackler
|
r43346 | objects = self.compiler.compile( | ||
['mercurial/exewrapper.c'], | ||||
output_dir=self.build_temp, | ||||
Augie Fackler
|
r49687 | macros=[('_UNICODE', None), ('UNICODE', None)], | ||
Augie Fackler
|
r43346 | ) | ||
self.compiler.link_executable( | ||||
objects, self.hgtarget, libraries=[], output_dir=self.build_temp | ||||
) | ||||
Matt Harbison
|
r50290 | |||
self.addlongpathsmanifest() | ||||
Kostia Balytskyi
|
r34531 | |||
def addlongpathsmanifest(self): | ||||
Matt Harbison
|
r50290 | """Add manifest pieces so that hg.exe understands long paths | ||
Kostia Balytskyi
|
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
|
r50291 | with open(manfname, 'w', encoding="UTF-8") as f: | ||
Kostia Balytskyi
|
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
|
r50291 | |||
Augie Fackler
|
r43346 | self.spawn( | ||
[ | ||||
Matt Harbison
|
r50290 | self.compiler.mt, | ||
Augie Fackler
|
r43346 | '-nologo', | ||
'-manifest', | ||||
manfname, | ||||
outputresource, | ||||
] | ||||
) | ||||
Kostia Balytskyi
|
r34531 | log.info("done updating hg.exe's manifest") | ||
os.remove(manfname) | ||||
Adrian Buehlmann
|
r17061 | |||
Gregory Szorc
|
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
|
r43346 | |||
Gregory Szorc
|
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
|
r43346 | [sys.executable, 'gendoc.py', root], os.environ, cwd='doc' | ||
) | ||||
Gregory Szorc
|
r42016 | if res: | ||
Augie Fackler
|
r43346 | raise SystemExit( | ||
Gregory Szorc
|
r45244 | 'error running gendoc.py: %s' | ||
% '\n'.join([sysstr(out), sysstr(err)]) | ||||
Augie Fackler
|
r43346 | ) | ||
Gregory Szorc
|
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
|
r43346 | cwd='doc', | ||
) | ||||
Gregory Szorc
|
r42016 | if res: | ||
Augie Fackler
|
r43346 | raise SystemExit( | ||
Gregory Szorc
|
r45244 | 'error running gendoc: %s' | ||
% '\n'.join([sysstr(out), sysstr(err)]) | ||||
Augie Fackler
|
r43346 | ) | ||
Gregory Szorc
|
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
|
r43346 | [ | ||
sys.executable, | ||||
'runrst', | ||||
'hgmanpage', | ||||
'--halt', | ||||
'warning', | ||||
'--strip-elements-with-class', | ||||
'htmlonly', | ||||
'%s.txt' % root, | ||||
root, | ||||
], | ||||
Gregory Szorc
|
r42016 | os.environ, | ||
Augie Fackler
|
r43346 | cwd='doc', | ||
) | ||||
Gregory Szorc
|
r42016 | if res: | ||
Augie Fackler
|
r43346 | raise SystemExit( | ||
Gregory Szorc
|
r45244 | 'error running runrst: %s' | ||
% '\n'.join([sysstr(out), sysstr(err)]) | ||||
Augie Fackler
|
r43346 | ) | ||
Gregory Szorc
|
r42016 | |||
normalizecrlf('doc/%s' % root) | ||||
def genhtml(root): | ||||
log.info('generating doc/%s.html' % root) | ||||
res, out, err = runcmd( | ||||
Augie Fackler
|
r43346 | [ | ||
sys.executable, | ||||
'runrst', | ||||
'html', | ||||
'--halt', | ||||
'warning', | ||||
'--link-stylesheet', | ||||
'--stylesheet-path', | ||||
'style.css', | ||||
'%s.txt' % root, | ||||
'%s.html' % root, | ||||
], | ||||
Gregory Szorc
|
r42016 | os.environ, | ||
Augie Fackler
|
r43346 | cwd='doc', | ||
) | ||||
Gregory Szorc
|
r42016 | if res: | ||
Augie Fackler
|
r43346 | raise SystemExit( | ||
Gregory Szorc
|
r45244 | 'error running runrst: %s' | ||
% '\n'.join([sysstr(out), sysstr(err)]) | ||||
Augie Fackler
|
r43346 | ) | ||
Gregory Szorc
|
r42016 | |||
normalizecrlf('doc/%s.html' % root) | ||||
# This logic is duplicated in doc/Makefile. | ||||
Augie Fackler
|
r44937 | sources = { | ||
Augie Fackler
|
r43346 | f | ||
Augie Fackler
|
r44188 | for f in os.listdir('mercurial/helptext') | ||
Augie Fackler
|
r43346 | if re.search(r'[0-9]\.txt$', f) | ||
Augie Fackler
|
r44937 | } | ||
Gregory Szorc
|
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
|
r43346 | |||
Matt Harbison
|
r32647 | class hginstall(install): | ||
Augie Fackler
|
r32725 | |||
user_options = install.user_options + [ | ||||
Augie Fackler
|
r43346 | ( | ||
'old-and-unmanageable', | ||||
None, | ||||
'noop, present for eggless setuptools compat', | ||||
), | ||||
( | ||||
'single-version-externally-managed', | ||||
None, | ||||
'noop, present for eggless setuptools compat', | ||||
), | ||||
Augie Fackler
|
r32725 | ] | ||
Matthew Martin
|
r49845 | sub_commands = install.sub_commands + [ | ||
('install_completion', lambda self: True) | ||||
] | ||||
Augie Fackler
|
r32725 | # Also helps setuptools not be sad while we refuse to create eggs. | ||
single_version_externally_managed = True | ||||
Matt Harbison
|
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
|
r44937 | excl = {'bdist_egg'} | ||
r47033 | return filter(lambda x: x not in excl, install.get_sub_commands(self)) | |||
Matt Harbison
|
r32647 | |||
Augie Fackler
|
r43346 | |||
Kyle Lippincott
|
r22640 | class hginstalllib(install_lib): | ||
Augie Fackler
|
r46554 | """ | ||
Kyle Lippincott
|
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
|
r46554 | """ | ||
Kyle Lippincott
|
r22640 | |||
def run(self): | ||||
realcopyfile = file_util.copy_file | ||||
Augie Fackler
|
r43346 | |||
Kyle Lippincott
|
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
|
r24941 | setmode = int('0755', 8) | ||
Kyle Lippincott
|
r22640 | else: | ||
Augie Fackler
|
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
|
r43346 | |||
Kyle Lippincott
|
r22640 | file_util.copy_file = copyfileandsetmode | ||
try: | ||||
install_lib.run(self) | ||||
finally: | ||||
file_util.copy_file = realcopyfile | ||||
Augie Fackler
|
r43346 | |||
Dan Villiom Podlaski Christiansen
|
r12661 | class hginstallscripts(install_scripts): | ||
Augie Fackler
|
r46554 | """ | ||
Dan Villiom Podlaski Christiansen
|
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
|
r46554 | """ | ||
Dan Villiom Podlaski Christiansen
|
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
|
r43346 | self.set_undefined_options('install', ('install_lib', 'install_lib')) | ||
Dan Villiom Podlaski Christiansen
|
r12661 | |||
def run(self): | ||||
install_scripts.run(self) | ||||
Gregory Szorc
|
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
|
r43346 | if ( | ||
os.path.splitdrive(self.install_dir)[0] | ||||
!= os.path.splitdrive(self.install_lib)[0] | ||||
): | ||||
Dan Villiom Podlaski Christiansen
|
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
|
r44539 | libdir = os.path.relpath(self.install_lib, self.install_dir) | ||
Dan Villiom Podlaski Christiansen
|
r12661 | |||
for outfile in self.outfiles: | ||||
timeless
|
r28418 | with open(outfile, 'rb') as fp: | ||
data = fp.read() | ||||
Dan Villiom Podlaski Christiansen
|
r12661 | |||
# skip binary files | ||||
Gregory Szorc
|
r27348 | if b'\0' in data: | ||
Dan Villiom Podlaski Christiansen
|
r12661 | continue | ||
Gregory Szorc
|
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
|
r43346 | log.info( | ||
'not rewriting @LIBDIR@ in %s because install path ' | ||||
'not known' % outfile | ||||
) | ||||
Gregory Szorc
|
r27269 | continue | ||
Augie Fackler
|
r49683 | data = data.replace(b'@LIBDIR@', libdir.encode('unicode_escape')) | ||
timeless
|
r28418 | with open(outfile, 'wb') as fp: | ||
fp.write(data) | ||||
Dan Villiom Podlaski Christiansen
|
r12661 | |||
Augie Fackler
|
r43346 | |||
Matthew Martin
|
r49845 | class hginstallcompletion(Command): | ||
description = 'Install shell completion' | ||||
def initialize_options(self): | ||||
self.install_dir = None | ||||
Raphaël Gomès
|
r49914 | self.outputs = [] | ||
Matthew Martin
|
r49845 | |||
def finalize_options(self): | ||||
self.set_undefined_options( | ||||
'install_data', ('install_dir', 'install_dir') | ||||
) | ||||
Raphaël Gomès
|
r49914 | def get_outputs(self): | ||
return self.outputs | ||||
Matthew Martin
|
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
|
r49914 | |||
dest = os.path.join(dir, dest) | ||||
self.outputs.append(dest) | ||||
self.copy_file(os.path.join('contrib', src), dest) | ||||
Matthew Martin
|
r49845 | |||
Gregory Szorc
|
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
|
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
|
r42360 | |||
import opcode | ||||
Augie Fackler
|
r43346 | |||
distutilsreal = os.path.join( | ||||
os.path.dirname(opcode.__file__), 'distutils' | ||||
) | ||||
Gregory Szorc
|
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
|
r43346 | modname = modname[: -len('.__init__')] | ||
Gregory Szorc
|
r42360 | path = os.path.dirname(full) | ||
else: | ||||
path = None | ||||
Augie Fackler
|
r43346 | res.modules[modname] = py2exemodule( | ||
modname, full, path=path | ||||
) | ||||
Gregory Szorc
|
r42360 | |||
if 'distutils' not in res.modules: | ||||
raise SystemExit('could not find distutils modules') | ||||
return res | ||||
Augie Fackler
|
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
|
r49845 | 'install_completion': hginstallcompletion, | ||
Augie Fackler
|
r43346 | 'install_lib': hginstalllib, | ||
'install_scripts': hginstallscripts, | ||||
'build_hgexe': buildhgexe, | ||||
} | ||||
Thomas Arendsen Hein
|
r3238 | |||
Gregory Szorc
|
r42360 | if py2exehacked: | ||
cmdclass['py2exe'] = hgbuildpy2exe | ||||
Augie Fackler
|
r43346 | packages = [ | ||
'mercurial', | ||||
Franck Bret
|
r51880 | 'mercurial.admin', | ||
Augie Fackler
|
r43346 | 'mercurial.cext', | ||
'mercurial.cffi', | ||||
Matt Harbison
|
r44478 | 'mercurial.defaultrc', | ||
Simon Sapin
|
r48474 | 'mercurial.dirstateutils', | ||
Matt Harbison
|
r44031 | 'mercurial.helptext', | ||
'mercurial.helptext.internals', | ||||
Augie Fackler
|
r43346 | 'mercurial.hgweb', | ||
'mercurial.interfaces', | ||||
'mercurial.pure', | ||||
pacien
|
r51288 | 'mercurial.stabletailgraph', | ||
Martin von Zweigbergk
|
r45864 | 'mercurial.templates', | ||
Augie Fackler
|
r43346 | 'mercurial.thirdparty', | ||
'mercurial.thirdparty.attr', | ||||
Raphaël Gomès
|
r51654 | 'mercurial.thirdparty.tomli', | ||
Augie Fackler
|
r43346 | 'mercurial.thirdparty.zope', | ||
'mercurial.thirdparty.zope.interface', | ||||
r46661 | 'mercurial.upgrade_utils', | |||
Augie Fackler
|
r43346 | 'mercurial.utils', | ||
'mercurial.revlogutils', | ||||
'mercurial.testing', | ||||
'hgext', | ||||
'hgext.convert', | ||||
'hgext.fsmonitor', | ||||
'hgext.fastannotate', | ||||
'hgext.fsmonitor.pywatchman', | ||||
Augie Fackler
|
r44961 | 'hgext.git', | ||
Augie Fackler
|
r43346 | 'hgext.highlight', | ||
Joerg Sonnenberger
|
r44897 | 'hgext.hooklib', | ||
Augie Fackler
|
r43346 | 'hgext.largefiles', | ||
'hgext.lfs', | ||||
'hgext.narrow', | ||||
'hgext.remotefilelog', | ||||
'hgext.zeroconf', | ||||
'hgext3rd', | ||||
'hgdemandimport', | ||||
] | ||||
Martin von Zweigbergk
|
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
|
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
|
r43346 | common_depends = [ | ||
'mercurial/bitmanipulation.h', | ||||
'mercurial/compat.h', | ||||
'mercurial/cext/util.h', | ||||
] | ||||
Yuya Nishihara
|
r32206 | common_include_dirs = ['mercurial'] | ||
Wei, Elson
|
r19724 | |||
Matt Harbison
|
r45079 | common_cflags = [] | ||
Augie Fackler
|
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
|
r45079 | common_cflags = ['-Werror=declaration-after-statement'] | ||
Jun Wu
|
r30408 | osutil_cflags = [] | ||
Adrian Buehlmann
|
r25073 | osutil_ldflags = [] | ||
Jun Wu
|
r31561 | # platform specific macros | ||
Jun Wu
|
r31622 | for plat, func in [('bsd', 'setproctitle')]: | ||
Jun Wu
|
r31561 | if re.search(plat, sys.platform) and hasfunction(new_compiler(), func): | ||
Jun Wu
|
r30408 | osutil_cflags.append('-DHAVE_%s' % func.upper()) | ||
Jun Wu
|
r31596 | for plat, macro, code in [ | ||
Augie Fackler
|
r43346 | ( | ||
'bsd|darwin', | ||||
'BSD_STATFS', | ||||
''' | ||||
Jun Wu
|
r31596 | #include <sys/param.h> | ||
#include <sys/mount.h> | ||||
int main() { struct statfs s; return sizeof(s.f_fstypename); } | ||||
Augie Fackler
|
r43346 | ''', | ||
), | ||||
( | ||||
'linux', | ||||
'LINUX_STATFS', | ||||
''' | ||||
Jun Wu
|
r31622 | #include <linux/magic.h> | ||
#include <sys/vfs.h> | ||||
int main() { struct statfs s; return sizeof(s.f_type); } | ||||
Augie Fackler
|
r43346 | ''', | ||
), | ||||
Jun Wu
|
r31596 | ]: | ||
if re.search(plat, sys.platform) and cancompile(new_compiler(), code): | ||||
osutil_cflags.append('-DHAVE_%s' % macro) | ||||
Adrian Buehlmann
|
r25073 | if sys.platform == 'darwin': | ||
osutil_ldflags += ['-framework', 'ApplicationServices'] | ||||
Alexander Pyhalov
|
r45199 | if sys.platform == 'sunos5': | ||
osutil_ldflags += ['-lsocket'] | ||||
Jun Wu
|
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
|
r43346 | |||
Georges Racinet
|
r41002 | class RustCompilationError(CCompilerError): | ||
"""Exception class for Rust compilation errors.""" | ||||
Augie Fackler
|
r43346 | |||
Georges Racinet
|
r40309 | class RustExtension(Extension): | ||
Augie Fackler
|
r46554 | """Base classes for concrete Rust Extension classes.""" | ||
Georges Racinet
|
r40309 | |||
rusttargetdir = os.path.join('rust', 'target', 'release') | ||||
Simon Sapin
|
r48996 | def __init__(self, mpath, sources, rustlibname, subcrate, **kw): | ||
Georges Racinet
|
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
|
r43346 | self.depends.extend( | ||
os.path.join(dirpath, fname) | ||||
for fname in fnames | ||||
if os.path.splitext(fname)[1] == '.rs' | ||||
) | ||||
Georges Racinet
|
r40309 | |||
Georges Racinet
|
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
|
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
|
r43346 | |||
Georges Racinet
|
r40309 | env['HOME'] = pwd.getpwuid(os.getuid()).pw_dir | ||
Valentin Gatien-Baron
|
r45365 | cargocmd = ['cargo', 'rustc', '--release'] | ||
Raphaël Gomès
|
r44786 | |||
rust_features = env.get("HG_RUST_FEATURES") | ||||
if rust_features: | ||||
Simon Sapin
|
r49697 | cargocmd.extend(('--features', rust_features)) | ||
Raphaël Gomès
|
r44786 | |||
Georges Racinet
|
r42659 | cargocmd.append('--') | ||
Georges Racinet
|
r42658 | if sys.platform == 'darwin': | ||
Augie Fackler
|
r43346 | cargocmd.extend( | ||
("-C", "link-arg=-undefined", "-C", "link-arg=dynamic_lookup") | ||||
) | ||||
Georges Racinet
|
r41002 | try: | ||
subprocess.check_call(cargocmd, env=env, cwd=self.rustsrcdir) | ||||
Manuel Jacob
|
r50205 | except FileNotFoundError: | ||
raise RustCompilationError("Cargo not found") | ||||
except PermissionError: | ||||
raise RustCompilationError( | ||||
"Cargo found, but permission to execute it is denied" | ||||
) | ||||
Georges Racinet
|
r41002 | except subprocess.CalledProcessError: | ||
raise RustCompilationError( | ||||
"Cargo failed. Working directory: %r, " | ||||
Philippe Pepiot
|
r42243 | "command: %r, environment: %r" | ||
Augie Fackler
|
r43346 | % (self.rustsrcdir, cargocmd, env) | ||
) | ||||
Georges Racinet
|
r40309 | |||
Georges Racinet
|
r41003 | class RustStandaloneExtension(RustExtension): | ||
def __init__(self, pydottedname, rustcrate, dylibname, **kw): | ||||
Augie Fackler
|
r43346 | RustExtension.__init__( | ||
self, pydottedname, [], dylibname, rustcrate, **kw | ||||
) | ||||
Georges Racinet
|
r41003 | self.dylibname = dylibname | ||
def build(self, target_dir): | ||||
self.rustbuild() | ||||
target = [target_dir] | ||||
target.extend(self.name.split('.')) | ||||
Georges Racinet
|
r42657 | target[-1] += DYLIB_SUFFIX | ||
Cédric Krier
|
r50851 | target = os.path.join(*target) | ||
os.makedirs(os.path.dirname(target), exist_ok=True) | ||||
Augie Fackler
|
r43346 | shutil.copy2( | ||
os.path.join( | ||||
self.rusttargetdir, self.dylibname + self.rustdylibsuffix() | ||||
), | ||||
Cédric Krier
|
r50851 | target, | ||
Augie Fackler
|
r43346 | ) | ||
Georges Racinet
|
r41003 | |||
Martin Geisler
|
r10000 | extmodules = [ | ||
Augie Fackler
|
r43346 | Extension( | ||
'mercurial.cext.base85', | ||||
['mercurial/cext/base85.c'], | ||||
include_dirs=common_include_dirs, | ||||
Matt Harbison
|
r45079 | extra_compile_args=common_cflags, | ||
Augie Fackler
|
r43346 | depends=common_depends, | ||
), | ||||
Extension( | ||||
'mercurial.cext.bdiff', | ||||
['mercurial/bdiff.c', 'mercurial/cext/bdiff.c'] + xdiff_srcs, | ||||
include_dirs=common_include_dirs, | ||||
Matt Harbison
|
r45079 | extra_compile_args=common_cflags, | ||
Augie Fackler
|
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
|
r45079 | extra_compile_args=common_cflags, | ||
Augie Fackler
|
r43346 | depends=common_depends, | ||
), | ||||
r44956 | Extension( | |||
Augie Fackler
|
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
|
r41003 | include_dirs=common_include_dirs, | ||
Matt Harbison
|
r45079 | extra_compile_args=common_cflags, | ||
Augie Fackler
|
r43346 | depends=common_depends | ||
Augie Fackler
|
r46554 | + [ | ||
'mercurial/cext/charencode.h', | ||||
'mercurial/cext/revlog.h', | ||||
], | ||||
Augie Fackler
|
r43346 | ), | ||
Gregory Szorc
|
r37197 | Extension( | ||
Augie Fackler
|
r43346 | 'mercurial.cext.osutil', | ||
['mercurial/cext/osutil.c'], | ||||
include_dirs=common_include_dirs, | ||||
Matt Harbison
|
r45079 | extra_compile_args=common_cflags + osutil_cflags, | ||
Augie Fackler
|
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
|
r45079 | extra_compile_args=common_cflags, | ||
Augie Fackler
|
r43346 | ), | ||
Extension( | ||||
Augie Fackler
|
r44510 | 'mercurial.thirdparty.sha1dc', | ||
[ | ||||
'mercurial/thirdparty/sha1dc/cext.c', | ||||
'mercurial/thirdparty/sha1dc/lib/sha1.c', | ||||
'mercurial/thirdparty/sha1dc/lib/ubc_check.c', | ||||
], | ||||
Matt Harbison
|
r45079 | extra_compile_args=common_cflags, | ||
Augie Fackler
|
r44510 | ), | ||
Extension( | ||||
Matt Harbison
|
r45079 | 'hgext.fsmonitor.pywatchman.bser', | ||
['hgext/fsmonitor/pywatchman/bser.c'], | ||||
extra_compile_args=common_cflags, | ||||
Augie Fackler
|
r43346 | ), | ||
RustStandaloneExtension( | ||||
Simon Sapin
|
r48996 | 'mercurial.rustext', | ||
'hg-cpython', | ||||
'librusthg', | ||||
Augie Fackler
|
r43346 | ), | ||
] | ||||
Bryan O'Sullivan
|
r5396 | |||
Georges Racinet
|
r41003 | |||
Gregory Szorc
|
r30436 | sys.path.insert(0, 'contrib/python-zstandard') | ||
import setup_zstd | ||||
Augie Fackler
|
r43346 | |||
Matt Harbison
|
r45079 | zstd = setup_zstd.get_c_extension( | ||
name='mercurial.zstd', root=os.path.abspath(os.path.dirname(__file__)) | ||||
Augie Fackler
|
r43346 | ) | ||
Matt Harbison
|
r45079 | zstd.extra_compile_args += common_cflags | ||
extmodules.append(zstd) | ||||
Gregory Szorc
|
r30436 | |||
Ludovic Chabant
|
r23677 | try: | ||
from distutils import cygwinccompiler | ||||
# the -mno-cygwin option has been deprecated for years | ||||
Mike Hommey
|
r33780 | mingw32compilerclass = cygwinccompiler.Mingw32CCompiler | ||
Bryan O'Sullivan
|
r17121 | |||
Ludovic Chabant
|
r23677 | class HackedMingw32CCompiler(cygwinccompiler.Mingw32CCompiler): | ||
def __init__(self, *args, **kwargs): | ||||
Mike Hommey
|
r33780 | mingw32compilerclass.__init__(self, *args, **kwargs) | ||
Ludovic Chabant
|
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
|
r17121 | |||
Ludovic Chabant
|
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
|
r49801 | class HackedMingw32CCompiler: | ||
Ludovic Chabant
|
r23677 | pass | ||
Bryan O'Sullivan
|
r17121 | |||
Augie Fackler
|
r43346 | |||
Matt Harbison
|
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
|
r33780 | msvccompilerclass = msvccompiler.MSVCCompiler | ||
Matt Harbison
|
r32782 | |||
class HackedMSVCCompiler(msvccompiler.MSVCCompiler): | ||||
def initialize(self): | ||||
Mike Hommey
|
r33780 | msvccompilerclass.initialize(self) | ||
Matt Harbison
|
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
|
r43346 | packagedata = { | ||
Augie Fackler
|
r46554 | 'mercurial': [ | ||
Raphaël Gomès
|
r51655 | 'configitems.toml', | ||
Augie Fackler
|
r46554 | 'locale/*/LC_MESSAGES/hg.mo', | ||
'dummycert.pem', | ||||
], | ||||
'mercurial.defaultrc': [ | ||||
'*.rc', | ||||
], | ||||
'mercurial.helptext': [ | ||||
'*.txt', | ||||
], | ||||
'mercurial.helptext.internals': [ | ||||
'*.txt', | ||||
], | ||||
Matt Harbison
|
r50550 | 'mercurial.thirdparty.attr': [ | ||
'*.pyi', | ||||
'py.typed', | ||||
], | ||||
Augie Fackler
|
r43346 | } | ||
Dan Villiom Podlaski Christiansen
|
r9999 | |||
def ordinarypath(p): | ||||
return p and p[0] != '.' and p[-1] != '~' | ||||
Augie Fackler
|
r43346 | |||
Matt Mackall
|
r10282 | for root in ('templates',): | ||
Dan Villiom Podlaski Christiansen
|
r9999 | for curdir, dirs, files in os.walk(os.path.join('mercurial', root)): | ||
Martin von Zweigbergk
|
r45865 | packagename = curdir.replace(os.sep, '.') | ||
packagedata[packagename] = list(filter(ordinarypath, files)) | ||||
Dan Villiom Podlaski Christiansen
|
r9999 | |||
Martin Geisler
|
r7648 | datafiles = [] | ||
Gregory Szorc
|
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
|
r47262 | assert isinstance(version, str) | ||
setupversion = version | ||||
Gregory Szorc
|
r31316 | |||
Adrian Buehlmann
|
r10400 | extra = {} | ||
Gregory Szorc
|
r42017 | py2exepackages = [ | ||
'hgdemandimport', | ||||
Gregory Szorc
|
r42084 | 'hgext3rd', | ||
Gregory Szorc
|
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
|
r47109 | py2exe_includes = [] | ||
Gregory Szorc
|
r42082 | py2exeexcludes = [] | ||
Gregory Szorc
|
r42120 | py2exedllexcludes = ['crypt32.dll'] | ||
Gregory Szorc
|
r42082 | |||
Yuya Nishihara
|
r33601 | if issetuptools: | ||
extra['python_requires'] = supportedpy | ||||
Gregory Szorc
|
r42017 | |||
Adrian Buehlmann
|
r10400 | if py2exeloaded: | ||
extra['console'] = [ | ||||
Augie Fackler
|
r43346 | { | ||
'script': 'hg', | ||||
Matt Harbison
|
r50725 | 'copyright': 'Copyright (C) 2005-2023 Olivia Mackall and others', | ||
Augie Fackler
|
r43346 | 'product_version': version, | ||
} | ||||
] | ||||
Gregory Szorc
|
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
|
r25409 | # put dlls in sub directory so that they won't pollute PATH | ||
extra['zipfile'] = 'lib/library.zip' | ||||
Adrian Buehlmann
|
r10400 | |||
Gregory Szorc
|
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
|
r42017 | |||
Matt Harbison
|
r47109 | extra_includes = os.environ.get('HG_PY2EXE_EXTRA_INCLUDES') | ||
if extra_includes: | ||||
py2exe_includes.extend(extra_includes.split(' ')) | ||||
Gregory Szorc
|
r42082 | excludes = os.environ.get('HG_PY2EXE_EXTRA_EXCLUDES') | ||
if excludes: | ||||
py2exeexcludes.extend(excludes.split(' ')) | ||||
Gregory Szorc
|
r42017 | |||
Gregory Szorc
|
r42082 | dllexcludes = os.environ.get('HG_PY2EXE_EXTRA_DLL_EXCLUDES') | ||
if dllexcludes: | ||||
py2exedllexcludes.extend(dllexcludes.split(' ')) | ||||
Gregory Szorc
|
r42017 | |||
Martin von Zweigbergk
|
r45692 | if os.environ.get('PYOXIDIZER'): | ||
hgbuild.sub_commands.insert(0, ('build_hgextindex', None)) | ||||
Adrian Buehlmann
|
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
|
r40419 | setupversion = setupversion.split(r'+', 1)[0] | ||
Martin Geisler
|
r7648 | |||
Augie Fackler
|
r43346 | setup( | ||
name='mercurial', | ||||
version=setupversion, | ||||
Raphaël Gomès
|
r47575 | author='Olivia Mackall and many others', | ||
Augie Fackler
|
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
|
r47109 | 'includes': py2exe_includes, | ||
Augie Fackler
|
r43346 | 'excludes': py2exeexcludes, | ||
'packages': py2exepackages, | ||||
}, | ||||
'bdist_mpkg': { | ||||
'zipdist': False, | ||||
'license': 'COPYING', | ||||
'readme': 'contrib/packaging/macosx/Readme.html', | ||||
'welcome': 'contrib/packaging/macosx/Welcome.html', | ||||
}, | ||||
}, | ||||
r52053 | **extra, | |||
Augie Fackler
|
r43346 | ) | ||