130 raise SystemExit( |
130 raise SystemExit( |
131 "Couldn't import standard bz2 (incomplete Python install).") |
131 "Couldn't import standard bz2 (incomplete Python install).") |
132 |
132 |
133 ispypy = "PyPy" in sys.version |
133 ispypy = "PyPy" in sys.version |
134 |
134 |
135 iswithrustextensions = 'HGWITHRUSTEXT' in os.environ |
135 hgrustext = os.environ.get('HGWITHRUSTEXT') |
|
136 # TODO record it for proper rebuild upon changes |
|
137 # (see mercurial/__modulepolicy__.py) |
|
138 if hgrustext != 'cpython' and hgrustext is not None: |
|
139 hgrustext = 'direct-ffi' |
136 |
140 |
137 import ctypes |
141 import ctypes |
138 import errno |
142 import errno |
139 import stat, subprocess, time |
143 import stat, subprocess, time |
140 import re |
144 import re |
456 def initialize_options(self): |
460 def initialize_options(self): |
457 self.zstd = True |
461 self.zstd = True |
458 return build_ext.initialize_options(self) |
462 return build_ext.initialize_options(self) |
459 |
463 |
460 def build_extensions(self): |
464 def build_extensions(self): |
|
465 ruststandalones = [e for e in self.extensions |
|
466 if isinstance(e, RustStandaloneExtension)] |
|
467 self.extensions = [e for e in self.extensions |
|
468 if e not in ruststandalones] |
461 # Filter out zstd if disabled via argument. |
469 # Filter out zstd if disabled via argument. |
462 if not self.zstd: |
470 if not self.zstd: |
463 self.extensions = [e for e in self.extensions |
471 self.extensions = [e for e in self.extensions |
464 if e.name != 'mercurial.zstd'] |
472 if e.name != 'mercurial.zstd'] |
|
473 |
|
474 for rustext in ruststandalones: |
|
475 rustext.build('' if self.inplace else self.build_lib) |
465 |
476 |
466 return build_ext.build_extensions(self) |
477 return build_ext.build_extensions(self) |
467 |
478 |
468 def build_extension(self, ext): |
479 def build_extension(self, ext): |
469 if isinstance(ext, RustExtension): |
480 if isinstance(ext, RustExtension): |
901 |
912 |
902 class RustCompilationError(CCompilerError): |
913 class RustCompilationError(CCompilerError): |
903 """Exception class for Rust compilation errors.""" |
914 """Exception class for Rust compilation errors.""" |
904 |
915 |
905 class RustExtension(Extension): |
916 class RustExtension(Extension): |
906 """A C Extension, conditionnally enhanced with Rust code. |
917 """Base classes for concrete Rust Extension classes. |
907 |
|
908 if iswithrustextensions is False, does nothing else than plain Extension |
|
909 """ |
918 """ |
910 |
919 |
911 rusttargetdir = os.path.join('rust', 'target', 'release') |
920 rusttargetdir = os.path.join('rust', 'target', 'release') |
912 |
921 |
913 def __init__(self, mpath, sources, rustlibname, subcrate, **kw): |
922 def __init__(self, mpath, sources, rustlibname, subcrate, **kw): |
914 Extension.__init__(self, mpath, sources, **kw) |
923 Extension.__init__(self, mpath, sources, **kw) |
915 if not iswithrustextensions: |
924 if hgrustext is None: |
916 return |
925 return |
917 srcdir = self.rustsrcdir = os.path.join('rust', subcrate) |
926 srcdir = self.rustsrcdir = os.path.join('rust', subcrate) |
918 self.libraries.append(rustlibname) |
|
919 self.extra_compile_args.append('-DWITH_RUST') |
|
920 |
927 |
921 # adding Rust source and control files to depends so that the extension |
928 # adding Rust source and control files to depends so that the extension |
922 # gets rebuilt if they've changed |
929 # gets rebuilt if they've changed |
923 self.depends.append(os.path.join(srcdir, 'Cargo.toml')) |
930 self.depends.append(os.path.join(srcdir, 'Cargo.toml')) |
924 cargo_lock = os.path.join(srcdir, 'Cargo.lock') |
931 cargo_lock = os.path.join(srcdir, 'Cargo.lock') |
928 self.depends.extend(os.path.join(dirpath, fname) |
935 self.depends.extend(os.path.join(dirpath, fname) |
929 for fname in fnames |
936 for fname in fnames |
930 if os.path.splitext(fname)[1] == '.rs') |
937 if os.path.splitext(fname)[1] == '.rs') |
931 |
938 |
932 def rustbuild(self): |
939 def rustbuild(self): |
933 if not iswithrustextensions: |
940 if hgrustext is None: |
934 return |
941 return |
935 env = os.environ.copy() |
942 env = os.environ.copy() |
936 if 'HGTEST_RESTOREENV' in env: |
943 if 'HGTEST_RESTOREENV' in env: |
937 # Mercurial tests change HOME to a temporary directory, |
944 # Mercurial tests change HOME to a temporary directory, |
938 # but, if installed with rustup, the Rust toolchain needs |
945 # but, if installed with rustup, the Rust toolchain needs |
960 except subprocess.CalledProcessError: |
967 except subprocess.CalledProcessError: |
961 raise RustCompilationError( |
968 raise RustCompilationError( |
962 "Cargo failed. Working directory: %r, " |
969 "Cargo failed. Working directory: %r, " |
963 "command: %r, environment: %r" % (self.rustsrcdir, cmd, env)) |
970 "command: %r, environment: %r" % (self.rustsrcdir, cmd, env)) |
964 |
971 |
|
972 class RustEnhancedExtension(RustExtension): |
|
973 """A C Extension, conditionally enhanced with Rust code. |
|
974 |
|
975 If the HGRUSTEXT environment variable is set to something else |
|
976 than 'cpython', the Rust sources get compiled and linked within the |
|
977 C target shared library object. |
|
978 """ |
|
979 |
|
980 def __init__(self, mpath, sources, rustlibname, subcrate, **kw): |
|
981 RustExtension.__init__(self, mpath, sources, rustlibname, subcrate, |
|
982 **kw) |
|
983 if hgrustext != 'direct-ffi': |
|
984 return |
|
985 self.extra_compile_args.append('-DWITH_RUST') |
|
986 self.libraries.append(rustlibname) |
|
987 self.library_dirs.append(self.rusttargetdir) |
|
988 |
|
989 class RustStandaloneExtension(RustExtension): |
|
990 |
|
991 def __init__(self, pydottedname, rustcrate, dylibname, **kw): |
|
992 RustExtension.__init__(self, pydottedname, [], dylibname, rustcrate, |
|
993 **kw) |
|
994 self.dylibname = dylibname |
|
995 |
|
996 def build(self, target_dir): |
|
997 self.rustbuild() |
|
998 target = [target_dir] |
|
999 target.extend(self.name.split('.')) |
|
1000 ext = '.so' # TODO Unix only |
|
1001 target[-1] += ext |
|
1002 shutil.copy2(os.path.join(self.rusttargetdir, self.dylibname + ext), |
|
1003 os.path.join(*target)) |
|
1004 |
|
1005 |
965 extmodules = [ |
1006 extmodules = [ |
966 Extension('mercurial.cext.base85', ['mercurial/cext/base85.c'], |
1007 Extension('mercurial.cext.base85', ['mercurial/cext/base85.c'], |
967 include_dirs=common_include_dirs, |
1008 include_dirs=common_include_dirs, |
968 depends=common_depends), |
1009 depends=common_depends), |
969 Extension('mercurial.cext.bdiff', ['mercurial/bdiff.c', |
1010 Extension('mercurial.cext.bdiff', ['mercurial/bdiff.c', |
972 depends=common_depends + ['mercurial/bdiff.h'] + xdiff_headers), |
1013 depends=common_depends + ['mercurial/bdiff.h'] + xdiff_headers), |
973 Extension('mercurial.cext.mpatch', ['mercurial/mpatch.c', |
1014 Extension('mercurial.cext.mpatch', ['mercurial/mpatch.c', |
974 'mercurial/cext/mpatch.c'], |
1015 'mercurial/cext/mpatch.c'], |
975 include_dirs=common_include_dirs, |
1016 include_dirs=common_include_dirs, |
976 depends=common_depends), |
1017 depends=common_depends), |
977 RustExtension('mercurial.cext.parsers', ['mercurial/cext/charencode.c', |
1018 RustEnhancedExtension( |
978 'mercurial/cext/dirs.c', |
1019 'mercurial.cext.parsers', ['mercurial/cext/charencode.c', |
979 'mercurial/cext/manifest.c', |
1020 'mercurial/cext/dirs.c', |
980 'mercurial/cext/parsers.c', |
1021 'mercurial/cext/manifest.c', |
981 'mercurial/cext/pathencode.c', |
1022 'mercurial/cext/parsers.c', |
982 'mercurial/cext/revlog.c'], |
1023 'mercurial/cext/pathencode.c', |
983 'hgdirectffi', |
1024 'mercurial/cext/revlog.c'], |
984 'hg-direct-ffi', |
1025 'hgdirectffi', |
985 include_dirs=common_include_dirs, |
1026 'hg-direct-ffi', |
986 depends=common_depends + ['mercurial/cext/charencode.h', |
1027 include_dirs=common_include_dirs, |
987 'mercurial/cext/revlog.h', |
1028 depends=common_depends + ['mercurial/cext/charencode.h', |
988 'rust/hg-core/src/ancestors.rs', |
1029 'mercurial/cext/revlog.h', |
989 'rust/hg-core/src/lib.rs']), |
1030 'rust/hg-core/src/ancestors.rs', |
|
1031 'rust/hg-core/src/lib.rs']), |
990 Extension('mercurial.cext.osutil', ['mercurial/cext/osutil.c'], |
1032 Extension('mercurial.cext.osutil', ['mercurial/cext/osutil.c'], |
991 include_dirs=common_include_dirs, |
1033 include_dirs=common_include_dirs, |
992 extra_compile_args=osutil_cflags, |
1034 extra_compile_args=osutil_cflags, |
993 extra_link_args=osutil_ldflags, |
1035 extra_link_args=osutil_ldflags, |
994 depends=common_depends), |
1036 depends=common_depends), |
997 'mercurial/thirdparty/zope/interface/_zope_interface_coptimizations.c', |
1039 'mercurial/thirdparty/zope/interface/_zope_interface_coptimizations.c', |
998 ]), |
1040 ]), |
999 Extension('hgext.fsmonitor.pywatchman.bser', |
1041 Extension('hgext.fsmonitor.pywatchman.bser', |
1000 ['hgext/fsmonitor/pywatchman/bser.c']), |
1042 ['hgext/fsmonitor/pywatchman/bser.c']), |
1001 ] |
1043 ] |
|
1044 |
|
1045 if hgrustext == 'cpython': |
|
1046 extmodules.append( |
|
1047 RustStandaloneExtension('mercurial.rustext', 'hg-cpython', 'librusthg') |
|
1048 ) |
|
1049 |
1002 |
1050 |
1003 sys.path.insert(0, 'contrib/python-zstandard') |
1051 sys.path.insert(0, 'contrib/python-zstandard') |
1004 import setup_zstd |
1052 import setup_zstd |
1005 extmodules.append(setup_zstd.get_c_extension( |
1053 extmodules.append(setup_zstd.get_c_extension( |
1006 name='mercurial.zstd', |
1054 name='mercurial.zstd', |