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

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.

Related

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.

Conditional compilation for Rust build.rs script?

The Rust language supports conditional compilation using attributes like #[cfg(test)].
Rust also supports build scripts using a build.rs file to run code as part of the build process to prepare for compilation.
I would like to use conditional compilation in Rust code to conditionally compile depending on whether we're compiling for a build script, similar to how that is possible for test builds.
Imagine the following:
#[cfg(build)]
fn main() {
// Part of build script
}
#[cfg(not(build))]
fn main() {
// Not part of build script, probably regular build
}
This does not work, because build is not a valid identifier here.
Is it possible to do this using a different attribute, or could some other trick be used to achieve something similar?
For some context on this issue:
My goal is to generate shell completion scripts through clap at compile time. I've quite a comprehensive App definition across multiple files in the application. I'd like to use this in build.rs by including these parts using the include!(...) macro (as suggested by clap), so I don't have to define App a second time. This pulls some dependencies with it, which I'd like to exclude when used by the build.rs file as they aren't needed in that case. This is what I'm trying to make available in my build.rs script.
You can just put the build code in build.rs (or presumably have build.rs declare a mod xyz to pull in another file).
I wonder if the question you are trying to ask is whether you can reference the same code from build.rs and main.rs, and if so can that code tell if it's being called by one or the other. It seems you could switch on an environment variable set when using build.rs (using something like option_env, but possibly a nicer way might be to enable a feature in the main code from within build.rs.
(Have a read of the documentation for build scripts if you haven't already.)
This is what works for me:
fn main() {
if std::env::var("PROFILE").unwrap() == "debug" {
// Here I do something that is needed for tests
// only, not for 'release'
}
}

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 can I include an arbitrary set of Protobuf-built files without knowing their names?

I'm planning on using the rust-protobuf library. I've written a bash script that builds everything (including my code) and builds the .proto files I have into .rs files. The way the documentation tells me to proceed is to specifically just do:
mod foo;
for each of the .rs files generated. I'm hoping that my users can just drop in new .proto files into a directory, run the build script, and my code will take care of including all of the Rust implementations of the compiled .proto files.
I know Rust doesn't really support reflection, so is there some way I can essentially "determine all the .rs generated files in a directory and use them in my code" (in a TLDR statement).
You could write a Cargo build script which would scan your directory and generate a Rust file that looks like:
mod file1;
mod file2;
//etc
You can then include this file in your library with the include! macro.

Multiple Rust source files for cargo

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.

Resources