mirror of
https://github.com/facebook/sapling.git
synced 2024-10-12 01:39:21 +03:00
ebf037644f
Summary: This was breaking perfsuite runs on the megarepo because it was not getting included in the package. Reviewed By: singhsrb Differential Revision: D8402910 fbshipit-source-id: 84115e1d81e09b35729f23e656655dc9e5c480ca
1623 lines
55 KiB
Python
1623 lines
55 KiB
Python
# This is the mercurial setup script.
|
|
#
|
|
# 'python setup.py install', or
|
|
# 'python setup.py --help' for more options
|
|
|
|
# isort:skip_file
|
|
|
|
from __future__ import absolute_import
|
|
from __future__ import division
|
|
from __future__ import print_function
|
|
from distutils.version import LooseVersion
|
|
import glob
|
|
import os
|
|
import struct
|
|
|
|
supportedpy = "~= 2.7"
|
|
if os.environ.get("HGALLOWPYTHON3", ""):
|
|
# Mercurial will never work on Python 3 before 3.5 due to a lack
|
|
# of % formatting on bytestrings, and can't work on 3.6.0 or 3.6.1
|
|
# due to a bug in % formatting in bytestrings.
|
|
#
|
|
# TODO: when we actually work on Python 3, use this string as the
|
|
# actual supportedpy string.
|
|
supportedpy = ",".join(
|
|
[
|
|
">=2.7",
|
|
"!=3.0.*",
|
|
"!=3.1.*",
|
|
"!=3.2.*",
|
|
"!=3.3.*",
|
|
"!=3.4.*",
|
|
"!=3.6.0",
|
|
"!=3.6.1",
|
|
]
|
|
)
|
|
|
|
import sys, platform
|
|
|
|
if sys.version_info[0] >= 3:
|
|
printf = eval("print")
|
|
libdir_escape = "unicode_escape"
|
|
|
|
def sysstr(s):
|
|
return s.decode("latin-1")
|
|
|
|
|
|
else:
|
|
libdir_escape = "string_escape"
|
|
|
|
def printf(*args, **kwargs):
|
|
f = kwargs.get("file", sys.stdout)
|
|
end = kwargs.get("end", "\n")
|
|
f.write(b" ".join(args) + end)
|
|
|
|
def sysstr(s):
|
|
return s
|
|
|
|
|
|
try:
|
|
import Cython
|
|
except ImportError:
|
|
havecython = False
|
|
else:
|
|
havecython = LooseVersion(Cython.__version__) >= LooseVersion("0.22")
|
|
|
|
if not havecython:
|
|
raise RuntimeError("Cython >= 0.22 is required")
|
|
|
|
from Cython.Build import cythonize
|
|
|
|
# Attempt to guide users to a modern pip - this means that 2.6 users
|
|
# should have a chance of getting a 4.2 release, and when we ratchet
|
|
# the version requirement forward again hopefully everyone will get
|
|
# something that works for them.
|
|
if sys.version_info < (2, 7, 0, "final"):
|
|
pip_message = (
|
|
"This may be due to an out of date pip. " "Make sure you have pip >= 9.0.1."
|
|
)
|
|
try:
|
|
import pip
|
|
|
|
pip_version = tuple([int(x) for x in pip.__version__.split(".")[:3]])
|
|
if pip_version < (9, 0, 1):
|
|
pip_message = (
|
|
"Your pip version is out of date, please install "
|
|
"pip >= 9.0.1. pip {} detected.".format(pip.__version__)
|
|
)
|
|
else:
|
|
# pip is new enough - it must be something else
|
|
pip_message = ""
|
|
except Exception:
|
|
pass
|
|
error = """
|
|
Mercurial does not support Python older than 2.7.
|
|
Python {py} detected.
|
|
{pip}
|
|
""".format(
|
|
py=sys.version_info, pip=pip_message
|
|
)
|
|
printf(error, file=sys.stderr)
|
|
sys.exit(1)
|
|
|
|
# Solaris Python packaging brain damage
|
|
try:
|
|
import hashlib
|
|
|
|
sha = hashlib.sha1()
|
|
except ImportError:
|
|
try:
|
|
import sha
|
|
|
|
sha.sha # silence unused import warning
|
|
except ImportError:
|
|
raise SystemExit(
|
|
"Couldn't import standard hashlib (incomplete Python install)."
|
|
)
|
|
|
|
try:
|
|
import zlib
|
|
|
|
zlib.compressobj # silence unused import warning
|
|
except ImportError:
|
|
raise SystemExit("Couldn't import standard zlib (incomplete Python install).")
|
|
|
|
# The base IronPython distribution (as of 2.7.1) doesn't support bz2
|
|
isironpython = False
|
|
try:
|
|
isironpython = platform.python_implementation().lower().find("ironpython") != -1
|
|
except AttributeError:
|
|
pass
|
|
|
|
if isironpython:
|
|
sys.stderr.write("warning: IronPython detected (no bz2 support)\n")
|
|
else:
|
|
try:
|
|
import bz2
|
|
|
|
bz2.BZ2Compressor # silence unused import warning
|
|
except ImportError:
|
|
raise SystemExit("Couldn't import standard bz2 (incomplete Python install).")
|
|
|
|
ispypy = "PyPy" in sys.version
|
|
|
|
import ctypes
|
|
import stat, subprocess
|
|
import re
|
|
import shutil
|
|
import tempfile
|
|
from distutils import log
|
|
|
|
# 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.
|
|
issetuptools = os.name == "nt" or "FORCE_SETUPTOOLS" in os.environ
|
|
if issetuptools:
|
|
from setuptools import setup
|
|
else:
|
|
from distutils.core import setup
|
|
from distutils.ccompiler import new_compiler
|
|
from distutils.core import Command, Extension
|
|
from distutils.dist import Distribution
|
|
from distutils.command.build import build
|
|
from distutils.command.build_ext import build_ext
|
|
from distutils.command.build_py import build_py
|
|
from distutils.command.build_scripts import build_scripts
|
|
from distutils.command.install import install
|
|
from distutils.command.install_lib import install_lib
|
|
from distutils.command.install_scripts import install_scripts
|
|
from distutils.spawn import spawn, find_executable
|
|
from distutils import file_util
|
|
from distutils.errors import CCompilerError, DistutilsError, DistutilsExecError
|
|
from distutils.sysconfig import get_python_inc, get_config_var
|
|
from distutils.version import StrictVersion, LooseVersion
|
|
from distutils_rust import RustExtension, RustBinary, RustVendoredCrates, BuildRustExt
|
|
import distutils
|
|
|
|
haverust = False
|
|
try:
|
|
cargo_version = subprocess.check_output(["cargo", "--version"]).split()[1]
|
|
except Exception:
|
|
sys.stderr.write("not compiling Rust extensions: cargo is not available\n")
|
|
else:
|
|
required_cargo_version = "0.21"
|
|
if LooseVersion(cargo_version) >= LooseVersion(required_cargo_version):
|
|
haverust = True
|
|
else:
|
|
sys.stderr.write(
|
|
"not compiling Rust extensions: cargo is too old "
|
|
+ "(found %s, need %s or higher)\n"
|
|
% (cargo_version, required_cargo_version)
|
|
)
|
|
|
|
if not haverust:
|
|
raise RuntimeError("Rust (and cargo >= 0.21) is required")
|
|
|
|
iswindows = os.name == "nt"
|
|
NOOPTIMIZATION = "/Od" if iswindows else "-O0"
|
|
PRODUCEDEBUGSYMBOLS = "/DEBUG:FULL" if iswindows else "-g"
|
|
SHA1_LIBRARY = "sha1detectcoll"
|
|
SHA1LIB_DEFINE = "/DSHA1_USE_SHA1DC" if iswindows else "-DSHA1_USE_SHA1DC"
|
|
STDC99 = "" if iswindows else "-std=c99"
|
|
STDCPP0X = "" if iswindows else "-std=c++0x"
|
|
STDCPP11 = "" if iswindows else "-std=c++11"
|
|
WALL = "/Wall" if iswindows else "-Wall"
|
|
WSTRICTPROTOTYPES = None if iswindows else "-Werror=strict-prototypes"
|
|
|
|
cflags = [SHA1LIB_DEFINE]
|
|
|
|
# if this is set, compile all C extensions with -O0 -g for easy debugging. note
|
|
# that this is not manifested in any way in the Makefile dependencies.
|
|
# therefore, if you already have build products, they won't be rebuilt!
|
|
if os.getenv("FB_HGEXT_CDEBUG") is not None:
|
|
cflags.extend([NOOPTIMIZATION, PRODUCEDEBUGSYMBOLS])
|
|
|
|
|
|
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)
|
|
|
|
|
|
scripts = ["hg"]
|
|
|
|
# Rename hg to $HGNAME. Useful when "hg" is a wrapper calling $HGNAME (or chg).
|
|
hgname = os.environ.get("HGNAME", "hg")
|
|
if not re.match("\Ahg[.0-9a-z-]*\Z", hgname):
|
|
raise RuntimeError("Illegal HGNAME: %s" % hgname)
|
|
|
|
if os.name == "nt":
|
|
# We remove hg.bat if we are able to build hg.exe.
|
|
scripts.append("contrib/win32/hg.bat")
|
|
|
|
|
|
def cancompile(cc, code):
|
|
tmpdir = tempfile.mkdtemp(prefix="hg-install-")
|
|
devnull = oldstderr = None
|
|
try:
|
|
fname = os.path.join(tmpdir, "testcomp.c")
|
|
f = open(fname, "w")
|
|
f.write(code)
|
|
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"))
|
|
return True
|
|
except Exception:
|
|
return False
|
|
finally:
|
|
if oldstderr is not None:
|
|
os.dup2(oldstderr, sys.stderr.fileno())
|
|
if devnull is not None:
|
|
devnull.close()
|
|
shutil.rmtree(tmpdir)
|
|
|
|
|
|
# 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)
|
|
|
|
|
|
def hasheader(cc, headername):
|
|
code = "#include <%s>\nint main(void) { return 0; }\n" % headername
|
|
return cancompile(cc, code)
|
|
|
|
|
|
# py2exe needs to be installed to work
|
|
try:
|
|
import py2exe
|
|
|
|
py2exe.Distribution # silence unused import warning
|
|
py2exeloaded = True
|
|
# import py2exe's patched Distribution class
|
|
from distutils.core import Distribution # noqa: F811
|
|
except ImportError:
|
|
py2exeloaded = False
|
|
|
|
|
|
def runcmd(cmd, env):
|
|
p = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, env=env)
|
|
out, err = p.communicate()
|
|
return p.returncode, out, err
|
|
|
|
|
|
class hgcommand(object):
|
|
def __init__(self, cmd, env):
|
|
self.cmd = cmd
|
|
self.env = env
|
|
|
|
def run(self, args):
|
|
cmd = self.cmd + args
|
|
returncode, out, err = runcmd(cmd, self.env)
|
|
err = filterhgerr(err)
|
|
if err or returncode != 0:
|
|
printf("stderr from '%s':" % (" ".join(cmd)), file=sys.stderr)
|
|
printf(err, file=sys.stderr)
|
|
return ""
|
|
return out
|
|
|
|
|
|
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.
|
|
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"devel-warn:")
|
|
)
|
|
]
|
|
return b"\n".join(b" " + e for e in err)
|
|
|
|
|
|
def findhg():
|
|
"""Try to figure out how we should invoke hg for examining the local
|
|
repository contents.
|
|
|
|
Returns an hgcommand object, or None if a working hg command cannot be
|
|
found.
|
|
"""
|
|
# 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
|
|
# path works and can successfully interact with this repository.
|
|
check_cmd = ["log", "-r.", "-Ttest"]
|
|
try:
|
|
retcode, out, err = runcmd(hgcmd + check_cmd, hgenv)
|
|
except EnvironmentError:
|
|
retcode = -1
|
|
if retcode == 0 and not filterhgerr(err):
|
|
return hgcommand(hgcmd, hgenv)
|
|
|
|
# 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
|
|
# if retcode == 0 and not filterhgerr(err):
|
|
return hgcommand(hgcmd, hgenv)
|
|
|
|
# Neither local or system hg can be used.
|
|
return None
|
|
|
|
|
|
def localhgenv():
|
|
"""Get an environment dictionary to use for invoking or importing
|
|
mercurial from the local repository."""
|
|
# Execute hg out of this directory with a custom environment which takes
|
|
# care to not use any hgrc files and do no localization.
|
|
env = {
|
|
"HGMODULEPOLICY": "py",
|
|
"HGRCPATH": "",
|
|
"LANGUAGE": "C",
|
|
"PATH": "",
|
|
} # make pypi modules that use os.environ['PATH'] happy
|
|
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"]
|
|
return env
|
|
|
|
|
|
def pickversion():
|
|
hg = findhg()
|
|
if not hg:
|
|
# if hg is not found, fallback to a fixed version
|
|
return "4.4.2"
|
|
# New version system: YYMMDD_HHmmSS_hash
|
|
# This is duplicated a bit from build_rpm.py:auto_release_str()
|
|
template = '{sub("([:+-]|\d\d\d\d$)", "",date|isodatesec)} {node|short}'
|
|
out = sysstr(hg.run(["log", "-r.", "-T", template]))
|
|
# Some tools parse this number to figure out if they support this version of
|
|
# Mercurial, so prepend with 4.4.2.
|
|
# ex. 4.4.2_20180105_214829_58fda95a0202
|
|
return "_".join(["4.4.2"] + out.split())
|
|
|
|
|
|
version = pickversion()
|
|
versionb = version
|
|
if not isinstance(versionb, bytes):
|
|
versionb = versionb.encode("ascii")
|
|
|
|
# calculate a versionhash, which is used by chg to make sure the client
|
|
# connects to a compatible server.
|
|
versionhash = struct.unpack(">Q", hashlib.sha1(versionb).digest()[:8])[0]
|
|
|
|
write_if_changed(
|
|
"mercurial/__version__.py",
|
|
b"".join(
|
|
[
|
|
b"# this file is autogenerated by setup.py\n"
|
|
b'version = "%s"\n' % versionb,
|
|
b"versionhash = %s\n" % versionhash,
|
|
]
|
|
),
|
|
)
|
|
|
|
try:
|
|
oldpolicy = os.environ.get("HGMODULEPOLICY", None)
|
|
os.environ["HGMODULEPOLICY"] = "py"
|
|
from mercurial import __version__
|
|
|
|
version = __version__.version
|
|
except ImportError:
|
|
version = "unknown"
|
|
finally:
|
|
if oldpolicy is None:
|
|
del os.environ["HGMODULEPOLICY"]
|
|
else:
|
|
os.environ["HGMODULEPOLICY"] = oldpolicy
|
|
|
|
|
|
class hgbuild(build):
|
|
# Insert hgbuildmo first so that files in mercurial/locale/ are found
|
|
# when build_py is run next.
|
|
sub_commands = [("build_mo", None)] + build.sub_commands
|
|
|
|
|
|
class hgbuildmo(build):
|
|
|
|
description = "build translations (.mo files)"
|
|
|
|
def run(self):
|
|
if not find_executable("msgfmt"):
|
|
self.warn(
|
|
"could not find msgfmt executable, no translations " "will be built"
|
|
)
|
|
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")
|
|
mobuildfile = join("mercurial", mofile)
|
|
cmd = ["msgfmt", "-v", "-o", mobuildfile, pofile]
|
|
if sys.platform != "sunos5":
|
|
# msgfmt on Solaris does not know about -c
|
|
cmd.append("-c")
|
|
self.mkpath(join("mercurial", modir))
|
|
self.make_file([pofile], mobuildfile, spawn, (cmd,))
|
|
|
|
|
|
class hgdist(Distribution):
|
|
pure = False
|
|
cffi = ispypy
|
|
|
|
global_options = Distribution.global_options + [
|
|
("pure", None, "use pure (slow) Python " "code instead of C extensions")
|
|
]
|
|
|
|
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)
|
|
|
|
|
|
# This is ugly as a one-liner. So use a variable.
|
|
buildextnegops = dict(getattr(build_ext, "negative_options", {}))
|
|
|
|
|
|
class hgbuildext(build_ext):
|
|
user_options = build_ext.user_options + [
|
|
("re2-src=", None, "directory containing re2 source code")
|
|
]
|
|
|
|
def initialize_options(self):
|
|
self.re2_src = os.environ.get("RE2SRC", "")
|
|
return build_ext.initialize_options(self)
|
|
|
|
def build_extensions(self):
|
|
re2path = self.re2_src
|
|
if re2path:
|
|
self.extensions.append(
|
|
Extension(
|
|
"mercurial.thirdparty.pyre2._re2",
|
|
sources=[
|
|
"mercurial/thirdparty/pyre2/_re2.cc",
|
|
os.path.join(re2path, "re2/bitstate.cc"),
|
|
os.path.join(re2path, "re2/compile.cc"),
|
|
os.path.join(re2path, "re2/dfa.cc"),
|
|
os.path.join(re2path, "re2/filtered_re2.cc"),
|
|
os.path.join(re2path, "re2/mimics_pcre.cc"),
|
|
os.path.join(re2path, "re2/nfa.cc"),
|
|
os.path.join(re2path, "re2/onepass.cc"),
|
|
os.path.join(re2path, "re2/parse.cc"),
|
|
os.path.join(re2path, "re2/perl_groups.cc"),
|
|
os.path.join(re2path, "re2/prefilter.cc"),
|
|
os.path.join(re2path, "re2/prefilter_tree.cc"),
|
|
os.path.join(re2path, "re2/prog.cc"),
|
|
os.path.join(re2path, "re2/re2.cc"),
|
|
os.path.join(re2path, "re2/regexp.cc"),
|
|
os.path.join(re2path, "re2/set.cc"),
|
|
os.path.join(re2path, "re2/simplify.cc"),
|
|
os.path.join(re2path, "re2/stringpiece.cc"),
|
|
os.path.join(re2path, "re2/tostring.cc"),
|
|
os.path.join(re2path, "re2/unicode_casefold.cc"),
|
|
os.path.join(re2path, "re2/unicode_groups.cc"),
|
|
os.path.join(re2path, "util/rune.cc"),
|
|
os.path.join(re2path, "util/strutil.cc"),
|
|
],
|
|
include_dirs=[re2path] + include_dirs,
|
|
depends=common_depends
|
|
+ [
|
|
os.path.join(re2path, "re2/bitmap256.h"),
|
|
os.path.join(re2path, "re2/filtered_re2.h"),
|
|
os.path.join(re2path, "re2/prefilter.h"),
|
|
os.path.join(re2path, "re2/prefilter_tree.h"),
|
|
os.path.join(re2path, "re2/prog.h"),
|
|
os.path.join(re2path, "re2/re2.h"),
|
|
os.path.join(re2path, "re2/regexp.h"),
|
|
os.path.join(re2path, "re2/set.h"),
|
|
os.path.join(re2path, "re2/stringpiece.h"),
|
|
os.path.join(re2path, "re2/unicode_casefold.h"),
|
|
os.path.join(re2path, "re2/unicode_groups.h"),
|
|
os.path.join(re2path, "re2/walker-inl.h"),
|
|
os.path.join(re2path, "util/logging.h"),
|
|
os.path.join(re2path, "util/mix.h"),
|
|
os.path.join(re2path, "util/mutex.h"),
|
|
os.path.join(re2path, "util/sparse_array.h"),
|
|
os.path.join(re2path, "util/sparse_set.h"),
|
|
os.path.join(re2path, "util/strutil.h"),
|
|
os.path.join(re2path, "util/utf.h"),
|
|
os.path.join(re2path, "util/util.h"),
|
|
],
|
|
extra_compile_args=filter(None, [STDCPP11, PRODUCEDEBUGSYMBOLS]),
|
|
)
|
|
)
|
|
|
|
return build_ext.build_extensions(self)
|
|
|
|
def build_extension(self, ext):
|
|
try:
|
|
build_ext.build_extension(self, ext)
|
|
except CCompilerError:
|
|
if not getattr(ext, "optional", False):
|
|
raise
|
|
log.warn("Failed to build optional extension '%s' (skipping)", ext.name)
|
|
|
|
|
|
class hgbuildscripts(build_scripts):
|
|
def run(self):
|
|
# Build chg on non-Windows platform
|
|
if not iswindows:
|
|
cc = new_compiler()
|
|
chgcflags = [
|
|
"-std=c99",
|
|
"-D_GNU_SOURCE",
|
|
"-DHGVERSIONHASH=%sULL" % versionhash,
|
|
]
|
|
if hgname != "hg":
|
|
chgcflags.append('-DHGPATH="%s"' % hgname)
|
|
objs = cc.compile(
|
|
glob.glob("contrib/chg/*.c"), debug=True, extra_preargs=chgcflags
|
|
)
|
|
dest = os.path.join(self.build_dir, "chg")
|
|
cc.link_executable(objs, dest)
|
|
|
|
# Potentially build hg.bat/exe
|
|
if os.name != "nt" or self.distribution.pure:
|
|
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)
|
|
|
|
def copy_scripts(self):
|
|
build_scripts.copy_scripts(self)
|
|
# Rename hg to hgname
|
|
if hgname != "hg":
|
|
oldpath = os.path.join(self.build_dir, "hg")
|
|
newpath = os.path.join(self.build_dir, hgname)
|
|
os.rename(oldpath, newpath)
|
|
|
|
|
|
class hgbuildpy(build_py):
|
|
def finalize_options(self):
|
|
build_py.finalize_options(self)
|
|
|
|
if self.distribution.pure:
|
|
self.distribution.ext_modules = []
|
|
elif self.distribution.cffi:
|
|
from mercurial.cffi import bdiffbuild, mpatchbuild
|
|
|
|
exts = [
|
|
mpatchbuild.ffi.distutils_extension(),
|
|
bdiffbuild.ffi.distutils_extension(),
|
|
]
|
|
# cffi modules go here
|
|
if sys.platform == "darwin":
|
|
from mercurial.cffi import osutilbuild
|
|
|
|
exts.append(osutilbuild.ffi.distutils_extension())
|
|
self.distribution.ext_modules = exts
|
|
else:
|
|
h = os.path.join(get_python_inc(), "Python.h")
|
|
if not os.path.exists(h):
|
|
raise SystemExit(
|
|
"Python headers are required to build "
|
|
"Mercurial but weren't found in %s" % h
|
|
)
|
|
|
|
def run(self):
|
|
basepath = os.path.join(self.build_lib, "mercurial")
|
|
self.mkpath(basepath)
|
|
|
|
if self.distribution.pure:
|
|
modulepolicy = "py"
|
|
elif self.build_lib == ".":
|
|
# in-place build should run without rebuilding C extensions
|
|
modulepolicy = "allow"
|
|
else:
|
|
modulepolicy = "c"
|
|
|
|
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)
|
|
|
|
build_py.run(self)
|
|
|
|
|
|
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):
|
|
with open(self._indexfilename, "w") as f:
|
|
f.write("# empty\n")
|
|
|
|
# here no extension enabled, disabled() lists up everything
|
|
code = (
|
|
"import pprint; from mercurial import extensions; "
|
|
"pprint.pprint(extensions.disabled())"
|
|
)
|
|
returncode, out, err = runcmd([sys.executable, "-c", code], localhgenv())
|
|
if err or returncode != 0:
|
|
raise DistutilsExecError(err)
|
|
|
|
with open(self._indexfilename, "w") as f:
|
|
f.write("# this file is autogenerated by setup.py\n")
|
|
f.write("docs = ")
|
|
f.write(out)
|
|
|
|
|
|
class buildhgexe(build_ext):
|
|
description = "compile hg.exe from mercurial/exewrapper.c"
|
|
user_options = build_ext.user_options + [
|
|
(
|
|
"long-paths-support",
|
|
None,
|
|
"enable support for long paths on "
|
|
"Windows (off by default and "
|
|
"experimental)",
|
|
)
|
|
]
|
|
|
|
LONG_PATHS_MANIFEST = """
|
|
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
|
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
|
|
<application>
|
|
<windowsSettings
|
|
xmlns:ws2="http://schemas.microsoft.com/SMI/2016/WindowsSettings">
|
|
<ws2:longPathAware>true</ws2:longPathAware>
|
|
</windowsSettings>
|
|
</application>
|
|
</assembly>"""
|
|
|
|
def initialize_options(self):
|
|
build_ext.initialize_options(self)
|
|
self.long_paths_support = False
|
|
|
|
def build_extensions(self):
|
|
if os.name != "nt":
|
|
return
|
|
if isinstance(self.compiler, HackedMingw32CCompiler):
|
|
self.compiler.compiler_so = self.compiler.compiler # no -mdll
|
|
self.compiler.dll_libraries = [] # no -lmsrvc90
|
|
|
|
# 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(".dll"):
|
|
raise SystemExit("Python DLL does not end with .dll: %s" % dllbasename)
|
|
pythonlib = dllbasename[:-4]
|
|
else:
|
|
log.warn("could not determine Python DLL filename; " "assuming pythonXY")
|
|
|
|
hv = sys.hexversion
|
|
pythonlib = "python%d%d" % (hv >> 24, (hv >> 16) & 0xff)
|
|
|
|
log.info("using %s as Python library name" % pythonlib)
|
|
with open("mercurial/hgpythonlib.h", "wb") as f:
|
|
f.write("/* this file is autogenerated by setup.py */\n")
|
|
f.write('#define HGPYTHONLIB "%s"\n' % pythonlib)
|
|
objects = self.compiler.compile(
|
|
["mercurial/exewrapper.c"], output_dir=self.build_temp
|
|
)
|
|
dir = os.path.dirname(self.get_ext_fullpath("dummy"))
|
|
self.hgtarget = os.path.join(dir, "hg")
|
|
self.compiler.link_executable(
|
|
objects, self.hgtarget, libraries=[], output_dir=self.build_temp
|
|
)
|
|
if self.long_paths_support:
|
|
self.addlongpathsmanifest()
|
|
|
|
def addlongpathsmanifest(self):
|
|
"""Add manifest pieces so that hg.exe understands long paths
|
|
|
|
This is an EXPERIMENTAL feature, use with care.
|
|
To enable long paths support, one needs to do two things:
|
|
- build Mercurial with --long-paths-support option
|
|
- change HKLM\SYSTEM\CurrentControlSet\Control\FileSystem\
|
|
LongPathsEnabled to have value 1.
|
|
|
|
Please ignore 'warning 81010002: Unrecognized Element "longPathAware"';
|
|
it happens because Mercurial uses mt.exe circa 2008, which is not
|
|
yet aware of long paths support in the manifest (I think so at least).
|
|
This does not stop mt.exe from embedding/merging the XML properly.
|
|
|
|
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, output_dir=self.build_temp
|
|
)
|
|
fdauto, manfname = tempfile.mkstemp(suffix=".hg.exe.manifest")
|
|
os.close(fdauto)
|
|
with open(manfname, "w") as f:
|
|
f.write(self.LONG_PATHS_MANIFEST)
|
|
log.info("long paths manifest is written to '%s'" % manfname)
|
|
inputresource = "-inputresource:%s;#1" % exefname
|
|
outputresource = "-outputresource:%s;#1" % exefname
|
|
log.info("running mt.exe to update hg.exe's manifest in-place")
|
|
# supplying both -manifest and -inputresource to mt.exe makes
|
|
# it merge the embedded and supplied manifests in the -outputresource
|
|
self.spawn(
|
|
["mt.exe", "-nologo", "-manifest", manfname, inputresource, outputresource]
|
|
)
|
|
log.info("done updating hg.exe's manifest")
|
|
os.remove(manfname)
|
|
|
|
@property
|
|
def hgexepath(self):
|
|
dir = os.path.dirname(self.get_ext_fullpath("dummy"))
|
|
return os.path.join(self.build_temp, dir, "hg.exe")
|
|
|
|
|
|
class hginstall(install):
|
|
|
|
user_options = install.user_options + [
|
|
("old-and-unmanageable", None, "noop, present for eggless setuptools compat"),
|
|
(
|
|
"single-version-externally-managed",
|
|
None,
|
|
"noop, present for eggless setuptools compat",
|
|
),
|
|
]
|
|
|
|
# Also helps setuptools not be sad while we refuse to create eggs.
|
|
single_version_externally_managed = True
|
|
|
|
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.
|
|
excl = set(["bdist_egg"])
|
|
return filter(lambda x: x not in excl, install.get_sub_commands(self))
|
|
|
|
|
|
class hginstalllib(install_lib):
|
|
"""
|
|
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.
|
|
"""
|
|
|
|
def run(self):
|
|
realcopyfile = file_util.copy_file
|
|
|
|
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:
|
|
setmode = int("0755", 8)
|
|
else:
|
|
setmode = int("0644", 8)
|
|
m = stat.S_IMODE(st[stat.ST_MODE])
|
|
m = (m & ~int("0777", 8)) | setmode
|
|
os.chmod(dst, m)
|
|
|
|
file_util.copy_file = copyfileandsetmode
|
|
try:
|
|
install_lib.run(self)
|
|
finally:
|
|
file_util.copy_file = realcopyfile
|
|
|
|
|
|
class hginstallscripts(install_scripts):
|
|
"""
|
|
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.
|
|
"""
|
|
|
|
def initialize_options(self):
|
|
install_scripts.initialize_options(self)
|
|
|
|
self.install_lib = None
|
|
|
|
def finalize_options(self):
|
|
install_scripts.finalize_options(self)
|
|
self.set_undefined_options("install", ("install_lib", "install_lib"))
|
|
|
|
def run(self):
|
|
install_scripts.run(self)
|
|
|
|
# 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.
|
|
|
|
if (
|
|
os.path.splitdrive(self.install_dir)[0]
|
|
!= os.path.splitdrive(self.install_lib)[0]
|
|
):
|
|
# can't make relative paths from one drive to another, so use an
|
|
# absolute path instead
|
|
libdir = self.install_lib
|
|
else:
|
|
common = os.path.commonprefix((self.install_dir, self.install_lib))
|
|
rest = self.install_dir[len(common) :]
|
|
uplevel = len([n for n in os.path.split(rest) if n])
|
|
|
|
libdir = uplevel * (".." + os.sep) + self.install_lib[len(common) :]
|
|
|
|
for outfile in self.outfiles:
|
|
with open(outfile, "rb") as fp:
|
|
data = fp.read()
|
|
|
|
# skip binary files
|
|
if b"\0" in data:
|
|
continue
|
|
|
|
# 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"):
|
|
log.info(
|
|
"not rewriting @LIBDIR@ in %s because install path "
|
|
"not known" % outfile
|
|
)
|
|
continue
|
|
|
|
data = data.replace(b"@LIBDIR@", libdir.encode(libdir_escape))
|
|
with open(outfile, "wb") as fp:
|
|
fp.write(data)
|
|
|
|
|
|
cmdclass = {
|
|
"build": hgbuild,
|
|
"build_mo": hgbuildmo,
|
|
"build_ext": hgbuildext,
|
|
"build_py": hgbuildpy,
|
|
"build_scripts": hgbuildscripts,
|
|
"build_hgextindex": buildhgextindex,
|
|
"install": hginstall,
|
|
"install_lib": hginstalllib,
|
|
"install_scripts": hginstallscripts,
|
|
"build_hgexe": buildhgexe,
|
|
"build_rust_ext": BuildRustExt,
|
|
}
|
|
|
|
packages = [
|
|
"mercurial",
|
|
"mercurial.cext",
|
|
"mercurial.cffi",
|
|
"mercurial.fb",
|
|
"mercurial.hgweb",
|
|
"mercurial.httpclient",
|
|
"mercurial.pure",
|
|
"mercurial.rust",
|
|
"mercurial.thirdparty",
|
|
"mercurial.thirdparty.attr",
|
|
"mercurial.thirdparty.pyre2",
|
|
"hgext",
|
|
"hgext.absorb",
|
|
"hgext.commitcloud",
|
|
"hgext.convert",
|
|
"hgext.extlib",
|
|
"hgext.extlib.phabricator",
|
|
"hgext.extlib.pywatchman",
|
|
"hgext.extlib.watchmanclient",
|
|
"hgext.fastannotate",
|
|
"hgext.fastmanifest",
|
|
"hgext.fbamend",
|
|
"hgext.fsmonitor",
|
|
"hgext.hggit",
|
|
"hgext.hgevents",
|
|
"hgext.hgsubversion",
|
|
"hgext.hgsubversion.hooks",
|
|
"hgext.hgsubversion.layouts",
|
|
"hgext.hgsubversion.svnwrap",
|
|
"hgext.infinitepush",
|
|
"hgext.p4fastimport",
|
|
"hgext.remotefilelog",
|
|
"hgext.treemanifest",
|
|
"hgext.highlight",
|
|
"hgext.lfs",
|
|
"hgext.zeroconf",
|
|
"hgext3rd",
|
|
"hgdemandimport",
|
|
]
|
|
|
|
common_depends = [
|
|
"mercurial/bitmanipulation.h",
|
|
"mercurial/compat.h",
|
|
"mercurial/cext/util.h",
|
|
]
|
|
|
|
|
|
def get_env_path_list(var_name, default=None):
|
|
"""Get a path list from an environment variable. The variable is parsed as
|
|
a colon-separated list."""
|
|
value = os.environ.get(var_name)
|
|
if not value:
|
|
return default
|
|
return value.split(os.path.pathsep)
|
|
|
|
|
|
def filter_existing_dirs(dirs):
|
|
"""Filters the given list and keeps only existing directory names."""
|
|
return [d for d in dirs if os.path.isdir(d)]
|
|
|
|
|
|
def distutils_dir_name(dname):
|
|
"""Returns the name of a distutils build directory"""
|
|
f = "{dirname}.{platform}-{version}"
|
|
return f.format(
|
|
dirname=dname, platform=distutils.util.get_platform(), version=sys.version[:3]
|
|
)
|
|
|
|
|
|
# Historical default values.
|
|
# We should perhaps clean these up in the future after verifying that it
|
|
# doesn't break the build on any platforms.
|
|
#
|
|
# The /usr/local/* directories shouldn't actually be needed--the compiler
|
|
# should already use these directories when appropriate (e.g., if we are
|
|
# using the standard system compiler that has them in its default paths).
|
|
#
|
|
# The /opt/local paths may be necessary on Darwin builds.
|
|
include_dirs = get_env_path_list("INCLUDE_DIRS")
|
|
if include_dirs is None:
|
|
if iswindows:
|
|
include_dirs = []
|
|
else:
|
|
include_dirs = filter_existing_dirs(
|
|
["/usr/local/include", "/opt/local/include", "/opt/homebrew/include/"]
|
|
)
|
|
include_dirs = ["."] + include_dirs
|
|
|
|
library_dirs = get_env_path_list("LIBRARY_DIRS")
|
|
if library_dirs is None:
|
|
if iswindows:
|
|
library_dirs = []
|
|
else:
|
|
library_dirs = filter_existing_dirs(
|
|
["/usr/local/lib", "/opt/local/lib", "/opt/homebrew/lib/"]
|
|
)
|
|
library_dirs.append("build/" + distutils_dir_name("lib"))
|
|
|
|
extra_libs = get_env_path_list("EXTRA_LIBS", [])
|
|
|
|
osutil_cflags = []
|
|
osutil_ldflags = []
|
|
|
|
# platform specific macros
|
|
for plat, func in [("bsd", "setproctitle")]:
|
|
if re.search(plat, sys.platform) and hasfunction(new_compiler(), func):
|
|
osutil_cflags.append("-DHAVE_%s" % func.upper())
|
|
|
|
for plat, macro, code in [
|
|
(
|
|
"bsd|darwin",
|
|
"BSD_STATFS",
|
|
"""
|
|
#include <sys/param.h>
|
|
#include <sys/mount.h>
|
|
int main() { struct statfs s; return sizeof(s.f_fstypename); }
|
|
""",
|
|
),
|
|
(
|
|
"linux",
|
|
"LINUX_STATFS",
|
|
"""
|
|
#include <linux/magic.h>
|
|
#include <sys/vfs.h>
|
|
int main() { struct statfs s; return sizeof(s.f_type); }
|
|
""",
|
|
),
|
|
]:
|
|
if re.search(plat, sys.platform) and cancompile(new_compiler(), code):
|
|
osutil_cflags.append("-DHAVE_%s" % macro)
|
|
|
|
if sys.platform == "darwin":
|
|
osutil_ldflags += ["-framework", "ApplicationServices"]
|
|
|
|
extmodules = [
|
|
Extension(
|
|
"mercurial.cext.base85",
|
|
["mercurial/cext/base85.c"],
|
|
include_dirs=include_dirs,
|
|
depends=common_depends,
|
|
),
|
|
Extension(
|
|
"mercurial.cext.bdiff",
|
|
["mercurial/bdiff.c", "mercurial/cext/bdiff.c"],
|
|
include_dirs=include_dirs,
|
|
depends=common_depends + ["mercurial/bdiff.h"],
|
|
),
|
|
Extension(
|
|
"mercurial.cext.diffhelpers",
|
|
["mercurial/cext/diffhelpers.c"],
|
|
include_dirs=include_dirs,
|
|
depends=common_depends,
|
|
),
|
|
Extension(
|
|
"mercurial.cext.mpatch",
|
|
["mercurial/mpatch.c", "mercurial/cext/mpatch.c"],
|
|
include_dirs=include_dirs,
|
|
depends=common_depends + ["mercurial/mpatch.h"],
|
|
),
|
|
Extension(
|
|
"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",
|
|
],
|
|
include_dirs=include_dirs,
|
|
depends=common_depends + ["mercurial/cext/charencode.h"],
|
|
),
|
|
Extension(
|
|
"mercurial.cext.osutil",
|
|
["mercurial/cext/osutil.c"],
|
|
include_dirs=include_dirs,
|
|
extra_compile_args=osutil_cflags,
|
|
extra_link_args=osutil_ldflags,
|
|
depends=common_depends,
|
|
),
|
|
Extension(
|
|
"mercurial.cext.xdiff",
|
|
sources=[
|
|
"lib/third-party/xdiff/xdiffi.c",
|
|
"lib/third-party/xdiff/xprepare.c",
|
|
"lib/third-party/xdiff/xutils.c",
|
|
"mercurial/cext/xdiff.c",
|
|
],
|
|
include_dirs=include_dirs + ["lib/third-party/xdiff"],
|
|
depends=common_depends
|
|
+ [
|
|
"lib/third-party/xdiff/xdiff.h",
|
|
"lib/third-party/xdiff/xdiffi.h",
|
|
"lib/third-party/xdiff/xinclude.h",
|
|
"lib/third-party/xdiff/xmacros.h",
|
|
"lib/third-party/xdiff/xprepare.h",
|
|
"lib/third-party/xdiff/xtypes.h",
|
|
"lib/third-party/xdiff/xutils.h",
|
|
],
|
|
),
|
|
Extension("hgext.extlib.pywatchman.bser", ["hgext/extlib/pywatchman/bser.c"]),
|
|
Extension(
|
|
"hgext.extlib.cstore",
|
|
sources=[
|
|
"hgext/extlib/cstore/datapackstore.cpp",
|
|
"hgext/extlib/cstore/deltachain.cpp",
|
|
"hgext/extlib/cstore/py-cstore.cpp",
|
|
"hgext/extlib/cstore/pythonutil.cpp",
|
|
"hgext/extlib/cstore/pythondatastore.cpp",
|
|
"hgext/extlib/cstore/uniondatapackstore.cpp",
|
|
"hgext/extlib/ctreemanifest/manifest.cpp",
|
|
"hgext/extlib/ctreemanifest/manifest_entry.cpp",
|
|
"hgext/extlib/ctreemanifest/manifest_fetcher.cpp",
|
|
"hgext/extlib/ctreemanifest/manifest_ptr.cpp",
|
|
"hgext/extlib/ctreemanifest/treemanifest.cpp",
|
|
],
|
|
depends=[
|
|
"hgext/extlib/cstore/datapackstore.h",
|
|
"hgext/extlib/cstore/datastore.h",
|
|
"hgext/extlib/cstore/deltachain.h",
|
|
"hgext/extlib/cstore/key.h",
|
|
"hgext/extlib/cstore/match.h",
|
|
"hgext/extlib/cstore/py-cdatapack.h",
|
|
"hgext/extlib/cstore/py-datapackstore.h",
|
|
"hgext/extlib/cstore/py-structs.h",
|
|
"hgext/extlib/cstore/py-treemanifest.h",
|
|
"hgext/extlib/cstore/pythondatastore.h",
|
|
"hgext/extlib/cstore/pythonkeyiterator.h",
|
|
"hgext/extlib/cstore/pythonutil.h",
|
|
"hgext/extlib/cstore/store.h",
|
|
"hgext/extlib/cstore/uniondatapackstore.h",
|
|
"hgext/extlib/cstore/util.h",
|
|
],
|
|
include_dirs=include_dirs,
|
|
library_dirs=["build/" + distutils_dir_name("lib")] + library_dirs,
|
|
libraries=["datapack", "lz4", "mpatch", SHA1_LIBRARY],
|
|
extra_compile_args=filter(None, [STDCPP0X, WALL] + cflags),
|
|
),
|
|
Extension(
|
|
"hgext.extlib.cfastmanifest",
|
|
sources=[
|
|
"hgext/extlib/cfastmanifest.c",
|
|
"hgext/extlib/cfastmanifest/bsearch.c",
|
|
"lib/clib/buffer.c",
|
|
"hgext/extlib/cfastmanifest/checksum.c",
|
|
"hgext/extlib/cfastmanifest/node.c",
|
|
"hgext/extlib/cfastmanifest/tree.c",
|
|
"hgext/extlib/cfastmanifest/tree_arena.c",
|
|
"hgext/extlib/cfastmanifest/tree_convert.c",
|
|
"hgext/extlib/cfastmanifest/tree_copy.c",
|
|
"hgext/extlib/cfastmanifest/tree_diff.c",
|
|
"hgext/extlib/cfastmanifest/tree_disk.c",
|
|
"hgext/extlib/cfastmanifest/tree_iterator.c",
|
|
"hgext/extlib/cfastmanifest/tree_path.c",
|
|
],
|
|
depends=[
|
|
"hgext/extlib/cfastmanifest/bsearch.h",
|
|
"hgext/extlib/cfastmanifest/checksum.h",
|
|
"hgext/extlib/cfastmanifest/internal_result.h",
|
|
"hgext/extlib/cfastmanifest/node.h",
|
|
"hgext/extlib/cfastmanifest/path_buffer.h",
|
|
"hgext/extlib/cfastmanifest/result.h",
|
|
"hgext/extlib/cfastmanifest/tests.h",
|
|
"hgext/extlib/cfastmanifest/tree_arena.h",
|
|
"hgext/extlib/cfastmanifest/tree.h",
|
|
"hgext/extlib/cfastmanifest/tree_iterator.h",
|
|
"hgext/extlib/cfastmanifest/tree_path.h",
|
|
],
|
|
include_dirs=include_dirs,
|
|
library_dirs=library_dirs,
|
|
libraries=[SHA1_LIBRARY],
|
|
extra_compile_args=filter(None, [STDC99, WALL, WSTRICTPROTOTYPES] + cflags),
|
|
),
|
|
]
|
|
|
|
# Cython modules
|
|
# see http://cython.readthedocs.io/en/latest/src/reference/compilation.html
|
|
cythonopts = {"unraisable_tracebacks": False, "c_string_type": "bytes"}
|
|
|
|
extmodules += cythonize(
|
|
[
|
|
Extension(
|
|
"hgext.clindex",
|
|
sources=["hgext/clindex.pyx"],
|
|
extra_compile_args=filter(None, [STDC99, PRODUCEDEBUGSYMBOLS]),
|
|
),
|
|
Extension(
|
|
"hgext.extlib.litemmap",
|
|
sources=["hgext/extlib/litemmap.pyx"],
|
|
extra_compile_args=filter(None, [STDC99, PRODUCEDEBUGSYMBOLS]),
|
|
),
|
|
Extension(
|
|
"hgext.patchrmdir",
|
|
sources=["hgext/patchrmdir.pyx"],
|
|
extra_compile_args=filter(None, [PRODUCEDEBUGSYMBOLS]),
|
|
),
|
|
Extension(
|
|
"hgext.traceprof",
|
|
sources=["hgext/traceprof.pyx"],
|
|
include_dirs=include_dirs,
|
|
extra_compile_args=filter(None, [STDCPP11, PRODUCEDEBUGSYMBOLS]),
|
|
),
|
|
Extension(
|
|
"hgext.extlib.linelog",
|
|
sources=["hgext/extlib/linelog.pyx"],
|
|
include_dirs=include_dirs,
|
|
extra_compile_args=filter(None, [STDC99, PRODUCEDEBUGSYMBOLS]),
|
|
),
|
|
],
|
|
compiler_directives=cythonopts,
|
|
)
|
|
|
|
libraries = [
|
|
(
|
|
"datapack",
|
|
{
|
|
"sources": ["lib/cdatapack/cdatapack.c"],
|
|
"depends": ["lib/cdatapack/cdatapack.h"],
|
|
"include_dirs": ["."] + include_dirs,
|
|
"libraries": ["lz4", SHA1_LIBRARY],
|
|
"extra_args": filter(None, [STDC99, WALL, WSTRICTPROTOTYPES] + cflags),
|
|
},
|
|
),
|
|
(
|
|
"sha1detectcoll",
|
|
{
|
|
"sources": [
|
|
"lib/third-party/sha1dc/sha1.c",
|
|
"lib/third-party/sha1dc/ubc_check.c",
|
|
],
|
|
"depends": [
|
|
"lib/third-party/sha1dc/sha1.h",
|
|
"lib/third-party/sha1dc/ubc_check.h",
|
|
],
|
|
"include_dirs": ["lib/third-party"] + include_dirs,
|
|
"extra_args": filter(None, [STDC99, WALL, WSTRICTPROTOTYPES] + cflags),
|
|
},
|
|
),
|
|
(
|
|
"mpatch",
|
|
{
|
|
"sources": ["mercurial/mpatch.c"],
|
|
"depends": [
|
|
"mercurial/bitmanipulation.h",
|
|
"mercurial/compat.h",
|
|
"mercurial/mpatch.h",
|
|
],
|
|
"include_dirs": ["."] + include_dirs,
|
|
},
|
|
),
|
|
]
|
|
|
|
# let's add EXTRA_LIBS to every buildable
|
|
for extmodule in extmodules:
|
|
extmodule.libraries.extend(extra_libs)
|
|
for libname, libspec in libraries:
|
|
libspec["libraries"] = libspec.get("libraries", []) + extra_libs
|
|
|
|
try:
|
|
from distutils import cygwinccompiler
|
|
|
|
# the -mno-cygwin option has been deprecated for years
|
|
mingw32compilerclass = cygwinccompiler.Mingw32CCompiler
|
|
|
|
class HackedMingw32CCompiler(cygwinccompiler.Mingw32CCompiler):
|
|
def __init__(self, *args, **kwargs):
|
|
mingw32compilerclass.__init__(self, *args, **kwargs)
|
|
for i in "compiler compiler_so linker_exe linker_so".split():
|
|
try:
|
|
getattr(self, i).remove("-mno-cygwin")
|
|
except ValueError:
|
|
pass
|
|
|
|
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
|
|
class HackedMingw32CCompiler(object):
|
|
pass
|
|
|
|
|
|
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
|
|
|
|
msvccompilerclass = msvccompiler.MSVCCompiler
|
|
|
|
class HackedMSVCCompiler(msvccompiler.MSVCCompiler):
|
|
def initialize(self):
|
|
msvccompilerclass.initialize(self)
|
|
# "warning LNK4197: export 'func' specified multiple times"
|
|
self.ldflags_shared.append("/ignore:4197")
|
|
self.ldflags_shared.append("/DEBUG")
|
|
self.ldflags_shared_debug.append("/ignore:4197")
|
|
self.compile_options.append("/Z7")
|
|
|
|
msvccompiler.MSVCCompiler = HackedMSVCCompiler
|
|
|
|
packagedata = {
|
|
"mercurial": [
|
|
"locale/*/LC_MESSAGES/hg.mo",
|
|
"help/*.txt",
|
|
"help/internals/*.txt",
|
|
"help/subversion/*.rst",
|
|
"default.d/*.rc",
|
|
"dummycert.pem",
|
|
]
|
|
}
|
|
|
|
|
|
def ordinarypath(p):
|
|
return p and p[0] != "." and p[-1] != "~"
|
|
|
|
|
|
for root in ("templates",):
|
|
for curdir, dirs, files in os.walk(os.path.join("mercurial", root)):
|
|
curdir = curdir.split(os.sep, 1)[1]
|
|
dirs[:] = filter(ordinarypath, dirs)
|
|
for f in filter(ordinarypath, files):
|
|
f = os.path.join(curdir, f)
|
|
packagedata["mercurial"].append(f)
|
|
|
|
datafiles = [("", ["CONTRIBUTING", "CONTRIBUTORS"])]
|
|
templatesdir = "mercurial/templates"
|
|
for parent, dirs, files in os.walk(templatesdir):
|
|
dirfiles = [os.path.join(parent, fn) for fn in files]
|
|
datafiles.append((os.path.join("templates", parent), dirfiles))
|
|
|
|
|
|
# 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.
|
|
assert isinstance(version, bytes)
|
|
setupversion = version.decode("ascii")
|
|
|
|
extra = {}
|
|
|
|
if issetuptools:
|
|
extra["python_requires"] = supportedpy
|
|
if py2exeloaded:
|
|
extra["console"] = [
|
|
{
|
|
"script": "hg",
|
|
"copyright": "Copyright (C) 2005-2017 Matt Mackall and others",
|
|
"product_version": version,
|
|
}
|
|
]
|
|
# sub command of 'build' because 'py2exe' does not handle sub_commands
|
|
build.sub_commands.insert(0, ("build_hgextindex", None))
|
|
# put dlls in sub directory so that they won't pollute PATH
|
|
extra["zipfile"] = "lib/library.zip"
|
|
|
|
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
|
|
setupversion = version.split("+", 1)[0]
|
|
|
|
if sys.platform == "darwin" and os.path.exists("/usr/bin/xcodebuild"):
|
|
version = runcmd(["/usr/bin/xcodebuild", "-version"], {})[1].splitlines()
|
|
if version:
|
|
version = version[0]
|
|
if sys.version_info[0] == 3:
|
|
version = version.decode("utf-8")
|
|
xcode4 = version.startswith("Xcode") and StrictVersion(
|
|
version.split()[1]
|
|
) >= StrictVersion("4.0")
|
|
xcode51 = re.match(r"^Xcode\s+5\.1", version) is not None
|
|
else:
|
|
# xcodebuild returns empty on OS X Lion with XCode 4.3 not
|
|
# installed, but instead with only command-line tools. Assume
|
|
# that only happens on >= Lion, thus no PPC support.
|
|
xcode4 = True
|
|
xcode51 = False
|
|
|
|
# XCode 4.0 dropped support for ppc architecture, which is hardcoded in
|
|
# distutils.sysconfig
|
|
if xcode4:
|
|
os.environ["ARCHFLAGS"] = ""
|
|
|
|
# XCode 5.1 changes clang such that it now fails to compile if the
|
|
# -mno-fused-madd flag is passed, but the version of Python shipped with
|
|
# OS X 10.9 Mavericks includes this flag. This causes problems in all
|
|
# C extension modules, and a bug has been filed upstream at
|
|
# http://bugs.python.org/issue21244. We also need to patch this here
|
|
# so Mercurial can continue to compile in the meantime.
|
|
if xcode51:
|
|
cflags = get_config_var("CFLAGS")
|
|
if cflags and re.search(r"-mno-fused-madd\b", cflags) is not None:
|
|
os.environ["CFLAGS"] = os.environ.get("CFLAGS", "") + " -Qunused-arguments"
|
|
|
|
import distutils.command.build_clib
|
|
from distutils.dep_util import newer_group
|
|
from distutils.errors import DistutilsSetupError
|
|
|
|
|
|
def build_libraries(self, libraries):
|
|
for (lib_name, build_info) in libraries:
|
|
sources = build_info.get("sources")
|
|
if sources is None or not isinstance(sources, (list, tuple)):
|
|
raise DistutilsSetupError(
|
|
"in 'libraries' option (library '%s'), "
|
|
+ "'sources' must be present and must be "
|
|
+ "a list of source filenames"
|
|
) % lib_name
|
|
sources = list(sources)
|
|
|
|
lib_path = self.compiler.library_filename(lib_name, output_dir=self.build_clib)
|
|
depends = sources + build_info.get("depends", [])
|
|
if not (self.force or newer_group(depends, lib_path, "newer")):
|
|
log.debug("skipping '%s' library (up-to-date)", lib_name)
|
|
continue
|
|
else:
|
|
log.info("building '%s' library" % lib_name)
|
|
|
|
# First, compile the source code to object files in the library
|
|
# directory. (This should probably change to putting object
|
|
# files in a temporary build directory.)
|
|
macros = build_info.get("macros", [])
|
|
include_dirs = build_info.get("include_dirs")
|
|
extra_args = build_info.get("extra_args")
|
|
objects = self.compiler.compile(
|
|
sources,
|
|
output_dir=self.build_temp,
|
|
macros=macros,
|
|
include_dirs=include_dirs,
|
|
debug=self.debug,
|
|
extra_postargs=extra_args,
|
|
)
|
|
|
|
# Now "link" the object files together into a static library.
|
|
# (On Unix at least, this isn't really linking -- it just
|
|
# builds an archive. Whatever.)
|
|
libraries = build_info.get("libraries", [])
|
|
for lib in libraries:
|
|
self.compiler.add_library(lib)
|
|
self.compiler.create_static_lib(
|
|
objects, lib_name, output_dir=self.build_clib, debug=self.debug
|
|
)
|
|
|
|
|
|
distutils.command.build_clib.build_clib.build_libraries = build_libraries
|
|
|
|
rustvendoredcrates = [RustVendoredCrates("hg-vendored-crates", dest="build")]
|
|
|
|
rustextmodules = [
|
|
RustExtension(
|
|
"pyrevisionstore",
|
|
package="hgext.extlib",
|
|
manifest="hgext/extlib/pyrevisionstore/Cargo.toml",
|
|
),
|
|
RustExtension(
|
|
"indexes", package="hgext.extlib", manifest="hgext/extlib/indexes/Cargo.toml"
|
|
),
|
|
RustExtension(
|
|
"matcher",
|
|
package="mercurial.rust",
|
|
manifest="mercurial/rust/matcher/Cargo.toml",
|
|
),
|
|
RustExtension(
|
|
"treestate",
|
|
package="mercurial.rust",
|
|
manifest="mercurial/rust/treestate/Cargo.toml",
|
|
),
|
|
RustExtension(
|
|
"zstd", package="mercurial.rust", manifest="mercurial/rust/zstd/Cargo.toml"
|
|
),
|
|
]
|
|
|
|
rustextbinaries = [RustBinary("scm_daemon", manifest="exec/scm_daemon/Cargo.toml")]
|
|
|
|
setup(
|
|
name="mercurial",
|
|
version=setupversion,
|
|
author="Matt Mackall and many others",
|
|
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,
|
|
libraries=libraries,
|
|
rust_vendored_crates=rustvendoredcrates,
|
|
rust_ext_modules=rustextmodules,
|
|
rust_ext_binaries=rustextbinaries,
|
|
data_files=datafiles,
|
|
package_data=packagedata,
|
|
cmdclass=cmdclass,
|
|
distclass=hgdist,
|
|
options={
|
|
"py2exe": {
|
|
"packages": [
|
|
"hgdemandimport",
|
|
"hgext",
|
|
"email",
|
|
# implicitly imported per module policy
|
|
# (cffi wouldn't be used as a frozen exe)
|
|
"mercurial.cext",
|
|
# 'mercurial.cffi',
|
|
"mercurial.pure",
|
|
],
|
|
"excludes": ["Tkinter", "sqlite3", "_sqlite3", "sqlite3.dbapi"],
|
|
"includes": ["dumbdbm", "dbhash"],
|
|
},
|
|
"bdist_mpkg": {
|
|
"zipdist": False,
|
|
"license": "COPYING",
|
|
"readme": "contrib/macosx/Readme.html",
|
|
"welcome": "contrib/macosx/Welcome.html",
|
|
},
|
|
},
|
|
**extra
|
|
)
|