How to specify the target_os in `Cargo.toml`? - rust

I'm trying to write a crate that only works on linux. I have done some searches but all answers are just talking about:
add #[cfg(target_os = xxx)] in the source code
add [target.'cfg(target_os = "linux")'.dependencies] in
Cargo.toml.
But in my case, the crate entirely won't work in other platform so I don't want to add #[cfg] in my code. If I do so, I need to add it to every piece of my code.
Is there a easy way to make my code only compile in specific platform?

You can conditionally include an invocation of compile_error:
#[cfg(not(target_os = "linux"))]
compile_error!("only linux is supported");
This only needs to appear in your code once; I'd suggest somewhere near the start of the root module.

Related

Can I rename lib.rs to the crate name?

Right now I'm working on a project with multiple separate crates.
I would prefer lib.rs to be renamed to the crate's name itself, as when I open multiple at the same time it takes me an extra second to find the one I'm looking for, not a big deal just curious if it's possible or a good idea.
ne_log/lib.rs into: ne_log/ne_log.rs
Yes, in each Cargo.toml add a lib section with the desired path:
[lib]
path = "src/some_other_file.rs"
Read more here: Cargo Targets
As to whether its a good idea or not; it will make your file structure non-standard, but the option is available since there are plenty of non-standard workflows. Use your own discretion.

Why duplicate function names in mod.rs as another .rs file?

For example, have module within /xyz/ sub-directory. Inside the directory are two files, mod.rs and network.rs say.
Why do mod.rs and network.rs have the same function names, but different code within the functions? Is there any reason for this? I thought mod.rs was just basically a defintions file to declare a module, and specify which other .rs files within the sub-directory should be treated as their own creates / modules.
Any help?
It sounds like you are referring to a design decision made by a specific crate. You are correct in assuming there is no special consideration given by the compiler to function/type/ident names in separate files/modules.
That being said it seems likely that what you are referring to might be using conditional compilation. Conditional compilation lets the compiler decide if a given piece of code is compiled or not. You will usually see this used to handle which implementation of a function is used when compiling code on different operating systems since it is often it too inefficient or simply impossible to check at runtime. Some library authors might also decide to add an implementation that it can fallback to instead of throwing a hard error.
Here is a quick example of why xyz might want to have 3 different implementations of foobar.
// xyz/mod.rs
mod windows;
mod unix;
// If this crate is compiled on windows re-export the contents of windows.rs
#[cfg(windows)]
pub use windows::*;
// If this crate is compiled on unix/linux re-export the contents of unix.rs
#[cfg(unix)]
pub use unix::*;
// If not on either windows or unix provide a default implementation to use instead
#[cfg(not(any(windows, unix)))]
pub fn foobar() -> i32 {
panic!("This function is unsupported on the current os")
}

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

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