rust-cpython: start cpython crate bindings
This changeset introduces the hg-cpython crate,
that compiles as a shared library holding a whole
Python package (mercurial.rustext), with only the empty
'ancestor' submodule for now.
Such bindings will be easier and safer to develop and maintain
that those of `hg-direct-ffi`.
They don't involve C code, only unsafe Rust that's mostly isolated
within the cpython crate.
The long-term goal would be to import the provided modules, such
as rustext.ancestor with mercurial.policy.importmod, same as
we already do with cext modules.
Differential Revision: https://phab.mercurial-scm.org/D5434
--- a/rust/Cargo.lock Mon Dec 03 06:54:19 2018 +0100
+++ b/rust/Cargo.lock Mon Dec 03 06:52:17 2018 +0100
@@ -1,8 +1,42 @@
+[[package]]
+name = "aho-corasick"
+version = "0.6.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "memchr 2.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "cfg-if"
+version = "0.1.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
+name = "cpython"
+version = "0.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "libc 0.2.45 (registry+https://github.com/rust-lang/crates.io-index)",
+ "num-traits 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)",
+ "python27-sys 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
[[package]]
name = "hg-core"
version = "0.1.0"
[[package]]
+name = "hg-cpython"
+version = "0.1.0"
+dependencies = [
+ "cpython 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "hg-core 0.1.0",
+ "libc 0.2.45 (registry+https://github.com/rust-lang/crates.io-index)",
+ "python27-sys 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "python3-sys 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
name = "hgdirectffi"
version = "0.1.0"
dependencies = [
@@ -11,9 +45,104 @@
]
[[package]]
+name = "lazy_static"
+version = "1.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
name = "libc"
version = "0.2.45"
source = "registry+https://github.com/rust-lang/crates.io-index"
+[[package]]
+name = "memchr"
+version = "2.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "cfg-if 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
+ "libc 0.2.45 (registry+https://github.com/rust-lang/crates.io-index)",
+ "version_check 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "num-traits"
+version = "0.2.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
+name = "python27-sys"
+version = "0.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "libc 0.2.45 (registry+https://github.com/rust-lang/crates.io-index)",
+ "regex 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "python3-sys"
+version = "0.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "libc 0.2.45 (registry+https://github.com/rust-lang/crates.io-index)",
+ "regex 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "regex"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "aho-corasick 0.6.9 (registry+https://github.com/rust-lang/crates.io-index)",
+ "memchr 2.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
+ "regex-syntax 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)",
+ "thread_local 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
+ "utf8-ranges 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "regex-syntax"
+version = "0.6.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "ucd-util 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "thread_local"
+version = "0.3.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "lazy_static 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "ucd-util"
+version = "0.1.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
+name = "utf8-ranges"
+version = "1.0.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
+name = "version_check"
+version = "0.1.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
[metadata]
+"checksum aho-corasick 0.6.9 (registry+https://github.com/rust-lang/crates.io-index)" = "1e9a933f4e58658d7b12defcf96dc5c720f20832deebe3e0a19efd3b6aaeeb9e"
+"checksum cfg-if 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "082bb9b28e00d3c9d39cc03e64ce4cea0f1bb9b3fde493f0cbc008472d22bdf4"
+"checksum cpython 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b489034e723e7f5109fecd19b719e664f89ef925be785885252469e9822fa940"
+"checksum lazy_static 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a374c89b9db55895453a74c1e38861d9deec0b01b405a82516e9d5de4820dea1"
"checksum libc 0.2.45 (registry+https://github.com/rust-lang/crates.io-index)" = "2d2857ec59fadc0773853c664d2d18e7198e83883e7060b63c924cb077bd5c74"
+"checksum memchr 2.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "db4c41318937f6e76648f42826b1d9ade5c09cafb5aef7e351240a70f39206e9"
+"checksum num-traits 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "0b3a5d7cc97d6d30d8b9bc8fa19bf45349ffe46241e8816f50f62f6d6aaabee1"
+"checksum python27-sys 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "56114c37d4dca82526d74009df7782a28c871ac9d36b19d4cb9e67672258527e"
+"checksum python3-sys 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "61e4aac43f833fd637e429506cb2ac9d7df672c4b68f2eaaa163649b7fdc0444"
+"checksum regex 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "37e7cbbd370869ce2e8dff25c7018702d10b21a20ef7135316f8daecd6c25b7f"
+"checksum regex-syntax 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)" = "4e47a2ed29da7a9e1960e1639e7a982e6edc6d49be308a3b02daf511504a16d1"
+"checksum thread_local 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "c6b53e329000edc2b34dbe8545fd20e55a333362d0a321909685a19bd28c3f1b"
+"checksum ucd-util 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "535c204ee4d8434478593480b8f86ab45ec9aae0e83c568ca81abf0fd0e88f86"
+"checksum utf8-ranges 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "796f7e48bef87609f7ade7e06495a87d5cd06c7866e6a5cbfceffc558a243737"
+"checksum version_check 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "914b1a6776c4c929a602fafd8bc742e06365d4bcbe48c30f9cca5824f70dc9dd"
--- a/rust/Cargo.toml Mon Dec 03 06:54:19 2018 +0100
+++ b/rust/Cargo.toml Mon Dec 03 06:52:17 2018 +0100
@@ -1,3 +1,3 @@
[workspace]
-members = ["hg-core", "hg-direct-ffi"]
+members = ["hg-core", "hg-direct-ffi", "hg-cpython"]
exclude = ["chg", "hgcli"]
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/rust/hg-cpython/Cargo.toml Mon Dec 03 06:52:17 2018 +0100
@@ -0,0 +1,29 @@
+[package]
+name = "hg-cpython"
+version = "0.1.0"
+authors = ["Georges Racinet <gracinet@anybox.fr>"]
+
+[lib]
+name='rusthg'
+crate-type = ["cdylib"]
+
+[features]
+default = ["python27", "python27-sys"]
+
+python27 = ["cpython/python27-sys", "cpython/extension-module-2-7"]
+
+[dependencies]
+hg-core = { path = "../hg-core" }
+libc = '*'
+
+[dependencies.cpython]
+version = "*"
+default-features = false
+
+[dependencies.python27-sys]
+version = "0.2.1"
+optional = true
+
+[dependencies.python3-sys]
+version = "0.2.1"
+optional = true
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/rust/hg-cpython/rustfmt.toml Mon Dec 03 06:52:17 2018 +0100
@@ -0,0 +1,3 @@
+max_width = 79
+wrap_comments = true
+error_on_line_overflow = true
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/rust/hg-cpython/src/ancestors.rs Mon Dec 03 06:52:17 2018 +0100
@@ -0,0 +1,30 @@
+// ancestors.rs
+//
+// Copyright 2018 Georges Racinet <gracinet@anybox.fr>
+//
+// This software may be used and distributed according to the terms of the
+// GNU General Public License version 2 or any later version.
+
+//! Bindings for the hg::ancestors module provided by the
+//! `hg-core` crate. From Python, this will be seen as `rustext.ancestor`
+use cpython::{PyDict, PyModule, PyResult, Python};
+
+/// Create the module, with __package__ given from parent
+pub fn init_module(py: Python, package: &str) -> PyResult<PyModule> {
+ let dotted_name = &format!("{}.ancestor", package);
+ let m = PyModule::new(py, dotted_name)?;
+ m.add(py, "__package__", package)?;
+ m.add(
+ py,
+ "__doc__",
+ "Generic DAG ancestor algorithms - Rust implementation",
+ )?;
+
+ let sys = PyModule::import(py, "sys")?;
+ let sys_modules: PyDict = sys.get(py, "modules")?.extract(py)?;
+ sys_modules.set_item(py, dotted_name, &m)?;
+ // Example C code (see pyexpat.c and import.c) will "give away the
+ // reference", but we won't because it will be consumed once the
+ // Rust PyObject is dropped.
+ Ok(m)
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/rust/hg-cpython/src/exceptions.rs Mon Dec 03 06:52:17 2018 +0100
@@ -0,0 +1,15 @@
+use cpython::exc::ValueError;
+use cpython::{PyErr, Python};
+use hg;
+
+py_exception!(rustext, GraphError, ValueError);
+
+impl GraphError {
+ pub fn pynew(py: Python, inner: hg::GraphError) -> PyErr {
+ match inner {
+ hg::GraphError::ParentOutOfRange(r) => {
+ GraphError::new(py, ("ParentOutOfRange", r))
+ }
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/rust/hg-cpython/src/lib.rs Mon Dec 03 06:52:17 2018 +0100
@@ -0,0 +1,40 @@
+// lib.rs
+//
+// Copyright 2018 Georges Racinet <gracinet@anybox.fr>
+//
+// This software may be used and distributed according to the terms of the
+// GNU General Public License version 2 or any later version.
+
+//! Python bindings of `hg-core` objects using the `cpython` crate.
+//! Once compiled, the resulting single shared library object can be placed in
+//! the `mercurial` package directly as `rustext.so` or `rustext.dll`.
+//! It holds several modules, so that from the point of view of Python,
+//! it behaves as the `cext` package.
+//!
+//! Example:
+//! ```
+//! >>> from mercurial.rustext import ancestor
+//! >>> ancestor.__doc__
+//! 'Generic DAG ancestor algorithms - Rust implementation'
+//! ```
+
+#[macro_use]
+extern crate cpython;
+extern crate hg;
+
+mod ancestors;
+mod exceptions;
+
+py_module_initializer!(rustext, initrustext, PyInit_rustext, |py, m| {
+ m.add(
+ py,
+ "__doc__",
+ "Mercurial core concepts - Rust implementation",
+ )?;
+
+ let dotted_name: String = m.get(py, "__name__")?.extract(py)?;
+ m.add(py, "__package__", "mercurial")?;
+ m.add(py, "ancestor", ancestors::init_module(py, &dotted_name)?)?;
+ m.add(py, "GraphError", py.get_type::<exceptions::GraphError>())?;
+ Ok(())
+});