I'm writing a Rust interface to a small C library, which has headers spread in a few locations. It's not a system library, and is normally used by some executables in the same package; I'm currently including it as a git submodule in my Cargo project.
Building the library seems to be pretty easy; I've opted to use the gcc crate from build.rs:
gcc::Config::new()
.file("external/foo/dir1/file1.c")
.file("external/foo/dir2/file2.c")
.include("external/foo/dir1/")
.include("external/foo/dir2/")
.include("external/foo/config_a/")
.compile("libfoo.a");
Now I was hoping to use the bindgen crate to generate the FFI interface without too much fuss, but it doesn't seem to have a way of setting include paths.
I can create a wrapper.h as suggested by this blog and include several headers, but if dir1/dir1.h includes conf.h directly, which works when building due to .include("external/foo/config_a/") it can't be found.
I can't find anything in bindgen's API to help here (essentially I want to pass the equivalent of gcc/clang's -I option). Am I missing anything?
The best option I can think of so far is to copy the various headers from the library source into some temporary directory in build.rs and run bindgen on that, but that seems somewhat messy if there's a nicer way.
With the API you can use Builder::clang_arg with arbitrary arguments:
let b = bindgen::builder().header("foo.h").clang_arg("-I/path");
From the command line you can do the same by appending arguments after --, like:
bindgen foo.h -- -I/path
Related
In my library I have few examples -- (normally) each one is represented by a single x<N>.rs file living in examples directory.
One example uses a .proto file -- this file needs to be compiled during build (of said example) and it's generated output is used by example itself.
I've tried this in my Cargo.toml:
[[example]]
name = "x1"
path = "examples/x1/main.rs"
build = "examples/x1/build.rs"
but build key gets ignored when I run cargo build --example x1
Is it possible to have example-specific build.rs file?
If not -- what is the correct way to deal with this situation?
Edit: I ended up processing that .proto file in crate's build.rs (even though it is not required to build that crate) and using artefacts in the example like this:
pub mod my_proto {
include!(concat!(env!("OUT_DIR"), "/my_proto.rs"));
}
This is not possible. This issue explains why, but in a nutshell build scripts are used for whole crate. So you could move your example into separate crate.
So I am generating bindings for a library and on top of that I'm generating most of the safe wrappers for that library.
The way I'm doing is simply generating a String with all the contents and writing to the file with the File trait...
I know the bindings crate supports formatting the generated code, but this particular one is not generated by it. Is there a way to make the build.rs call rustfmt on that generated file?
I found out that rust bindgen uses a regular command call. I thought it was doing through some kind of rustfmt library.
Reference: https://docs.rs/bindgen/0.51.1/src/bindgen/lib.rs.html#1945
I have a Rust library crate which I only use on Linux, but I don't know of any reason it shouldn't work on Windows; however one of the examples is Unix-specific, so it the crate fails the build on AppVeyor.
The reason is that there's a Unix-only dependency (Termion) used by one of the examples, so when I used an AppVeyor template, it fails to build that dev-dependency.
So I made the dependency conditional:
[target.'cfg(unix)'.dev-dependencies]
termion = "1.0"
So far so good, but of course now that example fails on extern crate termion.
What I need is to just not build that example on non-Unix-like targets. I was hoping something like:
[[target.'cfg(unix)'.example]]
name = "foo"
would work, but I get:
warning: unused manifest key: target.cfg(unix).example
error: no example target named `foo`
Another promising way forward was Cargo's required-features, but as of writing it's apparently not in stable Cargo, which means that doesn't help me checking that it works on stable Rust on Windows.
The last option I can think of is to #[cfg(unix)] away most of the example's source, turning it into a stub on Windows, but I'd really like a cleaner way of doing it.
Is there some existing way of skipping an example on some unsupported targets that works with current stable Rust/Cargo?
I have a Cargo project named foo that produces a libfoo-<random-hash>.dylib. How do I link against it? clang only finds it if I rename it to libfoo.dylib but libfoo-<random-hash>.dylib is needed for running the program. My workaround is to manually copy and rename the lib but this can’t be the proper procedure.
My procedure to compile and run the C program is:
$ cargo build
$ cp target/debug/libfoo-*.dylib target/debug/libfoo.dylib
$ clang -lfoo -L target/debug -o bar bar.c
$ LD_LIBRARY_PATH=target/debug ./bar
The hash serves a purpose: It gives a link error in case of a version mismatch (especially important with cross-crate inlining). It also allows one binaries to link to two versions of a crate (e.g., because your application uses two libraries that internally use different versions). But in the C world, nobody cares about that. If you don't care either, or care more about making it easy to link to your library, just rename the shared object to get rid of the hash.
Note that the hash is added by Cargo. If you build by directly invoking rustc, no hash is added — but nobody wants that. AFAIK Cargo doesn't offer an option to omit the hash, but you might want to try asking the developers or filing an issue.
Alternatively, you could extract the full library name (including hash) from the file name, and instruct clang to link to that. Then you get the above advantages, but it makes the build process of everyone who wants to link to your library more complicated.
Since Rust 1.11, Cargo supports the
cdylib
artifact type for this exact purpose:
[lib]
name = "foo"
crate-type = ["rlib", "cdylib"]
This definition yields a ./target/{release,debug}/libfoo.so with
all the Rust dependencies statically linked in so it can be linked into C
projects right away.
If I have multiple .rs files in the src directory of a Cargo package, what are the rules for visibility, importing, etc.?
Currently, any extra (i.e. not the file that is explicitly identified as the source for the executable in Cargo.toml) files are ignored.
What do I need to do to fix this?
There is nothing special about Cargo at all in this way. It’s all the perfectly normal Rust module system. If Cargo will be compiling src/lib.rs, that’s more or less equivalent to having executed rustc --crate-type lib src/lib.rs (there are more command line arguments in practice, but that’s the basics of it).
Other files are then used with mod, use and so forth. Files are not automatically imported or anything like that. This part is not documented very clearly yet; a couple of things that show briefly how to achieve things are http://rustbyexample.com/mod/split.html and http://doc.rust-lang.org/reference.html#modules, but any non-trivial code base will use them and so you can pick just about any code base to look at for examples.
It's hard to say what you're getting tripped up on from the info you shared. Here are three seemingly trivial things that I still had to refer to the documentaton to figure out:
First of all,
mod foo;
looks like a declaration, but it without arguments it is actually something like an include. So you use the same keyword both for declaring and including modules, i.e. there is no using:: keyword.
Second, modules themselves can be public or private. If you didn't add a pub keyword both on the function in question AND on the containing module, that may be tripping you up.
pub mod foo {pub fn bar();}
Third, there seems to be an implicit module added at the top of every file. This is confusing; the reference manual talks about a strict separation between file paths and names, and the module paths in your code, but that abstraction seems to be leaky here.
Note, Rust is still pre-1.0 (0.12) at the time of writing, at the module system and file paths are relatively high level, so don't be surprised if what I said may already wrong by the time you read this.
Files are implicitly included from your rust code.
For instance, if a file src/foo.rs pointed by path in a [lib] or [[bin]] section of your Cargo.toml contains:
mod bar;
It tells cargo to build src/bar.rs too, and include it.