Some problems when packaging my interface with C

Hi there, I'm having some trouble packaging my interface with Rust and Cargo. The project has the C code in src/c, and the build script uses make to build the C code into a .a library. After that the project links to it and does everything fine. On my computer, if I do cargo test everything works perfectly.

On Travis-ci, on the other hand, the doc tests do not pass, and I cannot figure out why. As you can see here, the error it gives is error: linking with `cc` failed: exit code: 1. What I do not understand is why does the rest of the build and testing go perfectly, and that doesn't. As I say, in my computer, it works.

Moreover, when doing cargo package && cargo publish everything goes smoothly (if the library is already compiled in the .a file. The main problem is that I cannot get it to work in another crate if I put it as a dependency, it simply won't build! :confused:

Those are my main concerns right now, but I am also trying to add conditional compilation if SSE3 exists. The original library checks for the __SSE3__ flag, but I don't know how to do that in Rust so I don't compile functions that will not work in certain conditions.

Thanks in advance for your help!

You seem to be running into this issue, which have been closed mistakenly.

Adding -L src/c to the rustdoc command line passes the tests.

1 Like

How can I add that with Cargo?

This was intended as a confirmation that cargo probably should be adding that argument.

Just a few quick comments:

  • You have seen that warning about Drop + #[repr(C)], yes? It's not kidding: Drop changes the layout of the struct, so if it's supposed to match something on the C-side, it won't anymore.

  • Are you aware of the gcc crate? If the C library doesn't support Windows, you can probably just use make; if it does, remember that make is non-portable and a tremendous pain in the ass for Windows devs.

  • I don't know of any way to test for specific CPU features from within Rust. However, you can probably work around this by using C. Write a C program that tests the features of interest and prints out appropriate lines (see the page on build script output) when run from within the build script. These should then be testable from #[cfg(blah)] attributes. Unfortunately, I don't believe the gcc crate allows you to build executables.

Hi, thanks!

I have fixed the Drop warning. I wast just postponing it. About the GCC crate, the thing is that the C library itself has its own makefiles, each for each operating system. I guess I could probably reimplement that make files in the build.rs script, doing conditional compilation for each platform. But, if I want a static link, should I do something like this?:

extern crate gcc;

fn main() {
    gcc::Config::new()
        .file("src/c/src/crypto_hash_sha512.c")
        .file("src/c/src/crypto_stream.c")
        .file("src/c/src/randombytes.c")
        .file("src/c/src/fastrandombytes.c")
        .file("src/c/src/shred.c")
        .file("src/c/src/convert.c")
        .file("src/c/src/pack.c")
        .file("src/c/src/pol.c")
        .file("src/c/src/params.c")
        .file("src/c/src/pqntrusign.c")
        .include("src/c/src")
        .compile("libntrumls.a");
}

Would this be compatible with Windows too? and would the C code receive the flags telling if it's being compiled in Windows/Linux/BSD? and what about the __SSE__ flags?

I guess I would need to create an executable during the build process, build it, and check for it's output before building the library, and after building the library, build the main program. Would then this be easier to package for crates.io?

I finally ported all the makefile to Rust build.rs script. I have a cfg flag that will be on if SSE support exists, and I can use it in the library doing #[cfg(SSE)]. My question is how can I use the cfg!() macro to conditional compile parts of the code? I have a long function and at the end, if SEE is enabled should add 2 more lines of code, that use that function. The problem is that if I use this:

    if cfg!(SSE3) {
        let (c_tern, _) = b.mult_tern_sse(&a, 2048 - 1);
        assert!(c_tern.equals_mod(&c_int, 2048));
    }

The code inside the brckets gets checked, and if no SSE is present, mult_tern_sse() is not implemented, so it will not be able to build. How can I solve this?

/// Expands to its argument if SSE3 support is configured and to `()` otherwise
#[cfg(not(SSE3))]
macro_rules! with_sse3 {
    ($ex:expr) => (
        ()
    );
    ($bl:block) => {
        ()
    }
}

/// Expands to its argument if SSE3 support is configured and to `()` otherwise
#[cfg(SSE3)]
macro_rules! with_sse3 {
    ($ex:expr) => (
        $ex
    );
    ($bl:block) => {
        $bl
    }
}
    with_sse3! {{
        let (c_tern, _) = b.mult_tern_sse(&a, 2048 - 1);
        assert!(c_tern.equals_mod(&c_int, 2048));
    }}

Perhaps one could make a macro to automate this a bit...

1 Like

Thanks! it's working now!