Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Cannot generate rust bindings for fbx c++ library #937

Closed
franksl opened this issue Sep 24, 2021 · 9 comments
Closed

Cannot generate rust bindings for fbx c++ library #937

franksl opened this issue Sep 24, 2021 · 9 comments

Comments

@franksl
Copy link

franksl commented Sep 24, 2021

Hello!
I'm trying to generate rust bindings for the autodesk fbx c++ library to be able to use the fbx functionalities inside a rust application.
The fbx library is comprised of precompiled dynamic libraries and header files and it's publicly available.
I created a build.rs file like this:

fn main() {
    println!("cargo:rustc-link-search=/path/to/lib/gcc/x64/release/"); // path to the compiled fbx library
    println!("cargo:rustc-link-lib=fbxsdk");

    cxx_build::bridge("src/main.rs")
        .flag_if_supported("-std=c++11")
        .include("/path/to/lib/include")  // path to the fbx headers files
        .compile("cxxbridge-demo");
    println!("cargo:rerun-if-changed=src/main.rs");
    println!("cargo:rerun-if-changed=src/shim.h");
}

My main.rs file is like this:

#[cxx::bridge]
mod ffi {
    unsafe extern "C++" {
        include!("testcxx/lib/include/fbxsdk.h");
        include!("testcxx/src/shim.h");

        type FbxManager;

        fn FbxManager_Create() -> UniquePtr<FbxManager>;
    }
}
fn main() {
    let mgr = ffi::FbxManager_Create();
}

In shim.h I tried to create a shim for the FbxManager::Create() static method which is usually the first method called to initilaize the library.
This is shim.h

#pragma once
#include <fbxsdk.h>
#include <memory>

std::unique_ptr<FbxManager> FbxManager_Create() {
    return std::unique_ptr<FbxManager>(FbxManager::Create());
}

When I run cargo build with this situation I get an ld error that says:

path/to/testcxx/target/debug/build/testcxx-59666266ff383069/out/libcxxbridge-demo.a(main.rs.o): in function `FbxManager_Create()':
          /path/to/testcxx/target/debug/build/testcxx-59666266ff383069/out/cxxbridge/crate/testcxx/src/shim.h:6: undefined reference to `fbxsdk::FbxManager::Create()'
          collect2: error: ld returned 1 exit status

I would like to ask help for this specific case but also how in general I should behave when trying to bind to c++ static methods and creating instances of c++ classes.
Many thanks,
Frank

@adetaylor
Copy link
Collaborator

Perhaps add inline:

inline std::unique_ptr<FbxManager> FbxManager_Create() {
    return std::unique_ptr<FbxManager>(FbxManager::Create());
}

@franksl
Copy link
Author

franksl commented Sep 24, 2021

Sorry I forgot to transcribe the 'inline' statement because I did thousands of attempts, for a second I wished it would be that easy :-)
Unfortunately the error is the same even with inline added.
Thanks,
Frank

@adetaylor
Copy link
Collaborator

Ah, yes, reading your error message more carefully it's not that simple :)

 undefined reference to `fbxsdk::FbxManager::Create()'
          collect2: error: ld returned 1 exit status

It seems like the SDK itself is not being linked in. I'd double-check these lines are having the desired effect

    println!("cargo:rustc-link-search=/path/to/lib/gcc/x64/release/"); // path to the compiled fbx library
    println!("cargo:rustc-link-lib=fbxsdk");

cargo build -vvv may give clues about the linker command actually being used? You could also use commands like nm to list the symbols inside the fbxsdk to ensure it really does contain the Create method you're calling.

@franksl
Copy link
Author

franksl commented Sep 24, 2021

It looks like those two lines written like that are the only way I could make it compile without errors concerning library not found.
Those libraries are compiled with gcc 4.8 in both static and dynamic versions (.so and .a), could this be a problem?
I'm kind of desperate, the FbxManager::Create() method is the main entry point for the library so it's exported for sure.
Unfortunately I'm not an expert with c compilers and linkers...
Sorry, maybe the issue section is not the place to discuss these kind of problems, is there some other forum or community where I should ask instead?
Thanks,
Frank

@adetaylor
Copy link
Collaborator

adetaylor commented Sep 24, 2021

Without a lot more information (especially the full build output with -vvv and some nm-like dumps) I'm not sure there's much more I can suggest. All you really need to know about linking is that the libcxxbridge-demo.a generated by the cxx and cc build process will require certain symbol names, and those names need to be within libfbxsdk.so or libfbxsdk.a. You use nm or objdump to check that those lists match up. (My only theory is that somehow the old version of GCC is doing different C++ name mangling from the modern compiler, but this suggests it should be compatible).

@dtolnay
Copy link
Owner

dtolnay commented Sep 24, 2021

My other guess is it's an order sensitive link flag situation. If you're doing the link using gold or GNU ld, the rustc-link-lib (which are -l flags) are order sensitive. You might try moving rustc-link-lib=fbxsdk further down. Alternatively you can rule out order sensitivity by using lld for the linker, which is not order sensitive for -l flags. RUSTFLAGS="-C link-arg=-fuse-ld=lld"

@franksl
Copy link
Author

franksl commented Sep 27, 2021

Hi guys!
Thanks a lot for the suggestions!
I followed the advice of changing the order of linking and placed the rustc-link-lib=fbxsdk at the end of build.rs. Finally this revealed some enlightning messages:

note: /usr/bin/ld: /path/to/testcxx/lib/lib/gcc/x64/release//libfbxsdk.so: undefined reference to `deflateInit_'
          /usr/bin/ld: /path/to/testcxx/lib/lib/gcc/x64/release//libfbxsdk.so: undefined reference to `xmlDocSetRootElement'

and others like these.
This suggested me that the the linker missed gzlib and libxml2.
So I added them making my build.rs like this:

fn main() {
    println!("cargo:rustc-link-search=/path/to/testcxx/lib/lib/gcc/x64/release/");

    cxx_build::bridge("src/main.rs")
        .flag_if_supported("-std=c++11")
        .include("/path/to/testcxx/lib/include")
        .compile("cxxbridge-demo");
    println!("cargo:rerun-if-changed=src/main.rs");
    println!("cargo:rerun-if-changed=src/shim.h");

    println!("cargo:rustc-link-lib=fbxsdk");
    println!("cargo:rustc-link-lib=z");
    println!("cargo:rustc-link-lib=xml2");
}

and finally the build completed successfully!
Now I have another problem: when running cargo run I get this error:
target/debug/testcxx: error while loading shared libraries: libfbxsdk.so: cannot open shared object file: No such file or directory

what should I add to make it work?
Thanks,
Frank

@franksl
Copy link
Author

franksl commented Sep 27, 2021

Please forget my last question, I solved by adding libfbx to LD_LIBRARY_PATH.
Now the project builds and runs without errors but I have two problems.

1: many destructors in the fbx library are declared as protected. For example the FbxManager class is declared like this:

class FBXSDK_DLL FbxManager
{
...
protected:
	FbxManager();
	virtual ~FbxManager();
}

This leads to an error during the build:

/path/to/testcxx/target/debug/build/testcxx-59666266ff383069/out/cxxbridge/crate/testcxx/src/shim.h:6:60:   required from here
/usr/include/c++/9/bits/unique_ptr.h:81:2: error: ‘virtual fbxsdk::FbxManager::~FbxManager()’ is protected within this context
   81 |  delete __ptr;
      |  ^~~~~~

I can solve by modifing the headers and setting protected to public, is there another way?

2: I have a static method, used as constructor for the FbxIOSettings class, with this signature:
static Class* Create(FbxManager* pManager, const char* pName);

How can I declare it to be used in rust? I converted the pManager argument as a UniquePtr, what about the pName, which is a const char* in C++?

Thanks,
Frank

@dtolnay
Copy link
Owner

dtolnay commented Dec 8, 2021

I'm glad that the linkage issue got solved!

The protected destructor issue seems unrelated so I would appreciate if you could open a separate issue with a minimal repro — that can reproduce the same build error without needing the whole fbx library involved. Hopefully you can write your own small dummy types with protected destructor for the repro to use.

On the Create question: this would be appropriate for Stack Overflow or any of the resources shown in https://www.rust-lang.org/community.

@dtolnay dtolnay closed this as completed Dec 8, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants