rust-cpython: build via HGWITHRUSTEXT=cpython
authorGeorges Racinet <gracinet@anybox.fr>
Thu, 06 Dec 2018 16:34:22 +0100
changeset 40967 462a26756f70
parent 40966 1eaf62a67c1a
child 40968 74f41329bf55
rust-cpython: build via HGWITHRUSTEXT=cpython The existing behaviour, building the direct ffi bindings if HGIWTHRUSTEXT is just set is unchanged, but if HGWITHRUSTEXT is cpython, then the cpython bindings (aka mercurial/rustext.so) are built. Differential Revision: https://phab.mercurial-scm.org/D5436
Makefile
setup.py
--- a/Makefile	Thu Dec 06 16:23:20 2018 +0100
+++ b/Makefile	Thu Dec 06 16:34:22 2018 +0100
@@ -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
--- a/setup.py	Thu Dec 06 16:23:20 2018 +0100
+++ b/setup.py	Thu Dec 06 16:34:22 2018 +0100
@@ -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(