diff --git a/Makefile b/Makefile index 4fd6899..907aa89 100644 --- a/Makefile +++ b/Makefile @@ -28,22 +28,6 @@ ifneq (,$(findstring MINGW,$(detected_OS))) detected_OS := Windows endif -# liblibchat and librln are both Rust staticlibs that embed Rust std, -# resulting in duplicate symbol errors at link time. -# On Linux, --allow-multiple-definition suppresses the error. -# On Darwin, Apple ld does not support that flag; instead we partial-link -# liblibchat.a with ld -r, keeping only its public API symbols exported so -# that all Rust std internals (including _rust_eh_personality) are hidden -# and cannot conflict with the copy embedded in librln.a. -ifeq ($(detected_OS),Linux) - LDFLAGS := --passL:-Wl,--allow-multiple-definition -endif - -ifeq ($(detected_OS),Darwin) - LIBCHAT_A := build/liblibchat_clean.a -else - LIBCHAT_A := vendor/libchat/target/release/liblibchat.a -endif ########## ## Main ## @@ -90,50 +74,30 @@ NIM_PARAMS := $(NIM_PARAMS) -d:git_version=\"$(GIT_VERSION)\" ################## ## Dependencies ## ################## -.PHONY: build-waku-librln -LIBRLN_VERSION := v0.7.0 +RUST_BUNDLE_LIB := rust-bundle/target/release/lib_rust_bundle.a -ifeq ($(detected_OS),Windows) -LIBRLN_FILE := rln.lib -else -LIBRLN_FILE := librln_$(LIBRLN_VERSION).a -endif - - -build-waku-librln: - @echo "Start building waku librln" +# libchat and rln each embed Rust std when built as staticlibs; linking both +# causes duplicate-symbol errors. rust-bundle/ links them as rlibs so std +# is emitted once. [1] +# [1] https://doc.rust-lang.org/reference/linkage.html +.PHONY: build-rust-bundle +build-rust-bundle: + @echo "Building Rust bundle (libchat + rln)" $(MAKE) -C vendor/nwaku librln - $(eval NIM_PARAMS += --passL:./vendor/nwaku/${LIBRLN_FILE} --passL:-lm) - @echo "Completed building librln" + cargo build --release --manifest-path rust-bundle/Cargo.toml + @echo "Bundle library: $(RUST_BUNDLE_LIB)" build-waku-nat: @echo "Start building waku nat-libs" $(MAKE) -C vendor/nwaku nat-libs @echo "Completed building nat-libs" -.PHONY: build-libchat -build-libchat: - @echo "Start building libchat" - cd vendor/libchat && cargo build --release -ifeq ($(detected_OS),Darwin) - @mkdir -p build - @nm --no-llvm-bc -gjU vendor/libchat/target/release/liblibchat.a 2>/dev/null | \ - grep -E '^_(chat_|double_ratchet_|encrypt_result_|ffi_c_string_|installation_key_pair_|ratchet_state_|create_context|installation_name|destroy_context|destroy_string|create_intro_bundle|create_new_private_convo|send_content|handle_payload|destroy_intro_result|destroy_send_content_result|destroy_handle_payload_result|destroy_convo_result)' \ - > build/libchat_exports.txt - @ld -r -exported_symbols_list build/libchat_exports.txt \ - vendor/libchat/target/release/liblibchat.a \ - -o build/liblibchat_clean.o - @libtool -static -o $(LIBCHAT_A) build/liblibchat_clean.o -endif - @echo "Completed building libchat" - .PHONY: tests -tests: | build-waku-librln build-waku-nat build-libchat logos_chat.nims +tests: | build-rust-bundle build-waku-nat logos_chat.nims echo -e $(BUILD_MSG) "build/$@" && \ $(ENV_SCRIPT) nim tests $(NIM_PARAMS) \ - --passL:$(LIBCHAT_A) \ - $(LDFLAGS) \ + --passL:$(RUST_BUNDLE_LIB) --passL:-lm \ logos_chat.nims @@ -142,11 +106,10 @@ tests: | build-waku-librln build-waku-nat build-libchat logos_chat.nims ########## # Ensure there is a nimble task with a name that matches the target -tui bot_echo pingpong: | build-waku-librln build-waku-nat build-libchat logos_chat.nims +tui bot_echo pingpong: | build-rust-bundle build-waku-nat logos_chat.nims echo -e $(BUILD_MSG) "build/$@" && \ $(ENV_SCRIPT) nim $@ $(NIM_PARAMS) \ - --passL:$(LIBCHAT_A) \ - $(LDFLAGS) \ + --passL:$(RUST_BUNDLE_LIB) --passL:-lm \ --path:src logos_chat.nims ########### @@ -165,11 +128,10 @@ endif LIBLOGOSCHAT := build/liblogoschat.$(LIBLOGOSCHAT_EXT) .PHONY: liblogoschat -liblogoschat: | build-waku-librln build-waku-nat build-libchat logos_chat.nims +liblogoschat: | build-rust-bundle build-waku-nat logos_chat.nims echo -e $(BUILD_MSG) "$(LIBLOGOSCHAT)" && \ $(ENV_SCRIPT) nim liblogoschat $(NIM_PARAMS) \ - --passL:$(LIBCHAT_A) \ - $(LDFLAGS) \ + --passL:$(RUST_BUNDLE_LIB) --passL:-lm \ --path:src logos_chat.nims && \ echo -e "\n\x1B[92mLibrary built successfully:\x1B[39m" && \ echo " $(shell pwd)/$(LIBLOGOSCHAT)" diff --git a/flake.nix b/flake.nix index 9858867..4cec685 100644 --- a/flake.nix +++ b/flake.nix @@ -25,11 +25,11 @@ overlays = [ rust-overlay.overlays.default ]; }; libchatDrv = pkgs.callPackage ./nix/libchat.nix {}; - librlnDrv = pkgs.callPackage ./nix/librln.nix {}; + rustBundleDrv = pkgs.callPackage ./nix/bundle.nix { src = self; }; in { packages.default = pkgs.callPackage ./nix/default.nix { src = self; - inherit libchatDrv librlnDrv; + inherit rustBundleDrv; }; devShells.default = pkgs.callPackage ./nix/shell.nix { inherit libchatDrv; diff --git a/nix/bundle.nix b/nix/bundle.nix new file mode 100644 index 0000000..73c280f --- /dev/null +++ b/nix/bundle.nix @@ -0,0 +1,34 @@ +{ lib, stdenv, rust-bin, makeRustPlatform, perl, pkg-config, cmake, src }: + +let + rustToolchain = rust-bin.fromRustupToolchainFile (src + "/vendor/libchat/rust_toolchain.toml"); + rPlatform = makeRustPlatform { + cargo = rustToolchain; + rustc = rustToolchain; + }; +in rPlatform.buildRustPackage { + pname = "rust-bundle"; + version = "0.1.0"; + inherit src; + cargoRoot = "rust-bundle"; + + cargoLock = { + lockFile = src + "/rust-bundle/Cargo.lock"; + # Add outputHashes here for any git/non-registry deps that appear in + # rust-bundle/Cargo.lock (run `nix build` once; it will report missing hashes). + outputHashes = {}; + }; + + nativeBuildInputs = [ perl pkg-config cmake ]; + doCheck = false; + + buildAndTestSubdir = "rust-bundle"; + + installPhase = '' + runHook preInstall + mkdir -p $out/lib + cp rust-bundle/target/*/release/lib_rust_bundle.a $out/lib/ 2>/dev/null || true + cp rust-bundle/target/release/lib_rust_bundle.a $out/lib/ 2>/dev/null || true + runHook postInstall + ''; +} diff --git a/nix/default.nix b/nix/default.nix index e0de975..9aa05ff 100644 --- a/nix/default.nix +++ b/nix/default.nix @@ -1,8 +1,7 @@ { lib, stdenv, nim, which, pkg-config, writeScriptBin, openssl, miniupnpc, libnatpmp, src, # logos-chat source (self from flake, with submodules=1) - libchatDrv, # result of libchat.nix - librlnDrv }: # result of librln.nix + rustBundleDrv }: # result of bundle.nix # NOTE: this build requires git submodules to be present in src. # When fetching from GitHub use '?submodules=1#', e.g.: @@ -20,14 +19,12 @@ in stdenv.mkDerivation { inherit src; NIMFLAGS = lib.concatStringsSep " " [ - "--passL:${libchatDrv}/lib/liblibchat.a" - "--passL:${librlnDrv}/lib/librln_v0.7.0.a" + "--passL:${rustBundleDrv}/lib/lib_rust_bundle.a" "--passL:-lm" "-d:miniupnpcUseSystemLibs" "-d:libnatpmpUseSystemLibs" "--passL:-lminiupnpc" "--passL:-lnatpmp" - (lib.optionalString (!stdenv.isDarwin) "--passL:-Wl,--allow-multiple-definition") "-d:git_version=${revision}" ]; diff --git a/nix/libchat.nix b/nix/libchat.nix index f2f889d..8e3befd 100644 --- a/nix/libchat.nix +++ b/nix/libchat.nix @@ -1,4 +1,4 @@ -{ lib, stdenv, rust-bin, makeRustPlatform, perl, pkg-config, cmake }: +{ rust-bin, makeRustPlatform, perl, pkg-config, cmake }: let rustToolchain = rust-bin.fromRustupToolchainFile ../vendor/libchat/rust_toolchain.toml; @@ -31,20 +31,6 @@ in rPlatform.buildRustPackage { cp target/release/liblibchat.so $out/lib/ 2>/dev/null || true cp target/release/liblibchat.dylib $out/lib/ 2>/dev/null || true cp target/release/liblibchat.a $out/lib/ 2>/dev/null || true - ${lib.optionalString stdenv.isDarwin '' - # Partial-link liblibchat.a, keeping only its public API symbols exported. - # This hides all Rust std symbols (including _rust_eh_personality) so they - # do not conflict with the copy embedded in librln.a at final link time. - if [ -f "$out/lib/liblibchat.a" ]; then - nm --no-llvm-bc -gjU $out/lib/liblibchat.a 2>/dev/null | \ - grep -E '^_(chat_|double_ratchet_|encrypt_result_|ffi_c_string_|installation_key_pair_|ratchet_state_|create_context|installation_name|destroy_context|destroy_string|create_intro_bundle|create_new_private_convo|send_content|handle_payload|destroy_intro_result|destroy_send_content_result|destroy_handle_payload_result|destroy_convo_result)' \ - > $TMPDIR/libchat_exports.txt - ld -r -exported_symbols_list $TMPDIR/libchat_exports.txt \ - $out/lib/liblibchat.a \ - -o $TMPDIR/liblibchat_clean.o - libtool -static -o $out/lib/liblibchat.a $TMPDIR/liblibchat_clean.o - fi - ''} runHook postInstall ''; } diff --git a/rust-bundle/Cargo.toml b/rust-bundle/Cargo.toml new file mode 100644 index 0000000..8a52a24 --- /dev/null +++ b/rust-bundle/Cargo.toml @@ -0,0 +1,15 @@ +[package] +name = "rust-bundle" +version = "0.1.0" +edition = "2021" + +[lib] +name = "rust_bundle" +crate-type = ["staticlib"] + +[profile.release] +panic = "abort" + +[dependencies] +libchat = { path = "../vendor/libchat/conversations" } +rln = { path = "../vendor/nwaku/vendor/zerokit/rln", features = ["arkzkey"] } diff --git a/rust-bundle/src/lib.rs b/rust-bundle/src/lib.rs new file mode 100644 index 0000000..5f57bcc --- /dev/null +++ b/rust-bundle/src/lib.rs @@ -0,0 +1,4 @@ +// Force both rlibs into this staticlib. +// Their #[no_mangle] pub extern "C" symbols are exported from lib_rust_bundle.a. +extern crate libchat; +extern crate rln;