KDAB / cxx-qt

Safe interop between Rust and Qt
https://kdab.github.io/cxx-qt/book/
964 stars 66 forks source link

Linker error when trying to include more than one Rust bridge #956

Closed akiselev closed 1 month ago

akiselev commented 1 month ago

Repro repository here: https://github.com/akiselev/linker-repro

I am trying to bind several QWidgets classes but I'm getting undefined reference/symbol errors when trying to build more than one Rust file with CxxQt bindings.

For the above repro repository, this is the order in build.rs

    builder = builder.file("src/widgets/qmainwindow.rs");
    builder = builder.file("src/gui/window.rs");

This is the error (I have tried with lld and mold):

  = note: mold: error: undefined symbol: cxxbridge1$unique_ptr$QMainWindow$get
          >>> referenced by 1dndnmyx9n9inks4
          >>>               /home/akiselev/git/linker-repro/target/debug/deps/linker_repro-adcedbdfd5841a4f.1dndnmyx9n9inks4.rcgu.o:(cxx::unique_ptr::UniquePtr$LT$T$GT$::from_raw::h623f3965ec02390b)

          mold: error: undefined symbol: rust$cxxqtlib2$cxxbridge1$qmainwindow_new
          >>> referenced by 1dndnmyx9n9inks4
          >>>               /home/akiselev/git/linker-repro/target/debug/deps/linker_repro-adcedbdfd5841a4f.1dndnmyx9n9inks4.rcgu.o:(linker_repro::widgets::qmainwindow::ffi::qmainwindow_new::ha885b51589433ed5)

          mold: error: undefined symbol: cxxbridge1$unique_ptr$QMainWindow$drop
          >>> referenced by 1dndnmyx9n9inks4
          >>>               /home/akiselev/git/linker-repro/target/debug/deps/linker_repro-adcedbdfd5841a4f.1dndnmyx9n9inks4.rcgu.o:(_$LT$linker_repro..widgets..qmainwindow..ffi..QMainWindow$u20$as$u20$cxx..unique_ptr..UniquePtrTarget$GT$::__drop::h3a648050050949fc)

          mold: error: undefined symbol: cxxbridge1$QMainWindow$show
          >>> referenced by 1dndnmyx9n9inks4
          >>>               /home/akiselev/git/linker-repro/target/debug/deps/linker_repro-adcedbdfd5841a4f.1dndnmyx9n9inks4.rcgu.o:(linker_repro::widgets::qmainwindow::ffi::QMainWindow::show::h9d25c8f2737bd474)

          mold: error: undefined symbol: cxxbridge1$unique_ptr$QMainWindow$raw
          >>> referenced by 1dndnmyx9n9inks4
          >>>               /home/akiselev/git/linker-repro/target/debug/deps/linker_repro-adcedbdfd5841a4f.1dndnmyx9n9inks4.rcgu.o:(_$LT$linker_repro..widgets..qmainwindow..ffi..QMainWindow$u20$as$u20$cxx..unique_ptr..UniquePtrTarget$GT$::__raw::hed6cd98e7f4a8b30)

          collect2: error: ld returned 1 exit status

If I change the order of the builder.file calls in build.rs:

    builder = builder.file("src/gui/window.rs");
    builder = builder.file("src/widgets/qmainwindow.rs");

It finds the QMainWindow symbols but instead fails to find the QWindow symbols:

  = note: mold: error: undefined symbol: rust$cxxqtlib2$cxxbridge1$qwindow_new
          >>> referenced by 15ze6smicxvukw75
          >>>               /home/akiselev/git/linker-repro/target/debug/deps/linker_repro-adcedbdfd5841a4f.15ze6smicxvukw75.rcgu.o:(linker_repro::gui::window::ffi::qwindow_new::ha8d93fde6a8567c7)

          mold: error: undefined symbol: cxxbridge1$unique_ptr$QWindow$drop
          >>> referenced by 15ze6smicxvukw75
          >>>               /home/akiselev/git/linker-repro/target/debug/deps/linker_repro-adcedbdfd5841a4f.15ze6smicxvukw75.rcgu.o:(_$LT$linker_repro..gui..window..ffi..QWindow$u20$as$u20$cxx..unique_ptr..UniquePtrTarget$GT$::__drop::hb2b2db5a56693e73)

          mold: error: undefined symbol: cxxbridge1$QWindow$show
          >>> referenced by 15ze6smicxvukw75
          >>>               /home/akiselev/git/linker-repro/target/debug/deps/linker_repro-adcedbdfd5841a4f.15ze6smicxvukw75.rcgu.o:(linker_repro::gui::window::ffi::QWindow::show::h354f6b949148b13e)

          mold: error: undefined symbol: cxxbridge1$unique_ptr$QWindow$raw
          >>> referenced by 15ze6smicxvukw75
          >>>               /home/akiselev/git/linker-repro/target/debug/deps/linker_repro-adcedbdfd5841a4f.15ze6smicxvukw75.rcgu.o:(_$LT$linker_repro..gui..window..ffi..QWindow$u20$as$u20$cxx..unique_ptr..UniquePtrTarget$GT$::__raw::ha9746a1dec5643c6)

          mold: error: undefined symbol: cxxbridge1$unique_ptr$QWindow$get
          >>> referenced by 15ze6smicxvukw75
          >>>               /home/akiselev/git/linker-repro/target/debug/deps/linker_repro-adcedbdfd5841a4f.15ze6smicxvukw75.rcgu.o:(_$LT$linker_repro..gui..window..ffi..QWindow$u20$as$u20$cxx..unique_ptr..UniquePtrTarget$GT$::__get::ha18cfff8f68f3aa1)

          collect2: error: ld returned 1 exit status

Whichever Rust file is last is the one that gets linked in, the rest get ignored. If I use this order in build.rs:

    builder = builder.file("src/widgets/qmainwindow.rs");
    builder = builder.file("src/gui/window.rs");

and remove QMainWindow from main.rs altogether:

fn main() {
    let mut app = QGuiApplication::new();

    let mut window = gui::QWindow::new();
    window.as_mut().unwrap().show();

    if let Some(app) = app.as_mut() {
        app.exec();
    }
}

It builds and shows a window when run.


Does anyone have any idea why this is happening?

akiselev commented 1 month ago

After instrumenting cxx-qt-build it looks like it's generating all of the #[cxx_qt::bridge] code in the same ffi.cxx.cpp file path, where as cxx-qt-lib files annotated with #[cxx::bridge] have unique names.

warning: linker-repro@0.1.0: Generating "/home/akiselev/git/linker-repro/target/debug/build/linker-repro-ed6c7f718c01b592/out/cxx-qt-gen/src/ffi.cxx.cpp"
warning: linker-repro@0.1.0: Generating "/home/akiselev/git/linker-repro/target/debug/build/linker-repro-ed6c7f718c01b592/out/cxx-qt-gen/src/ffi.cxx.cpp"
warning: cxx-qt-lib@0.6.1: Generating "/home/akiselev/git/linker-repro/target/debug/build/cxx-qt-lib-22d15c8779448e22/out/cxx-qt-gen/src/qbytearray.cxx.cpp"
warning: cxx-qt-lib@0.6.1: Generating "/home/akiselev/git/linker-repro/target/debug/build/cxx-qt-lib-22d15c8779448e22/out/cxx-qt-gen/src/qcoreapplication.cxx.cpp"

Is this intentional behavior with cxx-qt? Is there a way I can fix it to generate separate files?

akiselev commented 1 month ago

I think I found the culprit

Since the file is named after the module (ffi in both files in this case), it overwrote them. Turns out I have to have unique module names for the CxxQtBridge.

ahayzen-kdab commented 1 month ago

Yup, until https://github.com/rust-lang/rust/issues/54725 is stabilised we cannot replicate the CXX behaviour, see the ancient WIP branch here https://github.com/KDAB/cxx-qt/pull/200 and issue here https://github.com/KDAB/cxx-qt/issues/855

Until that happens I was going to make cxx_file_stem mandatory to avoid this situation, one of the commits in https://github.com/KDAB/cxx-qt/pull/856