From 91663cca2d1c72b97ea92eed36036f44c9369bb9 Mon Sep 17 00:00:00 2001 From: eldritch horrors Date: Fri, 26 Jun 2026 18:22:22 +0200 Subject: [PATCH] libutil|rs: bring back c++ interop with zngur Change-Id: Ia9e9881f464d5b25110b56fb9d72ed30463eaeee --- Cargo.lock | 676 +++++++++++++++++++++++++- Cargo.toml | 6 + lix/lix-rs/Cargo.toml | 3 + lix/lix-rs/build.rs | 16 + lix/lix-rs/main.zng | 19 + lix/lix-rs/meson.build | 33 ++ lix/lix-rs/primitives.zng | 22 + lix/lix-rs/src/ffi_test.rs | 55 +++ lix/lix-rs/src/ffi_test.zng | 27 + lix/lix-rs/src/lib.rs | 34 ++ lix/lix-rs/src/lib.zng | 30 ++ lix/lix-rs/std-common.zng | 73 +++ lix/lix-rs/tuples.zng | 11 + lix/lix-rs/utils.cc | 48 ++ lix/lix-rs/utils.hh | 202 ++++++++ lix/meson.build | 20 +- meson.build | 2 + meson.options | 4 + meson/clang-tidy/clang-tidy-runner.py | 4 + package.nix | 35 ++ tests/unit/libutil/rust.cc | 90 ++++ tests/unit/meson.build | 9 + tools/licxxbridge/Cargo.toml | 12 + tools/licxxbridge/main.rs | 63 +++ tools/licxxbridge/meson.build | 24 + 25 files changed, 1509 insertions(+), 9 deletions(-) create mode 100644 lix/lix-rs/build.rs create mode 100644 lix/lix-rs/main.zng create mode 100644 lix/lix-rs/primitives.zng create mode 100644 lix/lix-rs/src/ffi_test.rs create mode 100644 lix/lix-rs/src/ffi_test.zng create mode 100644 lix/lix-rs/src/lib.zng create mode 100644 lix/lix-rs/std-common.zng create mode 100644 lix/lix-rs/tuples.zng create mode 100644 lix/lix-rs/utils.cc create mode 100644 lix/lix-rs/utils.hh create mode 100644 tests/unit/libutil/rust.cc create mode 100644 tools/licxxbridge/Cargo.toml create mode 100644 tools/licxxbridge/main.rs create mode 100644 tools/licxxbridge/meson.build diff --git a/Cargo.lock b/Cargo.lock index f51301c6f..cb0b380d0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,18 +2,287 @@ # It is not intended for manual editing. version = 4 +[[package]] +name = "aho-corasick" +version = "1.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ddd31a130427c27518df266943a5308ed92d4b226cc639f5a8f1002816174301" +dependencies = [ + "memchr", +] + +[[package]] +name = "allocator-api2" +version = "0.2.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923" + +[[package]] +name = "anstream" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "824a212faf96e9acacdbd09febd34438f8f711fb84e09a8916013cd7815ca28d" +dependencies = [ + "anstyle", + "anstyle-parse", + "anstyle-query", + "anstyle-wincon", + "colorchoice", + "is_terminal_polyfill", + "utf8parse", +] + +[[package]] +name = "anstyle" +version = "1.0.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "940b3a0ca603d1eade50a4846a2afffd5ef57a9feac2c0e2ec2e14f9ead76000" + +[[package]] +name = "anstyle-parse" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52ce7f38b242319f7cabaa6813055467063ecdc9d355bbb4ce0c68908cd8130e" +dependencies = [ + "utf8parse", +] + +[[package]] +name = "anstyle-query" +version = "1.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "40c48f72fd53cd289104fc64099abca73db4166ad86ea0b4341abe65af83dadc" +dependencies = [ + "windows-sys", +] + +[[package]] +name = "anstyle-wincon" +version = "3.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "291e6a250ff86cd4a820112fb8898808a366d8f9f58ce16d1f538353ad55747d" +dependencies = [ + "anstyle", + "once_cell_polyfill", + "windows-sys", +] + +[[package]] +name = "ar_archive_writer" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4087686b4b0a3427190bae57a1d9a478dbb2d40c5dc1bd6e2b6d797913bdd348" +dependencies = [ + "object", +] + +[[package]] +name = "ariadne" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72fe02fc62033df9ba41cba57ee19acf5e742511a140c7dbc3a873e19a19a1bd" +dependencies = [ + "unicode-width", + "yansi", +] + +[[package]] +name = "askama" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b79091df18a97caea757e28cd2d5fda49c6cd4bd01ddffd7ff01ace0c0ad2c28" +dependencies = [ + "askama_derive", + "askama_escape", + "humansize", + "num-traits", + "percent-encoding", +] + +[[package]] +name = "askama_derive" +version = "0.12.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19fe8d6cb13c4714962c072ea496f3392015f0989b1a2847bb4b2d9effd71d83" +dependencies = [ + "askama_parser", + "basic-toml", + "mime", + "mime_guess", + "proc-macro2", + "quote", + "serde", + "syn", +] + +[[package]] +name = "askama_escape" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "619743e34b5ba4e9703bba34deac3427c72507c7159f5fd030aea8cac0cfe341" + +[[package]] +name = "askama_parser" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "acb1161c6b64d1c3d83108213c2a2533a342ac225aabd0bda218278c2ddb00c0" +dependencies = [ + "nom", +] + +[[package]] +name = "autocfg" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2032f911046de80f0a198e0901378627c33f59ea0ac00e363d481118bd70a53" + +[[package]] +name = "basic-toml" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba62675e8242a4c4e806d12f11d136e626e6c8361d6b829310732241652a178a" +dependencies = [ + "serde", +] + +[[package]] +name = "block-buffer" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" +dependencies = [ + "generic-array", +] + +[[package]] +name = "cc" +version = "1.2.65" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e228eec9be7c17ccb640b59b36a5cd805ea2a564a4c5e162c2f659fea30d3b96" +dependencies = [ + "find-msvc-tools", + "shlex", +] + +[[package]] +name = "cfg-if" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" + +[[package]] +name = "chumsky" +version = "1.0.0-alpha.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e82d74e6c83060ec269fe9e0d408d6de4a1645d525f9a0bbbb841ba4efd91ac" +dependencies = [ + "hashbrown 0.15.5", + "regex-automata", + "serde", + "stacker", + "unicode-ident", + "unicode-segmentation", +] + +[[package]] +name = "clap" +version = "4.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ddb117e43bbf7dacf0a4190fef4d345b9bad68dfc649cb349e7d17d28428e51" +dependencies = [ + "clap_builder", + "clap_derive", +] + +[[package]] +name = "clap_builder" +version = "4.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "714a53001bf66416adb0e2ef5ac857140e7dc3a0c48fb28b2f10762fc4b5069f" +dependencies = [ + "anstream", + "anstyle", + "clap_lex", + "strsim", +] + +[[package]] +name = "clap_derive" +version = "4.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2ce8604710f6733aa641a2b3731eaa1e8b3d9973d5e3565da11800813f997a9" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "clap_lex" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8d4a3bb8b1e0c1050499d1815f5ab16d04f0959b233085fb31653fbfc9d98f9" + +[[package]] +name = "colorchoice" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d07550c9036bf2ae0c684c4297d503f838287c83c53686d05370d0e139ae570" + [[package]] name = "countme" version = "3.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7704b5fdd17b18ae31c4c1da5a2e0305a2bf17b5249300a9ee9ed7b72114c636" +[[package]] +name = "cpufeatures" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280" +dependencies = [ + "libc", +] + +[[package]] +name = "crypto-common" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78c8292055d1c1df0cce5d180393dc8cce0abec0a7102adb6c7b1eef6016d60a" +dependencies = [ + "generic-array", + "typenum", +] + +[[package]] +name = "digest" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" +dependencies = [ + "block-buffer", + "crypto-common", +] + [[package]] name = "dissimilar" version = "1.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "59f8e79d1fbf76bdfbde321e902714bf6c49df88a7dda6fc682fc2979226962d" +[[package]] +name = "either" +version = "1.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91622ff5e7162018101f2fea40d6ebf4a78bbe5a49736a2020649edf9693679e" + +[[package]] +name = "equivalent" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" + [[package]] name = "expect-test" version = "1.5.0" @@ -24,17 +293,123 @@ dependencies = [ "once_cell", ] +[[package]] +name = "find-msvc-tools" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582" + +[[package]] +name = "foldhash" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" + +[[package]] +name = "generic-array" +version = "0.14.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +dependencies = [ + "typenum", + "version_check", +] + [[package]] name = "hashbrown" version = "0.14.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" +[[package]] +name = "hashbrown" +version = "0.15.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1" +dependencies = [ + "allocator-api2", + "equivalent", + "foldhash", +] + +[[package]] +name = "hashbrown" +version = "0.17.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed5909b6e89a2db4456e54cd5f673791d7eca6732202bbf2a9cc504fe2f9b84a" + +[[package]] +name = "heck" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" + +[[package]] +name = "hex" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" + +[[package]] +name = "humansize" +version = "2.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6cb51c9a029ddc91b07a787f1d86b53ccfa49b0e86688c946ebe8d3555685dd7" +dependencies = [ + "libm", +] + +[[package]] +name = "indexmap" +version = "2.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d466e9454f08e4a911e14806c24e16fba1b4c121d1ea474396f396069cf949d9" +dependencies = [ + "equivalent", + "hashbrown 0.17.1", +] + +[[package]] +name = "is_terminal_polyfill" +version = "1.70.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a6cb138bb79a146c1bd460005623e142ef0181e3d0219cb493e02f7d08a35695" + +[[package]] +name = "itertools" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1c173a5686ce8bfa551b3563d0c2170bf24ca44da99c7ca4bfdab5418c3fe57" +dependencies = [ + "either", +] + +[[package]] +name = "libc" +version = "0.2.186" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68ab91017fe16c622486840e4c83c9a37afeff978bd239b5293d61ece587de66" + +[[package]] +name = "libm" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6d2cec3eae94f9f509c767b45932f1ada8350c4bdb85af2fcab4a3c14807981" + +[[package]] +name = "licxxbridge" +version = "0.0.0" +dependencies = [ + "clap", + "zngur", +] + [[package]] name = "lix" version = "0.0.0" dependencies = [ "lix-doc", + "zngur", ] [[package]] @@ -46,12 +421,125 @@ dependencies = [ "rowan", ] +[[package]] +name = "memchr" +version = "2.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88904434abc2901f197fe8cc55f0445e7ded921dba5911dad2e2b39b48e663c4" + +[[package]] +name = "mime" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" + +[[package]] +name = "mime_guess" +version = "2.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7c44f8e672c00fe5308fa235f821cb4198414e1c77935c1ab6948d3fd78550e" +dependencies = [ + "mime", + "unicase", +] + +[[package]] +name = "minimal-lexical" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" + +[[package]] +name = "nom" +version = "7.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" +dependencies = [ + "memchr", + "minimal-lexical", +] + +[[package]] +name = "num-traits" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" +dependencies = [ + "autocfg", +] + +[[package]] +name = "object" +version = "0.37.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff76201f031d8863c38aa7f905eca4f53abbfa15f609db4277d44cd8938f33fe" +dependencies = [ + "memchr", +] + [[package]] name = "once_cell" version = "1.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" +[[package]] +name = "once_cell_polyfill" +version = "1.70.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "384b8ab6d37215f3c5301a95a4accb5d64aa607f1fcb26a11b5303878451b4fe" + +[[package]] +name = "percent-encoding" +version = "2.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220" + +[[package]] +name = "proc-macro2" +version = "1.0.106" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "psm" +version = "0.1.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "645dbe486e346d9b5de3ef16ede18c26e6c70ad97418f4874b8b1889d6e761ea" +dependencies = [ + "ar_archive_writer", + "cc", +] + +[[package]] +name = "quote" +version = "1.0.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41f2619966050689382d2b44f664f4bc593e129785a36d6ee376ddf37259b924" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "regex-automata" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59b23e92ee4318893fa3fe3e6fb365258efbfe6ac6ab30f090cdcbb7aa37efa9" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dbb5fb1acd8a1a18b3dd5be62d25485eb770e05afb408a9627d14d451bae12da" + [[package]] name = "rnix" version = "0.12.0" @@ -68,7 +556,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0a542b0253fa46e632d27a1dc5cf7b930de4df8659dc6e720b647fc72147ae3d" dependencies = [ "countme", - "hashbrown", + "hashbrown 0.14.5", "rustc-hash", "text-size", ] @@ -79,8 +567,194 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" +[[package]] +name = "serde" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" +dependencies = [ + "serde_core", + "serde_derive", +] + +[[package]] +name = "serde_core" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "sha2" +version = "0.10.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + +[[package]] +name = "shlex" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8fadd59c855ef2080decdef8ff161eb6661b86933c9d82e5ba29dc602a55aba" + +[[package]] +name = "stacker" +version = "0.1.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "640c8cdd92b6b12f5bcb1803ca3bbf5ab96e5e6b6b96b9ab77dabe9e880b3190" +dependencies = [ + "cc", + "cfg-if", + "libc", + "psm", + "windows-sys", +] + +[[package]] +name = "strsim" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" + +[[package]] +name = "syn" +version = "2.0.118" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b9ae57f904213ebb649ce6895b8a66c66f0203b9319718f69a5612a065b1422" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + [[package]] name = "text-size" version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f18aa187839b2bdb1ad2fa35ead8c4c2976b64e4363c386d45ac0f7ee85c9233" + +[[package]] +name = "typenum" +version = "1.20.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6f5e870be6c3b371b77fe0ee0bafb859fa4964b4404c27de1d380043c4dda20" + +[[package]] +name = "unicase" +version = "2.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dbc4bc3a9f746d862c45cb89d705aa10f187bb96c76001afab07a0d35ce60142" + +[[package]] +name = "unicode-ident" +version = "1.0.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" + +[[package]] +name = "unicode-segmentation" +version = "1.13.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c6f5d3c3b1bf09027a88a6bc961fc00497d651009560b5463668dc81b0fa87a8" + +[[package]] +name = "unicode-width" +version = "0.1.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7dd6e30e90baa6f72411720665d41d89b9a3d039dc45b8faea1ddd07f617f6af" + +[[package]] +name = "utf8parse" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" + +[[package]] +name = "version_check" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" + +[[package]] +name = "windows-link" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" + +[[package]] +name = "windows-sys" +version = "0.61.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc" +dependencies = [ + "windows-link", +] + +[[package]] +name = "yansi" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09041cd90cf85f7f8b2df60c646f853b7f535ce68f85244eb6731cf89fa498ec" + +[[package]] +name = "zngur" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7fc912d12934b4d04aabc52c14db6fc88ae8aa5a47902f27e8759c2de254723f" +dependencies = [ + "zngur-generator", +] + +[[package]] +name = "zngur-def" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f27e49a62db537cea43a6c122ded7eda99c4ac0ca1d7cfd74f2bf6a6c88712d" +dependencies = [ + "indexmap", + "itertools", +] + +[[package]] +name = "zngur-generator" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ffad8c6994c477023aba7613bdf17c470003bbd191846d3096e84ffecf86d4c" +dependencies = [ + "askama", + "hex", + "indexmap", + "itertools", + "sha2", + "zngur-def", + "zngur-parser", +] + +[[package]] +name = "zngur-parser" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d66c1b85ca6eab9576df5de31758cb1f9046b4ce47b25f45dabcb0fc7ef8789" +dependencies = [ + "ariadne", + "chumsky", + "itertools", + "zngur-def", +] diff --git a/Cargo.toml b/Cargo.toml index 34c865e62..920ebfcbb 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,11 +3,17 @@ resolver = "2" members = [ "lix/lix-doc", "lix/lix-rs", + "tools/licxxbridge", ] [workspace.package] edition = "2021" +[workspace.dependencies] +clap = "4" +syn = "2.0" +zngur = "0.10" + [profile.dev] opt-level = 1 diff --git a/lix/lix-rs/Cargo.toml b/lix/lix-rs/Cargo.toml index f6dae1a3f..3d9daa121 100644 --- a/lix/lix-rs/Cargo.toml +++ b/lix/lix-rs/Cargo.toml @@ -12,3 +12,6 @@ default = [] [dependencies] lix-doc.path = "../lix-doc" + +[build-dependencies] +zngur.workspace = true diff --git a/lix/lix-rs/build.rs b/lix/lix-rs/build.rs new file mode 100644 index 000000000..9a96a3a22 --- /dev/null +++ b/lix/lix-rs/build.rs @@ -0,0 +1,16 @@ +use std::{env, fs}; + +fn main() { + // this is a hack to let rust-analyzer find the generated code. we could + // also include! from MESON_BUILD_DIR directly, but rust-analyzer cannot + // be told to look there. not with any method we came across yet anyway. + fs::write( + format!("{}/generated.rs", env::var("OUT_DIR").expect("OUT_DIR")), + fs::read(format!( + "{}/lix/lix-rs/main.zng.rs", + env::var("MESON_BUILD_DIR").expect("MESON_BUILD_DIR") + )) + .expect("read main.zng.rs"), + ) + .expect("write generated.rs"); +} diff --git a/lix/lix-rs/main.zng b/lix/lix-rs/main.zng new file mode 100644 index 000000000..b16af6f4c --- /dev/null +++ b/lix/lix-rs/main.zng @@ -0,0 +1,19 @@ +// always use conservative layouts, and use auto offset for anything that isn't critical for +// system performance. field offsets are kind of fucky anyway unless repr(C) is used, and we +// rarely have such types to expose. we *should* try to avoid exposing fields except for any +// tuple types, and even then it'd probably be better to find safer ways to do things. doing +// it anyway is not unsafe as such since zngur does have compile-time layout checks to avoid +// nasty surprises, but field offsets are still dependent on platform and compiler versions. +// +// types are generally sorted by category for builtin types or parent module for the others. +// type definitions should live as close to their least stdlib component as possible: result +// types generally live with their Ok variant types, error types generally live in dedicated +// module files, collection types live with the most reasonable parameter type. these aren't +// hard and fast rules though, just use some judgement. we just want to keep the definitions +// in a readable and maintainable state without scattering bits of types all over the place. + +merge "./primitives.zng"; +merge "./std-common.zng"; +merge "./tuples.zng"; + +merge "./src/lib.zng"; diff --git a/lix/lix-rs/meson.build b/lix/lix-rs/meson.build index 8ba2cb755..b234546ed 100644 --- a/lix/lix-rs/meson.build +++ b/lix/lix-rs/meson.build @@ -1,4 +1,37 @@ lix_rs_files += files( 'Cargo.toml', + # keep-sorted start + 'src/ffi_test.rs', 'src/lib.rs', + # keep-sorted end +) + +lixrs_zngur_bridge = custom_target( + command : [ + licxxbridge, + 'generate', + '--in=@INPUT@', + '--out-header=@OUTPUT0@', + '--out-shared-header=@OUTPUT1@', + '--out-impl=@OUTPUT2@', + '--out-rs=@OUTPUT3@', + '--out-deps=@DEPFILE@', + ], + input : files('main.zng'), + output : [ '@BASENAME@.gen.hh', 'zngur.gen.hh', '@BASENAME@.gen.cc', '@PLAINNAME@.rs' ], + depfile : '@PLAINNAME@.d', + install : true, + install_dir : [ includedir / 'lix/lix-rs', includedir / 'lix/lix-rs', false, false ], +) + +liblix_generated_headers += [ lixrs_zngur_bridge[0], lixrs_zngur_bridge[1] ] +liblix_sources += lixrs_zngur_bridge[2] +lix_rs_files += lixrs_zngur_bridge[3] + +liblix_sources += files('utils.cc') + +install_headers( + files('utils.hh'), + subdir : 'lix/lix-rs', + preserve_path : true, ) diff --git a/lix/lix-rs/primitives.zng b/lix/lix-rs/primitives.zng new file mode 100644 index 000000000..8f57627b9 --- /dev/null +++ b/lix/lix-rs/primitives.zng @@ -0,0 +1,22 @@ +// primitives go here. other modules *may* extend primitive declarations, and if a primitive +// operation is used only very rare that may help readability. in general we want to collect +// anything that's used a lot here to not clutter the remainder of our ffi definition files. + +type bool { + #layout(size = 1, align = 1); +} + +type [u8] { + wellknown_traits(?Sized); + + fn as_ptr(&self) -> *const u8; + fn len(&self) -> usize; +} + +type str { + wellknown_traits(?Sized); + + fn as_bytes(&self) -> &[u8]; + fn as_ptr(&self) -> *const u8; + fn len(&self) -> usize; +} diff --git a/lix/lix-rs/src/ffi_test.rs b/lix/lix-rs/src/ffi_test.rs new file mode 100644 index 000000000..f352c590d --- /dev/null +++ b/lix/lix-rs/src/ffi_test.rs @@ -0,0 +1,55 @@ +use std::{error::Error, io}; + +use crate::ffi; + +#[derive(Clone, Copy)] +pub struct TestMultiplyAddLenArgs { + pub a: u64, + pub b: u64, +} + +pub trait SetB { + fn set_b(&mut self, b: u64); +} + +impl SetB for TestMultiplyAddLenArgs { + fn set_b(&mut self, b: u64) { + self.b = b; + } +} + +impl TestMultiplyAddLenArgs { + pub fn new(a: u64, b: u64) -> Self { + Self { a, b } + } +} + +pub(crate) fn test_multiply_add_len(args: TestMultiplyAddLenArgs, s: &Vec) -> (String, u64) { + let result = args.a * args.b + s.len() as u64; + ( + format!("({} * {} + {s:?}.len()) = {result}", args.a, args.b), + result, + ) +} + +pub(crate) fn test_result() -> Result<(), Box> { + Err(Box::new(io::Error::new( + io::ErrorKind::Other, + "errors travel freely", + ))) +} + +pub(crate) fn test_option_some() -> Option { + Some(1) +} + +pub(crate) fn test_option_none() -> Option { + None +} + +pub(crate) fn test_exceptions(f: Box Result<(), ffi::Error>>) -> String { + match f() { + Ok(()) => "".into(), + Err(e) => e.to_string(), + } +} diff --git a/lix/lix-rs/src/ffi_test.zng b/lix/lix-rs/src/ffi_test.zng new file mode 100644 index 000000000..3fc19266e --- /dev/null +++ b/lix/lix-rs/src/ffi_test.zng @@ -0,0 +1,27 @@ +use ::std::error::Error as Error; +use ::std::option::Option as Option; +use ::std::result::Result as Result; +use ::std::string::String as String; +use ::std::vec::Vec as Vec; + +mod crate::ffi_test { + type TestMultiplyAddLenArgs { + #layout_conservative(size = 16, align = 8); + wellknown_traits(Copy); + + field a (offset = auto, type = u64); + field b (offset = auto, type = u64); + + fn new(u64, u64) -> TestMultiplyAddLenArgs; + fn set_b(&mut self, u64) use SetB; + } + + fn test_multiply_add_len(TestMultiplyAddLenArgs, &Vec) -> (String, u64); + + fn test_result() -> Result<(), Box>; + + fn test_option_some() -> Option; + fn test_option_none() -> Option; + + fn test_exceptions(Box Result<(), crate::ffi::Error>>) -> String; +} diff --git a/lix/lix-rs/src/lib.rs b/lix/lix-rs/src/lib.rs index 09b1b0b91..b1619ae55 100644 --- a/lix/lix-rs/src/lib.rs +++ b/lix/lix-rs/src/lib.rs @@ -1 +1,35 @@ extern crate lix_doc; + +mod generated { + include!(concat!(env!("OUT_DIR"), "/generated.rs")); +} + +mod ffi { + use std::{ + error, + fmt::{Debug, Display}, + slice::from_raw_parts, + }; + + pub unsafe fn from_raw_parts_u8<'a>(data: *const u8, length: usize) -> &'a [u8] { + from_raw_parts(data, length) + } + + pub(crate) use crate::generated::cpp::Error; + + impl Display for Error { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.write_str(&self.to_string()) + } + } + + impl Debug for Error { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.write_str(&self.to_string()) + } + } + + impl error::Error for Error {} +} + +mod ffi_test; diff --git a/lix/lix-rs/src/lib.zng b/lix/lix-rs/src/lib.zng new file mode 100644 index 000000000..7f1c6a4c3 --- /dev/null +++ b/lix/lix-rs/src/lib.zng @@ -0,0 +1,30 @@ +merge "./ffi_test.zng"; + +mod crate::ffi { + use ::std::result::Result as Result; + use ::std::string::String as String; + + fn from_raw_parts_u8(*const u8, usize) -> &[u8]; + + type Error { + #layout_conservative(size = 16, align = 8); + #cpp_value "0" "::std::exception_ptr"; + } + + extern "C++" { + impl Error { + safe fn to_string(&self) -> String; + } + } + + type Result<(), Error> { + #layout_conservative(size = 16, align = 8); + + constructor Ok(()); + constructor Err(Error); + } + + type Box Result<(), Error>> { + #layout_conservative(size = 16, align = 8); + } +} diff --git a/lix/lix-rs/std-common.zng b/lix/lix-rs/std-common.zng new file mode 100644 index 000000000..915b195d4 --- /dev/null +++ b/lix/lix-rs/std-common.zng @@ -0,0 +1,73 @@ +// common rust stdlib types go here. + +use ::std::string::String as String; +use ::std::vec::Vec as Vec; + +mod ::std::borrow { + type Cow { + #layout_conservative(size = 24, align = 8); + + fn into_owned(self) -> String; + } +} + +mod ::std::error { + type Box { + #layout_conservative(size = 16, align = 8); + wellknown_traits(Debug); + + fn to_string(&self) -> String; + } +} + +mod ::std::io { + type Error { + #layout_conservative(size = 16, align = 8); + } +} + +mod ::std::option { + type Option { + #layout_conservative(size = 16, align = 8); + + constructor Some(u64); + constructor None; + + fn unwrap(self) -> u64; + } +} + +mod ::std::result { + use ::std::error::Error as Error; + + type Result<(), Box> { + #layout_conservative(size = 16, align = 8); + + constructor Ok(()); + constructor Err(Box); + + fn unwrap(self) -> (); + fn unwrap_err(self) -> Box; + } +} + +mod ::std::string { + type String { + #layout_conservative(size = 24, align = 8); + + fn as_bytes(&self) -> &[u8]; + fn as_str(&self) -> &str; + fn from(&str) -> String; + fn from_utf8_lossy(&[u8]) -> ::std::borrow::Cow; + } +} + +mod ::std::vec { + type Vec { + #layout_conservative(size = 24, align = 8); + + fn new() -> Vec; + fn push(&mut self, String); + fn len(&self) -> usize; + } +} diff --git a/lix/lix-rs/tuples.zng b/lix/lix-rs/tuples.zng new file mode 100644 index 000000000..bed4abdc3 --- /dev/null +++ b/lix/lix-rs/tuples.zng @@ -0,0 +1,11 @@ +// tuple types of exclusively stdlib types go here. seems like a good place for them anyway. + +use ::std::option::Option as Option; +use ::std::string::String as String; + +type (String, u64) { + #layout_conservative(size = 32, align = 8); + + field 0 (offset = 0, type = String); + field 1 (offset = auto, type = u64); +} diff --git a/lix/lix-rs/utils.cc b/lix/lix-rs/utils.cc new file mode 100644 index 000000000..f855ecb45 --- /dev/null +++ b/lix/lix-rs/utils.cc @@ -0,0 +1,48 @@ +#include "utils.hh" + +#include "libutil/charptr-cast.hh" +#include "lix/lix-rs/main.gen.hh" + +namespace rust { +::std::string_view to_std_string_view(Ref s) +{ + return {nix::charptr_cast(s.as_ptr()), s.len()}; +} + +::std::string_view std::string::to_std_string_view(const String & s) +{ + auto data = s.as_bytes(); + return {nix::charptr_cast(data.as_ptr()), data.len()}; +} + +::std::string to_std_string(Ref s) +{ + return ::std::string(to_std_string_view(s)); +} + +::std::string std::string::to_std_string(const String & s) +{ + return ::std::string(to_std_string_view(s)); +} + +String to_string(::std::string_view sv) +{ + auto slice = lix::ffi::from_raw_parts_u8(nix::charptr_cast(sv.begin()), sv.size()); + return String::from_utf8_lossy(slice).into_owned(); +} + +std::string::String Impl::to_string(Ref ptr) +{ + using String = std::string::String; + + try { + ::std::rethrow_exception(ptr.cpp()); + } catch (const ::std::exception & ex) { // NOLINT(lix-foreign-exceptions) + auto slice = + lix::ffi::from_raw_parts_u8(nix::charptr_cast(ex.what()), strlen(ex.what())); + return String::from_utf8_lossy(slice).into_owned(); + } catch (...) { + return String::from("Unknown exception!"_rs); + } +} +} diff --git a/lix/lix-rs/utils.hh b/lix/lix-rs/utils.hh new file mode 100644 index 000000000..db7e4d45f --- /dev/null +++ b/lix/lix-rs/utils.hh @@ -0,0 +1,202 @@ +#pragma once +///@file convenience utilities for working with the rust ffi bits + +#include +#include +#include +#include +#include +#include + +// this header requires `std` to mean `::std` +#include "lix/lix-rs/zngur.gen.hh" + +// bunch of forward declarations to avoid including all other headers. +namespace rust { +template +struct Box; +template +struct Dyn; +template +struct Fn; +template +struct Ref; + +struct Str; + +namespace std::option { +template +struct Option; +} + +namespace std::result { +template +struct Result; +} + +namespace std::string { +struct String; +} + +namespace std::vec { +template +struct Vec; +} + +using std::option::Option; +using std::result::Result; +using std::string::String; +using std::vec::Vec; + +namespace lix::ffi { +struct Error; +} +} + +// here come the *actual* utilities +namespace rust { + +/// marker that turns a value into an Ok result when returned +template +struct as_ok +{ + V v; + + explicit as_ok(V v) : v(::std::move(v)) {} + + template + operator Result() + { + return Result::Ok(::std::move(v)); + } +}; + +/// marker that turns a value into an Err result when returned +template +struct as_err +{ + V v; + + explicit as_err(V v) : v(::std::move(v)) {} + + template + operator Result() + { + return Result::Err(::std::move(v)); + } +}; + +/// wrapper that turns a C++ function object into a `Box`. the function objects +/// *must not* throw exceptions, otherwise the program will just abort. only use this when +/// you are absolutely certain that your function does not throw! if you aren't certain it +/// will throw. if you *are* certain it will throw anyway. passed callables must be marked +/// `noexcept` to serve as a reminder, if not marked the conversions will not be provided. +template +struct make_box_fn_noexcept +{ + Func fn; + + explicit make_box_fn_noexcept(Func fn) : fn(::std::move(fn)) {} + + template + requires requires(Func fn, Args... args) { + { fn(::std::move(args)...) } noexcept -> ::std::same_as; + } + operator Box>>() const + { + return Box>>::make_box(::std::move(fn)); + } +}; + +/// wrapper that turns a C++ function object into a `Box`. the function objects +/// passed here may throw exceptions. to allow for this their return value is wrapped in a +/// `Result` and exceptions are automatically turned into Err results. +template +auto make_box_fn(Fn fn) +{ + return make_box_fn_noexcept([fn{::std::move(fn)}](Args &&... args) noexcept { + using result_inner = decltype(fn(::std::forward(args)...)); + using result_type = + Result<::std::conditional_t<::std::is_void_v, Unit, result_inner>, lix::ffi::Error>; + try { + if constexpr (::std::is_void_v) { + fn(::std::forward(args)...); + return result_type(as_ok(::rust::Unit())); + } else { + return result_type(as_ok(fn(::std::forward(args)...))); + } + } catch (...) { + // make the error type dependent to delay type checking + using error_type = ::std::enable_if_t, lix::ffi::Error>; + return result_type(as_err(error_type::build(::std::current_exception()))); + } + }); +} + +::std::string_view to_std_string_view(Ref s); +::std::string to_std_string(Ref s); + +namespace std::string { +::std::string_view to_std_string_view(const String & s); +::std::string to_std_string(const String & s); +} + +String to_string(::std::string_view sv); + +// enum type matching +namespace matches { +struct Ok; +struct Err; +} + +template +struct match +{ + T value; + + operator T() + { + return ::std::move(value); + } +}; + +namespace std::result { +template +auto to_std(Result r) +{ + using result_type = ::std:: + variant, match>; + + if (r.matches_Ok()) { + return result_type(match{r.unwrap()}); + } else { + return result_type(match{r.unwrap_err()}); + } +} + +template FnOk, ::std::invocable FnErr> +auto match_result(Result r, FnOk ok, FnErr err) + -> ::std::common_type_t<::std::invoke_result_t, ::std::invoke_result_t> +{ + return r.matches_Ok() ? ok(r.unwrap()) : err(r.unwrap_err()); +} +} + +namespace std::option { +template +auto to_std(Option r) +{ + using result_type = ::std::optional; + + if (r.matches_Some()) { + return result_type{r.unwrap()}; + } else { + return result_type{}; + } +} +} +} + +namespace nix { +using namespace rust::lix; +} diff --git a/lix/meson.build b/lix/meson.build index afcdd5a1c..99e909845 100644 --- a/lix/meson.build +++ b/lix/meson.build @@ -89,6 +89,11 @@ config_h = configure_file( install_headers(config_h, subdir : 'lix') +# we only collect sources and generated headers here. real headers are collected and +# installed by each subdir due to how meson generates paths for headers it installs. +liblix_sources = [] +liblix_generated_headers = embedded_sandbox_headers + lix_rs_files = files( '../Cargo.toml', '../Cargo.lock', @@ -98,11 +103,6 @@ subdir('lix-rs') # Subcomponents: these link into artifacts themselves, and have interdependencies. subdir('lix-doc') -# we only collect sources and generated headers here. real headers are collected and -# installed by each subdir due to how meson generates paths for headers it installs. -liblix_sources = [] -liblix_generated_headers = embedded_sandbox_headers - subdir('libutil') subdir('libstore') subdir('libfetchers') @@ -156,6 +156,10 @@ if host_machine.system() == 'freebsd' dependencies += [ libprocstat ] endif +lixrs_cargo_env = { + 'MESON_BUILD_DIR': meson.project_build_root(), +} + lixrs = custom_target( 'liblixrs', input : lix_rs_files, @@ -171,9 +175,7 @@ lixrs = custom_target( cargo_jobs_args, ], console : true, - env : { - 'MESON_BUILD_DIR': meson.project_build_root(), - }, + env : lixrs_cargo_env, ) if meson.can_run_host_binaries() @@ -187,6 +189,8 @@ if meson.can_run_host_binaries() cargo_jobs_args, ], suite : 'check', + env : lixrs_cargo_env, + timeout : 300, ) endif diff --git a/meson.build b/meson.build index 438bb3152..18302a651 100644 --- a/meson.build +++ b/meson.build @@ -502,6 +502,8 @@ if enable_docs mdbook = find_program('mdbook', native : true) endif +subdir('tools/licxxbridge') + # Used to workaround https://github.com/mesonbuild/meson/issues/2320 in lix/nix/meson.build. installcmd = find_program('install', native : true) diff --git a/meson.options b/meson.options index c4a941622..573589d16 100644 --- a/meson.options +++ b/meson.options @@ -112,3 +112,7 @@ option('builtin-dep-closure', type : 'array', option('cargo-jobs', type : 'integer', value : 0, description : 'Value to pass for Cargo -j. Use 0 to omit', ) + +option('licxxbridge', type : 'string', value : '', + description : 'optional path to an existing licxxbridge executable. must be built from the current source tree, beware when setting this', +) diff --git a/meson/clang-tidy/clang-tidy-runner.py b/meson/clang-tidy/clang-tidy-runner.py index 64268ca9e..fe936f901 100755 --- a/meson/clang-tidy/clang-tidy-runner.py +++ b/meson/clang-tidy/clang-tidy-runner.py @@ -46,6 +46,10 @@ def go( str(jobs), "-header-filter", r"lix/[^/]+/.*\.hh", + "-exclude-header-filter", + r"\.gen\.hh$", + "-source-filter", + r"(?!.*\.gen\.cc$).*", ] if werror: args += ["-warnings-as-errors", "*"] diff --git a/package.nix b/package.nix index 694af9c4e..5b0f08e6b 100644 --- a/package.nix +++ b/package.nix @@ -287,6 +287,7 @@ let ./scripts/meson.build (fileset.fileFilter (f: lib.strings.hasSuffix ".wrap" f.name) ./subprojects) ./subprojects/aws_sdk + ./tools # Required for meson to generate Cargo wraps ./Cargo.lock ./Cargo.toml @@ -338,6 +339,36 @@ stdenv.mkDerivation (finalAttrs: { ] ); + # precompile the bridge generator since the zngur parser takes so long to build. this way we can + # cache the generator in ci and for devshells, both of which decrease cycle times by quite a bit + licxxbridge = rustPlatform.buildRustPackage { + pname = "licxxbridge"; + version = "0.0.0"; + src = fileset.toSource { + root = ./.; + fileset = fileset.unions [ + ./Cargo.toml + ./Cargo.lock + ./tools/licxxbridge + # these are only needed to let cargo parse the manifests at all, they're not used elsewise + ./lix/lix-doc/Cargo.toml + ./lix/lix-doc/src/lib.rs + ./lix/lix-rs/Cargo.toml + ./lix/lix-rs/src/lib.rs + ]; + }; + cargoDeps = rustPlatform.importCargoLock { lockFile = ./Cargo.lock; }; + cargoBuildFlags = [ + "-p" + "licxxbridge" + ]; + cargoTestFlags = [ + "-p" + "licxxbridge" + ]; + meta.mainProgram = "licxxbridge"; + }; + buildTestShell = if hostPlatform.isLinux then "${pkgsStatic.bash}/bin" @@ -419,6 +450,7 @@ stdenv.mkDerivation (finalAttrs: { # musl doesn't support fibers, and we can't detect this with meson alone. ++ lib.optional hostPlatform.isMusl "-Ddisable-fibers=true" ++ lib.optional (finalAttrs.dontBuild && !lintInsteadOfBuild) "-Denable-build=false" + ++ lib.optional (!finalAttrs.dontBuild) "-Dlicxxbridge=${lib.getExe finalAttrs.licxxbridge}" ++ lib.optional lintInsteadOfBuild "-Dlix-clang-tidy-checks-path=${lix-clang-tidy}/lib/liblix-clang-tidy.${ if hostPlatform.isDarwin then "dylib" else "so" }" @@ -879,6 +911,9 @@ stdenv.mkDerivation (finalAttrs: { unset PYTHONPATH export MANPATH=$out/share/man:''${MANPATH:-} + # default build dir. lets rust-analyzer find generated code + export MESON_BUILD_DIR="$PWD/build" + # Make bash completion work. XDG_DATA_DIRS+=:$out/share diff --git a/tests/unit/libutil/rust.cc b/tests/unit/libutil/rust.cc new file mode 100644 index 000000000..8815a811f --- /dev/null +++ b/tests/unit/libutil/rust.cc @@ -0,0 +1,90 @@ +#include "lix/libutil/error.hh" +#include "lix/libutil/types.hh" +#include "lix/lix-rs/main.gen.hh" +#include "lix/lix-rs/utils.hh" +#include "zngur.gen.hh" +#include "gtest/gtest.h" +#include +#include +#include +#include + +namespace nix { + +TEST(rustSupport, testMultiplyAddLen) +{ + auto vec = rust::Vec::new_(); + vec.push(rust::String::from("1"_rs)); + vec.push(rust::String::from("2"_rs)); + vec.push(rust::String::from("3"_rs)); + vec.push(rust::String::from("4"_rs)); + vec.push(rust::String::from("5"_rs)); + + auto args = ffi_test::TestMultiplyAddLenArgs::new_(20, 1); + args.set_b(5); + auto product = ffi_test::test_multiply_add_len(args, std::move(vec)); + + ASSERT_EQ(to_std_string(product.f0.as_str()), R"-((20 * 5 + ["1", "2", "3", "4", "5"].len()) = 105)-"); + ASSERT_EQ(product.f1, 20 * 5 + 5); +} + +TEST(rustSupport, testResult) +{ + auto result = ffi_test::test_result(); + ASSERT_TRUE(result.matches_Err()); + EXPECT_DEATH(result.unwrap(), "called `Result::unwrap\\(\\)` on an `Err`"); + auto msg = to_std_string(result.unwrap_err().to_string()); + ASSERT_EQ(msg, "errors travel freely"); + + std::visit( + overloaded{ + [](rust::match) { FAIL(); }, + [](rust::match>> e) { + auto msg = to_std_string(e.value.to_string()); + ASSERT_EQ(msg, "errors travel freely"); + } + }, + to_std(ffi_test::test_result()) + ); + + std::visit( + overloaded{ + [](rust::Unit) { FAIL(); }, + [](rust::Box> e) { + auto msg = to_std_string(e.to_string()); + ASSERT_EQ(msg, "errors travel freely"); + } + }, + to_std(ffi_test::test_result()) + ); + + match_result( + ffi_test::test_result(), + [](rust::Unit) { FAIL(); }, + [](rust::Box> e) { + auto msg = to_std_string(e.to_string()); + ASSERT_EQ(msg, "errors travel freely"); + } + ); +} + +TEST(rustSupport, testOption) +{ + ASSERT_EQ(to_std(ffi_test::test_option_some()), 1); + ASSERT_FALSE(to_std(ffi_test::test_option_none()).has_value()); +} + +TEST(rustSupport, testResultFromCxx) +{ + using rust::make_box_fn, ffi_test::test_exceptions; + + auto result = test_exceptions(make_box_fn([]() -> ::rust::Unit { throw Error("test"); })); + ASSERT_EQ(to_std_string(result), "\x1B[31;1merror:\x1B[0m test"); + + result = test_exceptions(make_box_fn([] { throw Error("test"); })); + ASSERT_EQ(to_std_string(result), "\x1B[31;1merror:\x1B[0m test"); + + result = test_exceptions(make_box_fn([] {})); + ASSERT_EQ(to_std_string(result), ""); +} +} diff --git a/tests/unit/meson.build b/tests/unit/meson.build index 56666d4af..c5415bf65 100644 --- a/tests/unit/meson.build +++ b/tests/unit/meson.build @@ -27,6 +27,7 @@ libutil_test_support_sources = files( libutil_test_support = library( 'lixutil-test-support', libutil_test_support_sources, + liblix_generated_headers, dependencies : [ liblix, rapidcheck, @@ -66,6 +67,7 @@ libutil_tests_sources = files( 'libutil/paths-setting.cc', 'libutil/pool.cc', 'libutil/references.cc', + 'libutil/rust.cc', 'libutil/serialise.cc', 'libutil/suggestions.cc', 'libutil/tarfile.cc', @@ -82,6 +84,7 @@ libutil_tests_sources = files( libutil_tester = executable( 'liblixutil-tests', libutil_tests_sources, + liblix_generated_headers, dependencies : [ libasanoptions, rapidcheck, @@ -115,6 +118,7 @@ libstore_test_support_sources = files( libstore_test_support = library( 'lixstore-test-support', libstore_test_support_sources, + liblix_generated_headers, dependencies : [ liblixutil_test_support, liblix, @@ -150,6 +154,7 @@ libstore_tests_sources = files( libstore_tester = executable( 'liblixstore-tests', libstore_tests_sources, + liblix_generated_headers, dependencies : [ libasanoptions, liblixstore_test_support, @@ -182,6 +187,7 @@ libexpr_test_support_sources = files( libexpr_test_support = library( 'lixexpr-test-support', libexpr_test_support_sources, + liblix_generated_headers, dependencies : [ liblixstore_test_support, liblix, @@ -214,6 +220,7 @@ libexpr_tests_sources = files( libexpr_tester = executable( 'liblixexpr-tests', libexpr_tests_sources, + liblix_generated_headers, dependencies : [ libasanoptions, liblixexpr_test_support, @@ -242,6 +249,7 @@ test( libcmd_tester = executable( 'liblix-tests', files('libcmd/args.cc'), + liblix_generated_headers, dependencies : [ libasanoptions, liblix, @@ -277,6 +285,7 @@ libmain_tests_sources = files( libmain_tester = executable( 'liblixmain-tests', libmain_tests_sources, + liblix_generated_headers, dependencies : [ liblix, gtest, diff --git a/tools/licxxbridge/Cargo.toml b/tools/licxxbridge/Cargo.toml new file mode 100644 index 000000000..d064451c1 --- /dev/null +++ b/tools/licxxbridge/Cargo.toml @@ -0,0 +1,12 @@ +[package] +name = "licxxbridge" +edition = "2021" +description = "small cxxbridge alternative that lets us use cxx versions nixpkgs doesn't have" + +[[bin]] +name = "licxxbridge" +path = "main.rs" + +[dependencies] +clap = { workspace = true, features = ["derive"] } +zngur.workspace = true diff --git a/tools/licxxbridge/main.rs b/tools/licxxbridge/main.rs new file mode 100644 index 000000000..2bcc88430 --- /dev/null +++ b/tools/licxxbridge/main.rs @@ -0,0 +1,63 @@ +use std::{fs, path::PathBuf}; + +use clap::{Parser, Subcommand}; + +#[derive(Parser)] +struct Args { + #[command(subcommand)] + command: Command, +} + +#[derive(Subcommand, Clone)] +enum Command { + Generate { + #[arg(long = "in")] + input: PathBuf, + #[arg(long)] + out_rs: PathBuf, + #[arg(long)] + out_header: PathBuf, + #[arg(long)] + out_shared_header: PathBuf, + #[arg(long)] + out_impl: PathBuf, + #[arg(long)] + out_deps: PathBuf, + }, +} + +fn main() { + let args = Args::parse(); + + match args.command { + Command::Generate { + input, + out_rs, + out_header, + out_shared_header, + out_impl, + out_deps, + } => { + // create an empty impl file if zngur doesn't expose anything to make life easier + fs::write(&out_impl, "").expect("failed to touch impl file"); + + zngur::Zngur::from_zng_file(input) + .with_rs_file(out_rs) + .with_cpp_file(out_impl) + .with_h_file(&out_header) + .with_crate_name("lix") + .with_zng_header(&out_shared_header) + .with_depfile(out_deps) + .generate(); + + let content = fs::read_to_string(&out_header).expect("failed to read generated header"); + fs::write( + &out_header, + content.replace( + "#include ", + &format!("#include \"{}\"", out_shared_header.display()), + ), + ).expect("failed to rewrite generated header"); + } + } +} diff --git a/tools/licxxbridge/meson.build b/tools/licxxbridge/meson.build new file mode 100644 index 000000000..7747a12ca --- /dev/null +++ b/tools/licxxbridge/meson.build @@ -0,0 +1,24 @@ +licxxbridge = get_option('licxxbridge') + +if licxxbridge == '' + licxxbridge = custom_target( + 'licxxbridge', + input : files( + '../../Cargo.toml', + '../../Cargo.lock', + 'Cargo.toml', + 'main.rs', + ), + output : 'licxxbridge', + command : [ + cargo, + '-Z', 'unstable-options', + 'build', + '--artifact-dir=@0@'.format(meson.current_build_dir()), + '-p', + 'licxxbridge', + cargo_jobs_args, + ], + console : true, + ) +endif