diff --git a/Makefile b/Makefile --- a/Makefile +++ b/Makefile @@ -73,6 +73,7 @@ $(MAKE) -C doc clean $(MAKE) -C contrib/chg distclean rm -rf rust/target + rm -f mercurial/rustext.so clean: cleanbutpackages rm -rf packages diff --git a/setup.py b/setup.py --- a/setup.py +++ b/setup.py @@ -132,7 +132,11 @@ ispypy = "PyPy" in sys.version -iswithrustextensions = 'HGWITHRUSTEXT' in os.environ +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: + hgrustext = 'direct-ffi' import ctypes import errno @@ -458,11 +462,18 @@ return build_ext.initialize_options(self) def build_extensions(self): + ruststandalones = [e for e in self.extensions + if isinstance(e, RustStandaloneExtension)] + self.extensions = [e for e in self.extensions + if e not in ruststandalones] # Filter out zstd if disabled via argument. if not self.zstd: self.extensions = [e for e in self.extensions if e.name != 'mercurial.zstd'] + for rustext in ruststandalones: + rustext.build('' if self.inplace else self.build_lib) + return build_ext.build_extensions(self) def build_extension(self, ext): @@ -903,20 +914,16 @@ """Exception class for Rust compilation errors.""" class RustExtension(Extension): - """A C Extension, conditionnally enhanced with Rust code. - - if iswithrustextensions is False, does nothing else than plain Extension + """Base classes for concrete Rust Extension classes. """ rusttargetdir = os.path.join('rust', 'target', 'release') def __init__(self, mpath, sources, rustlibname, subcrate, **kw): Extension.__init__(self, mpath, sources, **kw) - if not iswithrustextensions: + if hgrustext is None: return srcdir = self.rustsrcdir = os.path.join('rust', subcrate) - self.libraries.append(rustlibname) - self.extra_compile_args.append('-DWITH_RUST') # adding Rust source and control files to depends so that the extension # gets rebuilt if they've changed @@ -930,7 +937,7 @@ if os.path.splitext(fname)[1] == '.rs') def rustbuild(self): - if not iswithrustextensions: + if hgrustext is None: return env = os.environ.copy() if 'HGTEST_RESTOREENV' in env: @@ -962,6 +969,40 @@ "Cargo failed. Working directory: %r, " "command: %r, environment: %r" % (self.rustsrcdir, cmd, env)) +class RustEnhancedExtension(RustExtension): + """A C Extension, conditionally enhanced with Rust code. + + If the HGRUSTEXT environment variable is set to something else + than 'cpython', the Rust sources get compiled and linked within the + C target shared library object. + """ + + def __init__(self, mpath, sources, rustlibname, subcrate, **kw): + RustExtension.__init__(self, mpath, sources, rustlibname, subcrate, + **kw) + if hgrustext != 'direct-ffi': + return + self.extra_compile_args.append('-DWITH_RUST') + self.libraries.append(rustlibname) + self.library_dirs.append(self.rusttargetdir) + +class RustStandaloneExtension(RustExtension): + + def __init__(self, pydottedname, rustcrate, dylibname, **kw): + RustExtension.__init__(self, pydottedname, [], dylibname, rustcrate, + **kw) + self.dylibname = dylibname + + def build(self, target_dir): + self.rustbuild() + target = [target_dir] + target.extend(self.name.split('.')) + ext = '.so' # TODO Unix only + target[-1] += ext + shutil.copy2(os.path.join(self.rusttargetdir, self.dylibname + ext), + os.path.join(*target)) + + extmodules = [ Extension('mercurial.cext.base85', ['mercurial/cext/base85.c'], include_dirs=common_include_dirs, @@ -974,19 +1015,20 @@ 'mercurial/cext/mpatch.c'], include_dirs=common_include_dirs, depends=common_depends), - RustExtension('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'], - 'hgdirectffi', - 'hg-direct-ffi', - include_dirs=common_include_dirs, - depends=common_depends + ['mercurial/cext/charencode.h', - 'mercurial/cext/revlog.h', - 'rust/hg-core/src/ancestors.rs', - 'rust/hg-core/src/lib.rs']), + RustEnhancedExtension( + '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'], + 'hgdirectffi', + 'hg-direct-ffi', + include_dirs=common_include_dirs, + depends=common_depends + ['mercurial/cext/charencode.h', + 'mercurial/cext/revlog.h', + 'rust/hg-core/src/ancestors.rs', + 'rust/hg-core/src/lib.rs']), Extension('mercurial.cext.osutil', ['mercurial/cext/osutil.c'], include_dirs=common_include_dirs, extra_compile_args=osutil_cflags, @@ -1000,6 +1042,12 @@ ['hgext/fsmonitor/pywatchman/bser.c']), ] +if hgrustext == 'cpython': + extmodules.append( + RustStandaloneExtension('mercurial.rustext', 'hg-cpython', 'librusthg') + ) + + sys.path.insert(0, 'contrib/python-zstandard') import setup_zstd extmodules.append(setup_zstd.get_c_extension(