How to compile multiple Rust files into a single C library? - rust

You can compile a Rust file to a C library like this:
rustc --crate-type=staticlib file.rs
But what if you have multiple Rust files, can you compile them into a single C library, or does each one have to be a different library? How does the Rust module system interact with building staticlibs?

Here, file.rs is your crate root, which is what results in the static library. So to add other files, you have to reference them from the crate root. That is, assuming you have a file other.rs, you could do something like mod other; in file.rs to effectively 'bring in' the contents of other.rs into file.rs.
See the guide for more information.

Related

Is it possible to have example-specific build.rs file?

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.

How to better understand Crate in Rust?

In this book- Rust By Example,Chapter 11:
A crate is a compilation unit in Rust. Whenever rustc some_file.rs is called, some_file.rs is treated as the crate file.
According to this book, what about the source file ?
The Rust Reference | Crates and source files
The compilation model centers on artifacts called crates. Each compilation processes a single crate in source form, and if successful, produces a single crate in binary form: either an executable or some sort of library.
The Rust compiler is always invoked with a single source file as input, and always produces a single output crate. The processing of that source file may result in other source files being loaded as modules. Source files have the extension .rs.
According to this statement, I think:
Source file(.rs file) --> corresponding crate
Just like : .java --> .class
Now I can't understand this problem; I'm all at sea.
This is the key part of the material you've quoted:
The processing of that source file may result in other source files being loaded as modules.
If you examine a typical library, you will find a file called src/lib.rs which contains several lines like mod foo;. Each of those identifies another file src/foo.rs which the compiler will interpret as another module making up part of the crate (or it can contain the module directly, in the same file).
It is not that one source file makes up a crate: it's that starting from that one source file, you can find all the files making up the crate, as opposed to other compilation models where the compiler might be given many file names to start from.

How to make an item visible in my binary crate targets but not any other crates?

I would like to generate multiple binaries using a lot of the same common code. If I write everything in src/main.rs I can simply mark items at pub(crate) and access the code without exporting it. However if I put the binary in src/bin/foo.rs then I can not find a way to access this without marking everything pub. I would not like to mark everything pub not only because I don't want others to depend on it but also because it renders visibility checking ineffective.
The only workaround I have found is to put the file inside of the src directory then put a simple shim in bin/foo-bar.rs that just calls my_crate::bin_foo_bar::main(). This isn't very tidy and requires a bunch of overhead.
Inside your package you may define a single lib crate and multiple binary crates. If you declare a type inside your library crate as pub(crate) it will obviously unvisible from your binary crates. So revise the definitions. A package is not a crate, it is a package of crates. And pub(crate) types are only visible inside the crate they belong to.

Setting the include path with bindgen

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

How to programmatically compile Rust programs?

Given metadata about the library I am trying to create in the form of an expression tree, I would like to be able to convert this into some sort of Rust-specific syntax tree that can be given to the Rust compiler.
I found a related question but it is outdated.
Yes there is. The Rust compiler itself. The entire compiler is a library and rustc is just a small crate that calls into the compiler. As an example there's the stupid-stats crate. It runs the rust compiler to generate some statistics about the code.
All you need is to import the rustc and rustc_driver crates (with extern crate) and implement the rustc_driver::CompilerCalls trait for a type (lets call it MyDriver). Then you can run rustc like this:
let args: Vec<_> = std::env::args().collect();
let mut my_driver = MyDriver::new();
rustc_driver::run_compiler(&args, &mut my_driver);
You need to make sure that the path to the standard library and core library is passed. In my case I added
"-L $HOME/.multirust/toolchains/nightly/lib/rustlib/x86_64-unknown-linux-gnu/lib"
to the command line. You cannot simply add this to the args vector, because $HOME isn't parsed here. So you need some more code that extracts the $HOME env var and builds your command.

Resources